Skip to content

Commit a33a6ea

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf: Allow reads from uninit stack'
Merge commit bf9bec4 ("Merge branch 'bpf: Allow reads from uninit stack'") from bpf-next to bpf tree to address verification issues in some programs due to stack usage. Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents e8c8361 + bf9bec4 commit a33a6ea

File tree

11 files changed

+204
-136
lines changed

11 files changed

+204
-136
lines changed

kernel/bpf/verifier.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3826,6 +3826,8 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
38263826
continue;
38273827
if (type == STACK_MISC)
38283828
continue;
3829+
if (type == STACK_INVALID && env->allow_uninit_stack)
3830+
continue;
38293831
verbose(env, "invalid read from stack off %d+%d size %d\n",
38303832
off, i, size);
38313833
return -EACCES;
@@ -3863,6 +3865,8 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
38633865
continue;
38643866
if (type == STACK_ZERO)
38653867
continue;
3868+
if (type == STACK_INVALID && env->allow_uninit_stack)
3869+
continue;
38663870
verbose(env, "invalid read from stack off %d+%d size %d\n",
38673871
off, i, size);
38683872
return -EACCES;
@@ -5754,7 +5758,8 @@ static int check_stack_range_initialized(
57545758
stype = &state->stack[spi].slot_type[slot % BPF_REG_SIZE];
57555759
if (*stype == STACK_MISC)
57565760
goto mark;
5757-
if (*stype == STACK_ZERO) {
5761+
if ((*stype == STACK_ZERO) ||
5762+
(*stype == STACK_INVALID && env->allow_uninit_stack)) {
57585763
if (clobber) {
57595764
/* helper can write anything into the stack */
57605765
*stype = STACK_MISC;
@@ -13936,6 +13941,10 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,
1393613941
if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_INVALID)
1393713942
continue;
1393813943

13944+
if (env->allow_uninit_stack &&
13945+
old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_MISC)
13946+
continue;
13947+
1393913948
/* explored stack has more populated slots than current stack
1394013949
* and these slots were used
1394113950
*/
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <test_progs.h>
4+
#include "uninit_stack.skel.h"
5+
6+
void test_uninit_stack(void)
7+
{
8+
RUN_TESTS(uninit_stack);
9+
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
#include "bpf_misc.h"
66

77
struct Small {
8-
int x;
8+
long x;
99
};
1010

1111
struct Big {
12-
int x;
13-
int y;
12+
long x;
13+
long y;
1414
};
1515

1616
__noinline int foo(const struct Big *big)
@@ -22,7 +22,7 @@ __noinline int foo(const struct Big *big)
2222
}
2323

2424
SEC("cgroup_skb/ingress")
25-
__failure __msg("invalid indirect read from stack")
25+
__failure __msg("invalid indirect access to stack")
2626
int global_func10(struct __sk_buff *skb)
2727
{
2828
const struct Small small = {.x = skb->len };
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <linux/bpf.h>
4+
#include <bpf/bpf_helpers.h>
5+
#include "bpf_misc.h"
6+
7+
/* Read an uninitialized value from stack at a fixed offset */
8+
SEC("socket")
9+
__naked int read_uninit_stack_fixed_off(void *ctx)
10+
{
11+
asm volatile (" \
12+
r0 = 0; \
13+
/* force stack depth to be 128 */ \
14+
*(u64*)(r10 - 128) = r1; \
15+
r1 = *(u8 *)(r10 - 8 ); \
16+
r0 += r1; \
17+
r1 = *(u8 *)(r10 - 11); \
18+
r1 = *(u8 *)(r10 - 13); \
19+
r1 = *(u8 *)(r10 - 15); \
20+
r1 = *(u16*)(r10 - 16); \
21+
r1 = *(u32*)(r10 - 32); \
22+
r1 = *(u64*)(r10 - 64); \
23+
/* read from a spill of a wrong size, it is a separate \
24+
* branch in check_stack_read_fixed_off() \
25+
*/ \
26+
*(u32*)(r10 - 72) = r1; \
27+
r1 = *(u64*)(r10 - 72); \
28+
r0 = 0; \
29+
exit; \
30+
"
31+
::: __clobber_all);
32+
}
33+
34+
/* Read an uninitialized value from stack at a variable offset */
35+
SEC("socket")
36+
__naked int read_uninit_stack_var_off(void *ctx)
37+
{
38+
asm volatile (" \
39+
call %[bpf_get_prandom_u32]; \
40+
/* force stack depth to be 64 */ \
41+
*(u64*)(r10 - 64) = r0; \
42+
r0 = -r0; \
43+
/* give r0 a range [-31, -1] */ \
44+
if r0 s<= -32 goto exit_%=; \
45+
if r0 s>= 0 goto exit_%=; \
46+
/* access stack using r0 */ \
47+
r1 = r10; \
48+
r1 += r0; \
49+
r2 = *(u8*)(r1 + 0); \
50+
exit_%=: r0 = 0; \
51+
exit; \
52+
"
53+
:
54+
: __imm(bpf_get_prandom_u32)
55+
: __clobber_all);
56+
}
57+
58+
static __noinline void dummy(void) {}
59+
60+
/* Pass a pointer to uninitialized stack memory to a helper.
61+
* Passed memory block should be marked as STACK_MISC after helper call.
62+
*/
63+
SEC("socket")
64+
__log_level(7) __msg("fp-104=mmmmmmmm")
65+
__naked int helper_uninit_to_misc(void *ctx)
66+
{
67+
asm volatile (" \
68+
/* force stack depth to be 128 */ \
69+
*(u64*)(r10 - 128) = r1; \
70+
r1 = r10; \
71+
r1 += -128; \
72+
r2 = 32; \
73+
call %[bpf_trace_printk]; \
74+
/* Call to dummy() forces print_verifier_state(..., true), \
75+
* thus showing the stack state, matched by __msg(). \
76+
*/ \
77+
call %[dummy]; \
78+
r0 = 0; \
79+
exit; \
80+
"
81+
:
82+
: __imm(bpf_trace_printk),
83+
__imm(dummy)
84+
: __clobber_all);
85+
}
86+
87+
char _license[] SEC("license") = "GPL";

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2221,19 +2221,22 @@
22212221
* that fp-8 stack slot was unused in the fall-through
22222222
* branch and will accept the program incorrectly
22232223
*/
2224-
BPF_JMP_IMM(BPF_JGT, BPF_REG_1, 2, 2),
2224+
BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
2225+
BPF_JMP_IMM(BPF_JGT, BPF_REG_0, 2, 2),
22252226
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
22262227
BPF_JMP_IMM(BPF_JA, 0, 0, 0),
22272228
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
22282229
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
22292230
BPF_LD_MAP_FD(BPF_REG_1, 0),
22302231
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
2232+
BPF_MOV64_IMM(BPF_REG_0, 0),
22312233
BPF_EXIT_INSN(),
22322234
},
2233-
.fixup_map_hash_48b = { 6 },
2234-
.errstr = "invalid indirect read from stack R2 off -8+0 size 8",
2235-
.result = REJECT,
2236-
.prog_type = BPF_PROG_TYPE_XDP,
2235+
.fixup_map_hash_48b = { 7 },
2236+
.errstr_unpriv = "invalid indirect read from stack R2 off -8+0 size 8",
2237+
.result_unpriv = REJECT,
2238+
/* in privileged mode reads from uninitialized stack locations are permitted */
2239+
.result = ACCEPT,
22372240
},
22382241
{
22392242
"calls: ctx read at start of subprog",

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

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,30 @@
2929
{
3030
"helper access to variable memory: stack, bitwise AND, zero included",
3131
.insns = {
32-
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
33-
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
34-
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
35-
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
36-
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
37-
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 64),
38-
BPF_MOV64_IMM(BPF_REG_3, 0),
39-
BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel),
32+
/* set max stack size */
33+
BPF_ST_MEM(BPF_DW, BPF_REG_10, -128, 0),
34+
/* set r3 to a random value */
35+
BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
36+
BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
37+
/* use bitwise AND to limit r3 range to [0, 64] */
38+
BPF_ALU64_IMM(BPF_AND, BPF_REG_3, 64),
39+
BPF_LD_MAP_FD(BPF_REG_1, 0),
40+
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
41+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64),
42+
BPF_MOV64_IMM(BPF_REG_4, 0),
43+
/* Call bpf_ringbuf_output(), it is one of a few helper functions with
44+
* ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.
45+
* For unpriv this should signal an error, because memory at &fp[-64] is
46+
* not initialized.
47+
*/
48+
BPF_EMIT_CALL(BPF_FUNC_ringbuf_output),
4049
BPF_EXIT_INSN(),
4150
},
42-
.errstr = "invalid indirect read from stack R1 off -64+0 size 64",
43-
.result = REJECT,
44-
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
51+
.fixup_map_ringbuf = { 4 },
52+
.errstr_unpriv = "invalid indirect read from stack R2 off -64+0 size 64",
53+
.result_unpriv = REJECT,
54+
/* in privileged mode reads from uninitialized stack locations are permitted */
55+
.result = ACCEPT,
4556
},
4657
{
4758
"helper access to variable memory: stack, bitwise AND + JMP, wrong max",
@@ -183,20 +194,31 @@
183194
{
184195
"helper access to variable memory: stack, JMP, no min check",
185196
.insns = {
186-
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
187-
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
188-
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
189-
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
190-
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
191-
BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 64, 3),
192-
BPF_MOV64_IMM(BPF_REG_3, 0),
193-
BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel),
197+
/* set max stack size */
198+
BPF_ST_MEM(BPF_DW, BPF_REG_10, -128, 0),
199+
/* set r3 to a random value */
200+
BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
201+
BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
202+
/* use JMP to limit r3 range to [0, 64] */
203+
BPF_JMP_IMM(BPF_JGT, BPF_REG_3, 64, 6),
204+
BPF_LD_MAP_FD(BPF_REG_1, 0),
205+
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
206+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64),
207+
BPF_MOV64_IMM(BPF_REG_4, 0),
208+
/* Call bpf_ringbuf_output(), it is one of a few helper functions with
209+
* ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.
210+
* For unpriv this should signal an error, because memory at &fp[-64] is
211+
* not initialized.
212+
*/
213+
BPF_EMIT_CALL(BPF_FUNC_ringbuf_output),
194214
BPF_MOV64_IMM(BPF_REG_0, 0),
195215
BPF_EXIT_INSN(),
196216
},
197-
.errstr = "invalid indirect read from stack R1 off -64+0 size 64",
198-
.result = REJECT,
199-
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
217+
.fixup_map_ringbuf = { 4 },
218+
.errstr_unpriv = "invalid indirect read from stack R2 off -64+0 size 64",
219+
.result_unpriv = REJECT,
220+
/* in privileged mode reads from uninitialized stack locations are permitted */
221+
.result = ACCEPT,
200222
},
201223
{
202224
"helper access to variable memory: stack, JMP (signed), no min check",
@@ -564,29 +586,41 @@
564586
{
565587
"helper access to variable memory: 8 bytes leak",
566588
.insns = {
567-
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
568-
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
569-
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
589+
/* set max stack size */
590+
BPF_ST_MEM(BPF_DW, BPF_REG_10, -128, 0),
591+
/* set r3 to a random value */
592+
BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
593+
BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
594+
BPF_LD_MAP_FD(BPF_REG_1, 0),
595+
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
596+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64),
570597
BPF_MOV64_IMM(BPF_REG_0, 0),
571598
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -64),
572599
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -56),
573600
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -48),
574601
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -40),
602+
/* Note: fp[-32] left uninitialized */
575603
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -24),
576604
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16),
577605
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
578-
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128),
579-
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128),
580-
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 63),
581-
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
582-
BPF_MOV64_IMM(BPF_REG_3, 0),
583-
BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel),
584-
BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16),
606+
/* Limit r3 range to [1, 64] */
607+
BPF_ALU64_IMM(BPF_AND, BPF_REG_3, 63),
608+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 1),
609+
BPF_MOV64_IMM(BPF_REG_4, 0),
610+
/* Call bpf_ringbuf_output(), it is one of a few helper functions with
611+
* ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.
612+
* For unpriv this should signal an error, because memory region [1, 64]
613+
* at &fp[-64] is not fully initialized.
614+
*/
615+
BPF_EMIT_CALL(BPF_FUNC_ringbuf_output),
616+
BPF_MOV64_IMM(BPF_REG_0, 0),
585617
BPF_EXIT_INSN(),
586618
},
587-
.errstr = "invalid indirect read from stack R1 off -64+32 size 64",
588-
.result = REJECT,
589-
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
619+
.fixup_map_ringbuf = { 3 },
620+
.errstr_unpriv = "invalid indirect read from stack R2 off -64+32 size 64",
621+
.result_unpriv = REJECT,
622+
/* in privileged mode reads from uninitialized stack locations are permitted */
623+
.result = ACCEPT,
590624
},
591625
{
592626
"helper access to variable memory: 8 bytes no leak (init memory)",

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@
5454
/* bpf_strtoul() */
5555
BPF_EMIT_CALL(BPF_FUNC_strtoul),
5656

57-
BPF_MOV64_IMM(BPF_REG_0, 1),
57+
BPF_MOV64_IMM(BPF_REG_0, 0),
5858
BPF_EXIT_INSN(),
5959
},
60-
.result = REJECT,
61-
.prog_type = BPF_PROG_TYPE_CGROUP_SYSCTL,
62-
.errstr = "invalid indirect read from stack R4 off -16+4 size 8",
60+
.result_unpriv = REJECT,
61+
.errstr_unpriv = "invalid indirect read from stack R4 off -16+4 size 8",
62+
/* in privileged mode reads from uninitialized stack locations are permitted */
63+
.result = ACCEPT,
6364
},
6465
{
6566
"ARG_PTR_TO_LONG misaligned",

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,10 @@
128128
BPF_EXIT_INSN(),
129129
},
130130
.fixup_map_hash_8b = { 3 },
131-
.errstr = "invalid read from stack off -16+0 size 8",
132-
.result = REJECT,
133-
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
131+
.errstr_unpriv = "invalid read from stack off -16+0 size 8",
132+
.result_unpriv = REJECT,
133+
/* in privileged mode reads from uninitialized stack locations are permitted */
134+
.result = ACCEPT,
134135
},
135136
{
136137
"precision tracking for u32 spill/fill",
@@ -258,6 +259,8 @@
258259
BPF_EXIT_INSN(),
259260
},
260261
.flags = BPF_F_TEST_STATE_FREQ,
261-
.errstr = "invalid read from stack off -8+1 size 8",
262-
.result = REJECT,
262+
.errstr_unpriv = "invalid read from stack off -8+1 size 8",
263+
.result_unpriv = REJECT,
264+
/* in privileged mode reads from uninitialized stack locations are permitted */
265+
.result = ACCEPT,
263266
},

0 commit comments

Comments
 (0)