diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2017-12-15 04:32:03 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2017-12-15 05:08:40 -0500 |
commit | cef31d9af908243421258f1df35a4a644604efbe (patch) | |
tree | c57537e57cda47a079d5e68a4cc08f1c020ae572 | |
parent | 50c4c4e268a2d7a3e58ebb698ac74da0de40ae36 (diff) |
posix-timer: Properly check sigevent->sigev_notify
timer_create() specifies via sigevent->sigev_notify the signal delivery for
the new timer. The valid modes are SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD
and (SIGEV_SIGNAL | SIGEV_THREAD_ID).
The sanity check in good_sigevent() is only checking the valid combination
for the SIGEV_THREAD_ID bit, i.e. SIGEV_SIGNAL, but if SIGEV_THREAD_ID is
not set it accepts any random value.
This has no real effects on the posix timer and signal delivery code, but
it affects show_timer() which handles the output of /proc/$PID/timers. That
function uses a string array to pretty print sigev_notify. The access to
that array has no bound checks, so random sigev_notify cause access beyond
the array bounds.
Add proper checks for the valid notify modes and remove the SIGEV_THREAD_ID
masking from various code pathes as SIGEV_NONE can never be set in
combination with SIGEV_THREAD_ID.
Reported-by: Eric Biggers <ebiggers3@gmail.com>
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Reported-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: John Stultz <john.stultz@linaro.org>
Cc: stable@vger.kernel.org
-rw-r--r-- | kernel/time/posix-timers.c | 29 |
1 files changed, 17 insertions, 12 deletions
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 13d6881f908b..ec999f32c840 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c | |||
@@ -434,17 +434,22 @@ static struct pid *good_sigevent(sigevent_t * event) | |||
434 | { | 434 | { |
435 | struct task_struct *rtn = current->group_leader; | 435 | struct task_struct *rtn = current->group_leader; |
436 | 436 | ||
437 | if ((event->sigev_notify & SIGEV_THREAD_ID ) && | 437 | switch (event->sigev_notify) { |
438 | (!(rtn = find_task_by_vpid(event->sigev_notify_thread_id)) || | 438 | case SIGEV_SIGNAL | SIGEV_THREAD_ID: |
439 | !same_thread_group(rtn, current) || | 439 | rtn = find_task_by_vpid(event->sigev_notify_thread_id); |
440 | (event->sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_SIGNAL)) | 440 | if (!rtn || !same_thread_group(rtn, current)) |
441 | return NULL; | ||
442 | /* FALLTHRU */ | ||
443 | case SIGEV_SIGNAL: | ||
444 | case SIGEV_THREAD: | ||
445 | if (event->sigev_signo <= 0 || event->sigev_signo > SIGRTMAX) | ||
446 | return NULL; | ||
447 | /* FALLTHRU */ | ||
448 | case SIGEV_NONE: | ||
449 | return task_pid(rtn); | ||
450 | default: | ||
441 | return NULL; | 451 | return NULL; |
442 | 452 | } | |
443 | if (((event->sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE) && | ||
444 | ((event->sigev_signo <= 0) || (event->sigev_signo > SIGRTMAX))) | ||
445 | return NULL; | ||
446 | |||
447 | return task_pid(rtn); | ||
448 | } | 453 | } |
449 | 454 | ||
450 | static struct k_itimer * alloc_posix_timer(void) | 455 | static struct k_itimer * alloc_posix_timer(void) |
@@ -669,7 +674,7 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting) | |||
669 | struct timespec64 ts64; | 674 | struct timespec64 ts64; |
670 | bool sig_none; | 675 | bool sig_none; |
671 | 676 | ||
672 | sig_none = (timr->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE; | 677 | sig_none = timr->it_sigev_notify == SIGEV_NONE; |
673 | iv = timr->it_interval; | 678 | iv = timr->it_interval; |
674 | 679 | ||
675 | /* interval timer ? */ | 680 | /* interval timer ? */ |
@@ -856,7 +861,7 @@ int common_timer_set(struct k_itimer *timr, int flags, | |||
856 | 861 | ||
857 | timr->it_interval = timespec64_to_ktime(new_setting->it_interval); | 862 | timr->it_interval = timespec64_to_ktime(new_setting->it_interval); |
858 | expires = timespec64_to_ktime(new_setting->it_value); | 863 | expires = timespec64_to_ktime(new_setting->it_value); |
859 | sigev_none = (timr->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE; | 864 | sigev_none = timr->it_sigev_notify == SIGEV_NONE; |
860 | 865 | ||
861 | kc->timer_arm(timr, expires, flags & TIMER_ABSTIME, sigev_none); | 866 | kc->timer_arm(timr, expires, flags & TIMER_ABSTIME, sigev_none); |
862 | timr->it_active = !sigev_none; | 867 | timr->it_active = !sigev_none; |