aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2017-06-13 05:31:16 -0400
committerEric W. Biederman <ebiederm@xmission.com>2017-06-17 13:20:22 -0400
commit57db7e4a2d92c2d3dfbca4ef8057849b2682436b (patch)
treeb5a83c2d1dea233faea73d7a3f4f5dbfd7834ed6
parent08332893e37af6ae779367e78e444f8f9571511d (diff)
signal: Only reschedule timers on signals timers have sent
Thomas Gleixner wrote: > The CRIU support added a 'feature' which allows a user space task to send > arbitrary (kernel) signals to itself. The changelog says: > > The kernel prevents sending of siginfo with positive si_code, because > these codes are reserved for kernel. I think we can allow a task to > send such a siginfo to itself. This operation should not be dangerous. > > Quite contrary to that claim, it turns out that it is outright dangerous > for signals with info->si_code == SI_TIMER. The following code sequence in > a user space task allows to crash the kernel: > > id = timer_create(CLOCK_XXX, ..... signo = SIGX); > timer_set(id, ....); > info->si_signo = SIGX; > info->si_code = SI_TIMER: > info->_sifields._timer._tid = id; > info->_sifields._timer._sys_private = 2; > rt_[tg]sigqueueinfo(..., SIGX, info); > sigemptyset(&sigset); > sigaddset(&sigset, SIGX); > rt_sigtimedwait(sigset, info); > > For timers based on CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID this > results in a kernel crash because sigwait() dequeues the signal and the > dequeue code observes: > > info->si_code == SI_TIMER && info->_sifields._timer._sys_private != 0 > > which triggers the following callchain: > > do_schedule_next_timer() -> posix_cpu_timer_schedule() -> arm_timer() > > arm_timer() executes a list_add() on the timer, which is already armed via > the timer_set() syscall. That's a double list add which corrupts the posix > cpu timer list. As a consequence the kernel crashes on the next operation > touching the posix cpu timer list. > > Posix clocks which are internally implemented based on hrtimers are not > affected by this because hrtimer_start() can handle already armed timers > nicely, but it's a reliable way to trigger the WARN_ON() in > hrtimer_forward(), which complains about calling that function on an > already armed timer. This problem has existed since the posix timer code was merged into 2.5.63. A few releases earlier in 2.5.60 ptrace gained the ability to inject not just a signal (which linux has supported since 1.0) but the full siginfo of a signal. The core problem is that the code will reschedule in response to signals getting dequeued not just for signals the timers sent but for other signals that happen to a si_code of SI_TIMER. Avoid this confusion by testing to see if the queued signal was preallocated as all timer signals are preallocated, and so far only the timer code preallocates signals. Move the check for if a timer needs to be rescheduled up into collect_signal where the preallocation check must be performed, and pass the result back to dequeue_signal where the code reschedules timers. This makes it clear why the code cares about preallocated timers. Cc: stable@vger.kernel.org Reported-by: Thomas Gleixner <tglx@linutronix.de> History Tree: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git Reference: 66dd34ad31e5 ("signal: allow to send any siginfo to itself") Reference: 1669ce53e2ff ("Add PTRACE_GETSIGINFO and PTRACE_SETSIGINFO") Fixes: db8b50ba75f2 ("[PATCH] POSIX clocks & timers") Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
-rw-r--r--kernel/signal.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/kernel/signal.c b/kernel/signal.c
index ca92bcfeb322..45b4c1ffe14e 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -510,7 +510,8 @@ int unhandled_signal(struct task_struct *tsk, int sig)
510 return !tsk->ptrace; 510 return !tsk->ptrace;
511} 511}
512 512
513static void collect_signal(int sig, struct sigpending *list, siginfo_t *info) 513static void collect_signal(int sig, struct sigpending *list, siginfo_t *info,
514 bool *resched_timer)
514{ 515{
515 struct sigqueue *q, *first = NULL; 516 struct sigqueue *q, *first = NULL;
516 517
@@ -532,6 +533,12 @@ static void collect_signal(int sig, struct sigpending *list, siginfo_t *info)
532still_pending: 533still_pending:
533 list_del_init(&first->list); 534 list_del_init(&first->list);
534 copy_siginfo(info, &first->info); 535 copy_siginfo(info, &first->info);
536
537 *resched_timer =
538 (first->flags & SIGQUEUE_PREALLOC) &&
539 (info->si_code == SI_TIMER) &&
540 (info->si_sys_private);
541
535 __sigqueue_free(first); 542 __sigqueue_free(first);
536 } else { 543 } else {
537 /* 544 /*
@@ -548,12 +555,12 @@ still_pending:
548} 555}
549 556
550static int __dequeue_signal(struct sigpending *pending, sigset_t *mask, 557static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
551 siginfo_t *info) 558 siginfo_t *info, bool *resched_timer)
552{ 559{
553 int sig = next_signal(pending, mask); 560 int sig = next_signal(pending, mask);
554 561
555 if (sig) 562 if (sig)
556 collect_signal(sig, pending, info); 563 collect_signal(sig, pending, info, resched_timer);
557 return sig; 564 return sig;
558} 565}
559 566
@@ -565,15 +572,16 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
565 */ 572 */
566int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) 573int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
567{ 574{
575 bool resched_timer = false;
568 int signr; 576 int signr;
569 577
570 /* We only dequeue private signals from ourselves, we don't let 578 /* We only dequeue private signals from ourselves, we don't let
571 * signalfd steal them 579 * signalfd steal them
572 */ 580 */
573 signr = __dequeue_signal(&tsk->pending, mask, info); 581 signr = __dequeue_signal(&tsk->pending, mask, info, &resched_timer);
574 if (!signr) { 582 if (!signr) {
575 signr = __dequeue_signal(&tsk->signal->shared_pending, 583 signr = __dequeue_signal(&tsk->signal->shared_pending,
576 mask, info); 584 mask, info, &resched_timer);
577#ifdef CONFIG_POSIX_TIMERS 585#ifdef CONFIG_POSIX_TIMERS
578 /* 586 /*
579 * itimer signal ? 587 * itimer signal ?
@@ -621,7 +629,7 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
621 current->jobctl |= JOBCTL_STOP_DEQUEUED; 629 current->jobctl |= JOBCTL_STOP_DEQUEUED;
622 } 630 }
623#ifdef CONFIG_POSIX_TIMERS 631#ifdef CONFIG_POSIX_TIMERS
624 if ((info->si_code & __SI_MASK) == __SI_TIMER && info->si_sys_private) { 632 if (resched_timer) {
625 /* 633 /*
626 * Release the siglock to ensure proper locking order 634 * Release the siglock to ensure proper locking order
627 * of timer locks outside of siglocks. Note, we leave 635 * of timer locks outside of siglocks. Note, we leave