mirror of
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9.git
synced 2026-09-09 00:08:12 +08:00
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:
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user