Skip to content

Commit 801c605

Browse files
committed
bpf: Fix leakage of uninitialized bpf stack under speculation
The current implemented mechanisms to mitigate data disclosure under speculation mainly address stack and map value oob access from the speculative domain. However, Piotr discovered that uninitialized BPF stack is not protected yet, and thus old data from the kernel stack, potentially including addresses of kernel structures, could still be extracted from that 512 bytes large window. The BPF stack is special compared to map values since it's not zero initialized for every program invocation, whereas map values /are/ zero initialized upon their initial allocation and thus cannot leak any prior data in either domain. In the non-speculative domain, the verifier ensures that every stack slot read must have a prior stack slot write by the BPF program to avoid such data leaking issue. However, this is not enough: for example, when the pointer arithmetic operation moves the stack pointer from the last valid stack offset to the first valid offset, the sanitation logic allows for any intermediate offsets during speculative execution, which could then be used to extract any restricted stack content via side-channel. Given for unprivileged stack pointer arithmetic the use of unknown but bounded scalars is generally forbidden, we can simply turn the register-based arithmetic operation into an immediate-based arithmetic operation without the need for masking. This also gives the benefit of reducing the needed instructions for the operation. Given after the work in 7fedb63 ("bpf: Tighten speculative pointer arithmetic mask"), the aux->alu_limit already holds the final immediate value for the offset register with the known scalar. Thus, a simple mov of the immediate to AX register with using AX as the source for the original instruction is sufficient and possible now in this case. Reported-by: Piotr Krysiuk <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Tested-by: Piotr Krysiuk <[email protected]> Reviewed-by: Piotr Krysiuk <[email protected]> Reviewed-by: John Fastabend <[email protected]> Acked-by: Alexei Starovoitov <[email protected]>
1 parent b9b34dd commit 801c605

File tree

2 files changed

+20
-12
lines changed

2 files changed

+20
-12
lines changed

include/linux/bpf_verifier.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,11 @@ struct bpf_verifier_state_list {
302302
};
303303

304304
/* Possible states for alu_state member. */
305-
#define BPF_ALU_SANITIZE_SRC 1U
306-
#define BPF_ALU_SANITIZE_DST 2U
305+
#define BPF_ALU_SANITIZE_SRC (1U << 0)
306+
#define BPF_ALU_SANITIZE_DST (1U << 1)
307307
#define BPF_ALU_NEG_VALUE (1U << 2)
308308
#define BPF_ALU_NON_POINTER (1U << 3)
309+
#define BPF_ALU_IMMEDIATE (1U << 4)
309310
#define BPF_ALU_SANITIZE (BPF_ALU_SANITIZE_SRC | \
310311
BPF_ALU_SANITIZE_DST)
311312

kernel/bpf/verifier.c

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6496,6 +6496,7 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
64966496
{
64976497
struct bpf_insn_aux_data *aux = commit_window ? cur_aux(env) : tmp_aux;
64986498
struct bpf_verifier_state *vstate = env->cur_state;
6499+
bool off_is_imm = tnum_is_const(off_reg->var_off);
64996500
bool off_is_neg = off_reg->smin_value < 0;
65006501
bool ptr_is_dst_reg = ptr_reg == dst_reg;
65016502
u8 opcode = BPF_OP(insn->code);
@@ -6526,6 +6527,7 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
65266527
alu_limit = abs(tmp_aux->alu_limit - alu_limit);
65276528
} else {
65286529
alu_state = off_is_neg ? BPF_ALU_NEG_VALUE : 0;
6530+
alu_state |= off_is_imm ? BPF_ALU_IMMEDIATE : 0;
65296531
alu_state |= ptr_is_dst_reg ?
65306532
BPF_ALU_SANITIZE_SRC : BPF_ALU_SANITIZE_DST;
65316533
}
@@ -12371,7 +12373,7 @@ static int do_misc_fixups(struct bpf_verifier_env *env)
1237112373
const u8 code_add = BPF_ALU64 | BPF_ADD | BPF_X;
1237212374
const u8 code_sub = BPF_ALU64 | BPF_SUB | BPF_X;
1237312375
struct bpf_insn *patch = &insn_buf[0];
12374-
bool issrc, isneg;
12376+
bool issrc, isneg, isimm;
1237512377
u32 off_reg;
1237612378

1237712379
aux = &env->insn_aux_data[i + delta];
@@ -12382,24 +12384,29 @@ static int do_misc_fixups(struct bpf_verifier_env *env)
1238212384
isneg = aux->alu_state & BPF_ALU_NEG_VALUE;
1238312385
issrc = (aux->alu_state & BPF_ALU_SANITIZE) ==
1238412386
BPF_ALU_SANITIZE_SRC;
12387+
isimm = aux->alu_state & BPF_ALU_IMMEDIATE;
1238512388

1238612389
off_reg = issrc ? insn->src_reg : insn->dst_reg;
12387-
if (isneg)
12388-
*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);
12389-
*patch++ = BPF_MOV32_IMM(BPF_REG_AX, aux->alu_limit);
12390-
*patch++ = BPF_ALU64_REG(BPF_SUB, BPF_REG_AX, off_reg);
12391-
*patch++ = BPF_ALU64_REG(BPF_OR, BPF_REG_AX, off_reg);
12392-
*patch++ = BPF_ALU64_IMM(BPF_NEG, BPF_REG_AX, 0);
12393-
*patch++ = BPF_ALU64_IMM(BPF_ARSH, BPF_REG_AX, 63);
12394-
*patch++ = BPF_ALU64_REG(BPF_AND, BPF_REG_AX, off_reg);
12390+
if (isimm) {
12391+
*patch++ = BPF_MOV32_IMM(BPF_REG_AX, aux->alu_limit);
12392+
} else {
12393+
if (isneg)
12394+
*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);
12395+
*patch++ = BPF_MOV32_IMM(BPF_REG_AX, aux->alu_limit);
12396+
*patch++ = BPF_ALU64_REG(BPF_SUB, BPF_REG_AX, off_reg);
12397+
*patch++ = BPF_ALU64_REG(BPF_OR, BPF_REG_AX, off_reg);
12398+
*patch++ = BPF_ALU64_IMM(BPF_NEG, BPF_REG_AX, 0);
12399+
*patch++ = BPF_ALU64_IMM(BPF_ARSH, BPF_REG_AX, 63);
12400+
*patch++ = BPF_ALU64_REG(BPF_AND, BPF_REG_AX, off_reg);
12401+
}
1239512402
if (!issrc)
1239612403
*patch++ = BPF_MOV64_REG(insn->dst_reg, insn->src_reg);
1239712404
insn->src_reg = BPF_REG_AX;
1239812405
if (isneg)
1239912406
insn->code = insn->code == code_add ?
1240012407
code_sub : code_add;
1240112408
*patch++ = *insn;
12402-
if (issrc && isneg)
12409+
if (issrc && isneg && !isimm)
1240312410
*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);
1240412411
cnt = patch - insn_buf;
1240512412

0 commit comments

Comments
 (0)