]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
uclampset: fix lost-update race in set_uclamp_one()
authorFurkan Caliskan <frn1furkan10@gmail.com>
Sun, 31 May 2026 08:36:58 +0000 (11:36 +0300)
committerFurkan Caliskan <frn1furkan10@gmail.com>
Sun, 31 May 2026 08:42:30 +0000 (11:42 +0300)
The function unconditionally sets both SCHED_FLAG_UTIL_CLAMP_MIN and
SCHED_FLAG_UTIL_CLAMP_MAX in sa.sched_flags regardless of which values
the user actually requested to change.

This creates a lost-update race: sched_getattr() fetches the current
clamp values, but between that call and sched_setattr(), another thread
may legitimately update the value we did not intend to touch. Because
both flags are always set, sched_setattr() forces the kernel to apply
the stale cached value, silently overwriting the concurrent update.

Fix by setting the flags conditionally.

Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com>
schedutils/uclampset.c

index dc912faee189d12d1a9f633b842eab581966ba73..3998d3a0a33b840adf7b681f374239ca7c8a63fc 100644 (file)
@@ -157,16 +157,16 @@ static int set_uclamp_one(struct uclampset *ctl, pid_t pid)
        if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0)
                err(EXIT_FAILURE, _("failed to get pid %d's uclamp values"), pid);
 
-       if (ctl->util_min_set)
+       sa.sched_flags = SCHED_FLAG_KEEP_POLICY | SCHED_FLAG_KEEP_PARAMS;
+
+       if (ctl->util_min_set) {
                sa.sched_util_min = ctl->util_min;
-       if (ctl->util_max_set)
+               sa.sched_flags |= SCHED_FLAG_UTIL_CLAMP_MIN;
+       }
+       if (ctl->util_max_set) {
                sa.sched_util_max = ctl->util_max;
-
-       sa.sched_flags = SCHED_FLAG_KEEP_POLICY |
-                        SCHED_FLAG_KEEP_PARAMS |
-                        SCHED_FLAG_UTIL_CLAMP_MIN |
-                        SCHED_FLAG_UTIL_CLAMP_MAX;
-
+               sa.sched_flags |= SCHED_FLAG_UTIL_CLAMP_MAX;
+       }
        if (ctl->reset_on_fork)
                sa.sched_flags |= SCHED_FLAG_RESET_ON_FORK;