aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2018-05-23 20:32:53 -0400
committerAlexei Starovoitov <ast@kernel.org>2018-05-24 11:15:43 -0400
commitc93552c443ebc63b14e26e46d2e76941c88e0d71 (patch)
treea9f74173e8e13c68a257f705ca1e4e8dcc8c0b05
parent1a2b80ecc7ad374e9ef6a3de6fdd032d94be2270 (diff)
bpf: properly enforce index mask to prevent out-of-bounds speculation
While reviewing the verifier code, I recently noticed that the following two program variants in relation to tail calls can be loaded. Variant 1: # bpftool p d x i 15 0: (15) if r1 == 0x0 goto pc+3 1: (18) r2 = map[id:5] 3: (05) goto pc+2 4: (18) r2 = map[id:6] 6: (b7) r3 = 7 7: (35) if r3 >= 0xa0 goto pc+2 8: (54) (u32) r3 &= (u32) 255 9: (85) call bpf_tail_call#12 10: (b7) r0 = 1 11: (95) exit # bpftool m s i 5 5: prog_array flags 0x0 key 4B value 4B max_entries 4 memlock 4096B # bpftool m s i 6 6: prog_array flags 0x0 key 4B value 4B max_entries 160 memlock 4096B Variant 2: # bpftool p d x i 20 0: (15) if r1 == 0x0 goto pc+3 1: (18) r2 = map[id:8] 3: (05) goto pc+2 4: (18) r2 = map[id:7] 6: (b7) r3 = 7 7: (35) if r3 >= 0x4 goto pc+2 8: (54) (u32) r3 &= (u32) 3 9: (85) call bpf_tail_call#12 10: (b7) r0 = 1 11: (95) exit # bpftool m s i 8 8: prog_array flags 0x0 key 4B value 4B max_entries 160 memlock 4096B # bpftool m s i 7 7: prog_array flags 0x0 key 4B value 4B max_entries 4 memlock 4096B In both cases the index masking inserted by the verifier in order to control out of bounds speculation from a CPU via b2157399cc98 ("bpf: prevent out-of-bounds speculation") seems to be incorrect in what it is enforcing. In the 1st variant, the mask is applied from the map with the significantly larger number of entries where we would allow to a certain degree out of bounds speculation for the smaller map, and in the 2nd variant where the mask is applied from the map with the smaller number of entries, we get buggy behavior since we truncate the index of the larger map. The original intent from commit b2157399cc98 is to reject such occasions where two or more different tail call maps are used in the same tail call helper invocation. However, the check on the BPF_MAP_PTR_POISON is never hit since we never poisoned the saved pointer in the first place! We do this explicitly for map lookups but in case of tail calls we basically used the tail call map in insn_aux_data that was processed in the most recent path which the verifier walked. Thus any prior path that stored a pointer in insn_aux_data at the helper location was always overridden. Fix it by moving the map pointer poison logic into a small helper that covers both BPF helpers with the same logic. After that in fixup_bpf_calls() the poison check is then hit for tail calls and the program rejected. Latter only happens in unprivileged case since this is the *only* occasion where a rewrite needs to happen, and where such rewrite is specific to the map (max_entries, index_mask). In the privileged case the rewrite is generic for the insn->imm / insn->code update so multiple maps from different paths can be handled just fine since all the remaining logic happens in the instruction processing itself. This is similar to the case of map lookups: in case there is a collision of maps in fixup_bpf_calls() we must skip the inlined rewrite since this will turn the generic instruction sequence into a non- generic one. Thus the patch_call_imm will simply update the insn->imm location where the bpf_map_lookup_elem() will later take care of the dispatch. Given we need this 'poison' state as a check, the information of whether a map is an unpriv_array gets lost, so enforcing it prior to that needs an additional state. In general this check is needed since there are some complex and tail call intensive BPF programs out there where LLVM tends to generate such code occasionally. We therefore convert the map_ptr rather into map_state to store all this w/o extra memory overhead, and the bit whether one of the maps involved in the collision was from an unpriv_array thus needs to be retained as well there. Fixes: b2157399cc98 ("bpf: prevent out-of-bounds speculation") Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r--include/linux/bpf_verifier.h2
-rw-r--r--kernel/bpf/verifier.c86
2 files changed, 65 insertions, 23 deletions
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 7e61c395fddf..52fb077d3c45 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -142,7 +142,7 @@ struct bpf_verifier_state_list {
142struct bpf_insn_aux_data { 142struct bpf_insn_aux_data {
143 union { 143 union {
144 enum bpf_reg_type ptr_type; /* pointer type for load/store insns */ 144 enum bpf_reg_type ptr_type; /* pointer type for load/store insns */
145 struct bpf_map *map_ptr; /* pointer for call insn into lookup_elem */ 145 unsigned long map_state; /* pointer/poison value for maps */
146 s32 call_imm; /* saved imm field of call insn */ 146 s32 call_imm; /* saved imm field of call insn */
147 }; 147 };
148 int ctx_field_size; /* the ctx field size for load insn, maybe 0 */ 148 int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5dd1dcb902bf..dcebf3f7365c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -156,7 +156,29 @@ struct bpf_verifier_stack_elem {
156#define BPF_COMPLEXITY_LIMIT_INSNS 131072 156#define BPF_COMPLEXITY_LIMIT_INSNS 131072
157#define BPF_COMPLEXITY_LIMIT_STACK 1024 157#define BPF_COMPLEXITY_LIMIT_STACK 1024
158 158
159#define BPF_MAP_PTR_POISON ((void *)0xeB9F + POISON_POINTER_DELTA) 159#define BPF_MAP_PTR_UNPRIV 1UL
160#define BPF_MAP_PTR_POISON ((void *)((0xeB9FUL << 1) + \
161 POISON_POINTER_DELTA))
162#define BPF_MAP_PTR(X) ((struct bpf_map *)((X) & ~BPF_MAP_PTR_UNPRIV))
163
164static bool bpf_map_ptr_poisoned(const struct bpf_insn_aux_data *aux)
165{
166 return BPF_MAP_PTR(aux->map_state) == BPF_MAP_PTR_POISON;
167}
168
169static bool bpf_map_ptr_unpriv(const struct bpf_insn_aux_data *aux)
170{
171 return aux->map_state & BPF_MAP_PTR_UNPRIV;
172}
173
174static void bpf_map_ptr_store(struct bpf_insn_aux_data *aux,
175 const struct bpf_map *map, bool unpriv)
176{
177 BUILD_BUG_ON((unsigned long)BPF_MAP_PTR_POISON & BPF_MAP_PTR_UNPRIV);
178 unpriv |= bpf_map_ptr_unpriv(aux);
179 aux->map_state = (unsigned long)map |
180 (unpriv ? BPF_MAP_PTR_UNPRIV : 0UL);
181}
160 182
161struct bpf_call_arg_meta { 183struct bpf_call_arg_meta {
162 struct bpf_map *map_ptr; 184 struct bpf_map *map_ptr;
@@ -2333,6 +2355,29 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
2333 return 0; 2355 return 0;
2334} 2356}
2335 2357
2358static int
2359record_func_map(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
2360 int func_id, int insn_idx)
2361{
2362 struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
2363
2364 if (func_id != BPF_FUNC_tail_call &&
2365 func_id != BPF_FUNC_map_lookup_elem)
2366 return 0;
2367 if (meta->map_ptr == NULL) {
2368 verbose(env, "kernel subsystem misconfigured verifier\n");
2369 return -EINVAL;
2370 }
2371
2372 if (!BPF_MAP_PTR(aux->map_state))
2373 bpf_map_ptr_store(aux, meta->map_ptr,
2374 meta->map_ptr->unpriv_array);
2375 else if (BPF_MAP_PTR(aux->map_state) != meta->map_ptr)
2376 bpf_map_ptr_store(aux, BPF_MAP_PTR_POISON,
2377 meta->map_ptr->unpriv_array);
2378 return 0;
2379}
2380
2336static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn_idx) 2381static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn_idx)
2337{ 2382{
2338 const struct bpf_func_proto *fn = NULL; 2383 const struct bpf_func_proto *fn = NULL;
@@ -2387,13 +2432,6 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
2387 err = check_func_arg(env, BPF_REG_2, fn->arg2_type, &meta); 2432 err = check_func_arg(env, BPF_REG_2, fn->arg2_type, &meta);
2388 if (err) 2433 if (err)
2389 return err; 2434 return err;
2390 if (func_id == BPF_FUNC_tail_call) {
2391 if (meta.map_ptr == NULL) {
2392 verbose(env, "verifier bug\n");
2393 return -EINVAL;
2394 }
2395 env->insn_aux_data[insn_idx].map_ptr = meta.map_ptr;
2396 }
2397 err = check_func_arg(env, BPF_REG_3, fn->arg3_type, &meta); 2435 err = check_func_arg(env, BPF_REG_3, fn->arg3_type, &meta);
2398 if (err) 2436 if (err)
2399 return err; 2437 return err;
@@ -2404,6 +2442,10 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
2404 if (err) 2442 if (err)
2405 return err; 2443 return err;
2406 2444
2445 err = record_func_map(env, &meta, func_id, insn_idx);
2446 if (err)
2447 return err;
2448
2407 /* Mark slots with STACK_MISC in case of raw mode, stack offset 2449 /* Mark slots with STACK_MISC in case of raw mode, stack offset
2408 * is inferred from register state. 2450 * is inferred from register state.
2409 */ 2451 */
@@ -2428,8 +2470,6 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
2428 } else if (fn->ret_type == RET_VOID) { 2470 } else if (fn->ret_type == RET_VOID) {
2429 regs[BPF_REG_0].type = NOT_INIT; 2471 regs[BPF_REG_0].type = NOT_INIT;
2430 } else if (fn->ret_type == RET_PTR_TO_MAP_VALUE_OR_NULL) { 2472 } else if (fn->ret_type == RET_PTR_TO_MAP_VALUE_OR_NULL) {
2431 struct bpf_insn_aux_data *insn_aux;
2432
2433 regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL; 2473 regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL;
2434 /* There is no offset yet applied, variable or fixed */ 2474 /* There is no offset yet applied, variable or fixed */
2435 mark_reg_known_zero(env, regs, BPF_REG_0); 2475 mark_reg_known_zero(env, regs, BPF_REG_0);
@@ -2445,11 +2485,6 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
2445 } 2485 }
2446 regs[BPF_REG_0].map_ptr = meta.map_ptr; 2486 regs[BPF_REG_0].map_ptr = meta.map_ptr;
2447 regs[BPF_REG_0].id = ++env->id_gen; 2487 regs[BPF_REG_0].id = ++env->id_gen;
2448 insn_aux = &env->insn_aux_data[insn_idx];
2449 if (!insn_aux->map_ptr)
2450 insn_aux->map_ptr = meta.map_ptr;
2451 else if (insn_aux->map_ptr != meta.map_ptr)
2452 insn_aux->map_ptr = BPF_MAP_PTR_POISON;
2453 } else { 2488 } else {
2454 verbose(env, "unknown return type %d of func %s#%d\n", 2489 verbose(env, "unknown return type %d of func %s#%d\n",
2455 fn->ret_type, func_id_name(func_id), func_id); 2490 fn->ret_type, func_id_name(func_id), func_id);
@@ -5417,6 +5452,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
5417 struct bpf_insn *insn = prog->insnsi; 5452 struct bpf_insn *insn = prog->insnsi;
5418 const struct bpf_func_proto *fn; 5453 const struct bpf_func_proto *fn;
5419 const int insn_cnt = prog->len; 5454 const int insn_cnt = prog->len;
5455 struct bpf_insn_aux_data *aux;
5420 struct bpf_insn insn_buf[16]; 5456 struct bpf_insn insn_buf[16];
5421 struct bpf_prog *new_prog; 5457 struct bpf_prog *new_prog;
5422 struct bpf_map *map_ptr; 5458 struct bpf_map *map_ptr;
@@ -5491,19 +5527,22 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
5491 insn->imm = 0; 5527 insn->imm = 0;
5492 insn->code = BPF_JMP | BPF_TAIL_CALL; 5528 insn->code = BPF_JMP | BPF_TAIL_CALL;
5493 5529
5530 aux = &env->insn_aux_data[i + delta];
5531 if (!bpf_map_ptr_unpriv(aux))
5532 continue;
5533
5494 /* instead of changing every JIT dealing with tail_call 5534 /* instead of changing every JIT dealing with tail_call
5495 * emit two extra insns: 5535 * emit two extra insns:
5496 * if (index >= max_entries) goto out; 5536 * if (index >= max_entries) goto out;
5497 * index &= array->index_mask; 5537 * index &= array->index_mask;
5498 * to avoid out-of-bounds cpu speculation 5538 * to avoid out-of-bounds cpu speculation
5499 */ 5539 */
5500 map_ptr = env->insn_aux_data[i + delta].map_ptr; 5540 if (bpf_map_ptr_poisoned(aux)) {
5501 if (map_ptr == BPF_MAP_PTR_POISON) {
5502 verbose(env, "tail_call abusing map_ptr\n"); 5541 verbose(env, "tail_call abusing map_ptr\n");
5503 return -EINVAL; 5542 return -EINVAL;
5504 } 5543 }
5505 if (!map_ptr->unpriv_array) 5544
5506 continue; 5545 map_ptr = BPF_MAP_PTR(aux->map_state);
5507 insn_buf[0] = BPF_JMP_IMM(BPF_JGE, BPF_REG_3, 5546 insn_buf[0] = BPF_JMP_IMM(BPF_JGE, BPF_REG_3,
5508 map_ptr->max_entries, 2); 5547 map_ptr->max_entries, 2);
5509 insn_buf[1] = BPF_ALU32_IMM(BPF_AND, BPF_REG_3, 5548 insn_buf[1] = BPF_ALU32_IMM(BPF_AND, BPF_REG_3,
@@ -5527,9 +5566,12 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
5527 */ 5566 */
5528 if (prog->jit_requested && BITS_PER_LONG == 64 && 5567 if (prog->jit_requested && BITS_PER_LONG == 64 &&
5529 insn->imm == BPF_FUNC_map_lookup_elem) { 5568 insn->imm == BPF_FUNC_map_lookup_elem) {
5530 map_ptr = env->insn_aux_data[i + delta].map_ptr; 5569 aux = &env->insn_aux_data[i + delta];
5531 if (map_ptr == BPF_MAP_PTR_POISON || 5570 if (bpf_map_ptr_poisoned(aux))
5532 !map_ptr->ops->map_gen_lookup) 5571 goto patch_call_imm;
5572
5573 map_ptr = BPF_MAP_PTR(aux->map_state);
5574 if (!map_ptr->ops->map_gen_lookup)
5533 goto patch_call_imm; 5575 goto patch_call_imm;
5534 5576
5535 cnt = map_ptr->ops->map_gen_lookup(map_ptr, insn_buf); 5577 cnt = map_ptr->ops->map_gen_lookup(map_ptr, insn_buf);