Thread (59 messages) 59 messages, 4 authors, 10d ago

Re: [PATCH bpf-next v2 33/43] bpf: Keep the symbols of a program until it has ended

flat view

From: bot+bpf-ci@kernel.org
Date: 2026-10-17 17:17:24
Also in: bpf

bpf: Keep the symbols of a program until it has ended
TL;DR: Design question: kallsyms removal now happens after the RCU callback
frees btf and linfo, so a symbol lookup in that window might touch freed
memory. The review found no such reader.
quoted hunk ↗ jump to hunk
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index e1216927373a..6e32db9777c2 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -3085,12 +3085,40 @@ static void bpf_free_used_btfs(struct bpf_prog_aux *aux)
[ ... ]
quoted hunk ↗ jump to hunk
+static void bpf_prog_free_deferred(struct work_struct *work)
+{
+	struct bpf_prog_aux *aux;
+	bool ksyms;
+
 	aux = container_of(work, struct bpf_prog_aux, work);
+	/*
+	 * A prog that still runs when its last reference goes needs its symbols,
+	 * by which its exception table is found.  It has ended by now.
+	 */
+	ksyms = aux->real_func_cnt || !list_empty(&aux->ksym.lnode);
+	bpf_prog_kallsyms_del_all(aux->prog);
 #ifdef CONFIG_BPF_SYSCALL
 	bpf_free_kfunc_btf_tab(aux->kfunc_btf_tab);
 #endif
[ ... ]
quoted hunk ↗ jump to hunk
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index c412bcd1903f..ddb986324f2f 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2461,6 +2461,8 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
 	struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);
 
 	btf_put(aux->btf);
+	kvfree(aux->jited_linfo);
+	kvfree(aux->linfo);
 	kvfree(aux->func_info);
 	kfree(aux->func_info_aux);
 	free_uid(aux->user);
@@ -2485,10 +2487,7 @@ static void __bpf_prog_put_rcu_tasks(struct rcu_head *rcu)
 
 static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred)
 {
-	bpf_prog_kallsyms_del_all(prog);
 	module_put(prog->aux->mod);
-	kvfree(prog->aux->jited_linfo);
-	kvfree(prog->aux->linfo);
 	kfree(prog->aux->kfunc_tab);
 	kfree(prog->aux->ctx_arg_info);
 	if (prog->aux->attach_btf)
A subsystem pattern flags the ordering here as potentially concerning, so
this is a question rather than a confirmed bug.

__bpf_prog_put_noref() still issues call_rcu() (or call_rcu_tasks() or
call_rcu_tasks_trace()) on &prog->aux->rcu:

	if (deferred) {
		if (IS_ENABLED(CONFIG_TASKS_RCU) && prog->aux->tramp_linked)
			call_rcu_tasks(&prog->aux->rcu, __bpf_prog_put_rcu_tasks);
		else if (bpf_prog_runs_sleepable(prog))
			call_rcu_tasks_trace(&prog->aux->rcu, __bpf_prog_put_rcu);
		else
			call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
	}

but with bpf_prog_kallsyms_del_all() gone from this function, aux->ksym is
still linked in the RCU-protected bpf_tree latch tree and the bpf_kallsyms
list when the callback is queued.

The removal now happens in bpf_prog_free_deferred(), which is a work item
scheduled from inside the callback chain:

__bpf_prog_put_rcu()
  bpf_prog_free()
    schedule_work()
      bpf_prog_free_deferred()
        bpf_prog_kallsyms_del_all()

In between, __bpf_prog_put_rcu() has already done:

	btf_put(aux->btf);
	kvfree(aux->jited_linfo);
	kvfree(aux->linfo);
	kvfree(aux->func_info);
	kfree(aux->func_info_aux);

and bpf_prog_free() has dropped aux->dst_prog and aux->token.

Can a tree reader that runs after the grace period but before
bpf_prog_free_deferred() still get the prog from bpf_prog_ksym_find() and
then dereference those fields?  For example:

dump_stack_cb()
  bpf_prog_get_file_line()
    prog->aux->btf, prog->aux->linfo, prog->aux->jited_linfo

The final free of aux and of the JIT images does look to be behind the
second grace period from queue_rcu_work(), so the exposure would only cover
the fields freed in __bpf_prog_put_rcu().

Looking at the callers of bpf_prog_ksym_find() that go past the ksym
fields (search_bpf_extables(), dump_stack_cb(), find_from_stack_cb(),
bpf_stack_walker(), bpf_prog_report_arena_violation()), they resolve
either the IP of a frame on the current stack through arch_bpf_stack_walk()
or the faulting IP.  In both cases the program is still running, so the
grace period chosen in __bpf_prog_put_noref() should not have finished.

The name-only readers (bpf_address_lookup(), bpf_get_kallsym(),
is_bpf_text_address(), bpf_has_frame_pointer()) read only aux->ksym, which
stays valid until the extra grace period.  I did not find a reader that
reaches the freed linfo or btf for a program that is not running.

Is the ordering of call_rcu() before the ksym removal intended, given that
the pattern of queueing an RCU callback on an object that is still
reachable through an RCU-protected structure is normally treated as a bug?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/39051545464
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help