Skip to content

Commit 9aa8fe2

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-fix-oob-read-and-add-tests-for-load-acquire-store-release'
Kohei Enju says: ==================== bpf: Fix OOB read and add tests for load-acquire/store-release This patch series addresses an out-of-bounds read issue in check_atomic_load/store() reported by syzkaller when an invalid register number (MAX_BPF_REG or greater) is used. The first patch fixes the actual bug by changing the order of validity checks, ensuring register validity is checked before atomic_ptr_type_ok() is called. It also updates some tests that were assuming the previous order of checks. The second patch adds new tests specifically for the invalid register number case to prevent regression in the future. Changes: v3: - Change invalid register from R11 to R15 in new tests v2: https://lore.kernel.org/all/20250321110010.95217-4-enjuk@amazon.com/ - Just swap atomic_ptr_type_ok() and check_load_mem()/check_store_reg() - Update some tests that were assuming the previous order of checks - Add new tests specifically for the invalid register number v1: https://lore.kernel.org/bpf/20250314195619.23772-2-enjuk@amazon.com/ Reported-by: syzbot+a5964227adc0f904549c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=a5964227adc0f904549c ==================== Link: https://patch.msgid.link/20250322045340.18010-4-enjuk@amazon.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 307ef66 + 5f3077d commit 9aa8fe2

File tree

3 files changed

+63
-7
lines changed

3 files changed

+63
-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: 24 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,28 @@ __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)))
189+
: __clobber_all);
190+
}
191+
192+
SEC("socket")
193+
__description("load-acquire with invalid register R15")
194+
__failure __failure_unpriv __msg("R15 is invalid")
195+
__naked void load_acquire_with_invalid_reg(void)
196+
{
197+
asm volatile (
198+
".8byte %[load_acquire_insn];" // r0 = load_acquire((u64 *)(r15 + 0));
199+
"exit;"
200+
:
201+
: __imm_insn(load_acquire_insn,
202+
BPF_ATOMIC_OP(BPF_DW, BPF_LOAD_ACQ, BPF_REG_0, 15 /* invalid reg */, 0))
181203
: __clobber_all);
182204
}
183205

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

Lines changed: 25 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 (
@@ -249,6 +257,20 @@ __naked void store_release_leak_pointer_to_map(void)
249257
: __clobber_all);
250258
}
251259

260+
SEC("socket")
261+
__description("store-release with invalid register R15")
262+
__failure __failure_unpriv __msg("R15 is invalid")
263+
__naked void store_release_with_invalid_reg(void)
264+
{
265+
asm volatile (
266+
".8byte %[store_release_insn];" // store_release((u64 *)(r15 + 0), r1);
267+
"exit;"
268+
:
269+
: __imm_insn(store_release_insn,
270+
BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, 15 /* invalid reg */, BPF_REG_1, 0))
271+
: __clobber_all);
272+
}
273+
252274
#else
253275

254276
SEC("socket")

0 commit comments

Comments
 (0)