Commit 7b4cc5d8 authored by Davidlohr Bueso's avatar Davidlohr Bueso Committed by Linus Torvalds

ipc: move locking out of ipcctl_pre_down_nolock

This function currently acquires both the rw_mutex and the rcu lock on
successful lookups, leaving the callers to explicitly unlock them,
creating another two level locking situation.

Make the callers (including those that still use ipcctl_pre_down())
explicitly lock and unlock the rwsem and rcu lock.
Signed-off-by: default avatarDavidlohr Bueso <davidlohr.bueso@hp.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Rik van Riel <riel@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent cf9d5d78
...@@ -407,31 +407,38 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, ...@@ -407,31 +407,38 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
return -EFAULT; return -EFAULT;
} }
down_write(&msg_ids(ns).rw_mutex);
rcu_read_lock();
ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd, ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd,
&msqid64.msg_perm, msqid64.msg_qbytes); &msqid64.msg_perm, msqid64.msg_qbytes);
if (IS_ERR(ipcp)) if (IS_ERR(ipcp)) {
return PTR_ERR(ipcp); err = PTR_ERR(ipcp);
/* the ipc lock is not held upon failure */
goto out_unlock1;
}
msq = container_of(ipcp, struct msg_queue, q_perm); msq = container_of(ipcp, struct msg_queue, q_perm);
err = security_msg_queue_msgctl(msq, cmd); err = security_msg_queue_msgctl(msq, cmd);
if (err) if (err)
goto out_unlock; goto out_unlock0;
switch (cmd) { switch (cmd) {
case IPC_RMID: case IPC_RMID:
/* freeque unlocks the ipc object and rcu */
freeque(ns, ipcp); freeque(ns, ipcp);
goto out_up; goto out_up;
case IPC_SET: case IPC_SET:
if (msqid64.msg_qbytes > ns->msg_ctlmnb && if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
!capable(CAP_SYS_RESOURCE)) { !capable(CAP_SYS_RESOURCE)) {
err = -EPERM; err = -EPERM;
goto out_unlock; goto out_unlock0;
} }
err = ipc_update_perm(&msqid64.msg_perm, ipcp); err = ipc_update_perm(&msqid64.msg_perm, ipcp);
if (err) if (err)
goto out_unlock; goto out_unlock0;
msq->q_qbytes = msqid64.msg_qbytes; msq->q_qbytes = msqid64.msg_qbytes;
...@@ -448,8 +455,11 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, ...@@ -448,8 +455,11 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
default: default:
err = -EINVAL; err = -EINVAL;
} }
out_unlock:
msg_unlock(msq); out_unlock0:
ipc_unlock_object(&msq->q_perm);
out_unlock1:
rcu_read_unlock();
out_up: out_up:
up_write(&msg_ids(ns).rw_mutex); up_write(&msg_ids(ns).rw_mutex);
return err; return err;
......
...@@ -1289,39 +1289,44 @@ static int semctl_down(struct ipc_namespace *ns, int semid, ...@@ -1289,39 +1289,44 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
return -EFAULT; return -EFAULT;
} }
down_write(&sem_ids(ns).rw_mutex);
rcu_read_lock();
ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd, ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd,
&semid64.sem_perm, 0); &semid64.sem_perm, 0);
if (IS_ERR(ipcp)) if (IS_ERR(ipcp)) {
return PTR_ERR(ipcp); err = PTR_ERR(ipcp);
/* the ipc lock is not held upon failure */
goto out_unlock1;
}
sma = container_of(ipcp, struct sem_array, sem_perm); sma = container_of(ipcp, struct sem_array, sem_perm);
err = security_sem_semctl(sma, cmd); err = security_sem_semctl(sma, cmd);
if (err) { if (err)
rcu_read_unlock(); goto out_unlock1;
goto out_up;
}
switch(cmd){ switch (cmd) {
case IPC_RMID: case IPC_RMID:
sem_lock(sma, NULL, -1); sem_lock(sma, NULL, -1);
/* freeary unlocks the ipc object and rcu */
freeary(ns, ipcp); freeary(ns, ipcp);
goto out_up; goto out_up;
case IPC_SET: case IPC_SET:
sem_lock(sma, NULL, -1); sem_lock(sma, NULL, -1);
err = ipc_update_perm(&semid64.sem_perm, ipcp); err = ipc_update_perm(&semid64.sem_perm, ipcp);
if (err) if (err)
goto out_unlock; goto out_unlock0;
sma->sem_ctime = get_seconds(); sma->sem_ctime = get_seconds();
break; break;
default: default:
rcu_read_unlock();
err = -EINVAL; err = -EINVAL;
goto out_up; goto out_unlock1;
} }
out_unlock: out_unlock0:
sem_unlock(sma, -1); sem_unlock(sma, -1);
out_unlock1:
rcu_read_unlock(); rcu_read_unlock();
out_up: out_up:
up_write(&sem_ids(ns).rw_mutex); up_write(&sem_ids(ns).rw_mutex);
......
...@@ -757,31 +757,42 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, ...@@ -757,31 +757,42 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
return -EFAULT; return -EFAULT;
} }
down_write(&shm_ids(ns).rw_mutex);
rcu_read_lock();
ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd, ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd,
&shmid64.shm_perm, 0); &shmid64.shm_perm, 0);
if (IS_ERR(ipcp)) if (IS_ERR(ipcp)) {
return PTR_ERR(ipcp); err = PTR_ERR(ipcp);
/* the ipc lock is not held upon failure */
goto out_unlock1;
}
shp = container_of(ipcp, struct shmid_kernel, shm_perm); shp = container_of(ipcp, struct shmid_kernel, shm_perm);
err = security_shm_shmctl(shp, cmd); err = security_shm_shmctl(shp, cmd);
if (err) if (err)
goto out_unlock; goto out_unlock0;
switch (cmd) { switch (cmd) {
case IPC_RMID: case IPC_RMID:
/* do_shm_rmid unlocks the ipc object and rcu */
do_shm_rmid(ns, ipcp); do_shm_rmid(ns, ipcp);
goto out_up; goto out_up;
case IPC_SET: case IPC_SET:
err = ipc_update_perm(&shmid64.shm_perm, ipcp); err = ipc_update_perm(&shmid64.shm_perm, ipcp);
if (err) if (err)
goto out_unlock; goto out_unlock0;
shp->shm_ctim = get_seconds(); shp->shm_ctim = get_seconds();
break; break;
default: default:
err = -EINVAL; err = -EINVAL;
} }
out_unlock:
shm_unlock(shp); out_unlock0:
ipc_unlock_object(&shp->shm_perm);
out_unlock1:
rcu_read_unlock();
out_up: out_up:
up_write(&shm_ids(ns).rw_mutex); up_write(&shm_ids(ns).rw_mutex);
return err; return err;
......
...@@ -746,8 +746,10 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) ...@@ -746,8 +746,10 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
* It must be called without any lock held and * It must be called without any lock held and
* - retrieves the ipc with the given id in the given table. * - retrieves the ipc with the given id in the given table.
* - performs some audit and permission check, depending on the given cmd * - performs some audit and permission check, depending on the given cmd
* - returns the ipc with both ipc and rw_mutex locks held in case of success * - returns the ipc with the ipc lock held in case of success
* or an err-code without any lock held otherwise. * or an err-code without any lock held otherwise.
*
* Call holding the both the rw_mutex and the rcu read lock.
*/ */
struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
struct ipc_ids *ids, int id, int cmd, struct ipc_ids *ids, int id, int cmd,
...@@ -772,13 +774,10 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, ...@@ -772,13 +774,10 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
int err = -EPERM; int err = -EPERM;
struct kern_ipc_perm *ipcp; struct kern_ipc_perm *ipcp;
down_write(&ids->rw_mutex);
rcu_read_lock();
ipcp = ipc_obtain_object_check(ids, id); ipcp = ipc_obtain_object_check(ids, id);
if (IS_ERR(ipcp)) { if (IS_ERR(ipcp)) {
err = PTR_ERR(ipcp); err = PTR_ERR(ipcp);
goto out_up; goto err;
} }
audit_ipc_obj(ipcp); audit_ipc_obj(ipcp);
...@@ -789,16 +788,8 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, ...@@ -789,16 +788,8 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
euid = current_euid(); euid = current_euid();
if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) || if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) ||
ns_capable(ns->user_ns, CAP_SYS_ADMIN)) ns_capable(ns->user_ns, CAP_SYS_ADMIN))
return ipcp; return ipcp; /* successful lookup */
err:
out_up:
/*
* Unsuccessful lookup, unlock and return
* the corresponding error.
*/
rcu_read_unlock();
up_write(&ids->rw_mutex);
return ERR_PTR(err); return ERR_PTR(err);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment