mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
bpf: collect only live registers in linked regs
Fix an inconsistency between func_states_equal() and
collect_linked_regs():
- regsafe() uses check_ids() to verify that cached and current states
have identical register id mapping.
- func_states_equal() calls regsafe() only for registers computed as
live by compute_live_registers().
- clean_live_states() is supposed to remove dead registers from cached
states, but it can skip states belonging to an iterator-based loop.
- collect_linked_regs() collects all registers sharing the same id,
ignoring the marks computed by compute_live_registers().
Linked registers are stored in the state's jump history.
- backtrack_insn() marks all linked registers for an instruction
as precise whenever one of the linked registers is precise.
The above might lead to a scenario:
- There is an instruction I with register rY known to be dead at I.
- Instruction I is reached via two paths: first A, then B.
- On path A:
- There is an id link between registers rX and rY.
- Checkpoint C is created at I.
- Linked register set {rX, rY} is saved to the jump history.
- rX is marked as precise at I, causing both rX and rY
to be marked precise at C.
- On path B:
- There is no id link between registers rX and rY,
otherwise register states are sub-states of those in C.
- Because rY is dead at I, check_ids() returns true.
- Current state is considered equal to checkpoint C,
propagate_precision() propagates spurious precision
mark for register rY along the path B.
- Depending on a program, this might hit verifier_bug()
in the backtrack_insn(), e.g. if rY ∈ [r1..r5]
and backtrack_insn() spots a function call.
The reproducer program is in the next patch.
This was hit by sched_ext scx_lavd scheduler code.
Changes in tests:
- verifier_scalar_ids.c selftests need modification to preserve
some registers as live for __msg() checks.
- exceptions_assert.c adjusted to match changes in the verifier log,
R0 is dead after conditional instruction and thus does not get
range.
- precise.c adjusted to match changes in the verifier log, register r9
is dead after comparison and it's range is not important for test.
Reported-by: Emil Tsalapatis <emil@etsalapatis.com>
Fixes: 0fb3cf6110 ("bpf: use register liveness information for func_states_equal")
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20260306-linked-regs-and-propagate-precision-v1-1-18e859be570d@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
6895e1d7c3
commit
2658a1720a
4 changed files with 73 additions and 38 deletions
|
|
@ -17359,17 +17359,24 @@ static void __collect_linked_regs(struct linked_regs *reg_set, struct bpf_reg_st
|
|||
* in verifier state, save R in linked_regs if R->id == id.
|
||||
* If there are too many Rs sharing same id, reset id for leftover Rs.
|
||||
*/
|
||||
static void collect_linked_regs(struct bpf_verifier_state *vstate, u32 id,
|
||||
static void collect_linked_regs(struct bpf_verifier_env *env,
|
||||
struct bpf_verifier_state *vstate,
|
||||
u32 id,
|
||||
struct linked_regs *linked_regs)
|
||||
{
|
||||
struct bpf_insn_aux_data *aux = env->insn_aux_data;
|
||||
struct bpf_func_state *func;
|
||||
struct bpf_reg_state *reg;
|
||||
u16 live_regs;
|
||||
int i, j;
|
||||
|
||||
id = id & ~BPF_ADD_CONST;
|
||||
for (i = vstate->curframe; i >= 0; i--) {
|
||||
live_regs = aux[frame_insn_idx(vstate, i)].live_regs_before;
|
||||
func = vstate->frame[i];
|
||||
for (j = 0; j < BPF_REG_FP; j++) {
|
||||
if (!(live_regs & BIT(j)))
|
||||
continue;
|
||||
reg = &func->regs[j];
|
||||
__collect_linked_regs(linked_regs, reg, id, i, j, true);
|
||||
}
|
||||
|
|
@ -17584,9 +17591,9 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
|
|||
* if parent state is created.
|
||||
*/
|
||||
if (BPF_SRC(insn->code) == BPF_X && src_reg->type == SCALAR_VALUE && src_reg->id)
|
||||
collect_linked_regs(this_branch, src_reg->id, &linked_regs);
|
||||
collect_linked_regs(env, this_branch, src_reg->id, &linked_regs);
|
||||
if (dst_reg->type == SCALAR_VALUE && dst_reg->id)
|
||||
collect_linked_regs(this_branch, dst_reg->id, &linked_regs);
|
||||
collect_linked_regs(env, this_branch, dst_reg->id, &linked_regs);
|
||||
if (linked_regs.cnt > 1) {
|
||||
err = push_jmp_history(env, this_branch, 0, linked_regs_pack(&linked_regs));
|
||||
if (err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue