diff options
-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 | ||