Thread (15 messages) 15 messages, 2 authors, 1d ago

Re: [PATCH 07/10] rv: Add throttle deadline monitor

From: sashiko-bot@kernel.org
Date: 2026-09-29 13:18:47

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] rv: timer overflow in runtime_left_ns() allows monitor evasion
- [High] rv: speculative monitor allocation causes memory leak and Use-After-Free
- [High] rv: concurrent hash table modifications in tracepoints corrupt monitor lists
- [Medium] rv: handle_sched_switch() skips switch-out events for background servers
- [Medium] rv: monitor timer is cancelled before syscall validation allowing evasion
--

commit 59f095bf10c27a9ddbf78eaa030f99a4df14f9eb
Author: Gabriele Monaco [off-list ref]

rv: Add throttle deadline monitor
    
This commit adds a monitor to validate the behavior of the deadline
scheduler, specifically ensuring that deadline entities are throttled when
they use up their runtime.
quoted hunk ↗ jump to hunk
--- /dev/null
+++ b/kernel/trace/rv/monitors/throttle/throttle.c
[ ... ]
+static inline u64 runtime_left_ns(struct ha_monitor *ha_mon)
+{
+	return ha_get_target(ha_mon)->runtime + RUNTIME_THRESH;
+}
[Severity: High]
Can the implicit conversion to u64 here cause a timer overflow? 

ha_get_target(ha_mon)->runtime is a signed 64-bit value that can be
negative when a task overruns its budget. Returning it as a u64 casts it
to a huge positive value, which is then passed to ha_start_timer_ns(). 

Will this push the enforcement timer far into the future and effectively
blind the monitor to task overruns?

[ ... ]
+static void handle_sched_switch(void *data, bool preempt,
+				struct task_struct *prev,
+				struct task_struct *next,
+				unsigned int prev_state)
+{
[ ... ]
+	/*
+	 * The server is available in next only if the next task is boosted,
+	 * otherwise we need to retrieve it.
+	 * Here the server continues in the state running/armed until actually
+	 * stopped, this works since we continue expecting a throttle.
+	 */
+	if (next->dl_server) {
+		da_handle_start_event(EXPAND_ID(next->dl_server, cpu,
+						get_server_type(next)),
+				      sched_switch_in_throttle);
+	} else {
+		handle_server_switch(next, cpu, DL_SERVER_FAIR);
+		if (IS_ENABLED(CONFIG_SCHED_CLASS_EXT))
+			handle_server_switch(next, cpu, DL_SERVER_EXT);
+	}
+}
[Severity: Medium]
Does this skip switch-out events for background servers?

If next->dl_server is non-NULL, we enter this branch and send a switch-in
event. However, this skips the else block entirely, meaning the preempted
fair or ext server on that CPU never receives its sched_switch_out_throttle
event.

Won't this leave the fair server incorrectly armed or running, potentially
causing false positive timer failures?

[ ... ]
+static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
+{
+	struct task_struct *p;
+	int new_policy = -1;
+	pid_t pid = 0;
+
+	new_policy = extract_params(regs, id, &pid);
+	if (new_policy < 0)
+		return;
+	guard(rcu)();
+	p = pid ? find_task_by_vpid(pid) : current;
+	if (unlikely(!p) || new_policy == p->policy)
+		return;
+
+	if (p->policy == SCHED_DEADLINE)
+		da_reset(EXPAND_ID_TASK(p));
[Severity: Medium]
Can a user evade the monitor by spamming invalid syscalls?

Calling da_reset() permanently cancels the enforcement timer via
ha_cancel_timer() before the syscall is validated. If the syscall fails
and returns an error code, the task remains in SCHED_DEADLINE but the
monitor's timer stays disabled for the remainder of the execution slice.
+	else if (new_policy == SCHED_DEADLINE)
+		da_create_or_get(EXPAND_ID_TASK(p));
+}
[Severity: High]
Does this speculative allocation create a memory leak and use-after-free?

da_create_or_get() is called here before the sched_setscheduler syscall
completes or validates permissions. If the syscall fails, the task's
policy remains unchanged.

When the task eventually exits, handle_exit() only destroys storage if
the task's current policy is SCHED_DEADLINE, leaving the speculatively
allocated monitor storage permanently leaked.

Furthermore, if the PID is later reused and the new task enters
SCHED_DEADLINE, da_create_or_get() will find the leaked storage without
updating the target pointer, potentially causing a use-after-free when
the monitor accesses target->runtime.

[Severity: High]
Is it safe to concurrently modify the RCU hash table here?

da_create_or_get() adds to the hash table via hash_add_rcu(), but it is
called directly from a tracepoint protected only by a read-side lock
(guard(rcu)()). 

Since tracepoints can trigger concurrently on different CPUs, won't this
race on the hlist pointers and corrupt the monitor lists? The same concern
applies to the hash_del_rcu() call in handle_exit() registered to the
sched_process_exit tracepoint below.

[ ... ]
+static int enable_throttle(void)
+{
[ ... ]
+	rv_attach_trace_probe("throttle", sched_switch, handle_sched_switch);
+	if (!should_skip_syscall_handle())
+		rv_attach_trace_probe("throttle", sys_enter, handle_sys_enter);
+	rv_attach_trace_probe("throttle", task_newtask, handle_newtask);
+	rv_attach_trace_probe("throttle", sched_dl_server_stop_tp, handle_dl_server_stop);
+	rv_attach_trace_probe("throttle", sched_process_exit, handle_exit);
+
+	return 0;
+}
-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260929124908.177676-1-gmonaco@redhat.com?part=7
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help