Linux kernel do_nanosleep() NULL Pointer Dereference
This bug was initially reported by Hiroshi Shimamoto to LKML on 3 August 2009. Today I saw an email about that by Eugene Teo on oss-security. As he states, the vulnerability must have been introduced in 2.6.28-rc1.
This code could be reached through clock_nanosleep(2) system call as H. Shimamoto demonstrated with his PoC code which is a simple:
#include <time.h> int main(void) { struct timespec ts; ts.tv_sec = 1; ts.tv_nsec = 0; return clock_nanosleep(4, 0, &ts, NULL); }
The code path is this…
/* * nanosleep for monotonic and realtime clocks */ static int common_nsleep(const clockid_t which_clock, int flags, struct timespec *tsave, struct timespec __user *rmtp) { return hrtimer_nanosleep(tsave, rmtp, flags & TIMER_ABSTIME ? HRTIMER_MODE_ABS : HRTIMER_MODE_REL, which_clock); }
This routine is located at kernel/posix-timers.c and now, let’s move to hrtimer_nanosleep() which can be found at kernel/hrtimer.c and includes this code:
long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, const enum hrtimer_mode mode, const clockid_t clockid) { struct restart_block *restart; struct hrtimer_sleeper t; int ret = 0; unsigned long slack; slack = current->timer_slack_ns; if (rt_task(current)) slack = 0; hrtimer_init_on_stack(&t.timer, clockid, mode); hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack); if (do_nanosleep(&t, mode)) goto out; ... }
And do_nanosleep() can be found at kernel/hrtimer.c and it begins with this code:
static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode) { hrtimer_init_sleeper(t, current); do { set_current_state(TASK_INTERRUPTIBLE); hrtimer_start_expires(&t->timer, mode); ... }
hrtimer_start_expires() is a simple inline routine from include/linux/hrtimer.h that calls hrtimer_start_range_ns() from kernel/hrtimer.c which is a simple wrapper around __hrtimer_start_range_ns() which will execute this:
int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_ns, const enum hrtimer_mode mode, int wakeup) { struct hrtimer_clock_base *base, *new_base; unsigned long flags; int ret, leftmost; base = lock_hrtimer_base(timer, &flags); ... }
The OOPS is triggered when lock_hrtimer_base() attempts to access the spinlock of that timer. To fix this bug, the following patch was applied:
@@ -202,6 +202,12 @@ static int no_timer_create(struct k_itimer *new_timer) return -EOPNOTSUPP; } +static int no_nsleep(const clockid_t which_clock, int flags, + struct timespec *tsave, struct timespec __user *rmtp) +{ + return -EOPNOTSUPP; +} + /* * Return nonzero if we know a priori this clockid_t value is bogus. */ @@ -254,6 +260,7 @@ static __init int init_posix_timers(void) .clock_get = posix_get_monotonic_raw, .clock_set = do_posix_clock_nosettime, .timer_create = no_timer_create, + .nsleep = no_nsleep, };
Leave a Reply