Skip to content

Commit 3666372

Browse files
committed
bpf: Allow nospec-protected var-offset stack access
Insert a nospec before the access to prevent it from ever using an index that is subject to speculative scalar-confusion. The access itself can either happen directly in the BPF program (reads only, check_stack_read_var_off()) or in a helper (read/write, check_helper_mem_access()). This relies on the fact that the speculative scalar confusion that leads to the variable-stack access going OOBs must stem from a prior speculative store or branch bypass. Adding a nospec before the variable-stack access will force all previously bypassed stores/branches to complete and cause the stack access to only ever go to the stack slot that is accessed architecturally. Alternatively, the variable-offset stack access might be a write that can itself be subject to speculative store bypass (this can happen in theory even if this code adds a nospec /before/ the variable-offset write). Only indirect writes by helpers might be affected here (e.g., those taking ARG_PTR_TO_MAP_VALUE). (Because check_stack_write_var_off() does not use check_stack_range_initialized(), in-program variable-offset writes are not affected.) If the in-helper write can be subject to Spectre v4 and the helper writes/overwrites pointers on the BPF stack, they are already a problem for fixed-offset stack accesses and should be subject to Spectre v4 sanitization. Signed-off-by: Luis Gerhorst <[email protected]> Acked-by: Henriette Herzog <[email protected]> Cc: Maximilian Ott <[email protected]> Cc: Milan Stephan <[email protected]>
1 parent 8098353 commit 3666372

File tree

1 file changed

+12
-12
lines changed

1 file changed

+12
-12
lines changed

kernel/bpf/verifier.c

+12-12
Original file line numberDiff line numberDiff line change
@@ -7894,6 +7894,11 @@ static int check_atomic(struct bpf_verifier_env *env, struct bpf_insn *insn)
78947894
}
78957895
}
78967896

7897+
static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env)
7898+
{
7899+
return &env->insn_aux_data[env->insn_idx];
7900+
}
7901+
78977902
/* When register 'regno' is used to read the stack (either directly or through
78987903
* a helper function) make sure that it's within stack boundary and, depending
78997904
* on the access type and privileges, that all elements of the stack are
@@ -7933,18 +7938,18 @@ static int check_stack_range_initialized(
79337938
if (tnum_is_const(reg->var_off)) {
79347939
min_off = max_off = reg->var_off.value + off;
79357940
} else {
7936-
/* Variable offset is prohibited for unprivileged mode for
7941+
/* Variable offset requires a nospec for unprivileged mode for
79377942
* simplicity since it requires corresponding support in
79387943
* Spectre masking for stack ALU.
79397944
* See also retrieve_ptr_limit().
79407945
*/
79417946
if (!env->bypass_spec_v1) {
7942-
char tn_buf[48];
7943-
7944-
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
7945-
verbose(env, "R%d variable offset stack access prohibited for !root, var_off=%s\n",
7946-
regno, tn_buf);
7947-
return -EACCES;
7947+
/* Allow the access, but prevent it from using a
7948+
* speculative offset using a nospec before the
7949+
* dereference op.
7950+
*/
7951+
cur_aux(env)->nospec = true;
7952+
WARN_ON_ONCE(cur_aux(env)->alu_state);
79487953
}
79497954
/* Only initialized buffer on stack is allowed to be accessed
79507955
* with variable offset. With uninitialized buffer it's hard to
@@ -11172,11 +11177,6 @@ static int check_get_func_ip(struct bpf_verifier_env *env)
1117211177
return -ENOTSUPP;
1117311178
}
1117411179

11175-
static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env)
11176-
{
11177-
return &env->insn_aux_data[env->insn_idx];
11178-
}
11179-
1118011180
static bool loop_flag_is_zero(struct bpf_verifier_env *env)
1118111181
{
1118211182
struct bpf_reg_state *regs = cur_regs(env);

0 commit comments

Comments
 (0)