diff options
Diffstat (limited to 'kernel/compat.c')
| -rw-r--r-- | kernel/compat.c | 24 | 
1 files changed, 21 insertions, 3 deletions
| diff --git a/kernel/compat.c b/kernel/compat.c index 633394f442f8..ebb3c369d03d 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -226,7 +226,7 @@ static long compat_nanosleep_restart(struct restart_block *restart)  	ret = hrtimer_nanosleep_restart(restart);  	set_fs(oldfs); -	if (ret) { +	if (ret == -ERESTART_RESTARTBLOCK) {  		rmtp = restart->nanosleep.compat_rmtp;  		if (rmtp && compat_put_timespec(&rmt, rmtp)) @@ -256,7 +256,26 @@ COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp,  				HRTIMER_MODE_REL, CLOCK_MONOTONIC);  	set_fs(oldfs); -	if (ret) { +	/* +	 * hrtimer_nanosleep() can only return 0 or +	 * -ERESTART_RESTARTBLOCK here because: +	 * +	 * - we call it with HRTIMER_MODE_REL and therefor exclude the +	 *   -ERESTARTNOHAND return path. +	 * +	 * - we supply the rmtp argument from the task stack (due to +	 *   the necessary compat conversion. So the update cannot +	 *   fail, which excludes the -EFAULT return path as well. If +	 *   it fails nevertheless we have a bigger problem and wont +	 *   reach this place anymore. +	 * +	 * - if the return value is 0, we do not have to update rmtp +	 *    because there is no remaining time. +	 * +	 * We check for -ERESTART_RESTARTBLOCK nevertheless if the +	 * core implementation decides to return random nonsense. +	 */ +	if (ret == -ERESTART_RESTARTBLOCK) {  		struct restart_block *restart  			= ¤t_thread_info()->restart_block; @@ -266,7 +285,6 @@ COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp,  		if (rmtp && compat_put_timespec(&rmt, rmtp))  			return -EFAULT;  	} -  	return ret;  } |