From c1aedd5b93b2b9677266106ed5a631ee8ca4097c Mon Sep 17 00:00:00 2001 From: Pu Lehui Date: Wed, 5 Nov 2025 10:03:02 +0000 Subject: [PATCH] bpf: Fix invalid mem access when update_effective_progs fails in __cgroup_bpf_detach Syzkaller triggers an invalid memory access issue following fault injection in update_effective_progs. The issue can be described as follows: __cgroup_bpf_detach update_effective_progs compute_effective_progs bpf_prog_array_alloc <-- fault inject purge_effective_progs /* change to dummy_bpf_prog */ array->items[index] = &dummy_bpf_prog.prog ---softirq start--- __do_softirq ... __cgroup_bpf_run_filter_skb __bpf_prog_run_save_cb bpf_prog_run stats = this_cpu_ptr(prog->stats) /* invalid memory access */ flags = u64_stats_update_begin_irqsave(&stats->syncp) ---softirq end--- static_branch_dec(&cgroup_bpf_enabled_key[atype]) The reason is that fault injection caused update_effective_progs to fail and then changed the original prog into dummy_bpf_prog.prog in purge_effective_progs. Then a softirq came, and accessing the members of dummy_bpf_prog.prog in the softirq triggers invalid mem access. To fix it, we can skip executing the prog when it's dummy_bpf_prog.prog. Fixes: 4c46091ee985 ("bpf: Fix KASAN use-after-free Read in compute_effective_progs") Signed-off-by: Pu Lehui --- include/linux/bpf.h | 6 ++++++ kernel/bpf/cgroup.c | 5 +++-- kernel/bpf/core.c | 5 ++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d808253f2e945..923687c47111a 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2212,6 +2212,12 @@ struct bpf_prog_array { struct bpf_prog_array_item items[]; }; +struct bpf_prog_dummy { + struct bpf_prog prog; +}; + +extern struct bpf_prog_dummy dummy_bpf_prog; + struct bpf_empty_prog_array { struct bpf_prog_array hdr; struct bpf_prog *null_prog; diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 248f517d66d04..baad33b34cefb 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -77,7 +77,9 @@ bpf_prog_run_array_cg(const struct cgroup_bpf *cgrp, item = &array->items[0]; old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx); while ((prog = READ_ONCE(item->prog))) { - run_ctx.prog_item = item; + run_ctx.prog_item = item++; + if (prog == &dummy_bpf_prog.prog) + continue; func_ret = run_prog(prog, ctx); if (ret_flags) { *(ret_flags) |= (func_ret >> 1); @@ -85,7 +87,6 @@ bpf_prog_run_array_cg(const struct cgroup_bpf *cgrp, } if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval)) run_ctx.retval = -EPERM; - item++; } bpf_reset_run_ctx(old_run_ctx); rcu_read_unlock_migrate(); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index d595fe512498c..eac8cc3417254 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2536,13 +2536,12 @@ static unsigned int __bpf_prog_ret1(const void *ctx, return 1; } -static struct bpf_prog_dummy { - struct bpf_prog prog; -} dummy_bpf_prog = { +struct bpf_prog_dummy dummy_bpf_prog = { .prog = { .bpf_func = __bpf_prog_ret1, }, }; +EXPORT_SYMBOL(dummy_bpf_prog); struct bpf_empty_prog_array bpf_empty_prog_array = { .null_prog = NULL,