mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
bpf: Fold reg->var_off into PTR_TO_FLOW_KEYS bounds check
Constant pointer arithmetic on a PTR_TO_FLOW_KEYS register lands the constant in reg->var_off (e.g. flow_keys(imm=4096)), but the PTR_TO_FLOW_KEYS path in check_mem_access() passes only insn->off to check_flow_keys_access() and never folds reg->var_off.value. The verifier therefore accepts an access that, at runtime, dereferences past struct bpf_flow_keys -- a verifier/runtime divergence that yields an out-of-bounds read and write of kernel stack memory. Commit022ac07508("bpf: use reg->var_off instead of reg->off for pointers") removed the generic "off += reg->off" that check_mem_access() applied before the per-type dispatch and replaced it with per-path folding of reg->var_off.value (for example the ctx path now folds the register offset via check_ctx_access()). The PTR_TO_FLOW_KEYS path was not given the equivalent fold, so a constant offset that used to be folded and rejected is now silently accepted: before 022ac0750883: the offset stays in reg->off and is folded generically, so the access is checked with off=4096 and rejected. after 022ac0750883: the offset lands in reg->var_off, the flow_keys path checks off=0 and accepts; at runtime the access dereferences base + 0x1000. For a BPF_PROG_TYPE_FLOW_DISSECTOR program the following is accepted: r2 = *(u64 *)(r1 + 144) ; R2=flow_keys (PTR_TO_FLOW_KEYS) r2 += 0x1000 ; R2=flow_keys(imm=4096), accepted r0 = *(u64 *)(r2 + 0) ; accepted, var_off.value=0x1000 ignored while the equivalent insn->off form r0 = *(u64 *)(r2 + 0x1000) has the same effective offset but is correctly rejected with "invalid access to flow keys off=4096 size=8", which isolates the defect to the missing var_off fold. Once attached as a flow dissector, the accepted program reads kernel stack past struct bpf_flow_keys (a kernel-stack / KASLR information leak) and can likewise write past it, corrupting kernel memory. Fix it by folding reg->var_off.value into the offset before the bounds check and rejecting non-constant offsets, mirroring the other pointer types (e.g. check_ctx_access()). Fixes:022ac07508("bpf: use reg->var_off instead of reg->off for pointers") Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20260606-c3-01-v3-v3-1-97c51f592f15@mails.tsinghua.edu.cn Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
8ddce41679
commit
37363191cb
|
|
@ -4728,9 +4728,21 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, struct b
|
|||
return err;
|
||||
}
|
||||
|
||||
static int check_flow_keys_access(struct bpf_verifier_env *env, int off,
|
||||
int size)
|
||||
static int check_flow_keys_access(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *reg, argno_t argno,
|
||||
int off, int size)
|
||||
{
|
||||
/* Only a constant offset is allowed here; fold it into off. */
|
||||
if (!tnum_is_const(reg->var_off)) {
|
||||
char tn_buf[48];
|
||||
|
||||
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
|
||||
verbose(env, "%s invalid variable offset to flow keys: off=%d, var_off=%s\n",
|
||||
reg_arg_name(env, argno), off, tn_buf);
|
||||
return -EACCES;
|
||||
}
|
||||
off += reg->var_off.value;
|
||||
|
||||
if (size < 0 || off < 0 ||
|
||||
(u64)off + size > sizeof(struct bpf_flow_keys)) {
|
||||
verbose(env, "invalid access to flow keys off=%d size=%d\n",
|
||||
|
|
@ -6239,7 +6251,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
|
|||
return -EACCES;
|
||||
}
|
||||
|
||||
err = check_flow_keys_access(env, off, size);
|
||||
err = check_flow_keys_access(env, reg, argno, off, size);
|
||||
if (!err && t == BPF_READ && value_regno >= 0)
|
||||
mark_reg_unknown(env, regs, value_regno);
|
||||
} else if (type_is_sk_pointer(reg->type)) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user