From: sashiko-bot@kernel.org
To: "Jiri Olsa" <jolsa@kernel.org>
Cc: linux-trace-kernel@vger.kernel.org, bpf@vger.kernel.org
Subject: Re: [PATCH 04/13] kprobes: Maintain an rbtree of registered probes
Date: Tue, 10 Nov 2026 23:45:53 +0000 [thread overview]
Message-ID: <sashiko-outbox-207953@kernel.org> (raw)
In-Reply-To: <20261109195517.1090787-1-jolsa@kernel.org>
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [High] kprobes: double rb_erase() in kprobe_tree_erase() via
freeing_list
It also noticed 2 related pre-existing issues this patch does not
introduce:
- [Critical] kprobes: missing cooling delay on forced unoptimization in
unoptimize_kprobe() [1]
- [Critical] kprobes: premature optinsn slot free in
kill_optimized_kprobe() [2]
--
commit 8d3f55a56b4caad208426449cba53c9ca99b1316
Author: Jiri Olsa <jolsa@kernel.org>
kprobes: Maintain an rbtree of registered probes
This commit adds an address-ordered rbtree for registered kprobes and
keeps it synchronized with kprobe_table across probe insertion, removal,
and aggregator replacement.
> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> index 2441b9e393438..6be1430a4ebe9 100644
> --- a/kernel/kprobes.c
> +++ b/kernel/kprobes.c
[ ... ]
> @@ -395,6 +396,45 @@ struct kprobe *get_kprobe(void *addr)
> }
> NOKPROBE_SYMBOL(get_kprobe);
>
[ ... ]
> +static void kprobe_tree_erase(struct kprobe *p)
> +{
> + lockdep_assert_held(&kprobe_mutex);
> + rb_erase(&p->rb_node, &kprobe_tree);
> + RB_CLEAR_NODE(&p->rb_node);
> +}
[ ... ]
> @@ -643,6 +683,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);
> + kprobe_tree_erase(&op->kp);
> } else {
> /*
> * Keep on cooling_list until the quiescence period
> @@ -722,6 +763,7 @@ static void optprobe_finalize_generation(struct optprobe_generation *gen)
> * it will be reclaimed after the next quiescence period.
> */
> hlist_del_init_rcu(&op->kp.hlist);
> + kprobe_tree_erase(&op->kp);
> list_move(&op->list, &optprobe_gens[optprobe_cur_gen].freeing_list);
> kick_kprobe_optimizer();
> } else {
[Severity: High]
Can this regression call rb_erase() twice on &op->kp.rb_node and corrupt
kprobe_tree?
When an unused optimized_kprobe is queued onto freeing_list (for example in
optprobe_finalize_generation(), unoptimize_kprobe(), or
kill_optimized_kprobe()), it calls hlist_del_init_rcu(&op->kp.hlist) and
kprobe_tree_erase(&op->kp). When optprobe_dispatch_generation() later
dispatches that generation, do_unoptimize_kprobes() walks freeing_list and
calls both functions again on the same probe.
Unlike hlist_del_init_rcu(), which checks !hlist_unhashed(),
kprobe_tree_erase() does not check !RB_EMPTY_NODE(&p->rb_node) before
calling rb_erase(). Because the first erase leaves stale child pointers in
rb_node and sets its parent to itself, the second rb_erase() links live
kprobe_tree nodes to the about-to-be-freed op->kp.rb_node.
> @@ -1006,6 +1048,7 @@ static void unoptimize_kprobe(struct kprobe *p, bool force)
> if (!kprobe_gone(&op->kp))
> arch_disarm_kprobe(&op->kp);
> hlist_del_init_rcu(&op->kp.hlist);
> + kprobe_tree_erase(&op->kp);
> list_move(&op->list, &cur->freeing_list);
> } else {
> list_move(&op->list, &cur->cooling_list);
> @@ -1021,6 +1064,7 @@ static void unoptimize_kprobe(struct kprobe *p, bool force)
> if (!kprobe_gone(&op->kp))
> arch_disarm_kprobe(&op->kp);
> hlist_del_init_rcu(&op->kp.hlist);
> + kprobe_tree_erase(&op->kp);
> list_move(&op->list, &cur->freeing_list);
> kick_kprobe_optimizer();
> }
[Severity: Critical]
This problem wasn't introduced by this patch, but in unoptimize_kprobe()
[1], can forced unoptimization of an already-optimized probe free
the probe and its detour trampoline before preempted tasks exit it?
kernel/kprobes.c:unoptimize_kprobe() {
...
if (!kprobe_optimized(p))
return;
/* Optimized kprobe case */
if (force) {
/* Forcibly update the code: this is a special case */
force_unoptimize_kprobe(op);
} else {
list_add(&op->list, &cur->unoptimizing_list);
kick_kprobe_optimizer();
}
}
Once a probe finishes optimizing, KPROBE_FLAG_OPTIMIZED is set and op->list
is empty. When unoptimize_kprobe(p, true) runs (such as from
add_new_kprobe() or disarm_all_kprobes()), force_unoptimize_kprobe(op)
patches the jump back to a breakpoint and clears KPROBE_FLAG_OPTIMIZED,
leaving op->list empty instead of queueing op onto cur->cooling_list.
A subsequent unregister_kprobe() sees kprobe_disarmed(ap) evaluate to true
in __unregister_kprobe_top(), unhashes and erases ap immediately, and waits
only for synchronize_rcu() before freeing struct optimized_kprobe and its
trampoline slot without waiting for a Tasks RCU grace period. If a task was
preempted inside the detour trampoline before the jump was patched, it
triggers a use-after-free when it resumes.
> @@ -1076,6 +1120,7 @@ static void kill_optimized_kprobe(struct kprobe *p)
> if (kprobe_unused(p)) {
> if (optprobe_queued_unopt(op) || optprobe_queued_cooling(op)) {
> hlist_del_init_rcu(&op->kp.hlist);
> + kprobe_tree_erase(&op->kp);
> list_move(&op->list,
> &optprobe_gens[optprobe_cur_gen].freeing_list);
> kick_kprobe_optimizer();
[Severity: Critical]
This problem wasn't introduced by this patch, but in
kill_optimized_kprobe() [2], can killing an aggregator probe that
still has child probes free its optinsn slot while tasks are still
preempted inside the trampoline?
kernel/kprobes.c:kill_optimized_kprobe() {
...
if (!list_empty(&op->list)) {
if (kprobe_unused(p)) {
...
} else {
list_del_init(&op->list);
}
}
op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
if (!optprobe_queued_freeing(op) && !optprobe_queued_unopt(op) &&
!optprobe_queued_cooling(op))
arch_remove_optimized_kprobe(op);
}
When kill_kprobe() is called on an aggregator probe that is queued on
unoptimizing_list or cooling_list and still has child probes
(!kprobe_unused(p)), the else branch runs list_del_init(&op->list). Because
optprobe_queued_unopt() and optprobe_queued_cooling() check queue membership
by walking those lists, detaching op->list first makes both checks return
false. arch_remove_optimized_kprobe() then frees the detour buffer
immediately without waiting for the pending Tasks RCU grace period.
[1] https://sashiko.dev/#/bug/linux-f9b316b8-64c0-4c88-8de2-81f70a772f7b
[2] https://sashiko.dev/#/bug/linux-cc62e94b-a020-483a-84cb-52080d1d23a0
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261109194916.1089703-1-jolsa@kernel.org?part=4
next prev parent reply other threads:[~2026-11-10 23:45 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 19:49 [PATCH 00/13] kprobes: Reduce registration and optimization lookup costs Jiri Olsa
2026-11-09 19:49 ` [PATCH 01/13] static_call: Index code text reservation lookups Jiri Olsa
2026-11-09 19:49 ` [PATCH 02/13] jump_label: Index " Jiri Olsa
2026-11-09 19:49 ` [PATCH 03/13] jump_label/x86: Avoid decoding instructions for jump entry size Jiri Olsa
2026-11-09 19:55 ` [PATCH 04/13] kprobes: Maintain an rbtree of registered probes Jiri Olsa
2026-11-10 8:12 ` Masami Hiramatsu (Google)
2026-11-10 10:37 ` Jiri Olsa
2026-11-10 15:38 ` Masami Hiramatsu (Google)
2026-11-11 10:49 ` Jiri Olsa
2026-11-10 23:45 ` sashiko-bot [this message]
2026-11-09 19:56 ` [PATCH 05/13] kprobes: Add an iterator for address ranges Jiri Olsa
2026-11-09 19:56 ` [PATCH 06/13] kprobes: Add an address iterator for probe lookup Jiri Olsa
2026-11-09 19:56 ` [PATCH 07/13] kprobes: Use kprobe iterator in get_optimized_kprobe Jiri Olsa
2026-11-09 19:57 ` [PATCH 08/13] x86/kprobes: Use kprobe iterator in __recover_optprobed_insn Jiri Olsa
2026-11-09 19:57 ` [PATCH 09/13] x86/kprobes: Use kprobe iterator in arch_check_optimized_kprobe Jiri Olsa
2026-11-11 0:17 ` sashiko-bot
2026-11-09 19:57 ` [PATCH 10/13] x86/kprobes: Use kprobe address iterator in can_optimize Jiri Olsa
2026-11-09 19:57 ` [PATCH 11/13] x86/kprobes: Use kprobe address iterator in can_probe Jiri Olsa
2026-11-09 19:58 ` [PATCH 12/13] x86/kprobes: Use kprobe address iterator in in copy_optimized_instructions Jiri Olsa
2026-11-09 19:58 ` [PATCH 13/13] samples/kprobes: Add batch probe example Jiri Olsa
2026-11-11 0:49 ` sashiko-bot
2026-11-11 2:20 ` kernel test robot
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=sashiko-outbox-207953@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=jolsa@kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/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