aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2019-04-05 10:50:08 -0400
committerDaniel Borkmann <daniel@iogearbox.net>2019-04-05 10:50:09 -0400
commit347807d3876aa9c75f64001c736e0a16a6b21acf (patch)
tree91f257572d01b1f53adb0ebf05bd74edf17e7bc5
parent636e78b1cdb40b77a79b143dbd9d94847b360efa (diff)
parent1fbd20f8b77b366ea4aeb92ade72daa7f36a7e3b (diff)
Merge branch 'bpf-varstack-fixes'
Andrey Ignatov says: ==================== v2->v3: - sanity check max value for variable offset. v1->v2: - rely on meta = NULL to reject var_off stack access to uninit buffer. This patch set is a follow-up for discussion [1]. It fixes variable offset stack access handling for raw and unprivileged mode, rejecting both of them, and sanity checks max variable offset value. Patch 1 handles raw (uninitialized) mode. Patch 2 adds test for raw mode. Patch 3 handles unprivileged mode. Patch 4 adds test for unprivileged mode. Patch 5 adds sanity check for max value of variable offset. Patch 6 adds test for variable offset max value checking. Patch 7 is a minor fix in verbose log. Unprivileged mode is an interesting case since one (and only?) way to come up with variable offset is to use pointer arithmetics. Though pointer arithmetics is already prohibited for unprivileged mode. I'm not sure if it's enough though and it seems like a good idea to still reject variable offset for unpriv in check_stack_boundary(). Please see patches 3 and 4 for more details on this. [1] https://marc.info/?l=linux-netdev&m=155419526427742&w=2 ==================== Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
-rw-r--r--kernel/bpf/verifier.c45
-rw-r--r--tools/testing/selftests/bpf/verifier/var_off.c111
2 files changed, 150 insertions, 6 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index bb27b675923c..48718e1da16d 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1426,7 +1426,7 @@ static int check_stack_access(struct bpf_verifier_env *env,
1426 char tn_buf[48]; 1426 char tn_buf[48];
1427 1427
1428 tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); 1428 tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
1429 verbose(env, "variable stack access var_off=%s off=%d size=%d", 1429 verbose(env, "variable stack access var_off=%s off=%d size=%d\n",
1430 tn_buf, off, size); 1430 tn_buf, off, size);
1431 return -EACCES; 1431 return -EACCES;
1432 } 1432 }
@@ -2226,16 +2226,50 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno,
2226 if (err) 2226 if (err)
2227 return err; 2227 return err;
2228 } else { 2228 } else {
2229 /* Variable offset is prohibited for unprivileged mode for
2230 * simplicity since it requires corresponding support in
2231 * Spectre masking for stack ALU.
2232 * See also retrieve_ptr_limit().
2233 */
2234 if (!env->allow_ptr_leaks) {
2235 char tn_buf[48];
2236
2237 tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
2238 verbose(env, "R%d indirect variable offset stack access prohibited for !root, var_off=%s\n",
2239 regno, tn_buf);
2240 return -EACCES;
2241 }
2242 /* Only initialized buffer on stack is allowed to be accessed
2243 * with variable offset. With uninitialized buffer it's hard to
2244 * guarantee that whole memory is marked as initialized on
2245 * helper return since specific bounds are unknown what may
2246 * cause uninitialized stack leaking.
2247 */
2248 if (meta && meta->raw_mode)
2249 meta = NULL;
2250
2251 if (reg->smax_value >= BPF_MAX_VAR_OFF ||
2252 reg->smax_value <= -BPF_MAX_VAR_OFF) {
2253 verbose(env, "R%d unbounded indirect variable offset stack access\n",
2254 regno);
2255 return -EACCES;
2256 }
2229 min_off = reg->smin_value + reg->off; 2257 min_off = reg->smin_value + reg->off;
2230 max_off = reg->umax_value + reg->off; 2258 max_off = reg->smax_value + reg->off;
2231 err = __check_stack_boundary(env, regno, min_off, access_size, 2259 err = __check_stack_boundary(env, regno, min_off, access_size,
2232 zero_size_allowed); 2260 zero_size_allowed);
2233 if (err) 2261 if (err) {
2262 verbose(env, "R%d min value is outside of stack bound\n",
2263 regno);
2234 return err; 2264 return err;
2265 }
2235 err = __check_stack_boundary(env, regno, max_off, access_size, 2266 err = __check_stack_boundary(env, regno, max_off, access_size,
2236 zero_size_allowed); 2267 zero_size_allowed);
2237 if (err) 2268 if (err) {
2269 verbose(env, "R%d max value is outside of stack bound\n",
2270 regno);
2238 return err; 2271 return err;
2272 }
2239 } 2273 }
2240 2274
2241 if (meta && meta->raw_mode) { 2275 if (meta && meta->raw_mode) {
@@ -3330,6 +3364,9 @@ static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
3330 3364
3331 switch (ptr_reg->type) { 3365 switch (ptr_reg->type) {
3332 case PTR_TO_STACK: 3366 case PTR_TO_STACK:
3367 /* Indirect variable offset stack access is prohibited in
3368 * unprivileged mode so it's not handled here.
3369 */
3333 off = ptr_reg->off + ptr_reg->var_off.value; 3370 off = ptr_reg->off + ptr_reg->var_off.value;
3334 if (mask_to_left) 3371 if (mask_to_left)
3335 *ptr_limit = MAX_BPF_STACK + off; 3372 *ptr_limit = MAX_BPF_STACK + off;
diff --git a/tools/testing/selftests/bpf/verifier/var_off.c b/tools/testing/selftests/bpf/verifier/var_off.c
index c4ebd0bb0781..8504ac937809 100644
--- a/tools/testing/selftests/bpf/verifier/var_off.c
+++ b/tools/testing/selftests/bpf/verifier/var_off.c
@@ -40,7 +40,35 @@
40 .prog_type = BPF_PROG_TYPE_LWT_IN, 40 .prog_type = BPF_PROG_TYPE_LWT_IN,
41}, 41},
42{ 42{
43 "indirect variable-offset stack access, out of bound", 43 "indirect variable-offset stack access, unbounded",
44 .insns = {
45 BPF_MOV64_IMM(BPF_REG_2, 6),
46 BPF_MOV64_IMM(BPF_REG_3, 28),
47 /* Fill the top 16 bytes of the stack. */
48 BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, 0),
49 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
50 /* Get an unknown value. */
51 BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_1, offsetof(struct bpf_sock_ops,
52 bytes_received)),
53 /* Check the lower bound but don't check the upper one. */
54 BPF_JMP_IMM(BPF_JSLT, BPF_REG_4, 0, 4),
55 /* Point the lower bound to initialized stack. Offset is now in range
56 * from fp-16 to fp+0x7fffffffffffffef, i.e. max value is unbounded.
57 */
58 BPF_ALU64_IMM(BPF_SUB, BPF_REG_4, 16),
59 BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_10),
60 BPF_MOV64_IMM(BPF_REG_5, 8),
61 /* Dereference it indirectly. */
62 BPF_EMIT_CALL(BPF_FUNC_getsockopt),
63 BPF_MOV64_IMM(BPF_REG_0, 0),
64 BPF_EXIT_INSN(),
65 },
66 .errstr = "R4 unbounded indirect variable offset stack access",
67 .result = REJECT,
68 .prog_type = BPF_PROG_TYPE_SOCK_OPS,
69},
70{
71 "indirect variable-offset stack access, max out of bound",
44 .insns = { 72 .insns = {
45 /* Fill the top 8 bytes of the stack */ 73 /* Fill the top 8 bytes of the stack */
46 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 74 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
@@ -60,7 +88,32 @@
60 BPF_EXIT_INSN(), 88 BPF_EXIT_INSN(),
61 }, 89 },
62 .fixup_map_hash_8b = { 5 }, 90 .fixup_map_hash_8b = { 5 },
63 .errstr = "invalid stack type R2 var_off", 91 .errstr = "R2 max value is outside of stack bound",
92 .result = REJECT,
93 .prog_type = BPF_PROG_TYPE_LWT_IN,
94},
95{
96 "indirect variable-offset stack access, min out of bound",
97 .insns = {
98 /* Fill the top 8 bytes of the stack */
99 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
100 /* Get an unknown value */
101 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0),
102 /* Make it small and 4-byte aligned */
103 BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4),
104 BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 516),
105 /* add it to fp. We now have either fp-516 or fp-512, but
106 * we don't know which
107 */
108 BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10),
109 /* dereference it indirectly */
110 BPF_LD_MAP_FD(BPF_REG_1, 0),
111 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
112 BPF_MOV64_IMM(BPF_REG_0, 0),
113 BPF_EXIT_INSN(),
114 },
115 .fixup_map_hash_8b = { 5 },
116 .errstr = "R2 min value is outside of stack bound",
64 .result = REJECT, 117 .result = REJECT,
65 .prog_type = BPF_PROG_TYPE_LWT_IN, 118 .prog_type = BPF_PROG_TYPE_LWT_IN,
66}, 119},
@@ -115,6 +168,60 @@
115 .prog_type = BPF_PROG_TYPE_LWT_IN, 168 .prog_type = BPF_PROG_TYPE_LWT_IN,
116}, 169},
117{ 170{
171 "indirect variable-offset stack access, priv vs unpriv",
172 .insns = {
173 /* Fill the top 16 bytes of the stack. */
174 BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, 0),
175 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
176 /* Get an unknown value. */
177 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0),
178 /* Make it small and 4-byte aligned. */
179 BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4),
180 BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 16),
181 /* Add it to fp. We now have either fp-12 or fp-16, we don't know
182 * which, but either way it points to initialized stack.
183 */
184 BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10),
185 /* Dereference it indirectly. */
186 BPF_LD_MAP_FD(BPF_REG_1, 0),
187 BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
188 BPF_MOV64_IMM(BPF_REG_0, 0),
189 BPF_EXIT_INSN(),
190 },
191 .fixup_map_hash_8b = { 6 },
192 .errstr_unpriv = "R2 stack pointer arithmetic goes out of range, prohibited for !root",
193 .result_unpriv = REJECT,
194 .result = ACCEPT,
195 .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
196},
197{
198 "indirect variable-offset stack access, uninitialized",
199 .insns = {
200 BPF_MOV64_IMM(BPF_REG_2, 6),
201 BPF_MOV64_IMM(BPF_REG_3, 28),
202 /* Fill the top 16 bytes of the stack. */
203 BPF_ST_MEM(BPF_W, BPF_REG_10, -16, 0),
204 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
205 /* Get an unknown value. */
206 BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1, 0),
207 /* Make it small and 4-byte aligned. */
208 BPF_ALU64_IMM(BPF_AND, BPF_REG_4, 4),
209 BPF_ALU64_IMM(BPF_SUB, BPF_REG_4, 16),
210 /* Add it to fp. We now have either fp-12 or fp-16, we don't know
211 * which, but either way it points to initialized stack.
212 */
213 BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_10),
214 BPF_MOV64_IMM(BPF_REG_5, 8),
215 /* Dereference it indirectly. */
216 BPF_EMIT_CALL(BPF_FUNC_getsockopt),
217 BPF_MOV64_IMM(BPF_REG_0, 0),
218 BPF_EXIT_INSN(),
219 },
220 .errstr = "invalid indirect read from stack var_off",
221 .result = REJECT,
222 .prog_type = BPF_PROG_TYPE_SOCK_OPS,
223},
224{
118 "indirect variable-offset stack access, ok", 225 "indirect variable-offset stack access, ok",
119 .insns = { 226 .insns = {
120 /* Fill the top 16 bytes of the stack. */ 227 /* Fill the top 16 bytes of the stack. */