* [PATCH] sched/uclamp: Reject clamp values above SCHED_CAPACITY_SCALE
@ 2026-11-02 13:48 Andrea Parri
0 siblings, 0 replies; only message in thread
From: Andrea Parri @ 2026-11-02 13:48 UTC (permalink / raw)
To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot
Cc: Andrea Parri, Dietmar Eggemann, Steven Rostedt, Ben Segall,
Mel Gorman, Valentin Schneider, K Prateek Nayak, Yun Hsiang,
Hui Su, linux-kernel
sched_setattr() accepts sched_util_min and sched_util_max values that do
not fit in uclamp_se::value. A single thread can do:
sched_setattr(max = 800) returns 0
sched_setattr(min = 0xfffffffe) returns 0
sched_getattr() min=2046 max=800
which leaves the minimum above both SCHED_CAPACITY_SCALE and the task's own
maximum.
The range check in uclamp_validate() relies on the + 1 wrapping -1, the
reset value, to 0:
if (util_min + 1 > SCHED_CAPACITY_SCALE + 1)
That only works for an unsigned type. util_min and util_max are int and
SCHED_CAPACITY_SCALE is a long, so every negative value passes the check,
and so does INT_MAX, whose + 1 wraps.
uclamp_se_set() then truncates what got through to the 11 bits of
uclamp_se::value, but computes the bucket from the full value. A minimum
of 0xfffff800 is stored as 0 in the top bucket, where uclamp_rq_max_value()
takes it for the highest active clamp once a higher value leaves the rq.
The rq clamp falls below what the remaining tasks requested, and emptying
their bucket trips the WARN_ON_ONCE() in uclamp_rq_dec_id(), without any
privilege:
WARNING: kernel/sched/core.c:1862 at dequeue_task+0x2a3/0x2b0, CPU#0: repro/126
Call Trace:
__schedule+0x933/0x1320
schedule+0x36/0x100
do_nanosleep+0x61/0x190
hrtimer_nanosleep+0xb5/0x1d0
Make util_min and util_max unsigned int, so that -1 is the only value that
wraps. The pair check below is unaffected, since it skips -1 explicitly and
anything else is at most SCHED_CAPACITY_SCALE by then.
Requests that were only accepted because of this now fail with -EINVAL.
The following program triggers the warning when run by any user outside
a child cpu cgroup. With this applied, its 0xfffff800 request fails with
-EINVAL and the warning no longer appears:
#define _GNU_SOURCE
#include <pthread.h>
#include <sched.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>
struct attr {
uint32_t size, policy;
uint64_t flags;
int32_t nice;
uint32_t prio;
uint64_t runtime, deadline, period;
uint32_t util_min, util_max;
};
static volatile int stop;
static void set_min(uint32_t v)
{
/* KEEP_POLICY | KEEP_PARAMS | UTIL_CLAMP_MIN */
struct attr a = { .size = sizeof(a), .flags = 0x38, .util_min = v };
syscall(SYS_sched_setattr, 0, &a, 0);
}
static void burn(long us)
{
struct timespec s, n;
clock_gettime(CLOCK_MONOTONIC, &s);
do
clock_gettime(CLOCK_MONOTONIC, &n);
while ((n.tv_sec - s.tv_sec) * 1000000 +
(n.tv_nsec - s.tv_nsec) / 1000 < us);
}
static void *bogus(void *arg)
{
set_min(0xfffff800);
while (!stop)
burn(1000);
return arg;
}
static void *worker(void *arg)
{
long v = (long)arg;
set_min(v);
while (!stop) {
burn(v == 800 ? 300 : 1500);
usleep(100);
}
return arg;
}
int main(void)
{
pthread_t t[3];
cpu_set_t c;
int i;
CPU_ZERO(&c);
CPU_SET(0, &c);
sched_setaffinity(0, sizeof(c), &c);
pthread_create(&t[0], NULL, bogus, NULL);
usleep(50000);
pthread_create(&t[1], NULL, worker, (void *)500L);
pthread_create(&t[2], NULL, worker, (void *)800L);
sleep(15);
stop = 1;
for (i = 0; i < 3; i++)
pthread_join(t[i], NULL);
return 0;
}
An LLM helped find the warning and write this patch and its tests.
Fixes: 480a6ca2dc6e ("sched/uclamp: Allow to reset a task uclamp constraint value")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://sashiko.dev/#/patchset/20261016062629.2102883-1-sh_def%40163.com
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
Cc: stable@vger.kernel.org
---
This textually conflicts with [1], which rewrites these declarations and
keeps them int. Whichever goes second needs to keep them unsigned.
Tested on x86_64: 0xfffffffe, 0x80000000 and 0x7fffffff now fail with
-EINVAL and -1 still resets. Not run on a 32-bit kernel.
[1] https://lore.kernel.org/20261016062629.2102883-1-sh_def@163.com/
kernel/sched/syscalls.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
index 45a127deeadd9..abe5ea6b3e36d 100644
--- a/kernel/sched/syscalls.c
+++ b/kernel/sched/syscalls.c
@@ -318,8 +318,8 @@ static inline void __setscheduler_dl_pi(int newprio, int policy,
static int uclamp_validate(struct task_struct *p,
const struct sched_attr *attr)
{
- int util_min = p->uclamp_req[UCLAMP_MIN].value;
- int util_max = p->uclamp_req[UCLAMP_MAX].value;
+ unsigned int util_min = p->uclamp_req[UCLAMP_MIN].value;
+ unsigned int util_max = p->uclamp_req[UCLAMP_MAX].value;
if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MIN) {
util_min = attr->sched_util_min;
--
2.53.0
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-11-02 13:48 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-02 13:48 [PATCH] sched/uclamp: Reject clamp values above SCHED_CAPACITY_SCALE Andrea Parri
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®