2.5.51 nanosleep fails
Hi Linus, Marco, Julie, Everyone,
It really is broken. I just didn't try enough combinations.
Thanks Julie, the test suite really helps.
The problem is that nanosleep didn't check for the NULL pointer case and always tries to copy out the remaining time. The attached patch will fix this problem.
Jim Houston - Concurrent Computer Corp.
kernel/timer.c +2 -2
@@ -1040,7 +1040,7 @@jiffies_to_timespec (expire, &t); ret = -ERESTART_RESTARTBLOCK ; -if (copy_to_user (rmtp, &t,sizeof (t))) +if (rmtp &©_to_user (rmtp, &t,sizeof (t))) ret = -EFAULT ;/* The 'restart' block is already filled in */ }
@@ -1067,7 +1067,7 @@if (expire) {struct restart_block *restart;jiffies_to_timespec (expire, &t); -if (copy_to_user (rmtp, &t,sizeof (t))) +if (rmtp &©_to_user (rmtp, &t,sizeof (t)))return -EFAULT ; restart = ¤t_thread_info ()->restart_block;
Hi All,
Quoted 4 lines · On Thu, 12 Dec 2002 17:27:46 -0500 Jim Houston <[email protected]> wrote:
The problem is that nanosleep didn't check for the NULL pointer case and always tries to copy out the remaining time. The attached patch will fix this problem.
Equivalent fix for the compatibility layer ...
Not compiled, not tested (I need to get a 64 bit machine ...)
Obviously correct (hopefully) :-)
Signature · 91 lines
Cheers, Stephen Rothwell [email protected] http://www.canb.auug.org.au/~sfr/ diff -ruN 2.5.51/kernel/compat.c 2.5.51-ns/kernel/compat.c --- 2.5.51/kernel/compat.c 2002-12-10 15:10:41.000000000 +1100 +++ 2.5.51-ns/kernel/compat.c 2002-12-13 12:12:00.000000000 +1100 @@ -21,8 +21,7 @@ static long compat_nanosleep_restart(struct restart_block *restart) { unsigned long expire = restart->arg0, now = jiffies; - struct timespec *rmtp = (struct timespec *) restart->arg1; - long ret; + struct compat_timespec *rmtp; /* Did it expire while we handled signals? */ if (!time_after(expire, now)) @@ -30,22 +29,22 @@ current->state = TASK_INTERRUPTIBLE; expire = schedule_timeout(expire - now); + if (expire == 0) + return 0; - ret = 0; - if (expire) { + rmtp = (struct compat_timespec *)restart->arg1; + if (rmtp) { struct compat_timespec ct; struct timespec t; jiffies_to_timespec(expire, &t); ct.tv_sec = t.tv_sec; ct.tv_nsec = t.tv_nsec; - - ret = -ERESTART_RESTARTBLOCK; if (copy_to_user(rmtp, &ct, sizeof(ct))) - ret = -EFAULT; - /* The 'restart' block is already filled in */ + return -EFAULT; } - return ret; + /* The 'restart' block is already filled in */ + return -ERESTART_RESTARTBLOCK; } asmlinkage long compat_sys_nanosleep(struct compat_timespec *rqtp, @@ -53,8 +52,8 @@ { struct compat_timespec ct; struct timespec t; + struct restart_block *restart; unsigned long expire; - s32 ret; if (copy_from_user(&ct, rqtp, sizeof(ct))) return -EFAULT; @@ -67,25 +66,21 @@ expire = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec); current->state = TASK_INTERRUPTIBLE; expire = schedule_timeout(expire); + if (expire == 0) + return 0; - ret = 0; - if (expire) { - struct restart_block *restart; - + if (rmtp) { jiffies_to_timespec(expire, &t); ct.tv_sec = t.tv_sec; ct.tv_nsec = t.tv_nsec; - if (copy_to_user(rmtp, &ct, sizeof(ct))) return -EFAULT; - - restart = ¤t_thread_info()->restart_block; - restart->fn = compat_nanosleep_restart; - restart->arg0 = jiffies + expire; - restart->arg1 = (unsigned long) rmtp; - ret = -ERESTART_RESTARTBLOCK; } - return ret; + restart = ¤t_thread_info()->restart_block; + restart->fn = compat_nanosleep_restart; + restart->arg0 = jiffies + expire; + restart->arg1 = (unsigned long) rmtp; + return -ERESTART_RESTARTBLOCK; } /*