Skip to content

Commit c03bb2f

Browse files
Kohei EnjuAlexei Starovoitov
authored andcommitted
bpf: Fix out-of-bounds read in check_atomic_load/store()
syzbot reported the following splat [0]. In check_atomic_load/store(), register validity is not checked before atomic_ptr_type_ok(). This causes the out-of-bounds read in is_ctx_reg() called from atomic_ptr_type_ok() when the register number is MAX_BPF_REG or greater. Call check_load_mem()/check_store_reg() before atomic_ptr_type_ok() to avoid the OOB read. However, some tests introduced by commit ff3afe5 ("selftests/bpf: Add selftests for load-acquire and store-release instructions") assume calling atomic_ptr_type_ok() before checking register validity. Therefore the swapping of order unintentionally changes verifier messages of these tests. For example in the test load_acquire_from_pkt_pointer(), expected message is 'BPF_ATOMIC loads from R2 pkt is not allowed' although actual messages are different. validate_msgs:FAIL:754 expect_msg VERIFIER LOG: ============= Global function load_acquire_from_pkt_pointer() doesn't return scalar. Only those are supported. 0: R1=ctx() R10=fp0 ; asm volatile ( @ verifier_load_acquire.c:140 0: (61) r2 = *(u32 *)(r1 +0) ; R1=ctx() R2_w=pkt(r=0) 1: (d3) r0 = load_acquire((u8 *)(r2 +0)) invalid access to packet, off=0 size=1, R2(id=0,off=0,r=0) R2 offset is outside of the packet processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 ============= EXPECTED SUBSTR: 'BPF_ATOMIC loads from R2 pkt is not allowed' #505/19 verifier_load_acquire/load-acquire from pkt pointer:FAIL This is because instructions in the test don't pass check_load_mem() and therefore don't enter the atomic_ptr_type_ok() path. In this case, we have to modify instructions so that they pass the check_load_mem() and trigger atomic_ptr_type_ok(). Similarly for store-release tests, we need to modify instructions so that they pass check_store_reg(). Like load_acquire_from_pkt_pointer(), modify instructions in: load_acquire_from_sock_pointer() store_release_to_ctx_pointer() store_release_to_pkt_pointer() Also in store_release_to_sock_pointer(), check_store_reg() returns error early and atomic_ptr_type_ok() is not triggered, since write to sock pointer is not possible in general. We might be able to remove the test, but for now let's leave it and just change the expected message. [0] BUG: KASAN: slab-out-of-bounds in is_ctx_reg kernel/bpf/verifier.c:6185 [inline] BUG: KASAN: slab-out-of-bounds in atomic_ptr_type_ok+0x3d7/0x550 kernel/bpf/verifier.c:6223 Read of size 4 at addr ffff888141b0d690 by task syz-executor143/5842 CPU: 1 UID: 0 PID: 5842 Comm: syz-executor143 Not tainted 6.14.0-rc3-syzkaller-gf28214603dc6 #0 Call Trace: <TASK> __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x241/0x360 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:408 [inline] print_report+0x16e/0x5b0 mm/kasan/report.c:521 kasan_report+0x143/0x180 mm/kasan/report.c:634 is_ctx_reg kernel/bpf/verifier.c:6185 [inline] atomic_ptr_type_ok+0x3d7/0x550 kernel/bpf/verifier.c:6223 check_atomic_store kernel/bpf/verifier.c:7804 [inline] check_atomic kernel/bpf/verifier.c:7841 [inline] do_check+0x89dd/0xedd0 kernel/bpf/verifier.c:19334 do_check_common+0x1678/0x2080 kernel/bpf/verifier.c:22600 do_check_main kernel/bpf/verifier.c:22691 [inline] bpf_check+0x165c8/0x1cca0 kernel/bpf/verifier.c:23821 Reported-by: syzbot+a5964227adc0f904549c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=a5964227adc0f904549c Tested-by: syzbot+a5964227adc0f904549c@syzkaller.appspotmail.com Fixes: e24bbad ("bpf: Introduce load-acquire and store-release instructions") Fixes: ff3afe5 ("selftests/bpf: Add selftests for load-acquire and store-release instructions") Signed-off-by: Kohei Enju <enjuk@amazon.com> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20250322045340.18010-5-enjuk@amazon.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 307ef66 commit c03bb2f

File tree

3 files changed

+35
-7
lines changed

3 files changed

+35
-7
lines changed

kernel/bpf/verifier.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7788,27 +7788,39 @@ static int check_atomic_rmw(struct bpf_verifier_env *env,
77887788
static int check_atomic_load(struct bpf_verifier_env *env,
77897789
struct bpf_insn *insn)
77907790
{
7791+
int err;
7792+
7793+
err = check_load_mem(env, insn, true, false, false, "atomic_load");
7794+
if (err)
7795+
return err;
7796+
77917797
if (!atomic_ptr_type_ok(env, insn->src_reg, insn)) {
77927798
verbose(env, "BPF_ATOMIC loads from R%d %s is not allowed\n",
77937799
insn->src_reg,
77947800
reg_type_str(env, reg_state(env, insn->src_reg)->type));
77957801
return -EACCES;
77967802
}
77977803

7798-
return check_load_mem(env, insn, true, false, false, "atomic_load");
7804+
return 0;
77997805
}
78007806

78017807
static int check_atomic_store(struct bpf_verifier_env *env,
78027808
struct bpf_insn *insn)
78037809
{
7810+
int err;
7811+
7812+
err = check_store_reg(env, insn, true);
7813+
if (err)
7814+
return err;
7815+
78047816
if (!atomic_ptr_type_ok(env, insn->dst_reg, insn)) {
78057817
verbose(env, "BPF_ATOMIC stores into R%d %s is not allowed\n",
78067818
insn->dst_reg,
78077819
reg_type_str(env, reg_state(env, insn->dst_reg)->type));
78087820
return -EACCES;
78097821
}
78107822

7811-
return check_store_reg(env, insn, true);
7823+
return 0;
78127824
}
78137825

78147826
static int check_atomic(struct bpf_verifier_env *env, struct bpf_insn *insn)

tools/testing/selftests/bpf/progs/verifier_load_acquire.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,16 @@ __naked void load_acquire_from_pkt_pointer(void)
139139
{
140140
asm volatile (
141141
"r2 = *(u32 *)(r1 + %[xdp_md_data]);"
142+
"r3 = *(u32 *)(r1 + %[xdp_md_data_end]);"
143+
"r1 = r2;"
144+
"r1 += 8;"
145+
"if r1 >= r3 goto l0_%=;"
142146
".8byte %[load_acquire_insn];" // w0 = load_acquire((u8 *)(r2 + 0));
147+
"l0_%=: r0 = 0;"
143148
"exit;"
144149
:
145150
: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
151+
__imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)),
146152
__imm_insn(load_acquire_insn,
147153
BPF_ATOMIC_OP(BPF_B, BPF_LOAD_ACQ, BPF_REG_0, BPF_REG_2, 0))
148154
: __clobber_all);
@@ -172,12 +178,14 @@ __naked void load_acquire_from_sock_pointer(void)
172178
{
173179
asm volatile (
174180
"r2 = *(u64 *)(r1 + %[sk_reuseport_md_sk]);"
175-
".8byte %[load_acquire_insn];" // w0 = load_acquire((u8 *)(r2 + 0));
181+
// w0 = load_acquire((u8 *)(r2 + offsetof(struct bpf_sock, family)));
182+
".8byte %[load_acquire_insn];"
176183
"exit;"
177184
:
178185
: __imm_const(sk_reuseport_md_sk, offsetof(struct sk_reuseport_md, sk)),
179186
__imm_insn(load_acquire_insn,
180-
BPF_ATOMIC_OP(BPF_B, BPF_LOAD_ACQ, BPF_REG_0, BPF_REG_2, 0))
187+
BPF_ATOMIC_OP(BPF_B, BPF_LOAD_ACQ, BPF_REG_0, BPF_REG_2,
188+
offsetof(struct bpf_sock, family)))
181189
: __clobber_all);
182190
}
183191

tools/testing/selftests/bpf/progs/verifier_store_release.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,13 @@ __naked void store_release_to_ctx_pointer(void)
140140
{
141141
asm volatile (
142142
"w0 = 0;"
143-
".8byte %[store_release_insn];" // store_release((u8 *)(r1 + 0), w0);
143+
// store_release((u8 *)(r1 + offsetof(struct __sk_buff, cb[0])), w0);
144+
".8byte %[store_release_insn];"
144145
"exit;"
145146
:
146147
: __imm_insn(store_release_insn,
147-
BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_1, BPF_REG_0, 0))
148+
BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_1, BPF_REG_0,
149+
offsetof(struct __sk_buff, cb[0])))
148150
: __clobber_all);
149151
}
150152

@@ -156,10 +158,16 @@ __naked void store_release_to_pkt_pointer(void)
156158
asm volatile (
157159
"w0 = 0;"
158160
"r2 = *(u32 *)(r1 + %[xdp_md_data]);"
161+
"r3 = *(u32 *)(r1 + %[xdp_md_data_end]);"
162+
"r1 = r2;"
163+
"r1 += 8;"
164+
"if r1 >= r3 goto l0_%=;"
159165
".8byte %[store_release_insn];" // store_release((u8 *)(r2 + 0), w0);
166+
"l0_%=: r0 = 0;"
160167
"exit;"
161168
:
162169
: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
170+
__imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)),
163171
__imm_insn(store_release_insn,
164172
BPF_ATOMIC_OP(BPF_B, BPF_STORE_REL, BPF_REG_2, BPF_REG_0, 0))
165173
: __clobber_all);
@@ -185,7 +193,7 @@ __naked void store_release_to_flow_keys_pointer(void)
185193

186194
SEC("sk_reuseport")
187195
__description("store-release to sock pointer")
188-
__failure __msg("BPF_ATOMIC stores into R2 sock is not allowed")
196+
__failure __msg("R2 cannot write into sock")
189197
__naked void store_release_to_sock_pointer(void)
190198
{
191199
asm volatile (

0 commit comments

Comments
 (0)