Merge: CVE-2026-64560: posix-cpu-timers: Prevent UAF caused by non-leader exec() race

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/8619

JIRA: https://redhat.atlassian.net/browse/RHEL-227838
CVE: CVE-2026-64560
CVE: CVE-2026-64370
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/8619

By incorporating a number of posix-timers patches from the series in
merge commit 9a7b0158aea7 ("Merge tag 'posix-timers-2024-07-29' of
git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks
into timers/core") and a few extra ones, the merge conflicts with
the posix_cpu_timer_set() and posix_cpu_timer_rearm() functions can be
eliminated. However, merge conflicts still exist for posix_cpu_timer_del()
and kernel/exit.c as resolving them will require pulling in a lot
more patches.

This MR also includes another minor CVE-2026-64370 fix in the same
posix timers code base.

Signed-off-by: Waiman Long <longman@redhat.com>

Approved-by: Tony Camuso <tcamuso@redhat.com>
Approved-by: Ricardo Robaina <rrobaina@redhat.com>
Approved-by: Rafael Aquini <raquini@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
CKI KWF Bot
2026-08-18 21:59:43 +00:00
7 changed files with 317 additions and 201 deletions
+11 -6
View File
@@ -4,6 +4,7 @@
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/alarmtimer.h>
#include <linux/timerqueue.h>
@@ -62,16 +63,18 @@ static inline int clockid_to_fd(const clockid_t clk)
* cpu_timer - Posix CPU timer representation for k_itimer
* @node: timerqueue node to queue in the task/sig
* @head: timerqueue head on which this timer is queued
* @task: Pointer to target task
* @pid: Pointer to target task PID
* @elist: List head for the expiry list
* @firing: Timer is currently firing
* @handling: Pointer to the task which handles expiry
*/
struct cpu_timer {
struct timerqueue_node node;
struct timerqueue_head *head;
struct pid *pid;
struct list_head elist;
int firing;
struct timerqueue_node node;
struct timerqueue_head *head;
struct pid *pid;
struct list_head elist;
int firing;
struct task_struct __rcu *handling;
};
static inline bool cpu_timer_enqueue(struct timerqueue_head *head,
@@ -135,10 +138,12 @@ struct posix_cputimers {
/**
* posix_cputimers_work - Container for task work based posix CPU timer expiry
* @work: The task work to be scheduled
* @mutex: Mutex held around expiry in context of this task work
* @scheduled: @work has been scheduled already, no further processing
*/
struct posix_cputimers_work {
struct callback_head work;
struct mutex mutex;
unsigned int scheduled;
};
+7 -1
View File
@@ -184,7 +184,13 @@ static void __exit_signal(struct task_struct *tsk)
* doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
*/
flush_sigqueue(&tsk->pending);
tsk->sighand = NULL;
/*
* Ensure that all preceeding state is visible. Pairs with
* the smp_acquire__after_ctrl_dep() in the sighand == NULL
* path of lock_task_sighand().
*/
smp_store_release(&tsk->sighand, NULL);
spin_unlock(&sighand->siglock);
__cleanup_sighand(sighand);
+9 -1
View File
@@ -1395,8 +1395,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
rcu_read_lock();
for (;;) {
sighand = rcu_dereference(tsk->sighand);
if (unlikely(sighand == NULL))
if (unlikely(sighand == NULL)) {
/*
* Pairs with the smp_store_release() in
* __exit_signal(). It ensures that all state
* modifications to the task preceeding the store are
* visible to the callers of lock_task_sighand().
*/
smp_acquire__after_ctrl_dep();
break;
}
/*
* This sighand can be already freed and even reused, but
+4 -2
View File
@@ -566,12 +566,13 @@ static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
* alarm_timer_rearm - Posix timer callback for rearming timer
* @timr: Pointer to the posixtimer data struct
*/
static void alarm_timer_rearm(struct k_itimer *timr)
static bool alarm_timer_rearm(struct k_itimer *timr)
{
struct alarm *alarm = &timr->it.alarm.alarmtimer;
timr->it_overrun += alarm_forward_now(alarm, timr->it_interval);
alarm_start(alarm, alarm->node.expires);
return true;
}
/**
@@ -627,7 +628,7 @@ static void alarm_timer_wait_running(struct k_itimer *timr)
* @absolute: Expiry value is absolute time
* @sigev_none: Posix timer does not deliver signals
*/
static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires,
static bool alarm_timer_arm(struct k_itimer *timr, ktime_t expires,
bool absolute, bool sigev_none)
{
struct alarm *alarm = &timr->it.alarm.alarmtimer;
@@ -639,6 +640,7 @@ static void alarm_timer_arm(struct k_itimer *timr, ktime_t expires,
alarm->node.expires = expires;
else
alarm_start(&timr->it.alarm.alarmtimer, expires);
return true;
}
/**
+272 -186
View File
@@ -19,7 +19,7 @@
#include "posix-timers.h"
static void posix_cpu_timer_rearm(struct k_itimer *timer);
static bool posix_cpu_timer_rearm(struct k_itimer *timer);
void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit)
{
@@ -462,6 +462,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
trigger_base_recalc_expires(timer, p);
}
/*
* Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
*
* This can race with the reaping of the task:
*
* CPU0 CPU1
*
* // Finds task
* p = pid_task(pid, pid_type); __exit_signal(p)
* lock(p, sighand);
* posix_cpu_timers*_exit();
* sighand = lock_task_sighand(p); unhash_task(p);
* p->sighand = NULL;
* unlock(sighand);
*
* In this case sighand is NULL, which means the task and the associated timer
* queue cannot be longer accessed safely.
*
* __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
* dead it also invokes posix_cpu_timers_group_exit(). These functions delete
* all pending timers from the related timer queues. The POSIX timers (k_itimer)
* themself are still accessible, but not longer connected to the task.
*
* exec() works slightly differently. The task which exec()'s terminates all
* other threads in the thread group and runs __exit_signal() on them. As the
* thread group is not dead they only clean up the per task timers via
* posix_cpu_timers_exit().
*
* As the TGID on exec() stays the same per process timers stay queued, if they
* are armed. This works without a problem when exec() is done by the thread
* group leader. If a non-leader thread exec()'s this can end up in the
* following scenario:
*
* CPU0 CPU1
* // Returns old leader
* p = pid_task(pid, pid_type); de_thread()
* switch_leader()
* release_task(old leader)
* __exit_signal()
* old_leader->sighand = NULL;
* // Returns NULL
* sighand = lock_task_sighand(p)
*
* That's problematic for several functions:
*
* - posix_cpu_timer_del(): If the timer is still enqueued on the task the
* underlying k_itimer will be freed which results in a UAF in
* run_posix_cpu_timers() or on timerqueue related add/delete operations.
* If the timer is not enqueued, the failure is harmless
*
* - posix_cpu_timer_set(): Independent of the enqueued state that results in a
* transient failure which is user space visible (-ESRCH) for regular posix
* timers. But for the use case in do_cpu_nanosleep() it's the same UAF
* problem just that the timer is allocated on the stack.
*
* - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
* silently ignores the rearm request, which is a functional problem as the
* timer wont expire anymore.
*/
static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
{
enum pid_type type = clock_pid_type(timer->it_clock);
struct cpu_timer *ctmr = &timer->it.cpu;
guard(rcu)();
for (;;) {
struct task_struct *t = pid_task(timer->it.cpu.pid, type);
/* Fail if the task cannot be found. */
if (!t)
break;
/* Try to lock the task's sighand */
if (lock_task_sighand(t, flags))
return t;
/*
* The next PID lookup might either fail or return the new
* leader. This is correct for both exit() and exec().
*/
}
/*
* If the timer is still enqueued, warn. There is nothing safe to do
* here as there might be two timers in there which are removed in
* parallel and that will cause more damage than good. This should never
* happen!
*
* Ensure that the stores to the timer and timerqueue are visible:
*
* __exit_signal()
* posix_cpu_timers*_exit()
* write_seqlock(seqlock)
* smp_wmb(); <-------
* __unhash_process() | !pid_task()
* ----> smp_rmb();
* WARN_ON_ONCE(...)
*/
smp_rmb();
WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
return NULL;
}
/*
* Clean up a CPU-clock timer that is about to be destroyed.
@@ -471,29 +574,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
*/
static int posix_cpu_timer_del(struct k_itimer *timer)
{
struct cpu_timer *ctmr = &timer->it.cpu;
struct sighand_struct *sighand;
struct task_struct *p;
unsigned long flags;
int ret = 0;
rcu_read_lock();
p = cpu_timer_task_rcu(timer);
if (!p)
goto out;
p = timer_lock_sighand(timer, &flags);
/*
* Protect against sighand release/switch in exit/exec and process/
* thread timer list entry concurrent read/writes.
*/
sighand = lock_task_sighand(p, &flags);
if (unlikely(sighand == NULL)) {
/*
* This raced with the reaping of the task. The exit cleanup
* should have removed this timer from the timer queue.
*/
WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
} else {
if (likely(p)) {
if (timer->it.cpu.firing)
ret = TIMER_RETRY;
else
@@ -502,10 +589,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
unlock_task_sighand(p, &flags);
}
out:
rcu_read_unlock();
if (!ret)
put_pid(ctmr->pid);
put_pid(timer->it.cpu.pid);
return ret;
}
@@ -585,12 +670,7 @@ static void cpu_timer_fire(struct k_itimer *timer)
{
struct cpu_timer *ctmr = &timer->it.cpu;
if ((timer->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE) {
/*
* User don't want any signal.
*/
cpu_timer_setexpires(ctmr, 0);
} else if (unlikely(timer->sigq == NULL)) {
if (unlikely(timer->sigq == NULL)) {
/*
* This a special case for clock_nanosleep,
* not a normal timer from sys_timer_create.
@@ -615,6 +695,8 @@ static void cpu_timer_fire(struct k_itimer *timer)
}
}
static void __posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp, u64 now);
/*
* Guts of sys_timer_settime for CPU timers.
* This is called with the timer locked and interrupts disabled.
@@ -624,24 +706,21 @@ static void cpu_timer_fire(struct k_itimer *timer)
static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
struct itimerspec64 *new, struct itimerspec64 *old)
{
bool sigev_none = timer->it_sigev_notify == SIGEV_NONE;
clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
u64 old_expires, new_expires, old_incr, val;
struct cpu_timer *ctmr = &timer->it.cpu;
struct sighand_struct *sighand;
u64 old_expires, new_expires, now;
struct task_struct *p;
unsigned long flags;
int ret = 0;
rcu_read_lock();
p = cpu_timer_task_rcu(timer);
if (!p) {
/*
* If p has just been reaped, we can no
* longer get any information about it at all.
*/
rcu_read_unlock();
p = timer_lock_sighand(timer, &flags);
/*
* If p has just been reaped, we can no longer get any information about
* it at all.
*/
if (!p)
return -ESRCH;
}
/*
* Use the to_ktime conversion because that clamps the maximum
@@ -649,24 +728,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
*/
new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
/*
* Protect against sighand release/switch in exit/exec and p->cpu_timers
* and p->signal->cpu_timers read/write in arm_timer()
*/
sighand = lock_task_sighand(p, &flags);
/*
* If p has just been reaped, we can no
* longer get any information about it at all.
*/
if (unlikely(sighand == NULL)) {
rcu_read_unlock();
return -ESRCH;
}
/*
* Disarm any old timer after extracting its expiry time.
*/
old_incr = timer->it_interval;
/* Retrieve the current expiry time before disarming the timer */
old_expires = cpu_timer_getexpires(ctmr);
if (unlikely(timer->it.cpu.firing)) {
@@ -677,65 +739,46 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
}
/*
* We need to sample the current value to convert the new
* value from to relative and absolute, and to convert the
* old value from absolute to relative. To set a process
* timer, we need a sample to balance the thread expiry
* times (in arm_timer). With an absolute time, we must
* check if it's already passed. In short, we need a sample.
* Sample the current clock for saving the previous setting
* and for rearming the timer.
*/
if (CPUCLOCK_PERTHREAD(timer->it_clock))
val = cpu_clock_sample(clkid, p);
now = cpu_clock_sample(clkid, p);
else
val = cpu_clock_sample_group(clkid, p, true);
now = cpu_clock_sample_group(clkid, p, !sigev_none);
/* Retrieve the previous expiry value if requested. */
if (old) {
if (old_expires == 0) {
old->it_value.tv_sec = 0;
old->it_value.tv_nsec = 0;
} else {
/*
* Update the timer in case it has overrun already.
* If it has, we'll report it as having overrun and
* with the next reloaded timer already ticking,
* though we are swallowing that pending
* notification here to install the new setting.
*/
u64 exp = bump_cpu_timer(timer, val);
if (val < exp) {
old_expires = exp - val;
old->it_value = ns_to_timespec64(old_expires);
} else {
old->it_value.tv_nsec = 1;
old->it_value.tv_sec = 0;
}
}
old->it_value = (struct timespec64){ };
if (old_expires)
__posix_cpu_timer_get(timer, old, now);
}
/* Retry if the timer expiry is running concurrently */
if (unlikely(ret)) {
/*
* We are colliding with the timer actually firing.
* Punt after filling in the timer's old value, and
* disable this firing since we are already reporting
* it as an overrun (thanks to bump_cpu_timer above).
*/
unlock_task_sighand(p, &flags);
goto out;
return ret;
}
if (new_expires != 0 && !(timer_flags & TIMER_ABSTIME)) {
new_expires += val;
}
/* Convert relative expiry time to absolute */
if (new_expires && !(timer_flags & TIMER_ABSTIME))
new_expires += now;
/* Set the new expiry time (might be 0) */
cpu_timer_setexpires(ctmr, new_expires);
/*
* Install the new expiry time (or zero).
* For a timer with no notification action, we don't actually
* arm the timer (we'll just fake it for timer_gettime).
* Arm the timer if it is not disabled, the new expiry value has
* not yet expired and the timer requires signal delivery.
* SIGEV_NONE timers are never armed. In case the timer is not
* armed, enforce the reevaluation of the timer base so that the
* process wide cputime counter can be disabled eventually.
*/
cpu_timer_setexpires(ctmr, new_expires);
if (new_expires != 0 && val < new_expires) {
arm_timer(timer, p);
if (likely(!sigev_none)) {
if (new_expires && now < new_expires)
arm_timer(timer, p);
else
trigger_base_recalc_expires(timer, p);
}
unlock_task_sighand(p, &flags);
@@ -755,76 +798,70 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
timer->it_overrun_last = 0;
timer->it_overrun = -1;
if (val >= new_expires) {
if (new_expires != 0) {
/*
* The designated time already passed, so we notify
* immediately, even if the thread never runs to
* accumulate more time on this clock.
*/
cpu_timer_fire(timer);
}
/*
* Make sure we don't keep around the process wide cputime
* counter or the tick dependency if they are not necessary.
*/
sighand = lock_task_sighand(p, &flags);
if (!sighand)
goto out;
if (!cpu_timer_queued(ctmr))
trigger_base_recalc_expires(timer, p);
unlock_task_sighand(p, &flags);
}
out:
rcu_read_unlock();
if (old)
old->it_interval = ns_to_timespec64(old_incr);
/*
* If the new expiry time was already in the past the timer was not
* queued. Fire it immediately even if the thread never runs to
* accumulate more time on this clock.
*/
if (!sigev_none && new_expires && now >= new_expires)
cpu_timer_fire(timer);
return ret;
}
static void __posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp, u64 now)
{
bool sigev_none = timer->it_sigev_notify == SIGEV_NONE;
u64 expires, iv = timer->it_interval;
/*
* Make sure that interval timers are moved forward for the
* following cases:
* - SIGEV_NONE timers which are never armed
* - Timers which expired, but the signal has not yet been
* delivered
*/
if (iv && ((timer->it_requeue_pending & REQUEUE_PENDING) || sigev_none))
expires = bump_cpu_timer(timer, now);
else
expires = cpu_timer_getexpires(&timer->it.cpu);
/*
* Expired interval timers cannot have a remaining time <= 0.
* The kernel has to move them forward so that the next
* timer expiry is > @now.
*/
if (now < expires) {
itp->it_value = ns_to_timespec64(expires - now);
} else {
/*
* A single shot SIGEV_NONE timer must return 0, when it is
* expired! Timers which have a real signal delivery mode
* must return a remaining time greater than 0 because the
* signal has not yet been delivered.
*/
if (!sigev_none)
itp->it_value.tv_nsec = 1;
}
}
static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp)
{
clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
struct cpu_timer *ctmr = &timer->it.cpu;
u64 now, expires = cpu_timer_getexpires(ctmr);
struct task_struct *p;
u64 now;
rcu_read_lock();
p = cpu_timer_task_rcu(timer);
if (!p)
goto out;
if (p && cpu_timer_getexpires(&timer->it.cpu)) {
itp->it_interval = ktime_to_timespec64(timer->it_interval);
/*
* Easy part: convert the reload time.
*/
itp->it_interval = ktime_to_timespec64(timer->it_interval);
if (CPUCLOCK_PERTHREAD(timer->it_clock))
now = cpu_clock_sample(clkid, p);
else
now = cpu_clock_sample_group(clkid, p, false);
if (!expires)
goto out;
/*
* Sample the clock to take the difference with the expiry time.
*/
if (CPUCLOCK_PERTHREAD(timer->it_clock))
now = cpu_clock_sample(clkid, p);
else
now = cpu_clock_sample_group(clkid, p, false);
if (now < expires) {
itp->it_value = ns_to_timespec64(expires - now);
} else {
/*
* The timer should have expired already, but the firing
* hasn't taken place yet. Say it's just about to expire.
*/
itp->it_value.tv_nsec = 1;
itp->it_value.tv_sec = 0;
__posix_cpu_timer_get(timer, itp, now);
}
out:
rcu_read_unlock();
}
@@ -847,6 +884,8 @@ static u64 collect_timerqueue(struct timerqueue_head *head,
return expires;
ctmr->firing = 1;
/* See posix_cpu_timer_wait_running() */
rcu_assign_pointer(ctmr->handling, current);
cpu_timer_dequeue(ctmr);
list_add_tail(&ctmr->elist, firing);
}
@@ -1042,24 +1081,20 @@ static void check_process_timers(struct task_struct *tsk,
/*
* This is called from the signal code (via posixtimer_rearm)
* when the last timer signal was delivered and we have to reload the timer.
*
* Return true unconditionally so the core code assumes the timer to be
* armed. Otherwise it would requeue the signal.
*/
static void posix_cpu_timer_rearm(struct k_itimer *timer)
static bool posix_cpu_timer_rearm(struct k_itimer *timer)
{
clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
struct task_struct *p;
struct sighand_struct *sighand;
unsigned long flags;
u64 now;
rcu_read_lock();
p = cpu_timer_task_rcu(timer);
if (!p)
goto out;
/* Protect timer list r/w in arm_timer() */
sighand = lock_task_sighand(p, &flags);
if (unlikely(sighand == NULL))
goto out;
p = timer_lock_sighand(timer, &flags);
if (unlikely(!p))
return true;
/*
* Fetch the current sample and update the timer's expiry time.
@@ -1076,8 +1111,7 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
*/
arm_timer(timer, p);
unlock_task_sighand(p, &flags);
out:
rcu_read_unlock();
return true;
}
/**
@@ -1162,7 +1196,49 @@ static void handle_posix_cpu_timers(struct task_struct *tsk);
#ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
static void posix_cpu_timers_work(struct callback_head *work)
{
struct posix_cputimers_work *cw = container_of(work, typeof(*cw), work);
mutex_lock(&cw->mutex);
handle_posix_cpu_timers(current);
mutex_unlock(&cw->mutex);
}
/*
* Invoked from the posix-timer core when a cancel operation failed because
* the timer is marked firing. The caller holds rcu_read_lock(), which
* protects the timer and the task which is expiring it from being freed.
*/
static void posix_cpu_timer_wait_running(struct k_itimer *timr)
{
struct task_struct *tsk = rcu_dereference(timr->it.cpu.handling);
/* Has the handling task completed expiry already? */
if (!tsk)
return;
/* Ensure that the task cannot go away */
get_task_struct(tsk);
/* Now drop the RCU protection so the mutex can be locked */
rcu_read_unlock();
/* Wait on the expiry mutex */
mutex_lock(&tsk->posix_cputimers_work.mutex);
/* Release it immediately again. */
mutex_unlock(&tsk->posix_cputimers_work.mutex);
/* Drop the task reference. */
put_task_struct(tsk);
/* Relock RCU so the callsite is balanced */
rcu_read_lock();
}
static void posix_cpu_timer_wait_running_nsleep(struct k_itimer *timr)
{
/* Ensure that timr->it.cpu.handling task cannot go away */
rcu_read_lock();
spin_unlock_irq(&timr->it_lock);
posix_cpu_timer_wait_running(timr);
rcu_read_unlock();
/* @timr is on stack and is valid */
spin_lock_irq(&timr->it_lock);
}
/*
@@ -1178,6 +1254,7 @@ void clear_posix_cputimers_work(struct task_struct *p)
sizeof(p->posix_cputimers_work.work));
init_task_work(&p->posix_cputimers_work.work,
posix_cpu_timers_work);
mutex_init(&p->posix_cputimers_work.mutex);
p->posix_cputimers_work.scheduled = false;
}
@@ -1256,6 +1333,18 @@ static inline void __run_posix_cpu_timers(struct task_struct *tsk)
lockdep_posixtimer_exit();
}
static void posix_cpu_timer_wait_running(struct k_itimer *timr)
{
cpu_relax();
}
static void posix_cpu_timer_wait_running_nsleep(struct k_itimer *timr)
{
spin_unlock_irq(&timr->it_lock);
cpu_relax();
spin_lock_irq(&timr->it_lock);
}
static inline bool posix_cpu_timers_work_scheduled(struct task_struct *tsk)
{
return false;
@@ -1364,6 +1453,8 @@ static void handle_posix_cpu_timers(struct task_struct *tsk)
*/
if (likely(cpu_firing >= 0))
cpu_timer_fire(timer);
/* See posix_cpu_timer_wait_running() */
rcu_assign_pointer(timer->it.cpu.handling, NULL);
spin_unlock(&timer->it_lock);
}
}
@@ -1477,6 +1568,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
spin_lock_irq(&timer.it_lock);
error = posix_cpu_timer_set(&timer, flags, &it, NULL);
if (error) {
posix_cpu_timer_del(&timer);
spin_unlock_irq(&timer.it_lock);
return error;
}
@@ -1507,23 +1599,16 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
expires = cpu_timer_getexpires(&timer.it.cpu);
error = posix_cpu_timer_set(&timer, 0, &zero_it, &it);
if (!error) {
/*
* Timer is now unarmed, deletion can not fail.
*/
/* Timer is now unarmed, deletion can not fail. */
posix_cpu_timer_del(&timer);
} else {
while (error == TIMER_RETRY) {
posix_cpu_timer_wait_running_nsleep(&timer);
error = posix_cpu_timer_del(&timer);
}
}
spin_unlock_irq(&timer.it_lock);
while (error == TIMER_RETRY) {
/*
* We need to handle case when timer was or is in the
* middle of firing. In other cases we already freed
* resources.
*/
spin_lock_irq(&timer.it_lock);
error = posix_cpu_timer_del(&timer);
spin_unlock_irq(&timer.it_lock);
}
spin_unlock_irq(&timer.it_lock);
if ((it.it_value.tv_sec | it.it_value.tv_nsec) == 0) {
/*
@@ -1633,6 +1718,7 @@ const struct k_clock clock_posix_cpu = {
.timer_del = posix_cpu_timer_del,
.timer_get = posix_cpu_timer_get,
.timer_rearm = posix_cpu_timer_rearm,
.timer_wait_running = posix_cpu_timer_wait_running,
};
const struct k_clock clock_process = {
+12 -3
View File
@@ -290,13 +290,14 @@ static inline int timer_overrun_to_int(struct k_itimer *timr, int baseval)
return sum > (s64)INT_MAX ? INT_MAX : (int)sum;
}
static void common_hrtimer_rearm(struct k_itimer *timr)
static bool common_hrtimer_rearm(struct k_itimer *timr)
{
struct hrtimer *timer = &timr->it.real.timer;
timr->it_overrun += hrtimer_forward(timer, timer->base->get_time(),
timr->it_interval);
hrtimer_restart(timer);
return true;
}
/*
@@ -790,7 +791,7 @@ SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id)
return overrun;
}
static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires,
static bool common_hrtimer_arm(struct k_itimer *timr, ktime_t expires,
bool absolute, bool sigev_none)
{
struct hrtimer *timer = &timr->it.real.timer;
@@ -818,6 +819,7 @@ static void common_hrtimer_arm(struct k_itimer *timr, ktime_t expires,
if (!sigev_none)
hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
return true;
}
static int common_hrtimer_try_to_cancel(struct k_itimer *timr)
@@ -846,6 +848,10 @@ static struct k_itimer *timer_wait_running(struct k_itimer *timer,
rcu_read_lock();
unlock_timer(timer, *flags);
/*
* kc->timer_wait_running() might drop RCU lock. So @timer
* cannot be touched anymore after the function returns!
*/
if (!WARN_ON_ONCE(!kc->timer_wait_running))
kc->timer_wait_running(timer);
@@ -902,7 +908,7 @@ static int do_timer_settime(timer_t timer_id, int tmr_flags,
const struct k_clock *kc;
struct k_itimer *timr;
unsigned long flags;
int error = 0;
int error;
if (!timespec64_valid(&new_spec64->it_interval) ||
!timespec64_valid(&new_spec64->it_value))
@@ -916,6 +922,9 @@ retry:
if (!timr)
return -EINVAL;
if (old_spec64)
old_spec64->it_interval = ktime_to_timespec64(timr->it_interval);
kc = timr->kclock;
if (WARN_ON_ONCE(!kc || !kc->timer_set))
error = -EINVAL;
+2 -2
View File
@@ -21,11 +21,11 @@ struct k_clock {
int (*timer_del)(struct k_itimer *timr);
void (*timer_get)(struct k_itimer *timr,
struct itimerspec64 *cur_setting);
void (*timer_rearm)(struct k_itimer *timr);
bool (*timer_rearm)(struct k_itimer *timr);
s64 (*timer_forward)(struct k_itimer *timr, ktime_t now);
ktime_t (*timer_remaining)(struct k_itimer *timr, ktime_t now);
int (*timer_try_to_cancel)(struct k_itimer *timr);
void (*timer_arm)(struct k_itimer *timr, ktime_t expires,
bool (*timer_arm)(struct k_itimer *timr, ktime_t expires,
bool absolute, bool sigev_none);
void (*timer_wait_running)(struct k_itimer *timr);
};