Detect code patterns where malicious 'speculative store bypass' can be used and sanitize such patterns. 39: (bf) r3 = r10 40: (07) r3 += -216 41: (79) r8 = *(u64 *)(r7 +0) // slow read 42: (7a) *(u64 *)(r10 -72) = 0 // verifier inserts this instruction 43: (7b) *(u64 *)(r8 +0) = r3 // this store becomes slow due to r8 44: (79) r1 = *(u64 *)(r6 +0) // cpu speculatively executes this load 45: (71) r2 = *(u8 *)(r1 +0) // speculatively arbitrary 'load byte' // is now sanitized Above code after x86 JIT becomes: e5: mov %rbp,%rdx e8: add $0xffffffffffffff28,%rdx ef: mov 0x0(%r13),%r14 f3: movq $0x0,-0x48(%rbp) fb: mov %rdx,0x0(%r14) ff: mov 0x0(%rbx),%rdi 103: movzbq 0x0(%rdi),%rsi
Showing the changes to kernel (1 of 2 files). Show all
Unified · Split
| @@ -978,7 +978,7 @@ static bool register_is_null(struct bpf_reg_state *reg) · 977 unmodified lines | ||
| 978 | 978 | */ |
| 979 | 979 | static int check_stack_write(struct bpf_verifier_env *env, |
| 980 | 980 | struct bpf_func_state *state, /* func where register points to */ |
| 981 | − int off, int size, int value_regno) | |
| 981 | + int off, int size, int value_regno, int insn_idx) | |
| 982 | 982 | { |
| 983 | 983 | struct bpf_func_state *cur; /* state of the current function */ |
| 984 | 984 | int i, slot = -off - 1, spi = slot / BPF_REG_SIZE, err; |
| @@ -1017,8 +1017,33 @@ static int check_stack_write(struct bpf_verifier_env *env, · 32 unmodified lines | ||
| 1017 | 1017 | state->stack[spi].spilled_ptr = cur->regs[value_regno]; |
| 1018 | 1018 | state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN; |
| 1019 | 1019 | |
| 1020 | − for (i = 0; i < BPF_REG_SIZE; i++) | |
| 1020 | + for (i = 0; i < BPF_REG_SIZE; i++) { | |
| 1021 | + if (state->stack[spi].slot_type[i] == STACK_MISC && | |
| 1022 | + !env->allow_ptr_leaks) { | |
| 1023 | + int *poff = &env->insn_aux_data[insn_idx].sanitize_stack_off; | |
| 1024 | + int soff = (-spi - 1) * BPF_REG_SIZE; | |
| 1025 | + | |
| 1026 | + /* detected reuse of integer stack slot with a pointer | |
| 1027 | + * which means either llvm is reusing stack slot or | |
| 1028 | + * an attacker is trying to exploit CVE-2018-3639 | |
| 1029 | + * (speculative store bypass) | |
| 1030 | + * Have to sanitize that slot with preemptive | |
| 1031 | + * store of zero. | |
| 1032 | + */ | |
| 1033 | + if (*poff && *poff != soff) { | |
| 1034 | + /* disallow programs where single insn stores | |
| 1035 | + * into two different stack slots, since verifier | |
| 1036 | + * cannot sanitize them | |
| 1037 | + */ | |
| 1038 | + verbose(env, | |
| 1039 | + "insn %d cannot access two stack slots fp%d and fp%d", | |
| 1040 | + insn_idx, *poff, soff); | |
| 1041 | + return -EINVAL; | |
| 1042 | + } | |
| 1043 | + *poff = soff; | |
| 1044 | + } | |
| 1021 | 1045 | state->stack[spi].slot_type[i] = STACK_SPILL; |
| 1046 | + } | |
| 1022 | 1047 | } else { |
| 1023 | 1048 | u8 type = STACK_MISC; |
| 1024 | 1049 | |
| @@ -1694,7 +1719,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn · 669 unmodified lines | ||
| 1694 | 1719 | |
| 1695 | 1720 | if (t == BPF_WRITE) |
| 1696 | 1721 | err = check_stack_write(env, state, off, size, |
| 1697 | − value_regno); | |
| 1722 | + value_regno, insn_idx); | |
| 1698 | 1723 | else |
| 1699 | 1724 | err = check_stack_read(env, state, off, size, |
| 1700 | 1725 | value_regno); |
| @@ -5169,6 +5194,34 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) · 3468 unmodified lines | ||
| 5169 | 5194 | else |
| 5170 | 5195 | continue; |
| 5171 | 5196 | |
| 5197 | + if (type == BPF_WRITE && | |
| 5198 | + env->insn_aux_data[i + delta].sanitize_stack_off) { | |
| 5199 | + struct bpf_insn patch[] = { | |
| 5200 | + /* Sanitize suspicious stack slot with zero. | |
| 5201 | + * There are no memory dependencies for this store, | |
| 5202 | + * since it's only using frame pointer and immediate | |
| 5203 | + * constant of zero | |
| 5204 | + */ | |
| 5205 | + BPF_ST_MEM(BPF_DW, BPF_REG_FP, | |
| 5206 | + env->insn_aux_data[i + delta].sanitize_stack_off, | |
| 5207 | + 0), | |
| 5208 | + /* the original STX instruction will immediately | |
| 5209 | + * overwrite the same stack slot with appropriate value | |
| 5210 | + */ | |
| 5211 | + *insn, | |
| 5212 | + }; | |
| 5213 | + | |
| 5214 | + cnt = ARRAY_SIZE(patch); | |
| 5215 | + new_prog = bpf_patch_insn_data(env, i + delta, patch, cnt); | |
| 5216 | + if (!new_prog) | |
| 5217 | + return -ENOMEM; | |
| 5218 | + | |
| 5219 | + delta += cnt - 1; | |
| 5220 | + env->prog = new_prog; | |
| 5221 | + insn = new_prog->insnsi + i + delta; | |
| 5222 | + continue; | |
| 5223 | + } | |
| 5224 | + | |
| 5172 | 5225 | if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX) |
| 5173 | 5226 | continue; |
| 5174 | 5227 | |