Linux Trace Kernel
 help / color / mirror / Atom feed
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

  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