Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Avoid verifier failure for 32bit pointer arithmetic

When do experiments with llvm (disabling instcombine and
simplifyCFG), I hit the following error with test_seg6_loop.o.

; R1=pkt(id=0,off=0,r=48,imm=0), R7=pkt(id=0,off=40,r=48,imm=0)
w2 = w7
; R2_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff))
w2 -= w1
R2 32-bit pointer arithmetic prohibited

The corresponding source code is:
uint32_t srh_off
// srh and skb->data are all packet pointers
srh_off = (char *)srh - (char *)(long)skb->data;

The verifier does not support 32-bit pointer/scalar arithmetic.

Without my llvm change, the code looks like

; R3=pkt(id=0,off=40,r=48,imm=0), R8=pkt(id=0,off=0,r=48,imm=0)
w3 -= w8
; R3_w=inv(id=0)

This is explicitly allowed in verifier if both registers are
pointers and the opcode is BPF_SUB.

To fix this problem, I changed the verifier to allow
32-bit pointer/scaler BPF_SUB operations.

At the source level, the issue could be workarounded with
inline asm or changing "uint32_t srh_off" to "uint64_t srh_off".
But I feel that verifier change might be the right thing to do.

Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Link: https://lore.kernel.org/bpf/20200618234631.3321118-1-yhs@fb.com

authored by

Yonghong Song and committed by
Daniel Borkmann
6c693541 7c7982cb

+5
+5
kernel/bpf/verifier.c
··· 5031 5031 5032 5032 if (BPF_CLASS(insn->code) != BPF_ALU64) { 5033 5033 /* 32-bit ALU ops on pointers produce (meaningless) scalars */ 5034 + if (opcode == BPF_SUB && env->allow_ptr_leaks) { 5035 + __mark_reg_unknown(env, dst_reg); 5036 + return 0; 5037 + } 5038 + 5034 5039 verbose(env, 5035 5040 "R%d 32-bit pointer arithmetic prohibited\n", 5036 5041 dst);