Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

posix-timers: Cure si_sys_private race

The si_sys_private member of the siginfo which is embedded in the
preallocated sigqueue is used by the posix timer code to decide whether a
timer must be reprogrammed on signal delivery.

The handling of this is racy as a long standing comment in that code
documents. It is modified with the timer lock held, but without sighand
lock being held. The actual signal delivery code checks for it under
sighand lock without holding the timer lock.

Hand the new value to send_sigqueue() as argument and store it with sighand
lock held. This is an intermediate change to address this issue.

The arguments to this function will be cleanup in subsequent changes.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/20241001083835.434338954@linutronix.de

+11 -16
+1 -1
include/linux/sched/signal.h
··· 340 340 extern int zap_other_threads(struct task_struct *p); 341 341 extern struct sigqueue *sigqueue_alloc(void); 342 342 extern void sigqueue_free(struct sigqueue *); 343 - extern int send_sigqueue(struct sigqueue *, struct pid *, enum pid_type); 343 + extern int send_sigqueue(struct sigqueue *, struct pid *, enum pid_type, int si_private); 344 344 extern int do_sigaction(int, struct k_sigaction *, struct k_sigaction *); 345 345 346 346 static inline void clear_notify_signal(void)
+9 -1
kernel/signal.c
··· 1919 1919 __sigqueue_free(q); 1920 1920 } 1921 1921 1922 - int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type) 1922 + int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type, int si_private) 1923 1923 { 1924 1924 int sig = q->info.si_signo; 1925 1925 struct sigpending *pending; ··· 1953 1953 t = current; 1954 1954 if (!likely(lock_task_sighand(t, &flags))) 1955 1955 goto ret; 1956 + 1957 + /* 1958 + * Update @q::info::si_sys_private for posix timer signals with 1959 + * sighand locked to prevent a race against dequeue_signal() which 1960 + * decides based on si_sys_private whether to invoke 1961 + * posixtimer_rearm() or not. 1962 + */ 1963 + q->info.si_sys_private = si_private; 1956 1964 1957 1965 ret = 1; /* the signal is ignored */ 1958 1966 result = TRACE_SIGNAL_IGNORED;
+1 -14
kernel/time/posix-timers.c
··· 299 299 if (timr->it_interval) 300 300 si_private = ++timr->it_requeue_pending; 301 301 302 - /* 303 - * FIXME: if ->sigq is queued we can race with 304 - * dequeue_signal()->posixtimer_rearm(). 305 - * 306 - * If dequeue_signal() sees the "right" value of 307 - * si_sys_private it calls posixtimer_rearm(). 308 - * We re-queue ->sigq and drop ->it_lock(). 309 - * posixtimer_rearm() locks the timer 310 - * and re-schedules it while ->sigq is pending. 311 - * Not really bad, but not that we want. 312 - */ 313 - timr->sigq->info.si_sys_private = si_private; 314 - 315 302 type = !(timr->it_sigev_notify & SIGEV_THREAD_ID) ? PIDTYPE_TGID : PIDTYPE_PID; 316 - ret = send_sigqueue(timr->sigq, timr->it_pid, type); 303 + ret = send_sigqueue(timr->sigq, timr->it_pid, type, si_private); 317 304 /* If we failed to send the signal the timer stops. */ 318 305 return ret > 0; 319 306 }