Linux Trace Kernel
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: wen.yang@linux.dev
Cc: linux-trace-kernel@vger.kernel.org
Subject: Re: [PATCH v5 6/9] rv: Add tlob hybrid automaton monitor
Date: Wed, 19 Aug 2026 18:34:30 +0000	[thread overview]
Message-ID: <20260819183431.3882C1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <392de71fe1497f2b9d8ce06c387ec83c4b116c47.1787161646.git.wen.yang@linux.dev>

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [Critical] Relying on the empty stub `ha_cancel_timer_sync()` leaves the budget timer active when windows close, causing Use-After-Free panics on timer expiry.
- [High] Concurrent task exit during `tlob_unbind_reap()` causes Use-After-Free and list corruption.
- [High] `tlob_destroy_task()` dereferences `ha_mon` outside of the RCU read-side critical section, causing a Use-After-Free.
- [High] The clock anchor `ha_mon->env_store[clk_elapsed_tlob]` is never initialized, causing immediate hrtimer firing due to integer underflow.
- [High] `tlob_destroy_task()` uses a PID lookup to find the monitor, risking UAF and corruption of an unrelated task's monitor due to PID recycling.
- [High] Dangling pointer in the RV framework hash table leading to UAF if `da_handle_start_run_event` fails.
- [Medium] Unbinding uprobes while a task is actively monitored permanently leaks its mempool slot.
- [Medium] Missing initialization of the `stopping` flag on fresh allocations allows sched tracepoints to trigger invalid monitor transitions.
--

--- Patch [6]: [PATCH v5 6/9] rv: Add tlob hybrid automaton monitor ---
commit 2dbaa3c57b50d6013814617edf9ea93aed73291d
Author: Wen Yang <wen.yang@linux.dev>
Subject: rv: Add tlob hybrid automaton monitor

This commit adds tlob (task latency over budget), a per-task hybrid
automaton RV monitor. It tracks wall-clock time across a user-delimited
code section and emits an error when elapsed time exceeds a configurable
budget.

> diff --git a/kernel/trace/rv/monitors/tlob/tlob.c b/kernel/trace/rv/monitors/tlob/tlob.c
> --- /dev/null
> +++ b/kernel/trace/rv/monitors/tlob/tlob.c
[ ... ]
> @@ -218,6 +218,22 @@ static inline void ha_setup_invariants(struct ha_monitor *ha_mon,
>  				       enum states curr_state, enum events event,
>  				       enum states next_state, u64 time_ns)
>  {
[ ... ]
> +	if (atomic_read_acquire(&ha_get_target(ha_mon)->stopping)) {
> +		/*
> +		 * Restart (stopped -> running): arm the timer, then clear
> +		 * stopping so __tlob_acc() admits sched events only once the
> +		 * state is already running_tlob.  _release pairs with the
> +		 * acquires in __tlob_acc/tlob_reset_notify.
> +		 */
> +		if (next_state < state_max_tlob)
> +			ha_start_timer_ns(ha_mon, clk_elapsed_tlob, BUDGET_NS(ha_mon), time_ns);
> +		atomic_set_release(&ha_get_target(ha_mon)->stopping, 0);
> +		return;
> +	}
> +
> +	if (next_state < state_max_tlob)
> +		ha_start_timer_ns(ha_mon, clk_elapsed_tlob, BUDGET_NS(ha_mon), time_ns);

[Severity: High]
Does this path forget to initialize the clock anchor?

ha_setup_invariants() does not seem to assign
ha_mon->env_store[clk_elapsed_tlob] = time_ns here. Because it remains at the
uninitialized sentinel value, won't the elapsed time calculation underflow
and cause the relative hrtimer to fire immediately upon window start?

> +	else
> +		ha_cancel_timer(ha_mon);
> +}
[ ... ]
> @@ -480,6 +480,41 @@ static int tlob_start_task(struct task_struct *task, u64 threshold_ns, void *bin
>  {
[ ... ]
> +	ws = tlob_ws_alloc();
> +	if (!ws)
> +		return -ENOSPC;
> +
> +	ws->task = task;

[Severity: Medium]
Should the stopping flag be initialized to 1 here?

For a fresh allocation, tlob_ws_alloc() zeroes the memory, leaving
ws->stopping = 0. If a scheduler event happens during start before
ha_setup_invariants() is reached, won't __tlob_acc() allow it through while
in stopped_tlob, causing an invalid transition and monitor reset?

> +	get_task_struct(task);
> +	ws->threshold_ns = threshold_ns;
> +	ws->last_ts = ktime_get();
> +	raw_spin_lock_init(&ws->entry_lock);
> +	ws->binding = binding;
> +	if (binding)
> +		list_add_tail(&ws->started_node,
> +			      &((struct tlob_uprobe_binding *)binding)->started_list);
> +
> +	/* Dispatch failed (pool exhausted or monitor disabled): unwind the slot. */
> +	if (!da_handle_start_run_event(task->pid, ws, start_tlob)) {
> +		if (binding)
> +			list_del_init(&ws->started_node);
> +		put_task_struct(task);
> +		tlob_ws_direct_return(ws);
> +		return -ENOSPC;
> +	}

[Severity: High]
By directly freeing ws via tlob_ws_direct_return(ws) without calling
da_destroy_storage(task->pid), does this leave a dangling pointer in the
framework hash table?

If da_handle_start_run_event() fails, it appears it has already allocated
storage in the RCU-protected da_monitor_ht hash table. Subsequent tracepoints
might dereference this freed memory.

> +
> +	return 0;
> +}
[ ... ]
> @@ -623,20 +623,32 @@ static void tlob_destroy_task(struct task_struct *task)
>  {
> +	struct ha_monitor *ha_mon;
> +	struct tlob_task_state *ws;
> +
> +	scoped_guard(rcu) {
> +		ha_mon = ha_get_monitor(task->pid, NULL);

[Severity: High]
Is it safe to look up the monitor by task->pid here?

If tlob_unbind_reap() delays calling tlob_destroy_task() for a task that has
already exited, the original PID might be recycled and assigned to a new
process. If this new process is also monitored by tlob, could this blindly
claim and destroy the new process's monitor while leaking the original
task state?

> +		if (!ha_mon)
> +			return;
> +		ws = ha_get_target(ha_mon);
> +		if (WARN_ON_ONCE(!ws))
> +			return;
> +		if (atomic_cmpxchg_release(&ws->destroying, 0, 1) != 0)
> +			return;
> +	}
> +
> +	tlob_detach_from_binding(ws);
> +
> +	/* Force the window ended: @task may never have reached STOP or a timer. */
> +	atomic_set(&ws->stopping, 1);
> +	ha_cancel_timer_sync(ha_mon);

[Severity: High]
Is ha_mon still safe to dereference after the RCU read-side critical section
ends?

The scoped_guard(rcu) block closes just above, dropping the RCU lock. If the
monitor is concurrently disabled, ha_monitor_destroy() issues
synchronize_rcu() and frees the monitor storage. Could the storage be freed
before the teardown calls on ha_mon execute here?

[Severity: Critical]
Is this stub definition intentional?

If ha_cancel_timer_sync() is defined as an empty inline stub in
include/rv/ha_monitor.h, it seems the hrtimer is never actually dequeued.
When tlob_destroy_task() subsequently frees the monitor storage, could the
still-active timer fire and execute its callback on freed memory?

> +
> +	scoped_guard(rcu) {
> +		da_monitor_reset(&ha_mon->da_mon);
> +	}
> +	da_destroy_storage(task->pid);
[ ... ]
> @@ -748,16 +748,22 @@ static void tlob_unbind_reap(struct tlob_uprobe_binding *b)
>  {
> +	struct tlob_task_state *ws, *tmp;
> +	LIST_HEAD(to_destroy);
> +
> +	scoped_guard(spinlock, &tlob_ws_lock) {
> +		list_for_each_entry_safe(ws, tmp, &b->started_list, started_node) {
> +			list_del_init(&ws->started_node);
> +			ws->binding = NULL;
> +			if (atomic_read(&ws->stopping))
> +				list_add_tail(&ws->started_node, &to_destroy);
> +		}
> +	}

[Severity: Medium]
Does this sequence permanently leak the mempool slot for active tasks?

If an active task is unbound, its ws->binding is set to NULL but it is not
added to to_destroy. Since the uprobe is unregistered, the stop probe never
fires. Even if it could, tlob_stop_task() returns -EALREADY because
ws->binding is NULL. Will the task remain in running_tlob forever, leaking
its pool slot until the task exits?

> +
> +	list_for_each_entry_safe(ws, tmp, &to_destroy, started_node) {

[Severity: High]
Can concurrent task exits cause list corruption and use-after-free here?

tlob_unbind_reap() populates to_destroy and clears ws->binding under lock,
then drops the lock and iterates to_destroy locklessly.

If a task exits concurrently:
handle_sched_process_exit()
  tlob_destroy_task()
    tlob_detach_from_binding()

Because ws->binding is already NULL, tlob_detach_from_binding() returns
early without acquiring the lock or calling list_del_init().
tlob_destroy_task() then frees ws, and when tlob_unbind_reap() resumes
iterating, it accesses the freed ws object.

> +		list_del_init(&ws->started_node);
> +		tlob_destroy_task(ws->task);
> +	}
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787161646.git.wen.yang@linux.dev?part=6

  reply	other threads:[~2026-08-19 18:34 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-19 18:15 [PATCH v5 0/9] rv: Add task latency over budget RV monitor wen.yang
2026-08-19 18:15 ` [PATCH v5 1/9] rv: Introduce DA_MON_ALLOCATION_STRATEGY wen.yang
2026-08-19 18:30   ` sashiko-bot
2026-08-19 18:15 ` [PATCH v5 2/9] rv: Add generic uprobe infrastructure for RV monitors wen.yang
2026-08-19 18:27   ` sashiko-bot
2026-08-19 18:15 ` [PATCH v5 3/9] rv: Add tlob model DOT file wen.yang
2026-08-19 18:25   ` sashiko-bot
2026-08-19 18:15 ` [PATCH v5 4/9] rv: Fix ha_invariant_passed_ns silent bypass of invariant check wen.yang
2026-08-19 18:32   ` sashiko-bot
2026-08-19 18:15 ` [PATCH v5 5/9] rv: Make da_monitor_reset_hook and EVENT_NONE_LBL overridable wen.yang
2026-08-19 18:30   ` sashiko-bot
2026-08-19 18:15 ` [PATCH v5 6/9] rv: Add tlob hybrid automaton monitor wen.yang
2026-08-19 18:34   ` sashiko-bot [this message]
2026-08-19 18:15 ` [PATCH v5 7/9] rv: Add KUnit tests for the tlob monitor wen.yang
2026-08-19 18:24   ` sashiko-bot
2026-08-19 18:15 ` [PATCH v5 8/9] selftests/verification: Add tlob selftests wen.yang
2026-08-19 18:27   ` sashiko-bot
2026-08-19 18:15 ` [PATCH v5 9/9] selftests/ftrace: Walk up to find test.d/functions when a subdirectory is passed wen.yang
2026-08-19 18:31   ` sashiko-bot

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=20260819183431.3882C1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=wen.yang@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