]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpf, arm64: Fix off-by-one in check_imm signed range check
authorDaniel Borkmann <daniel@iogearbox.net>
Wed, 15 Apr 2026 12:14:03 +0000 (14:14 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 15 Apr 2026 19:08:03 +0000 (12:08 -0700)
check_imm(bits, imm) is used in the arm64 BPF JIT to verify that
a branch displacement (in arm64 instruction units) fits into the
signed N-bit immediate field of a B, B.cond or CBZ/CBNZ encoding
before it is handed to the encoder. The macro currently tests for
(imm > 0 && imm >> bits) || (imm < 0 && ~imm >> bits) which admits
values in [-2^N, 2^N) — effectively a signed (N+1)-bit range. A
signed N-bit field only holds [-2^(N-1), 2^(N-1)), so the check
admits one extra bit of range on each side.

In particular, for check_imm19(), values in [2^18, 2^19) slip past
the check but do not fit into the 19-bit signed imm19 field of
B.cond. aarch64_insn_encode_immediate() then masks the raw value
into the 19-bit field, setting bit 18 (the sign bit) and flipping
a forward branch into a backward one. Same class of issue exists
for check_imm26() and the B/BL encoding. Shift by (bits - 1)
instead of bits so the actual signed N-bit range is enforced.

Fixes: e54bcde3d69d ("arm64: eBPF JIT compiler")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Puranjay Mohan <puranjay@kernel.org>
Link: https://lore.kernel.org/r/20260415121403.639619-2-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
arch/arm64/net/bpf_jit_comp.c

index adf84962d579d0672f35c3bb60a873669f45be48..4aad9483f8a501cfecf7a089fada02de04fde189 100644 (file)
@@ -35,8 +35,8 @@
 #define ARENA_VM_START (MAX_BPF_JIT_REG + 5)
 
 #define check_imm(bits, imm) do {                              \
-       if ((((imm) > 0) && ((imm) >> (bits))) ||               \
-           (((imm) < 0) && (~(imm) >> (bits)))) {              \
+       if ((((imm) > 0) && ((imm) >> ((bits) - 1))) ||         \
+           (((imm) < 0) && (~(imm) >> ((bits) - 1)))) {        \
                pr_info("[%2d] imm=%d(0x%x) out of range\n",    \
                        i, imm, imm);                           \
                return -EINVAL;                                 \