From: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
To: Steven Rostedt <rostedt@goodmis.org>,
Masami Hiramatsu <mhiramat@kernel.org>,
Naveen N Rao <naveen@kernel.org>,
"David S . Miller" <davem@davemloft.net>
Cc: linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
Mani Tofigh <mt4022@columbia.edu>
Subject: [PATCH v2 2/4] kprobes: Fix UAF and queue cooling probes on forced unoptimization
Date: Tue, 10 Nov 2026 10:20:04 +0900 [thread overview]
Message-ID: <179427360419.291326.11129449813483716145.stgit@devnote2> (raw)
In-Reply-To: <179427358690.291326.2078291446658598883.stgit@devnote2>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
When unoptimize_kprobe() is called with force = true on an actively
optimized probe (whose op->list is empty), force_unoptimize_kprobe()
replaces the relative jump with a breakpoint, but leaves op->list empty
without queueing op to cur->cooling_list.
Because op->list remains empty, kprobe_disarmed() returns true
prematurely as soon as the probe is disabled. When unregister_kprobes()
runs, __unregister_kprobe_top() unhashes the probe immediately, and
unregister_kprobes() frees op and the optinsn slot after synchronize_rcu()
without waiting for Tasks RCU. Any task preempted inside the optinsn slot
then encounters a use-after-free when resuming execution.
Queue forcibly unoptimized probes onto cur->cooling_list and include
gen->cooling_list in optprobe_has_queued_probes() so that the generation
is dispatched and flushed properly. In do_unoptimize_kprobes(), collect
newly unoptimized probes into a separate local list so that probes
already placed on freeing_list are not processed a second time.
Fixes: 06e0d3b89da1 ("kprobes: Make optprobe optimizer multi-generational and asynchronous")
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v2:
- Drop the unreachable kprobe_unused(&op->kp) branch when force is true
and list_empty(&op->list), since kprobe_disarmed() already checked for
disabled probes with an empty op->list at the start of
unoptimize_kprobe().
---
kernel/kprobes.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index b6ff971df451..c0637302eeae 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -552,17 +552,13 @@ static unsigned long optimizer_passes;
#define OPTIMIZE_DELAY 5
-/*
- * Note: gen->cooling_list is not checked here because it is strictly
- * an in-flight holding list for dispatched generations, so it is always
- * empty in optprobe_cur_gen.
- */
static bool optprobe_has_queued_probes(void)
{
struct optprobe_generation *gen = &optprobe_gens[optprobe_cur_gen];
return !list_empty(&gen->optimizing_list) ||
!list_empty(&gen->unoptimizing_list) ||
+ !list_empty(&gen->cooling_list) ||
!list_empty(&gen->freeing_list);
}
@@ -612,6 +608,7 @@ static void do_unoptimize_kprobes(struct list_head *unopt_list,
struct list_head *cooling_list)
{
struct optimized_kprobe *op, *tmp;
+ LIST_HEAD(unoptimized);
lockdep_assert_held(&text_mutex);
/*
@@ -626,11 +623,13 @@ static void do_unoptimize_kprobes(struct list_head *unopt_list,
*/
lockdep_assert_cpus_held();
- if (!list_empty(unopt_list))
- arch_unoptimize_kprobes(unopt_list, free_list);
+ if (list_empty(unopt_list))
+ return;
- /* Loop on 'free_list' for disarming and removing from kprobe hash list */
- list_for_each_entry_safe(op, tmp, free_list, list) {
+ arch_unoptimize_kprobes(unopt_list, &unoptimized);
+
+ /* Loop on newly unoptimized probes for disarming and hash list removal */
+ list_for_each_entry_safe(op, tmp, &unoptimized, list) {
/* Switching from detour code to origin */
op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
/*
@@ -647,6 +646,7 @@ static void do_unoptimize_kprobes(struct list_head *unopt_list,
* (reclaiming is done by do_free_cleaned_kprobes().)
*/
hlist_del_init_rcu(&op->kp.hlist);
+ list_move(&op->list, free_list);
} else {
/*
* Keep on cooling_list until the quiescence period
@@ -1044,10 +1044,11 @@ static void unoptimize_kprobe(struct kprobe *p, bool force)
if (force) {
/* Forcibly update the code: this is a special case */
force_unoptimize_kprobe(op);
+ list_add(&op->list, &cur->cooling_list);
} else {
list_add(&op->list, &cur->unoptimizing_list);
- kick_kprobe_optimizer();
}
+ kick_kprobe_optimizer();
}
/* Cancel unoptimizing for reusing */
next prev parent reply other threads:[~2026-11-10 1:20 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 1:19 [PATCH v2 0/4] kprobes: Fixes for ftrace ops and optprobe state machine Masami Hiramatsu (Google)
2026-11-10 1:19 ` [PATCH v2 1/4] kprobes: Switch ftrace ops when adding probe with post_handler Masami Hiramatsu (Google)
2026-11-10 1:20 ` Masami Hiramatsu (Google) [this message]
2026-11-10 1:20 ` [PATCH v2 3/4] kprobes: Re-optimize blocked sibling probe when cooling finishes Masami Hiramatsu (Google)
2026-11-10 1:20 ` [PATCH v2 4/4] kprobes: Add KUnit test cases for kprobes regressions Masami Hiramatsu (Google)
2026-11-10 1:40 ` sashiko-bot
2026-11-11 13:51 ` Masami Hiramatsu (Google)
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=179427360419.291326.11129449813483716145.stgit@devnote2 \
--to=mhiramat@kernel.org \
--cc=davem@davemloft.net \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=mt4022@columbia.edu \
--cc=naveen@kernel.org \
--cc=rostedt@goodmis.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.