History

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

Alexei Starovoitov committed 8 years ago

Commit af86ca41 parent 240da952 files · +57 −3Browse files

Showing the changes to kernel (1 of 2 files). Show all

Unified · Split

kernel/bpf/verifier.c+56−3
@@ -978,7 +978,7 @@ static bool register_is_null(struct bpf_reg_state *reg) · 977 unmodified lines
978978 */
979979 static int check_stack_write(struct bpf_verifier_env *env,
980980 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)
982982 {
983983 struct bpf_func_state *cur; /* state of the current function */
984984 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
10171017 state->stack[spi].spilled_ptr = cur->regs[value_regno];
10181018 state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN;
10191019
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+ }
10211045 state->stack[spi].slot_type[i] = STACK_SPILL;
1046+ }
10221047 } else {
10231048 u8 type = STACK_MISC;
10241049
@@ -1694,7 +1719,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn · 669 unmodified lines
16941719
16951720 if (t == BPF_WRITE)
16961721 err = check_stack_write(env, state, off, size,
1697− value_regno);
1722+ value_regno, insn_idx);
16981723 else
16991724 err = check_stack_read(env, state, off, size,
17001725 value_regno);
@@ -5169,6 +5194,34 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) · 3468 unmodified lines
51695194 else
51705195 continue;
51715196
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+
51725225 if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX)
51735226 continue;
51745227