diff options
| -rw-r--r-- | ipc/util.c | 31 | ||||
| -rw-r--r-- | ipc/util.h | 3 | 
2 files changed, 29 insertions, 5 deletions
diff --git a/ipc/util.c b/ipc/util.c index 813804ebdeb..3df0af3158a 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -824,11 +824,28 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,  				      struct ipc64_perm *perm, int extra_perm)  {  	struct kern_ipc_perm *ipcp; + +	ipcp = ipcctl_pre_down_nolock(ns, ids, id, cmd, perm, extra_perm); +	if (IS_ERR(ipcp)) +		goto out; + +	spin_lock(&ipcp->lock); +out: +	return ipcp; +} + +struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, +					     struct ipc_ids *ids, int id, int cmd, +					     struct ipc64_perm *perm, int extra_perm) +{  	kuid_t euid; -	int err; +	int err = -EPERM; +	struct kern_ipc_perm *ipcp;  	down_write(&ids->rw_mutex); -	ipcp = ipc_lock_check(ids, id); +	rcu_read_lock(); + +	ipcp = ipc_obtain_object_check(ids, id);  	if (IS_ERR(ipcp)) {  		err = PTR_ERR(ipcp);  		goto out_up; @@ -837,17 +854,21 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,  	audit_ipc_obj(ipcp);  	if (cmd == IPC_SET)  		audit_ipc_set_perm(extra_perm, perm->uid, -					 perm->gid, perm->mode); +				   perm->gid, perm->mode);  	euid = current_euid();  	if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid)  ||  	    ns_capable(ns->user_ns, CAP_SYS_ADMIN))  		return ipcp; -	err = -EPERM; -	ipc_unlock(ipcp);  out_up: +	/* +	 * Unsuccessful lookup, unlock and return +	 * the corresponding error. +	 */ +	rcu_read_unlock();  	up_write(&ids->rw_mutex); +  	return ERR_PTR(err);  } diff --git a/ipc/util.h b/ipc/util.h index bfc8d4ea6e4..13d92fea15a 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -128,6 +128,9 @@ struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id);  void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out);  void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out);  int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); +struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, +					     struct ipc_ids *ids, int id, int cmd, +					     struct ipc64_perm *perm, int extra_perm);  struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,  				      struct ipc_ids *ids, int id, int cmd,  				      struct ipc64_perm *perm, int extra_perm);  |