From: "Florent Revest" <florent.revest@linux.dev>
To: <bot+bpf-ci@kernel.org>, <bpf@vger.kernel.org>, <ast@kernel.org>,
<daniel@iogearbox.net>, <andrii@kernel.org>
Cc: <martin.lau@linux.dev>, <eddyz87@gmail.com>, <memxor@gmail.com>,
<song@kernel.org>, <yonghong.song@linux.dev>, <jolsa@kernel.org>,
<kpsingh@kernel.org>, <john.fastabend@gmail.com>,
<leon.hwang@linux.dev>, <zirajs7@gmail.com>,
<rhkrqnwk98@gmail.com>, <puranjay@kernel.org>,
<xukuohai@huaweicloud.com>, <iii@linux.ibm.com>,
<hbathini@linux.ibm.com>, <chleroy@kernel.org>,
<naveen@kernel.org>, <bjorn@kernel.org>, <pulehui@huawei.com>,
<yangtiezhu@loongson.cn>, <hengqi.chen@gmail.com>,
<linux-kernel@vger.kernel.org>, <martin.lau@kernel.org>,
<eddyz87@gmail.com>, <yonghong.song@linux.dev>,
<mason@kernel.org>, <ihor.solodrai@linux.dev>
Subject: Re: [PATCH bpf v3 3/3] selftests/bpf: Detach a trampoline prog while a task sleeps before it
Date: Fri, 25 Sep 2026 09:59:53 +0000 [thread overview]
Message-ID: <DLOB5OKRK23X.1VAD9QKRHYJ35@linux.dev> (raw)
In-Reply-To: <9ff9f911b980da0e9100578ccf6dba5c7a5414e0b0075922e750161220370554@mail.kernel.org>
On Thu Sep 24, 2026 at 5:53 PM UTC, wrote:
> > diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c
> > index ecc3d47919ad..f8497e764beb 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c
>
> [ ... ]
>
> > diff --git a/tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c b/tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c
> > new file mode 100644
> > index 000000000000..002eb4920c91
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c
>
> [ ... ]
>
> > +static void test_detach(bool sleepable_fexit, bool victim_fexit)
> > +{
> > + struct tramp_prog_detach *sleepable = NULL, *victim = NULL;
> > + struct pollfd pfd = { .events = POLLIN };
> > + struct uffdio_copy uffd_copy = {};
> > + struct uffd_msg uffd_msg;
> > + void *fault_page, *src_page = MAP_FAILED;
> > + long page_size = getpagesize();
> > + bool started = false;
> > + void *thread_ret;
> > + pthread_t thread;
> > + int uffd = -1;
>
> [ ... ]
>
> > + sleepable_prog = pick_prog(sleepable, sleepable_fexit, true);
> > +
> > + /* Not armed yet so this doesn't block, make sure sleepable runs first */
> > + if (!ASSERT_OK((long)run_sleepable(NULL), "dry run"))
> > + goto out;
> > + if (!ASSERT_LT(sleepable->bss->ts, victim->bss->ts, "prog order"))
> > + goto out;
> > +
> > + uffd = uffd_block_page(fault_page);
> > + if (!ASSERT_GE(uffd, 0, "userfaultfd open + register address"))
> > + goto out;
>
> A subsystem pattern flags this as potentially concerning: uffd_block_page()
> returns -errno when syscall(__NR_userfaultfd, O_CLOEXEC) fails. On a kernel
> without CONFIG_USERFAULTFD that is -ENOSYS, and the test then fails through
> ASSERT_GE() instead of calling test__skip() with a message saying userfaultfd
> is not available. While tools/testing/selftests/bpf/config sets
> CONFIG_USERFAULTFD=y so the BPF CI kernel always has it, and the same
> ASSERT_GE pattern already exists in bpf_mod_race.c (serial_test_bpf_mod_race)
> from before this commit, is this the intended behavior for environments
> without userfaultfd support?
This is the same as bpf_mod_race. CONFIG_USERFAULTFD is in the selftests config
so I'd just leave it like that.
> > + sleepable->bss->fault_addr = fault_page;
> > +
> > + if (!ASSERT_OK(pthread_create(&thread, NULL, run_sleepable, NULL),
> > + "pthread_create"))
> > + goto out;
> > + started = true;
> > +
> > + /* Wait for the thread to sleep in bpf_copy_from_user() */
> > + pfd.fd = uffd;
> > + if (!ASSERT_EQ(poll(&pfd, 1, 10000), 1, "poll uffd"))
> > + goto out;
> > + if (!ASSERT_EQ(read(uffd, &uffd_msg, sizeof(uffd_msg)), sizeof(uffd_msg),
> > + "read uffd"))
> > + goto out;
> > + if (!ASSERT_EQ(uffd_msg.event, UFFD_EVENT_PAGEFAULT, "uffd pagefault"))
> > + goto out;
> > +
> > + /* Detach and unload the victim prog, and make sure it is gone */
> > + tramp_prog_detach__destroy(victim);
> > + victim = NULL;
> > + kern_sync_rcu();
> > + usleep(100 * 1000);
> > + kern_sync_rcu();
>
> Does this wait actually ensure the victim prog is gone before the task
> resumes?
It doesn't anymore. With patch 1 the prog is freed much later, and the test
passed even without patch 2. v4 waits for the .bss map of the detached prog to
go away, which happens when the prog is freed, and the test crashes again with
patch 1 alone. The prog ID can't be used for this, it goes away when the last
reference is dropped, before the grace periods.
> The test only catches the use-after-free if the victim prog has actually been
> freed before UFFDIO_COPY releases the sleeping task. The wait here does not
> guarantee that, and on the kernel this series produces it most likely is not
> freed yet.
>
> Commit 1c2316cf73ba ("bpf: Wait for an RCU tasks grace period before freeing
> trampoline progs") changed __bpf_prog_put_noref() so that a tramp_linked
> prog is freed through call_rcu_tasks(&prog->aux->rcu, __bpf_prog_put_rcu_tasks),
> then call_rcu(), then bpf_prog_free(), which calls schedule_work() for the
> final vfree.
>
> RCU Tasks callbacks are lazy. call_rcu_tasks_generic() only sets needwake
> for wakeme_after_rcu (a synchronous waiter) or when the queue reaches
> rcu_task_lazy_lim. Otherwise it arms lazy_timer with rcu_tasks.lazy_jiffies
> = DIV_ROUND_UP(HZ, 4) (kernel/rcu/tasks.h:145), which is about 250ms by
> default.
>
> Nothing on the detach path forces a synchronous RCU Tasks wait. Detaching
> the victim while the sleepable prog stays attached goes through
> modify_ftrace_direct(), and its tmp_ops is static, so it is not
> FTRACE_OPS_FL_DYNAMIC and ftrace_shutdown() does not call
> synchronize_rcu_tasks().
>
> The test waits only kern_sync_rcu() + 100ms + kern_sync_rcu().
> kern_sync_rcu() is membarrier(MEMBARRIER_CMD_SHARED), which is
> synchronize_rcu() on SMP and a no-op with one CPU online. That is well short
> of 250ms lazy delay + RCU Tasks GP + RCU GP + workqueue.
>
> So when the thread resumes in the old image, the victim's bpf_prog and JIT
> image are usually still allocated. On a kernel that has 1c2316cf73ba but
> lacks 2ece3357872c (or breaks it later), the thread would run the victim
> prog without a KASAN report and the test would pass.
>
> Consider waiting well beyond the RCU Tasks lazy period (for example about 1s,
> or polling until the victim prog ID is no longer returned by
> bpf_prog_get_next_id()/bpf_prog_get_fd_by_id()) before releasing the thread.
>
> [ ... ]
>
>
> ---
> 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/36034251810
prev parent reply other threads:[~2026-09-25 10:00 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-24 17:05 [PATCH bpf v3 0/3] bpf: Fix use-after-free of progs detached from busy trampolines Florent Revest (Anthropic)
2026-09-24 17:05 ` [PATCH bpf v3 1/3] bpf: Wait for an RCU tasks grace period before freeing trampoline progs Florent Revest (Anthropic)
2026-09-24 17:05 ` [PATCH bpf v3 2/3] bpf: Skip the progs of trampoline images that are being freed Florent Revest (Anthropic)
2026-09-24 18:07 ` bot+bpf-ci
2026-09-25 9:52 ` Florent Revest
2026-09-25 0:35 ` Alexei Starovoitov
2026-09-25 9:23 ` Florent Revest
2026-09-24 17:05 ` [PATCH bpf v3 3/3] selftests/bpf: Detach a trampoline prog while a task sleeps before it Florent Revest (Anthropic)
2026-09-24 17:53 ` bot+bpf-ci
2026-09-25 9:59 ` Florent Revest [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DLOB5OKRK23X.1VAD9QKRHYJ35@linux.dev \
--to=florent.revest@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bjorn@kernel.org \
--cc=bot+bpf-ci@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=chleroy@kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=hbathini@linux.ibm.com \
--cc=hengqi.chen@gmail.com \
--cc=ihor.solodrai@linux.dev \
--cc=iii@linux.ibm.com \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=leon.hwang@linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@kernel.org \
--cc=martin.lau@linux.dev \
--cc=mason@kernel.org \
--cc=memxor@gmail.com \
--cc=naveen@kernel.org \
--cc=pulehui@huawei.com \
--cc=puranjay@kernel.org \
--cc=rhkrqnwk98@gmail.com \
--cc=song@kernel.org \
--cc=xukuohai@huaweicloud.com \
--cc=yangtiezhu@loongson.cn \
--cc=yonghong.song@linux.dev \
--cc=zirajs7@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®