diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2018-05-15 12:27:05 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2018-05-19 14:44:24 -0400 |
| commit | af86ca4e3088fe5eacf2f7e58c01fa68ca067672 (patch) | |
| tree | d01711e5fe7b1674c2929c473ead8047001ba886 | |
| parent | 240da953fcc6a9008c92fae5b1f727ee5ed167ab (diff) | |
bpf: Prevent memory disambiguation attack
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
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
| -rw-r--r-- | include/linux/bpf_verifier.h | 1 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 59 |
2 files changed, 57 insertions, 3 deletions
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 7e61c395fddf..65cfc2f59db9 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h | |||
| @@ -146,6 +146,7 @@ struct bpf_insn_aux_data { | |||
| 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 */ |
| 149 | int sanitize_stack_off; /* stack slot to be cleared */ | ||
| 149 | bool seen; /* this insn was processed by the verifier */ | 150 | bool seen; /* this insn was processed by the verifier */ |
| 150 | }; | 151 | }; |
| 151 | 152 | ||
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5dd1dcb902bf..2ce967a63ede 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
| @@ -978,7 +978,7 @@ static bool register_is_null(struct bpf_reg_state *reg) | |||
| 978 | */ | 978 | */ |
| 979 | static int check_stack_write(struct bpf_verifier_env *env, | 979 | static int check_stack_write(struct bpf_verifier_env *env, |
| 980 | struct bpf_func_state *state, /* func where register points to */ | 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 | struct bpf_func_state *cur; /* state of the current function */ | 983 | struct bpf_func_state *cur; /* state of the current function */ |
| 984 | int i, slot = -off - 1, spi = slot / BPF_REG_SIZE, err; | 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, | |||
| 1017 | state->stack[spi].spilled_ptr = cur->regs[value_regno]; | 1017 | state->stack[spi].spilled_ptr = cur->regs[value_regno]; |
| 1018 | state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN; | 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 | state->stack[spi].slot_type[i] = STACK_SPILL; | 1045 | state->stack[spi].slot_type[i] = STACK_SPILL; |
| 1046 | } | ||
| 1022 | } else { | 1047 | } else { |
| 1023 | u8 type = STACK_MISC; | 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 | |||
| 1694 | 1719 | ||
| 1695 | if (t == BPF_WRITE) | 1720 | if (t == BPF_WRITE) |
| 1696 | err = check_stack_write(env, state, off, size, | 1721 | err = check_stack_write(env, state, off, size, |
| 1697 | value_regno); | 1722 | value_regno, insn_idx); |
| 1698 | else | 1723 | else |
| 1699 | err = check_stack_read(env, state, off, size, | 1724 | err = check_stack_read(env, state, off, size, |
| 1700 | value_regno); | 1725 | value_regno); |
| @@ -5169,6 +5194,34 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) | |||
| 5169 | else | 5194 | else |
| 5170 | continue; | 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 | if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX) | 5225 | if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX) |
| 5173 | continue; | 5226 | continue; |
| 5174 | 5227 | ||
