Skip to content

Commit 4af20ab

Browse files
committed
Merge branch 'bpf-fix-accesses-to-uninit-stack-slots'
Andrei Matei says: ==================== bpf: fix accesses to uninit stack slots Fix two related issues issues around verifying stack accesses: 1. accesses to uninitialized stack memory was allowed inconsistently 2. the maximum stack depth needed for a program was not always maintained correctly The two issues are fixed together in one commit because the code for one affects the other. V4 to V5: - target bpf-next (Alexei) V3 to V4: - minor fixup to comment in patch 1 (Eduard) - C89-style in patch 3 (Andrii) V2 to V3: - address review comments from Andrii and Eduard - drop new verifier tests in favor of editing existing tests to check for stack depth - append a patch with a bit of cleanup coming out of the previous review ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Andrii Nakryiko <[email protected]>
2 parents 8b7b0e5 + 2929bfa commit 4af20ab

File tree

10 files changed

+114
-73
lines changed

10 files changed

+114
-73
lines changed

include/linux/bpf_verifier.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,17 @@ struct bpf_func_state {
321321
/* The following fields should be last. See copy_func_state() */
322322
int acquired_refs;
323323
struct bpf_reference_state *refs;
324+
/* The state of the stack. Each element of the array describes BPF_REG_SIZE
325+
* (i.e. 8) bytes worth of stack memory.
326+
* stack[0] represents bytes [*(r10-8)..*(r10-1)]
327+
* stack[1] represents bytes [*(r10-16)..*(r10-9)]
328+
* ...
329+
* stack[allocated_stack/8 - 1] represents [*(r10-allocated_stack)..*(r10-allocated_stack+7)]
330+
*/
324331
struct bpf_stack_state *stack;
332+
/* Size of the current stack, in bytes. The stack state is tracked below, in
333+
* `stack`. allocated_stack is always a multiple of BPF_REG_SIZE.
334+
*/
325335
int allocated_stack;
326336
};
327337

@@ -658,6 +668,10 @@ struct bpf_verifier_env {
658668
int exception_callback_subprog;
659669
bool explore_alu_limits;
660670
bool allow_ptr_leaks;
671+
/* Allow access to uninitialized stack memory. Writes with fixed offset are
672+
* always allowed, so this refers to reads (with fixed or variable offset),
673+
* to writes with variable offset and to indirect (helper) accesses.
674+
*/
661675
bool allow_uninit_stack;
662676
bool bpf_capable;
663677
bool bypass_spec_v1;

kernel/bpf/verifier.c

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,9 +1259,16 @@ static int resize_reference_state(struct bpf_func_state *state, size_t n)
12591259
return 0;
12601260
}
12611261

1262-
static int grow_stack_state(struct bpf_func_state *state, int size)
1262+
/* Possibly update state->allocated_stack to be at least size bytes. Also
1263+
* possibly update the function's high-water mark in its bpf_subprog_info.
1264+
*/
1265+
static int grow_stack_state(struct bpf_verifier_env *env, struct bpf_func_state *state, int size)
12631266
{
1264-
size_t old_n = state->allocated_stack / BPF_REG_SIZE, n = size / BPF_REG_SIZE;
1267+
size_t old_n = state->allocated_stack / BPF_REG_SIZE, n;
1268+
1269+
/* The stack size is always a multiple of BPF_REG_SIZE. */
1270+
size = round_up(size, BPF_REG_SIZE);
1271+
n = size / BPF_REG_SIZE;
12651272

12661273
if (old_n >= n)
12671274
return 0;
@@ -1271,6 +1278,11 @@ static int grow_stack_state(struct bpf_func_state *state, int size)
12711278
return -ENOMEM;
12721279

12731280
state->allocated_stack = size;
1281+
1282+
/* update known max for given subprogram */
1283+
if (env->subprog_info[state->subprogno].stack_depth < size)
1284+
env->subprog_info[state->subprogno].stack_depth = size;
1285+
12741286
return 0;
12751287
}
12761288

@@ -4440,9 +4452,6 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
44404452
struct bpf_reg_state *reg = NULL;
44414453
int insn_flags = insn_stack_access_flags(state->frameno, spi);
44424454

4443-
err = grow_stack_state(state, round_up(slot + 1, BPF_REG_SIZE));
4444-
if (err)
4445-
return err;
44464455
/* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0,
44474456
* so it's aligned access and [off, off + size) are within stack limits
44484457
*/
@@ -4595,10 +4604,6 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env,
45954604
(!value_reg && is_bpf_st_mem(insn) && insn->imm == 0))
45964605
writing_zero = true;
45974606

4598-
err = grow_stack_state(state, round_up(-min_off, BPF_REG_SIZE));
4599-
if (err)
4600-
return err;
4601-
46024607
for (i = min_off; i < max_off; i++) {
46034608
int spi;
46044609

@@ -5774,20 +5779,6 @@ static int check_ptr_alignment(struct bpf_verifier_env *env,
57745779
strict);
57755780
}
57765781

5777-
static int update_stack_depth(struct bpf_verifier_env *env,
5778-
const struct bpf_func_state *func,
5779-
int off)
5780-
{
5781-
u16 stack = env->subprog_info[func->subprogno].stack_depth;
5782-
5783-
if (stack >= -off)
5784-
return 0;
5785-
5786-
/* update known max for given subprogram */
5787-
env->subprog_info[func->subprogno].stack_depth = -off;
5788-
return 0;
5789-
}
5790-
57915782
/* starting from main bpf function walk all instructions of the function
57925783
* and recursively walk all callees that given function can call.
57935784
* Ignore jump and exit insns.
@@ -6577,13 +6568,14 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
65776568
* The minimum valid offset is -MAX_BPF_STACK for writes, and
65786569
* -state->allocated_stack for reads.
65796570
*/
6580-
static int check_stack_slot_within_bounds(s64 off,
6581-
struct bpf_func_state *state,
6582-
enum bpf_access_type t)
6571+
static int check_stack_slot_within_bounds(struct bpf_verifier_env *env,
6572+
s64 off,
6573+
struct bpf_func_state *state,
6574+
enum bpf_access_type t)
65836575
{
65846576
int min_valid_off;
65856577

6586-
if (t == BPF_WRITE)
6578+
if (t == BPF_WRITE || env->allow_uninit_stack)
65876579
min_valid_off = -MAX_BPF_STACK;
65886580
else
65896581
min_valid_off = -state->allocated_stack;
@@ -6632,7 +6624,7 @@ static int check_stack_access_within_bounds(
66326624
max_off = reg->smax_value + off + access_size;
66336625
}
66346626

6635-
err = check_stack_slot_within_bounds(min_off, state, type);
6627+
err = check_stack_slot_within_bounds(env, min_off, state, type);
66366628
if (!err && max_off > 0)
66376629
err = -EINVAL; /* out of stack access into non-negative offsets */
66386630

@@ -6647,8 +6639,13 @@ static int check_stack_access_within_bounds(
66476639
verbose(env, "invalid variable-offset%s stack R%d var_off=%s off=%d size=%d\n",
66486640
err_extra, regno, tn_buf, off, access_size);
66496641
}
6642+
return err;
66506643
}
6651-
return err;
6644+
6645+
/* Note that there is no stack access with offset zero, so the needed stack
6646+
* size is -min_off, not -min_off+1.
6647+
*/
6648+
return grow_stack_state(env, state, -min_off /* size */);
66526649
}
66536650

66546651
/* check whether memory at (regno + off) is accessible for t = (read | write)
@@ -6663,7 +6660,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
66636660
{
66646661
struct bpf_reg_state *regs = cur_regs(env);
66656662
struct bpf_reg_state *reg = regs + regno;
6666-
struct bpf_func_state *state;
66676663
int size, err = 0;
66686664

66696665
size = bpf_size_to_bytes(bpf_size);
@@ -6806,11 +6802,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
68066802
if (err)
68076803
return err;
68086804

6809-
state = func(env, reg);
6810-
err = update_stack_depth(env, state, off);
6811-
if (err)
6812-
return err;
6813-
68146805
if (t == BPF_READ)
68156806
err = check_stack_read(env, regno, off, size,
68166807
value_regno);
@@ -7004,7 +6995,8 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
70046995

70056996
/* When register 'regno' is used to read the stack (either directly or through
70066997
* a helper function) make sure that it's within stack boundary and, depending
7007-
* on the access type, that all elements of the stack are initialized.
6998+
* on the access type and privileges, that all elements of the stack are
6999+
* initialized.
70087000
*
70097001
* 'off' includes 'regno->off', but not its dynamic part (if any).
70107002
*
@@ -7112,8 +7104,11 @@ static int check_stack_range_initialized(
71127104

71137105
slot = -i - 1;
71147106
spi = slot / BPF_REG_SIZE;
7115-
if (state->allocated_stack <= slot)
7116-
goto err;
7107+
if (state->allocated_stack <= slot) {
7108+
verbose(env, "verifier bug: allocated_stack too small");
7109+
return -EFAULT;
7110+
}
7111+
71177112
stype = &state->stack[spi].slot_type[slot % BPF_REG_SIZE];
71187113
if (*stype == STACK_MISC)
71197114
goto mark;
@@ -7137,7 +7132,6 @@ static int check_stack_range_initialized(
71377132
goto mark;
71387133
}
71397134

7140-
err:
71417135
if (tnum_is_const(reg->var_off)) {
71427136
verbose(env, "invalid%s read from stack R%d off %d+%d size %d\n",
71437137
err_extra, regno, min_off, i - min_off, access_size);
@@ -7162,7 +7156,7 @@ static int check_stack_range_initialized(
71627156
* helper may write to the entire memory range.
71637157
*/
71647158
}
7165-
return update_stack_depth(env, state, min_off);
7159+
return 0;
71667160
}
71677161

71687162
static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ __naked int delayed_precision_mark(void)
846846
"call %[bpf_iter_num_next];"
847847
"if r0 == 0 goto 2f;"
848848
"if r6 != 42 goto 3f;"
849-
"r7 = -32;"
849+
"r7 = -33;"
850850
"call %[bpf_get_prandom_u32];"
851851
"r6 = r0;"
852852
"goto 1b;\n"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ __noinline int foo(int (*arr)[10])
1313
}
1414

1515
SEC("cgroup_skb/ingress")
16-
__failure __msg("invalid indirect read from stack")
16+
__success
1717
int global_func16(struct __sk_buff *skb)
1818
{
1919
int array[10];

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ __naked void stack_out_of_bounds(void)
2727

2828
SEC("socket")
2929
__description("uninitialized stack1")
30-
__failure __msg("invalid indirect read from stack")
31-
__failure_unpriv
30+
__success __log_level(4) __msg("stack depth 8")
31+
__failure_unpriv __msg_unpriv("invalid indirect read from stack")
3232
__naked void uninitialized_stack1(void)
3333
{
3434
asm volatile (" \
@@ -45,8 +45,8 @@ __naked void uninitialized_stack1(void)
4545

4646
SEC("socket")
4747
__description("uninitialized stack2")
48-
__failure __msg("invalid read from stack")
49-
__failure_unpriv
48+
__success __log_level(4) __msg("stack depth 8")
49+
__failure_unpriv __msg_unpriv("invalid read from stack")
5050
__naked void uninitialized_stack2(void)
5151
{
5252
asm volatile (" \

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
#include <bpf/bpf_helpers.h>
66
#include "bpf_misc.h"
77

8-
SEC("cgroup/sysctl")
8+
SEC("socket")
99
__description("ARG_PTR_TO_LONG uninitialized")
10-
__failure __msg("invalid indirect read from stack R4 off -16+0 size 8")
10+
__success
11+
__failure_unpriv __msg_unpriv("invalid indirect read from stack R4 off -16+0 size 8")
1112
__naked void arg_ptr_to_long_uninitialized(void)
1213
{
1314
asm volatile (" \

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
#include <bpf/bpf_helpers.h>
66
#include "bpf_misc.h"
77

8-
SEC("tc")
8+
SEC("socket")
99
__description("raw_stack: no skb_load_bytes")
10-
__failure __msg("invalid read from stack R6 off=-8 size=8")
10+
__success
11+
__failure_unpriv __msg_unpriv("invalid read from stack R6 off=-8 size=8")
1112
__naked void stack_no_skb_load_bytes(void)
1213
{
1314
asm volatile (" \

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

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ __naked void stack_read_priv_vs_unpriv(void)
5959
" ::: __clobber_all);
6060
}
6161

62-
SEC("lwt_in")
62+
SEC("cgroup/skb")
6363
__description("variable-offset stack read, uninitialized")
64-
__failure __msg("invalid variable-offset read from stack R2")
64+
__success
65+
__failure_unpriv __msg_unpriv("R2 variable stack access prohibited for !root")
6566
__naked void variable_offset_stack_read_uninitialized(void)
6667
{
6768
asm volatile (" \
@@ -83,12 +84,55 @@ __naked void variable_offset_stack_read_uninitialized(void)
8384

8485
SEC("socket")
8586
__description("variable-offset stack write, priv vs unpriv")
86-
__success __failure_unpriv
87+
__success
88+
/* Check that the maximum stack depth is correctly maintained according to the
89+
* maximum possible variable offset.
90+
*/
91+
__log_level(4) __msg("stack depth 16")
92+
__failure_unpriv
8793
/* Variable stack access is rejected for unprivileged.
8894
*/
8995
__msg_unpriv("R2 variable stack access prohibited for !root")
9096
__retval(0)
9197
__naked void stack_write_priv_vs_unpriv(void)
98+
{
99+
asm volatile (" \
100+
/* Get an unknown value */ \
101+
r2 = *(u32*)(r1 + 0); \
102+
/* Make it small and 8-byte aligned */ \
103+
r2 &= 8; \
104+
r2 -= 16; \
105+
/* Add it to fp. We now have either fp-8 or \
106+
* fp-16, but we don't know which \
107+
*/ \
108+
r2 += r10; \
109+
/* Dereference it for a stack write */ \
110+
r0 = 0; \
111+
*(u64*)(r2 + 0) = r0; \
112+
exit; \
113+
" ::: __clobber_all);
114+
}
115+
116+
/* Similar to the previous test, but this time also perform a read from the
117+
* address written to with a variable offset. The read is allowed, showing that,
118+
* after a variable-offset write, a priviledged program can read the slots that
119+
* were in the range of that write (even if the verifier doesn't actually know if
120+
* the slot being read was really written to or not.
121+
*
122+
* Despite this test being mostly a superset, the previous test is also kept for
123+
* the sake of it checking the stack depth in the case where there is no read.
124+
*/
125+
SEC("socket")
126+
__description("variable-offset stack write followed by read")
127+
__success
128+
/* Check that the maximum stack depth is correctly maintained according to the
129+
* maximum possible variable offset.
130+
*/
131+
__log_level(4) __msg("stack depth 16")
132+
__failure_unpriv
133+
__msg_unpriv("R2 variable stack access prohibited for !root")
134+
__retval(0)
135+
__naked void stack_write_followed_by_read(void)
92136
{
93137
asm volatile (" \
94138
/* Get an unknown value */ \
@@ -103,12 +147,7 @@ __naked void stack_write_priv_vs_unpriv(void)
103147
/* Dereference it for a stack write */ \
104148
r0 = 0; \
105149
*(u64*)(r2 + 0) = r0; \
106-
/* Now read from the address we just wrote. This shows\
107-
* that, after a variable-offset write, a priviledged\
108-
* program can read the slots that were in the range of\
109-
* that write (even if the verifier doesn't actually know\
110-
* if the slot being read was really written to or not.\
111-
*/ \
150+
/* Now read from the address we just wrote. */ \
112151
r3 = *(u64*)(r2 + 0); \
113152
r0 = 0; \
114153
exit; \
@@ -282,9 +321,10 @@ __naked void access_min_out_of_bound(void)
282321
: __clobber_all);
283322
}
284323

285-
SEC("lwt_in")
324+
SEC("cgroup/skb")
286325
__description("indirect variable-offset stack access, min_off < min_initialized")
287-
__failure __msg("invalid indirect read from stack R2 var_off")
326+
__success
327+
__failure_unpriv __msg_unpriv("R2 variable stack access prohibited for !root")
288328
__naked void access_min_off_min_initialized(void)
289329
{
290330
asm volatile (" \

tools/testing/selftests/bpf/verifier/atomic_cmpxchg.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,6 @@
8383
.result = REJECT,
8484
.errstr = "!read_ok",
8585
},
86-
{
87-
"Can't use cmpxchg on uninit memory",
88-
.insns = {
89-
BPF_MOV64_IMM(BPF_REG_0, 3),
90-
BPF_MOV64_IMM(BPF_REG_2, 4),
91-
BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_2, -8),
92-
BPF_EXIT_INSN(),
93-
},
94-
.result = REJECT,
95-
.errstr = "invalid read from stack",
96-
},
9786
{
9887
"BPF_W cmpxchg should zero top 32 bits",
9988
.insns = {

tools/testing/selftests/bpf/verifier/calls.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1505,7 +1505,9 @@
15051505
.prog_type = BPF_PROG_TYPE_XDP,
15061506
.fixup_map_hash_8b = { 23 },
15071507
.result = REJECT,
1508-
.errstr = "invalid read from stack R7 off=-16 size=8",
1508+
.errstr = "R0 invalid mem access 'scalar'",
1509+
.result_unpriv = REJECT,
1510+
.errstr_unpriv = "invalid read from stack R7 off=-16 size=8",
15091511
},
15101512
{
15111513
"calls: two calls that receive map_value via arg=ptr_stack_of_caller. test1",

0 commit comments

Comments
 (0)