diff options
| author | Davidlohr Bueso <davidlohr.bueso@hp.com> | 2013-07-08 19:01:12 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-09 13:33:27 -0400 |
| commit | 7b4cc5d8411bd4e9d61d8714f53859740cf830c2 (patch) | |
| tree | b95f3b875a5f4c927b0f27cc3c7ddcba5cc8e1e8 /ipc | |
| parent | cf9d5d78d05bca96df7618dfc3a5ee4414dcae58 (diff) | |
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: Davidlohr Bueso <davidlohr.bueso@hp.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Rik van Riel <riel@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
| -rw-r--r-- | ipc/msg.c | 24 | ||||
| -rw-r--r-- | ipc/sem.c | 27 | ||||
| -rw-r--r-- | ipc/shm.c | 23 | ||||
| -rw-r--r-- | ipc/util.c | 21 |
4 files changed, 56 insertions, 39 deletions
| @@ -407,31 +407,38 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | |||
| 407 | return -EFAULT; | 407 | return -EFAULT; |
| 408 | } | 408 | } |
| 409 | 409 | ||
| 410 | down_write(&msg_ids(ns).rw_mutex); | ||
| 411 | rcu_read_lock(); | ||
| 412 | |||
| 410 | ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd, | 413 | ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd, |
| 411 | &msqid64.msg_perm, msqid64.msg_qbytes); | 414 | &msqid64.msg_perm, msqid64.msg_qbytes); |
| 412 | if (IS_ERR(ipcp)) | 415 | if (IS_ERR(ipcp)) { |
| 413 | return PTR_ERR(ipcp); | 416 | err = PTR_ERR(ipcp); |
| 417 | /* the ipc lock is not held upon failure */ | ||
| 418 | goto out_unlock1; | ||
| 419 | } | ||
| 414 | 420 | ||
| 415 | msq = container_of(ipcp, struct msg_queue, q_perm); | 421 | msq = container_of(ipcp, struct msg_queue, q_perm); |
| 416 | 422 | ||
| 417 | err = security_msg_queue_msgctl(msq, cmd); | 423 | err = security_msg_queue_msgctl(msq, cmd); |
| 418 | if (err) | 424 | if (err) |
| 419 | goto out_unlock; | 425 | goto out_unlock0; |
| 420 | 426 | ||
| 421 | switch (cmd) { | 427 | switch (cmd) { |
| 422 | case IPC_RMID: | 428 | case IPC_RMID: |
| 429 | /* freeque unlocks the ipc object and rcu */ | ||
| 423 | freeque(ns, ipcp); | 430 | freeque(ns, ipcp); |
| 424 | goto out_up; | 431 | goto out_up; |
| 425 | case IPC_SET: | 432 | case IPC_SET: |
| 426 | if (msqid64.msg_qbytes > ns->msg_ctlmnb && | 433 | if (msqid64.msg_qbytes > ns->msg_ctlmnb && |
| 427 | !capable(CAP_SYS_RESOURCE)) { | 434 | !capable(CAP_SYS_RESOURCE)) { |
| 428 | err = -EPERM; | 435 | err = -EPERM; |
| 429 | goto out_unlock; | 436 | goto out_unlock0; |
| 430 | } | 437 | } |
| 431 | 438 | ||
| 432 | err = ipc_update_perm(&msqid64.msg_perm, ipcp); | 439 | err = ipc_update_perm(&msqid64.msg_perm, ipcp); |
| 433 | if (err) | 440 | if (err) |
| 434 | goto out_unlock; | 441 | goto out_unlock0; |
| 435 | 442 | ||
| 436 | msq->q_qbytes = msqid64.msg_qbytes; | 443 | msq->q_qbytes = msqid64.msg_qbytes; |
| 437 | 444 | ||
| @@ -448,8 +455,11 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | |||
| 448 | default: | 455 | default: |
| 449 | err = -EINVAL; | 456 | err = -EINVAL; |
| 450 | } | 457 | } |
| 451 | out_unlock: | 458 | |
| 452 | msg_unlock(msq); | 459 | out_unlock0: |
| 460 | ipc_unlock_object(&msq->q_perm); | ||
| 461 | out_unlock1: | ||
| 462 | rcu_read_unlock(); | ||
| 453 | out_up: | 463 | out_up: |
| 454 | up_write(&msg_ids(ns).rw_mutex); | 464 | up_write(&msg_ids(ns).rw_mutex); |
| 455 | return err; | 465 | return err; |
| @@ -1289,39 +1289,44 @@ static int semctl_down(struct ipc_namespace *ns, int semid, | |||
| 1289 | return -EFAULT; | 1289 | return -EFAULT; |
| 1290 | } | 1290 | } |
| 1291 | 1291 | ||
| 1292 | down_write(&sem_ids(ns).rw_mutex); | ||
| 1293 | rcu_read_lock(); | ||
| 1294 | |||
| 1292 | ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd, | 1295 | ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd, |
| 1293 | &semid64.sem_perm, 0); | 1296 | &semid64.sem_perm, 0); |
| 1294 | if (IS_ERR(ipcp)) | 1297 | if (IS_ERR(ipcp)) { |
| 1295 | return PTR_ERR(ipcp); | 1298 | err = PTR_ERR(ipcp); |
| 1299 | /* the ipc lock is not held upon failure */ | ||
| 1300 | goto out_unlock1; | ||
| 1301 | } | ||
| 1296 | 1302 | ||
| 1297 | sma = container_of(ipcp, struct sem_array, sem_perm); | 1303 | sma = container_of(ipcp, struct sem_array, sem_perm); |
| 1298 | 1304 | ||
| 1299 | err = security_sem_semctl(sma, cmd); | 1305 | err = security_sem_semctl(sma, cmd); |
| 1300 | if (err) { | 1306 | if (err) |
| 1301 | rcu_read_unlock(); | 1307 | goto out_unlock1; |
| 1302 | goto out_up; | ||
| 1303 | } | ||
| 1304 | 1308 | ||
| 1305 | switch(cmd){ | 1309 | switch (cmd) { |
| 1306 | case IPC_RMID: | 1310 | case IPC_RMID: |
| 1307 | sem_lock(sma, NULL, -1); | 1311 | sem_lock(sma, NULL, -1); |
| 1312 | /* freeary unlocks the ipc object and rcu */ | ||
| 1308 | freeary(ns, ipcp); | 1313 | freeary(ns, ipcp); |
| 1309 | goto out_up; | 1314 | goto out_up; |
| 1310 | case IPC_SET: | 1315 | case IPC_SET: |
| 1311 | sem_lock(sma, NULL, -1); | 1316 | sem_lock(sma, NULL, -1); |
| 1312 | err = ipc_update_perm(&semid64.sem_perm, ipcp); | 1317 | err = ipc_update_perm(&semid64.sem_perm, ipcp); |
| 1313 | if (err) | 1318 | if (err) |
| 1314 | goto out_unlock; | 1319 | goto out_unlock0; |
| 1315 | sma->sem_ctime = get_seconds(); | 1320 | sma->sem_ctime = get_seconds(); |
| 1316 | break; | 1321 | break; |
| 1317 | default: | 1322 | default: |
| 1318 | rcu_read_unlock(); | ||
| 1319 | err = -EINVAL; | 1323 | err = -EINVAL; |
| 1320 | goto out_up; | 1324 | goto out_unlock1; |
| 1321 | } | 1325 | } |
| 1322 | 1326 | ||
| 1323 | out_unlock: | 1327 | out_unlock0: |
| 1324 | sem_unlock(sma, -1); | 1328 | sem_unlock(sma, -1); |
| 1329 | out_unlock1: | ||
| 1325 | rcu_read_unlock(); | 1330 | rcu_read_unlock(); |
| 1326 | out_up: | 1331 | out_up: |
| 1327 | up_write(&sem_ids(ns).rw_mutex); | 1332 | up_write(&sem_ids(ns).rw_mutex); |
| @@ -757,31 +757,42 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, | |||
| 757 | return -EFAULT; | 757 | return -EFAULT; |
| 758 | } | 758 | } |
| 759 | 759 | ||
| 760 | down_write(&shm_ids(ns).rw_mutex); | ||
| 761 | rcu_read_lock(); | ||
| 762 | |||
| 760 | ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd, | 763 | ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd, |
| 761 | &shmid64.shm_perm, 0); | 764 | &shmid64.shm_perm, 0); |
| 762 | if (IS_ERR(ipcp)) | 765 | if (IS_ERR(ipcp)) { |
| 763 | return PTR_ERR(ipcp); | 766 | err = PTR_ERR(ipcp); |
| 767 | /* the ipc lock is not held upon failure */ | ||
| 768 | goto out_unlock1; | ||
| 769 | } | ||
| 764 | 770 | ||
| 765 | shp = container_of(ipcp, struct shmid_kernel, shm_perm); | 771 | shp = container_of(ipcp, struct shmid_kernel, shm_perm); |
| 766 | 772 | ||
| 767 | err = security_shm_shmctl(shp, cmd); | 773 | err = security_shm_shmctl(shp, cmd); |
| 768 | if (err) | 774 | if (err) |
| 769 | goto out_unlock; | 775 | goto out_unlock0; |
| 776 | |||
| 770 | switch (cmd) { | 777 | switch (cmd) { |
| 771 | case IPC_RMID: | 778 | case IPC_RMID: |
| 779 | /* do_shm_rmid unlocks the ipc object and rcu */ | ||
| 772 | do_shm_rmid(ns, ipcp); | 780 | do_shm_rmid(ns, ipcp); |
| 773 | goto out_up; | 781 | goto out_up; |
| 774 | case IPC_SET: | 782 | case IPC_SET: |
| 775 | err = ipc_update_perm(&shmid64.shm_perm, ipcp); | 783 | err = ipc_update_perm(&shmid64.shm_perm, ipcp); |
| 776 | if (err) | 784 | if (err) |
| 777 | goto out_unlock; | 785 | goto out_unlock0; |
| 778 | shp->shm_ctim = get_seconds(); | 786 | shp->shm_ctim = get_seconds(); |
| 779 | break; | 787 | break; |
| 780 | default: | 788 | default: |
| 781 | err = -EINVAL; | 789 | err = -EINVAL; |
| 782 | } | 790 | } |
| 783 | out_unlock: | 791 | |
| 784 | shm_unlock(shp); | 792 | out_unlock0: |
| 793 | ipc_unlock_object(&shp->shm_perm); | ||
| 794 | out_unlock1: | ||
| 795 | rcu_read_unlock(); | ||
| 785 | out_up: | 796 | out_up: |
| 786 | up_write(&shm_ids(ns).rw_mutex); | 797 | up_write(&shm_ids(ns).rw_mutex); |
| 787 | return err; | 798 | return err; |
diff --git a/ipc/util.c b/ipc/util.c index 399821ac0a9a..a0c139f3d1f3 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
| @@ -746,8 +746,10 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) | |||
| 746 | * It must be called without any lock held and | 746 | * It must be called without any lock held and |
| 747 | * - retrieves the ipc with the given id in the given table. | 747 | * - retrieves the ipc with the given id in the given table. |
| 748 | * - performs some audit and permission check, depending on the given cmd | 748 | * - performs some audit and permission check, depending on the given cmd |
| 749 | * - returns the ipc with both ipc and rw_mutex locks held in case of success | 749 | * - returns the ipc with the ipc lock held in case of success |
| 750 | * or an err-code without any lock held otherwise. | 750 | * or an err-code without any lock held otherwise. |
| 751 | * | ||
| 752 | * Call holding the both the rw_mutex and the rcu read lock. | ||
| 751 | */ | 753 | */ |
| 752 | struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | 754 | struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, |
| 753 | struct ipc_ids *ids, int id, int cmd, | 755 | 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 | int err = -EPERM; | 774 | int err = -EPERM; |
| 773 | struct kern_ipc_perm *ipcp; | 775 | struct kern_ipc_perm *ipcp; |
| 774 | 776 | ||
| 775 | down_write(&ids->rw_mutex); | ||
| 776 | rcu_read_lock(); | ||
| 777 | |||
| 778 | ipcp = ipc_obtain_object_check(ids, id); | 777 | ipcp = ipc_obtain_object_check(ids, id); |
| 779 | if (IS_ERR(ipcp)) { | 778 | if (IS_ERR(ipcp)) { |
| 780 | err = PTR_ERR(ipcp); | 779 | err = PTR_ERR(ipcp); |
| 781 | goto out_up; | 780 | goto err; |
| 782 | } | 781 | } |
| 783 | 782 | ||
| 784 | audit_ipc_obj(ipcp); | 783 | audit_ipc_obj(ipcp); |
| @@ -789,16 +788,8 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, | |||
| 789 | euid = current_euid(); | 788 | euid = current_euid(); |
| 790 | if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) || | 789 | if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) || |
| 791 | ns_capable(ns->user_ns, CAP_SYS_ADMIN)) | 790 | ns_capable(ns->user_ns, CAP_SYS_ADMIN)) |
| 792 | return ipcp; | 791 | return ipcp; /* successful lookup */ |
| 793 | 792 | err: | |
| 794 | out_up: | ||
| 795 | /* | ||
| 796 | * Unsuccessful lookup, unlock and return | ||
| 797 | * the corresponding error. | ||
| 798 | */ | ||
| 799 | rcu_read_unlock(); | ||
| 800 | up_write(&ids->rw_mutex); | ||
| 801 | |||
| 802 | return ERR_PTR(err); | 793 | return ERR_PTR(err); |
| 803 | } | 794 | } |
| 804 | 795 | ||
