Skip to content

Commit 4fc5df8

Browse files
bpf: verifier: Refactor helper access type tracking
JIRA: https://issues.redhat.com/browse/RHEL-93930 commit 37cce22 Author: Daniel Xu <dxu@dxuuu.xyz> Date: Tue Jan 14 13:28:44 2025 -0700 bpf: verifier: Refactor helper access type tracking Previously, the verifier was treating all PTR_TO_STACK registers passed to a helper call as potentially written to by the helper. However, all calls to check_stack_range_initialized() already have precise access type information available. Rather than treat ACCESS_HELPER as a proxy for BPF_WRITE, pass enum bpf_access_type to check_stack_range_initialized() to more precisely track helper arguments. One benefit from this precision is that registers tracked as valid spills and passed as a read-only helper argument remain tracked after the call. Rather than being marked STACK_MISC afterwards. An additional benefit is the verifier logs are also more precise. For this particular error, users will enjoy a slightly clearer message. See included selftest updates for examples. Acked-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Daniel Xu <dxu@dxuuu.xyz> Link: https://lore.kernel.org/r/ff885c0e5859e0cd12077c3148ff0754cad4f7ed.1736886479.git.dxu@dxuuu.xyz Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
1 parent faf1b9e commit 4fc5df8

File tree

13 files changed

+42
-54
lines changed

13 files changed

+42
-54
lines changed

kernel/bpf/verifier.c

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5128,7 +5128,7 @@ enum bpf_access_src {
51285128
static int check_stack_range_initialized(struct bpf_verifier_env *env,
51295129
int regno, int off, int access_size,
51305130
bool zero_size_allowed,
5131-
enum bpf_access_src type,
5131+
enum bpf_access_type type,
51325132
struct bpf_call_arg_meta *meta);
51335133

51345134
static struct bpf_reg_state *reg_state(struct bpf_verifier_env *env, int regno)
@@ -5161,7 +5161,7 @@ static int check_stack_read_var_off(struct bpf_verifier_env *env,
51615161
/* Note that we pass a NULL meta, so raw access will not be permitted.
51625162
*/
51635163
err = check_stack_range_initialized(env, ptr_regno, off, size,
5164-
false, ACCESS_DIRECT, NULL);
5164+
false, BPF_READ, NULL);
51655165
if (err)
51665166
return err;
51675167

@@ -7011,7 +7011,7 @@ static int check_stack_slot_within_bounds(struct bpf_verifier_env *env,
70117011
static int check_stack_access_within_bounds(
70127012
struct bpf_verifier_env *env,
70137013
int regno, int off, int access_size,
7014-
enum bpf_access_src src, enum bpf_access_type type)
7014+
enum bpf_access_type type)
70157015
{
70167016
struct bpf_reg_state *regs = cur_regs(env);
70177017
struct bpf_reg_state *reg = regs + regno;
@@ -7020,10 +7020,7 @@ static int check_stack_access_within_bounds(
70207020
int err;
70217021
char *err_extra;
70227022

7023-
if (src == ACCESS_HELPER)
7024-
/* We don't know if helpers are reading or writing (or both). */
7025-
err_extra = " indirect access to";
7026-
else if (type == BPF_READ)
7023+
if (type == BPF_READ)
70277024
err_extra = " read from";
70287025
else
70297026
err_extra = " write to";
@@ -7241,7 +7238,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
72417238

72427239
} else if (reg->type == PTR_TO_STACK) {
72437240
/* Basic bounds checks. */
7244-
err = check_stack_access_within_bounds(env, regno, off, size, ACCESS_DIRECT, t);
7241+
err = check_stack_access_within_bounds(env, regno, off, size, t);
72457242
if (err)
72467243
return err;
72477244

@@ -7461,13 +7458,11 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
74617458
static int check_stack_range_initialized(
74627459
struct bpf_verifier_env *env, int regno, int off,
74637460
int access_size, bool zero_size_allowed,
7464-
enum bpf_access_src type, struct bpf_call_arg_meta *meta)
7461+
enum bpf_access_type type, struct bpf_call_arg_meta *meta)
74657462
{
74667463
struct bpf_reg_state *reg = reg_state(env, regno);
74677464
struct bpf_func_state *state = func(env, reg);
74687465
int err, min_off, max_off, i, j, slot, spi;
7469-
char *err_extra = type == ACCESS_HELPER ? " indirect" : "";
7470-
enum bpf_access_type bounds_check_type;
74717466
/* Some accesses can write anything into the stack, others are
74727467
* read-only.
74737468
*/
@@ -7478,18 +7473,10 @@ static int check_stack_range_initialized(
74787473
return -EACCES;
74797474
}
74807475

7481-
if (type == ACCESS_HELPER) {
7482-
/* The bounds checks for writes are more permissive than for
7483-
* reads. However, if raw_mode is not set, we'll do extra
7484-
* checks below.
7485-
*/
7486-
bounds_check_type = BPF_WRITE;
7476+
if (type == BPF_WRITE)
74877477
clobber = true;
7488-
} else {
7489-
bounds_check_type = BPF_READ;
7490-
}
7491-
err = check_stack_access_within_bounds(env, regno, off, access_size,
7492-
type, bounds_check_type);
7478+
7479+
err = check_stack_access_within_bounds(env, regno, off, access_size, type);
74937480
if (err)
74947481
return err;
74957482

@@ -7506,8 +7493,8 @@ static int check_stack_range_initialized(
75067493
char tn_buf[48];
75077494

75087495
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
7509-
verbose(env, "R%d%s variable offset stack access prohibited for !root, var_off=%s\n",
7510-
regno, err_extra, tn_buf);
7496+
verbose(env, "R%d variable offset stack access prohibited for !root, var_off=%s\n",
7497+
regno, tn_buf);
75117498
return -EACCES;
75127499
}
75137500
/* Only initialized buffer on stack is allowed to be accessed
@@ -7588,14 +7575,14 @@ static int check_stack_range_initialized(
75887575
}
75897576

75907577
if (tnum_is_const(reg->var_off)) {
7591-
verbose(env, "invalid%s read from stack R%d off %d+%d size %d\n",
7592-
err_extra, regno, min_off, i - min_off, access_size);
7578+
verbose(env, "invalid read from stack R%d off %d+%d size %d\n",
7579+
regno, min_off, i - min_off, access_size);
75937580
} else {
75947581
char tn_buf[48];
75957582

75967583
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
7597-
verbose(env, "invalid%s read from stack R%d var_off %s+%d size %d\n",
7598-
err_extra, regno, tn_buf, i - min_off, access_size);
7584+
verbose(env, "invalid read from stack R%d var_off %s+%d size %d\n",
7585+
regno, tn_buf, i - min_off, access_size);
75997586
}
76007587
return -EACCES;
76017588
mark:
@@ -7670,7 +7657,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
76707657
return check_stack_range_initialized(
76717658
env,
76727659
regno, reg->off, access_size,
7673-
zero_size_allowed, ACCESS_HELPER, meta);
7660+
zero_size_allowed, access_type, meta);
76747661
case PTR_TO_BTF_ID:
76757662
return check_ptr_to_btf_access(env, regs, regno, reg->off,
76767663
access_size, BPF_READ, -1);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ int ringbuf_invalid_api(void *ctx)
192192

193193
/* Can't add a dynptr to a map */
194194
SEC("?raw_tp")
195-
__failure __msg("invalid indirect read from stack")
195+
__failure __msg("invalid read from stack")
196196
int add_dynptr_to_map1(void *ctx)
197197
{
198198
struct bpf_dynptr ptr;
@@ -210,7 +210,7 @@ int add_dynptr_to_map1(void *ctx)
210210

211211
/* Can't add a struct with an embedded dynptr to a map */
212212
SEC("?raw_tp")
213-
__failure __msg("invalid indirect read from stack")
213+
__failure __msg("invalid read from stack")
214214
int add_dynptr_to_map2(void *ctx)
215215
{
216216
struct test_info x;
@@ -398,7 +398,7 @@ int data_slice_missing_null_check2(void *ctx)
398398
* dynptr argument
399399
*/
400400
SEC("?raw_tp")
401-
__failure __msg("invalid indirect read from stack")
401+
__failure __msg("invalid read from stack")
402402
int invalid_helper1(void *ctx)
403403
{
404404
struct bpf_dynptr ptr;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ __noinline int foo(const struct Big *big)
2626
}
2727

2828
SEC("cgroup_skb/ingress")
29-
__failure __msg("invalid indirect access to stack")
29+
__failure __msg("invalid read from stack")
3030
int global_func10(struct __sk_buff *skb)
3131
{
3232
const struct Small small = {.x = skb->len };

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ __naked int helper_uninit_to_misc(void *ctx)
7070
r1 = r10; \
7171
r1 += -128; \
7272
r2 = 32; \
73-
call %[bpf_trace_printk]; \
73+
r3 = 0; \
74+
call %[bpf_probe_read_user]; \
7475
/* Call to dummy() forces print_verifier_state(..., true), \
7576
* thus showing the stack state, matched by __msg(). \
7677
*/ \
@@ -79,7 +80,7 @@ __naked int helper_uninit_to_misc(void *ctx)
7980
exit; \
8081
"
8182
:
82-
: __imm(bpf_trace_printk),
83+
: __imm(bpf_probe_read_user),
8384
__imm(dummy)
8485
: __clobber_all);
8586
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ __naked void stack_out_of_bounds(void)
2828
SEC("socket")
2929
__description("uninitialized stack1")
3030
__success __log_level(4) __msg("stack depth 8")
31-
__failure_unpriv __msg_unpriv("invalid indirect read from stack")
31+
__failure_unpriv __msg_unpriv("invalid read from stack")
3232
__naked void uninitialized_stack1(void)
3333
{
3434
asm volatile (" \

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ __naked void constant_should_keep_constant_type(void)
2525

2626
SEC("tracepoint")
2727
__description("constant register |= constant should not bypass stack boundary checks")
28-
__failure __msg("invalid indirect access to stack R1 off=-48 size=58")
28+
__failure __msg("invalid write to stack R1 off=-48 size=58")
2929
__naked void not_bypass_stack_boundary_checks_1(void)
3030
{
3131
asm volatile (" \
@@ -62,7 +62,7 @@ __naked void register_should_keep_constant_type(void)
6262

6363
SEC("tracepoint")
6464
__description("constant register |= constant register should not bypass stack boundary checks")
65-
__failure __msg("invalid indirect access to stack R1 off=-48 size=58")
65+
__failure __msg("invalid write to stack R1 off=-48 size=58")
6666
__naked void not_bypass_stack_boundary_checks_2(void)
6767
{
6868
asm volatile (" \

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ SEC("socket")
6767
__description("helper access to variable memory: stack, bitwise AND, zero included")
6868
/* in privileged mode reads from uninitialized stack locations are permitted */
6969
__success __failure_unpriv
70-
__msg_unpriv("invalid indirect read from stack R2 off -64+0 size 64")
70+
__msg_unpriv("invalid read from stack R2 off -64+0 size 64")
7171
__retval(0)
7272
__naked void stack_bitwise_and_zero_included(void)
7373
{
@@ -100,7 +100,7 @@ __naked void stack_bitwise_and_zero_included(void)
100100

101101
SEC("tracepoint")
102102
__description("helper access to variable memory: stack, bitwise AND + JMP, wrong max")
103-
__failure __msg("invalid indirect access to stack R1 off=-64 size=65")
103+
__failure __msg("invalid write to stack R1 off=-64 size=65")
104104
__naked void bitwise_and_jmp_wrong_max(void)
105105
{
106106
asm volatile (" \
@@ -187,7 +187,7 @@ l0_%=: r0 = 0; \
187187

188188
SEC("tracepoint")
189189
__description("helper access to variable memory: stack, JMP, bounds + offset")
190-
__failure __msg("invalid indirect access to stack R1 off=-64 size=65")
190+
__failure __msg("invalid write to stack R1 off=-64 size=65")
191191
__naked void memory_stack_jmp_bounds_offset(void)
192192
{
193193
asm volatile (" \
@@ -211,7 +211,7 @@ l0_%=: r0 = 0; \
211211

212212
SEC("tracepoint")
213213
__description("helper access to variable memory: stack, JMP, wrong max")
214-
__failure __msg("invalid indirect access to stack R1 off=-64 size=65")
214+
__failure __msg("invalid write to stack R1 off=-64 size=65")
215215
__naked void memory_stack_jmp_wrong_max(void)
216216
{
217217
asm volatile (" \
@@ -260,7 +260,7 @@ SEC("socket")
260260
__description("helper access to variable memory: stack, JMP, no min check")
261261
/* in privileged mode reads from uninitialized stack locations are permitted */
262262
__success __failure_unpriv
263-
__msg_unpriv("invalid indirect read from stack R2 off -64+0 size 64")
263+
__msg_unpriv("invalid read from stack R2 off -64+0 size 64")
264264
__retval(0)
265265
__naked void stack_jmp_no_min_check(void)
266266
{
@@ -750,7 +750,7 @@ SEC("socket")
750750
__description("helper access to variable memory: 8 bytes leak")
751751
/* in privileged mode reads from uninitialized stack locations are permitted */
752752
__success __failure_unpriv
753-
__msg_unpriv("invalid indirect read from stack R2 off -64+32 size 64")
753+
__msg_unpriv("invalid read from stack R2 off -64+32 size 64")
754754
__retval(0)
755755
__naked void variable_memory_8_bytes_leak(void)
756756
{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ __naked void arg_ptr_to_long_misaligned(void)
9696

9797
SEC("cgroup/sysctl")
9898
__description("arg pointer to long size < sizeof(long)")
99-
__failure __msg("invalid indirect access to stack R4 off=-4 size=8")
99+
__failure __msg("invalid write to stack R4 off=-4 size=8")
100100
__naked void to_long_size_sizeof_long(void)
101101
{
102102
asm volatile (" \

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ SEC("tc/ingress")
88
__description("uninit/mtu: write rejected")
99
__success
1010
__caps_unpriv(CAP_BPF|CAP_NET_ADMIN)
11-
__failure_unpriv __msg_unpriv("invalid indirect read from stack")
11+
__failure_unpriv __msg_unpriv("invalid read from stack")
1212
int tc_uninit_mtu(struct __sk_buff *ctx)
1313
{
1414
__u32 mtu;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ __naked void load_bytes_spilled_regs_data(void)
236236

237237
SEC("tc")
238238
__description("raw_stack: skb_load_bytes, invalid access 1")
239-
__failure __msg("invalid indirect access to stack R3 off=-513 size=8")
239+
__failure __msg("invalid write to stack R3 off=-513 size=8")
240240
__naked void load_bytes_invalid_access_1(void)
241241
{
242242
asm volatile (" \
@@ -255,7 +255,7 @@ __naked void load_bytes_invalid_access_1(void)
255255

256256
SEC("tc")
257257
__description("raw_stack: skb_load_bytes, invalid access 2")
258-
__failure __msg("invalid indirect access to stack R3 off=-1 size=8")
258+
__failure __msg("invalid write to stack R3 off=-1 size=8")
259259
__naked void load_bytes_invalid_access_2(void)
260260
{
261261
asm volatile (" \

0 commit comments

Comments
 (0)