Skip to content

Commit 37cb28e

Browse files
piotr-krysiukborkmann
authored andcommitted
bpf, mips: Validate conditional branch offsets
The conditional branch instructions on MIPS use 18-bit signed offsets allowing for a branch range of 128 KBytes (backward and forward). However, this limit is not observed by the cBPF JIT compiler, and so the JIT compiler emits out-of-range branches when translating certain cBPF programs. A specific example of such a cBPF program is included in the "BPF_MAXINSNS: exec all MSH" test from lib/test_bpf.c that executes anomalous machine code containing incorrect branch offsets under JIT. Furthermore, this issue can be abused to craft undesirable machine code, where the control flow is hijacked to execute arbitrary Kernel code. The following steps can be used to reproduce the issue: # echo 1 > /proc/sys/net/core/bpf_jit_enable # modprobe test_bpf test_name="BPF_MAXINSNS: exec all MSH" This should produce multiple warnings from build_bimm() similar to: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 209 at arch/mips/mm/uasm-mips.c:210 build_insn+0x558/0x590 Micro-assembler field overflow Modules linked in: test_bpf(+) CPU: 0 PID: 209 Comm: modprobe Not tainted 5.14.3 #1 Stack : 00000000 807bb824 82b33c9c 801843c 00000000 00000004 00000000 63c9b5ee 82b33af4 80999898 80910000 80900000 82fd6030 00000001 82b33a98 82087180 00000000 00000000 80873b28 00000000 000000fc 82b3394c 00000000 2e34312e 6d6d6f43 809a180f 809a1836 6f6d203a 80900000 00000001 82b33bac 80900000 00027f80 00000000 00000000 807bb824 00000000 804ed790 001cc317 00000001 [...] Call Trace: [<80108f44>] show_stack+0x38/0x118 [<807a7aac>] dump_stack_lvl+0x5c/0x7c [<807a4b3c>] __warn+0xcc/0x140 [<807a4c3c>] warn_slowpath_fmt+0x8c/0xb8 [<8011e198>] build_insn+0x558/0x590 [<8011e358>] uasm_i_bne+0x20/0x2c [<80127b48>] build_body+0xa58/0x2a94 [<80129c98>] bpf_jit_compile+0x114/0x1e4 [<80613fc4>] bpf_prepare_filter+0x2ec/0x4e4 [<8061423c>] bpf_prog_create+0x80/0xc4 [<c0a006e4>] test_bpf_init+0x300/0xba8 [test_bpf] [<8010051c>] do_one_initcall+0x50/0x1d4 [<801c5e54>] do_init_module+0x60/0x220 [<801c8b20>] sys_finit_module+0xc4/0xfc [<801144d0>] syscall_common+0x34/0x58 [...] ---[ end trace a287d9742503c645 ]--- Then the anomalous machine code executes: => 0xc0a18000: addiu sp,sp,-16 0xc0a18004: sw s3,0(sp) 0xc0a18008: sw s4,4(sp) 0xc0a1800c: sw s5,8(sp) 0xc0a18010: sw ra,12(sp) 0xc0a18014: move s5,a0 0xc0a18018: move s4,zero 0xc0a1801c: move s3,zero # __BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0) 0xc0a18020: lui t6,0x8012 0xc0a18024: ori t4,t6,0x9e14 0xc0a18028: li a1,0 0xc0a1802c: jalr t4 0xc0a18030: move a0,s5 0xc0a18034: bnez v0,0xc0a1ffb8 # incorrect branch offset 0xc0a18038: move v0,zero 0xc0a1803c: andi s4,s3,0xf 0xc0a18040: b 0xc0a18048 0xc0a18044: sll s4,s4,0x2 [...] # __BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0) 0xc0a1ffa0: lui t6,0x8012 0xc0a1ffa4: ori t4,t6,0x9e14 0xc0a1ffa8: li a1,0 0xc0a1ffac: jalr t4 0xc0a1ffb0: move a0,s5 0xc0a1ffb4: bnez v0,0xc0a1ffb8 # incorrect branch offset 0xc0a1ffb8: move v0,zero 0xc0a1ffbc: andi s4,s3,0xf 0xc0a1ffc0: b 0xc0a1ffc8 0xc0a1ffc4: sll s4,s4,0x2 # __BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0) 0xc0a1ffc8: lui t6,0x8012 0xc0a1ffcc: ori t4,t6,0x9e14 0xc0a1ffd0: li a1,0 0xc0a1ffd4: jalr t4 0xc0a1ffd8: move a0,s5 0xc0a1ffdc: bnez v0,0xc0a3ffb8 # correct branch offset 0xc0a1ffe0: move v0,zero 0xc0a1ffe4: andi s4,s3,0xf 0xc0a1ffe8: b 0xc0a1fff0 0xc0a1ffec: sll s4,s4,0x2 [...] # epilogue 0xc0a3ffb8: lw s3,0(sp) 0xc0a3ffbc: lw s4,4(sp) 0xc0a3ffc0: lw s5,8(sp) 0xc0a3ffc4: lw ra,12(sp) 0xc0a3ffc8: addiu sp,sp,16 0xc0a3ffcc: jr ra 0xc0a3ffd0: nop To mitigate this issue, we assert the branch ranges for each emit call that could generate an out-of-range branch. Fixes: 36366e3 ("MIPS: BPF: Restore MIPS32 cBPF JIT") Fixes: c6610de ("MIPS: net: Add BPF JIT") Signed-off-by: Piotr Krysiuk <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Tested-by: Johan Almbladh <[email protected]> Acked-by: Johan Almbladh <[email protected]> Cc: Paul Burton <[email protected]> Cc: Thomas Bogendoerfer <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 356ed64 commit 37cb28e

File tree

1 file changed

+43
-14
lines changed

1 file changed

+43
-14
lines changed

arch/mips/net/bpf_jit.c

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,11 @@ static void build_epilogue(struct jit_ctx *ctx)
662662
((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative : func) : \
663663
func##_positive)
664664

665+
static bool is_bad_offset(int b_off)
666+
{
667+
return b_off > 0x1ffff || b_off < -0x20000;
668+
}
669+
665670
static int build_body(struct jit_ctx *ctx)
666671
{
667672
const struct bpf_prog *prog = ctx->skf;
@@ -728,7 +733,10 @@ static int build_body(struct jit_ctx *ctx)
728733
/* Load return register on DS for failures */
729734
emit_reg_move(r_ret, r_zero, ctx);
730735
/* Return with error */
731-
emit_b(b_imm(prog->len, ctx), ctx);
736+
b_off = b_imm(prog->len, ctx);
737+
if (is_bad_offset(b_off))
738+
return -E2BIG;
739+
emit_b(b_off, ctx);
732740
emit_nop(ctx);
733741
break;
734742
case BPF_LD | BPF_W | BPF_IND:
@@ -775,8 +783,10 @@ static int build_body(struct jit_ctx *ctx)
775783
emit_jalr(MIPS_R_RA, r_s0, ctx);
776784
emit_reg_move(MIPS_R_A0, r_skb, ctx); /* delay slot */
777785
/* Check the error value */
778-
emit_bcond(MIPS_COND_NE, r_ret, 0,
779-
b_imm(prog->len, ctx), ctx);
786+
b_off = b_imm(prog->len, ctx);
787+
if (is_bad_offset(b_off))
788+
return -E2BIG;
789+
emit_bcond(MIPS_COND_NE, r_ret, 0, b_off, ctx);
780790
emit_reg_move(r_ret, r_zero, ctx);
781791
/* We are good */
782792
/* X <- P[1:K] & 0xf */
@@ -855,17 +865,21 @@ static int build_body(struct jit_ctx *ctx)
855865
/* A /= X */
856866
ctx->flags |= SEEN_X | SEEN_A;
857867
/* Check if r_X is zero */
858-
emit_bcond(MIPS_COND_EQ, r_X, r_zero,
859-
b_imm(prog->len, ctx), ctx);
868+
b_off = b_imm(prog->len, ctx);
869+
if (is_bad_offset(b_off))
870+
return -E2BIG;
871+
emit_bcond(MIPS_COND_EQ, r_X, r_zero, b_off, ctx);
860872
emit_load_imm(r_ret, 0, ctx); /* delay slot */
861873
emit_div(r_A, r_X, ctx);
862874
break;
863875
case BPF_ALU | BPF_MOD | BPF_X:
864876
/* A %= X */
865877
ctx->flags |= SEEN_X | SEEN_A;
866878
/* Check if r_X is zero */
867-
emit_bcond(MIPS_COND_EQ, r_X, r_zero,
868-
b_imm(prog->len, ctx), ctx);
879+
b_off = b_imm(prog->len, ctx);
880+
if (is_bad_offset(b_off))
881+
return -E2BIG;
882+
emit_bcond(MIPS_COND_EQ, r_X, r_zero, b_off, ctx);
869883
emit_load_imm(r_ret, 0, ctx); /* delay slot */
870884
emit_mod(r_A, r_X, ctx);
871885
break;
@@ -926,7 +940,10 @@ static int build_body(struct jit_ctx *ctx)
926940
break;
927941
case BPF_JMP | BPF_JA:
928942
/* pc += K */
929-
emit_b(b_imm(i + k + 1, ctx), ctx);
943+
b_off = b_imm(i + k + 1, ctx);
944+
if (is_bad_offset(b_off))
945+
return -E2BIG;
946+
emit_b(b_off, ctx);
930947
emit_nop(ctx);
931948
break;
932949
case BPF_JMP | BPF_JEQ | BPF_K:
@@ -1056,12 +1073,16 @@ static int build_body(struct jit_ctx *ctx)
10561073
break;
10571074
case BPF_RET | BPF_A:
10581075
ctx->flags |= SEEN_A;
1059-
if (i != prog->len - 1)
1076+
if (i != prog->len - 1) {
10601077
/*
10611078
* If this is not the last instruction
10621079
* then jump to the epilogue
10631080
*/
1064-
emit_b(b_imm(prog->len, ctx), ctx);
1081+
b_off = b_imm(prog->len, ctx);
1082+
if (is_bad_offset(b_off))
1083+
return -E2BIG;
1084+
emit_b(b_off, ctx);
1085+
}
10651086
emit_reg_move(r_ret, r_A, ctx); /* delay slot */
10661087
break;
10671088
case BPF_RET | BPF_K:
@@ -1075,7 +1096,10 @@ static int build_body(struct jit_ctx *ctx)
10751096
* If this is not the last instruction
10761097
* then jump to the epilogue
10771098
*/
1078-
emit_b(b_imm(prog->len, ctx), ctx);
1099+
b_off = b_imm(prog->len, ctx);
1100+
if (is_bad_offset(b_off))
1101+
return -E2BIG;
1102+
emit_b(b_off, ctx);
10791103
emit_nop(ctx);
10801104
}
10811105
break;
@@ -1133,8 +1157,10 @@ static int build_body(struct jit_ctx *ctx)
11331157
/* Load *dev pointer */
11341158
emit_load_ptr(r_s0, r_skb, off, ctx);
11351159
/* error (0) in the delay slot */
1136-
emit_bcond(MIPS_COND_EQ, r_s0, r_zero,
1137-
b_imm(prog->len, ctx), ctx);
1160+
b_off = b_imm(prog->len, ctx);
1161+
if (is_bad_offset(b_off))
1162+
return -E2BIG;
1163+
emit_bcond(MIPS_COND_EQ, r_s0, r_zero, b_off, ctx);
11381164
emit_reg_move(r_ret, r_zero, ctx);
11391165
if (code == (BPF_ANC | SKF_AD_IFINDEX)) {
11401166
BUILD_BUG_ON(sizeof_field(struct net_device, ifindex) != 4);
@@ -1244,7 +1270,10 @@ void bpf_jit_compile(struct bpf_prog *fp)
12441270

12451271
/* Generate the actual JIT code */
12461272
build_prologue(&ctx);
1247-
build_body(&ctx);
1273+
if (build_body(&ctx)) {
1274+
module_memfree(ctx.target);
1275+
goto out;
1276+
}
12481277
build_epilogue(&ctx);
12491278

12501279
/* Update the icache */

0 commit comments

Comments
 (0)