Skip to content

Commit 7a22930

Browse files
andreimateiAlexei Starovoitov
authored andcommitted
selftest/bpf: Verifier tests for var-off access
Add tests for the new functionality - reading and writing to the stack through a variable-offset pointer. Signed-off-by: Andrei Matei <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent a680cb3 commit 7a22930

File tree

1 file changed

+97
-2
lines changed
  • tools/testing/selftests/bpf/verifier

1 file changed

+97
-2
lines changed

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

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,109 @@
3131
* we don't know which
3232
*/
3333
BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10),
34-
/* dereference it */
34+
/* dereference it for a stack read */
35+
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0),
36+
BPF_MOV64_IMM(BPF_REG_0, 0),
37+
BPF_EXIT_INSN(),
38+
},
39+
.result = ACCEPT,
40+
.result_unpriv = REJECT,
41+
.errstr_unpriv = "R2 variable stack access prohibited for !root",
42+
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
43+
},
44+
{
45+
"variable-offset stack read, uninitialized",
46+
.insns = {
47+
/* Get an unknown value */
48+
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0),
49+
/* Make it small and 4-byte aligned */
50+
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4),
51+
BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 8),
52+
/* add it to fp. We now have either fp-4 or fp-8, but
53+
* we don't know which
54+
*/
55+
BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10),
56+
/* dereference it for a stack read */
3557
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0),
58+
BPF_MOV64_IMM(BPF_REG_0, 0),
3659
BPF_EXIT_INSN(),
3760
},
38-
.errstr = "variable stack access var_off=(0xfffffffffffffff8; 0x4)",
3961
.result = REJECT,
62+
.errstr = "invalid variable-offset read from stack R2",
4063
.prog_type = BPF_PROG_TYPE_LWT_IN,
4164
},
65+
{
66+
"variable-offset stack write, priv vs unpriv",
67+
.insns = {
68+
/* Get an unknown value */
69+
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0),
70+
/* Make it small and 8-byte aligned */
71+
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 8),
72+
BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 16),
73+
/* Add it to fp. We now have either fp-8 or fp-16, but
74+
* we don't know which
75+
*/
76+
BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10),
77+
/* Dereference it for a stack write */
78+
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
79+
/* Now read from the address we just wrote. This shows
80+
* that, after a variable-offset write, a priviledged
81+
* program can read the slots that were in the range of
82+
* that write (even if the verifier doesn't actually know
83+
* if the slot being read was really written to or not.
84+
*/
85+
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_2, 0),
86+
BPF_MOV64_IMM(BPF_REG_0, 0),
87+
BPF_EXIT_INSN(),
88+
},
89+
/* Variable stack access is rejected for unprivileged.
90+
*/
91+
.errstr_unpriv = "R2 variable stack access prohibited for !root",
92+
.result_unpriv = REJECT,
93+
.result = ACCEPT,
94+
},
95+
{
96+
"variable-offset stack write clobbers spilled regs",
97+
.insns = {
98+
/* Dummy instruction; needed because we need to patch the next one
99+
* and we can't patch the first instruction.
100+
*/
101+
BPF_MOV64_IMM(BPF_REG_6, 0),
102+
/* Make R0 a map ptr */
103+
BPF_LD_MAP_FD(BPF_REG_0, 0),
104+
/* Get an unknown value */
105+
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0),
106+
/* Make it small and 8-byte aligned */
107+
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 8),
108+
BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 16),
109+
/* Add it to fp. We now have either fp-8 or fp-16, but
110+
* we don't know which.
111+
*/
112+
BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10),
113+
/* Spill R0(map ptr) into stack */
114+
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
115+
/* Dereference the unknown value for a stack write */
116+
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
117+
/* Fill the register back into R2 */
118+
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8),
119+
/* Try to dereference R2 for a memory load */
120+
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 8),
121+
BPF_EXIT_INSN(),
122+
},
123+
.fixup_map_hash_8b = { 1 },
124+
/* The unpriviledged case is not too interesting; variable
125+
* stack access is rejected.
126+
*/
127+
.errstr_unpriv = "R2 variable stack access prohibited for !root",
128+
.result_unpriv = REJECT,
129+
/* In the priviledged case, dereferencing a spilled-and-then-filled
130+
* register is rejected because the previous variable offset stack
131+
* write might have overwritten the spilled pointer (i.e. we lose track
132+
* of the spilled register when we analyze the write).
133+
*/
134+
.errstr = "R2 invalid mem access 'inv'",
135+
.result = REJECT,
136+
},
42137
{
43138
"indirect variable-offset stack access, unbounded",
44139
.insns = {

0 commit comments

Comments
 (0)