Skip to content

Commit 79e3445

Browse files
Johan Almbladhborkmann
authored andcommitted
bpf, arm: Fix register clobbering in div/mod implementation
On ARM CPUs that lack div/mod instructions, ALU32 BPF_DIV and BPF_MOD are implemented using a call to a helper function. Before, the emitted code for those function calls failed to preserve caller-saved ARM registers. Since some of those registers happen to be mapped to BPF registers, it resulted in eBPF register values being overwritten. This patch emits code to push and pop the remaining caller-saved ARM registers r2-r3 into the stack during the div/mod function call. ARM registers r0-r1 are used as arguments and return value, and those were already saved and restored correctly. Fixes: 39c13c2 ("arm: eBPF JIT compiler") Signed-off-by: Johan Almbladh <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent d75fe9c commit 79e3445

File tree

1 file changed

+19
-0
lines changed

1 file changed

+19
-0
lines changed

arch/arm/net/bpf_jit_32.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
* +-----+
3737
* |RSVD | JIT scratchpad
3838
* current ARM_SP => +-----+ <= (BPF_FP - STACK_SIZE + SCRATCH_SIZE)
39+
* | ... | caller-saved registers
40+
* +-----+
41+
* | ... | arguments passed on stack
42+
* ARM_SP during call => +-----|
3943
* | |
4044
* | ... | Function call stack
4145
* | |
@@ -63,13 +67,21 @@
6367
*
6468
* When popping registers off the stack at the end of a BPF function, we
6569
* reference them via the current ARM_FP register.
70+
*
71+
* Some eBPF operations are implemented via a call to a helper function.
72+
* Such calls are "invisible" in the eBPF code, so it is up to the calling
73+
* program to preserve any caller-saved ARM registers during the call. The
74+
* JIT emits code to push and pop those registers onto the stack, immediately
75+
* above the callee stack frame.
6676
*/
6777
#define CALLEE_MASK (1 << ARM_R4 | 1 << ARM_R5 | 1 << ARM_R6 | \
6878
1 << ARM_R7 | 1 << ARM_R8 | 1 << ARM_R9 | \
6979
1 << ARM_FP)
7080
#define CALLEE_PUSH_MASK (CALLEE_MASK | 1 << ARM_LR)
7181
#define CALLEE_POP_MASK (CALLEE_MASK | 1 << ARM_PC)
7282

83+
#define CALLER_MASK (1 << ARM_R0 | 1 << ARM_R1 | 1 << ARM_R2 | 1 << ARM_R3)
84+
7385
enum {
7486
/* Stack layout - these are offsets from (top of stack - 4) */
7587
BPF_R2_HI,
@@ -464,6 +476,7 @@ static inline int epilogue_offset(const struct jit_ctx *ctx)
464476

465477
static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, u8 op)
466478
{
479+
const int exclude_mask = BIT(ARM_R0) | BIT(ARM_R1);
467480
const s8 *tmp = bpf2a32[TMP_REG_1];
468481

469482
#if __LINUX_ARM_ARCH__ == 7
@@ -495,11 +508,17 @@ static inline void emit_udivmod(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx, u8 op)
495508
emit(ARM_MOV_R(ARM_R0, rm), ctx);
496509
}
497510

511+
/* Push caller-saved registers on stack */
512+
emit(ARM_PUSH(CALLER_MASK & ~exclude_mask), ctx);
513+
498514
/* Call appropriate function */
499515
emit_mov_i(ARM_IP, op == BPF_DIV ?
500516
(u32)jit_udiv32 : (u32)jit_mod32, ctx);
501517
emit_blx_r(ARM_IP, ctx);
502518

519+
/* Restore caller-saved registers from stack */
520+
emit(ARM_POP(CALLER_MASK & ~exclude_mask), ctx);
521+
503522
/* Save return value */
504523
if (rd != ARM_R0)
505524
emit(ARM_MOV_R(rd, ARM_R0), ctx);

0 commit comments

Comments
 (0)