diff options
| -rw-r--r-- | include/linux/resource.h | 4 | ||||
| -rw-r--r-- | kernel/sys.c | 71 | 
2 files changed, 39 insertions, 36 deletions
diff --git a/include/linux/resource.h b/include/linux/resource.h index 037aa7e6335..88d36f9145b 100644 --- a/include/linux/resource.h +++ b/include/linux/resource.h @@ -80,8 +80,8 @@ struct rlimit64 {  struct task_struct;  int getrusage(struct task_struct *p, int who, struct rusage __user *ru); -int do_setrlimit(struct task_struct *tsk, unsigned int resource, -		struct rlimit *new_rlim); +int do_prlimit(struct task_struct *tsk, unsigned int resource, +		struct rlimit *new_rlim, struct rlimit *old_rlim);  #endif /* __KERNEL__ */ diff --git a/kernel/sys.c b/kernel/sys.c index c762eebdebf..bc7d1be0960 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1273,18 +1273,21 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,  #endif  /* make sure you are allowed to change @tsk limits before calling this */ -int do_setrlimit(struct task_struct *tsk, unsigned int resource, -		struct rlimit *new_rlim) +int do_prlimit(struct task_struct *tsk, unsigned int resource, +		struct rlimit *new_rlim, struct rlimit *old_rlim)  { -	struct rlimit *old_rlim; +	struct rlimit *rlim;  	int retval = 0;  	if (resource >= RLIM_NLIMITS)  		return -EINVAL; -	if (new_rlim->rlim_cur > new_rlim->rlim_max) -		return -EINVAL; -	if (resource == RLIMIT_NOFILE && new_rlim->rlim_max > sysctl_nr_open) -		return -EPERM; +	if (new_rlim) { +		if (new_rlim->rlim_cur > new_rlim->rlim_max) +			return -EINVAL; +		if (resource == RLIMIT_NOFILE && +				new_rlim->rlim_max > sysctl_nr_open) +			return -EPERM; +	}  	/* protect tsk->signal and tsk->sighand from disappearing */  	read_lock(&tasklist_lock); @@ -1293,42 +1296,42 @@ int do_setrlimit(struct task_struct *tsk, unsigned int resource,  		goto out;  	} -	old_rlim = tsk->signal->rlim + resource; +	rlim = tsk->signal->rlim + resource;  	task_lock(tsk->group_leader); -	if (new_rlim->rlim_max > old_rlim->rlim_max && -			!capable(CAP_SYS_RESOURCE)) -		retval = -EPERM; -	if (!retval) -		retval = security_task_setrlimit(tsk->group_leader, resource, -				new_rlim); - -	if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) { -		/* -		 * The caller is asking for an immediate RLIMIT_CPU -		 * expiry.  But we use the zero value to mean "it was -		 * never set".  So let's cheat and make it one second -		 * instead -		 */ -		new_rlim->rlim_cur = 1; +	if (new_rlim) { +		if (new_rlim->rlim_max > rlim->rlim_max && +				!capable(CAP_SYS_RESOURCE)) +			retval = -EPERM; +		if (!retval) +			retval = security_task_setrlimit(tsk->group_leader, +					resource, new_rlim); +		if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) { +			/* +			 * The caller is asking for an immediate RLIMIT_CPU +			 * expiry.  But we use the zero value to mean "it was +			 * never set".  So let's cheat and make it one second +			 * instead +			 */ +			new_rlim->rlim_cur = 1; +		} +	} +	if (!retval) { +		if (old_rlim) +			*old_rlim = *rlim; +		if (new_rlim) +			*rlim = *new_rlim;  	} - -	if (!retval) -		*old_rlim = *new_rlim;  	task_unlock(tsk->group_leader); -	if (retval || resource != RLIMIT_CPU) -		goto out; -  	/*  	 * RLIMIT_CPU handling.   Note that the kernel fails to return an error  	 * code if it rejected the user's attempt to set RLIMIT_CPU.  This is a  	 * very long-standing error, and fixing it now risks breakage of  	 * applications, so we live with it  	 */ -	if (new_rlim->rlim_cur == RLIM_INFINITY) -		goto out; - -	update_rlimit_cpu(tsk, new_rlim->rlim_cur); +	 if (!retval && new_rlim && resource == RLIMIT_CPU && +			 new_rlim->rlim_cur != RLIM_INFINITY) +		update_rlimit_cpu(tsk, new_rlim->rlim_cur);  out:  	read_unlock(&tasklist_lock);  	return retval; @@ -1340,7 +1343,7 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim)  	if (copy_from_user(&new_rlim, rlim, sizeof(*rlim)))  		return -EFAULT; -	return do_setrlimit(current, resource, &new_rlim); +	return do_prlimit(current, resource, &new_rlim, NULL);  }  /*  |