aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorRafael Aquini <aquini@redhat.com>2014-01-27 20:07:01 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-28 00:02:39 -0500
commit0f3d2b0135f4bdbfe47a99753923a64efd373d11 (patch)
tree4b211b7502e2412aedd94400bdcb012a2b5814b3 /ipc
parent78f5009cc35eb5e52d276a046d90ee2f41b60f8c (diff)
ipc: introduce ipc_valid_object() helper to sort out IPC_RMID races
After the locking semantics for the SysV IPC API got improved, a couple of IPC_RMID race windows were opened because we ended up dropping the 'kern_ipc_perm.deleted' check performed way down in ipc_lock(). The spotted races got sorted out by re-introducing the old test within the racy critical sections. This patch introduces ipc_valid_object() to consolidate the way we cope with IPC_RMID races by using the same abstraction across the API implementation. Signed-off-by: Rafael Aquini <aquini@redhat.com> Acked-by: Rik van Riel <riel@redhat.com> Acked-by: Greg Thelen <gthelen@google.com> Reviewed-by: Davidlohr Bueso <davidlohr@hp.com> Cc: Manfred Spraul <manfred@colorfullife.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.c7
-rw-r--r--ipc/sem.c24
-rw-r--r--ipc/shm.c16
-rw-r--r--ipc/util.h13
4 files changed, 41 insertions, 19 deletions
diff --git a/ipc/msg.c b/ipc/msg.c
index 558aa91186b6..8983ea57d970 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -696,7 +696,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
696 goto out_unlock0; 696 goto out_unlock0;
697 697
698 /* raced with RMID? */ 698 /* raced with RMID? */
699 if (msq->q_perm.deleted) { 699 if (!ipc_valid_object(&msq->q_perm)) {
700 err = -EIDRM; 700 err = -EIDRM;
701 goto out_unlock0; 701 goto out_unlock0;
702 } 702 }
@@ -731,7 +731,8 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
731 ipc_lock_object(&msq->q_perm); 731 ipc_lock_object(&msq->q_perm);
732 732
733 ipc_rcu_putref(msq, ipc_rcu_free); 733 ipc_rcu_putref(msq, ipc_rcu_free);
734 if (msq->q_perm.deleted) { 734 /* raced with RMID? */
735 if (!ipc_valid_object(&msq->q_perm)) {
735 err = -EIDRM; 736 err = -EIDRM;
736 goto out_unlock0; 737 goto out_unlock0;
737 } 738 }
@@ -909,7 +910,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl
909 ipc_lock_object(&msq->q_perm); 910 ipc_lock_object(&msq->q_perm);
910 911
911 /* raced with RMID? */ 912 /* raced with RMID? */
912 if (msq->q_perm.deleted) { 913 if (!ipc_valid_object(&msq->q_perm)) {
913 msg = ERR_PTR(-EIDRM); 914 msg = ERR_PTR(-EIDRM);
914 goto out_unlock0; 915 goto out_unlock0;
915 } 916 }
diff --git a/ipc/sem.c b/ipc/sem.c
index cc9ac35b793c..4d88194a5ffe 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -1284,7 +1284,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
1284 1284
1285 sem_lock(sma, NULL, -1); 1285 sem_lock(sma, NULL, -1);
1286 1286
1287 if (sma->sem_perm.deleted) { 1287 if (!ipc_valid_object(&sma->sem_perm)) {
1288 sem_unlock(sma, -1); 1288 sem_unlock(sma, -1);
1289 rcu_read_unlock(); 1289 rcu_read_unlock();
1290 return -EIDRM; 1290 return -EIDRM;
@@ -1344,7 +1344,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1344 int i; 1344 int i;
1345 1345
1346 sem_lock(sma, NULL, -1); 1346 sem_lock(sma, NULL, -1);
1347 if (sma->sem_perm.deleted) { 1347 if (!ipc_valid_object(&sma->sem_perm)) {
1348 err = -EIDRM; 1348 err = -EIDRM;
1349 goto out_unlock; 1349 goto out_unlock;
1350 } 1350 }
@@ -1363,7 +1363,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1363 1363
1364 rcu_read_lock(); 1364 rcu_read_lock();
1365 sem_lock_and_putref(sma); 1365 sem_lock_and_putref(sma);
1366 if (sma->sem_perm.deleted) { 1366 if (!ipc_valid_object(&sma->sem_perm)) {
1367 err = -EIDRM; 1367 err = -EIDRM;
1368 goto out_unlock; 1368 goto out_unlock;
1369 } 1369 }
@@ -1411,7 +1411,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1411 } 1411 }
1412 rcu_read_lock(); 1412 rcu_read_lock();
1413 sem_lock_and_putref(sma); 1413 sem_lock_and_putref(sma);
1414 if (sma->sem_perm.deleted) { 1414 if (!ipc_valid_object(&sma->sem_perm)) {
1415 err = -EIDRM; 1415 err = -EIDRM;
1416 goto out_unlock; 1416 goto out_unlock;
1417 } 1417 }
@@ -1437,7 +1437,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum,
1437 goto out_rcu_wakeup; 1437 goto out_rcu_wakeup;
1438 1438
1439 sem_lock(sma, NULL, -1); 1439 sem_lock(sma, NULL, -1);
1440 if (sma->sem_perm.deleted) { 1440 if (!ipc_valid_object(&sma->sem_perm)) {
1441 err = -EIDRM; 1441 err = -EIDRM;
1442 goto out_unlock; 1442 goto out_unlock;
1443 } 1443 }
@@ -1701,7 +1701,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
1701 /* step 3: Acquire the lock on semaphore array */ 1701 /* step 3: Acquire the lock on semaphore array */
1702 rcu_read_lock(); 1702 rcu_read_lock();
1703 sem_lock_and_putref(sma); 1703 sem_lock_and_putref(sma);
1704 if (sma->sem_perm.deleted) { 1704 if (!ipc_valid_object(&sma->sem_perm)) {
1705 sem_unlock(sma, -1); 1705 sem_unlock(sma, -1);
1706 rcu_read_unlock(); 1706 rcu_read_unlock();
1707 kfree(new); 1707 kfree(new);
@@ -1848,7 +1848,15 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
1848 1848
1849 error = -EIDRM; 1849 error = -EIDRM;
1850 locknum = sem_lock(sma, sops, nsops); 1850 locknum = sem_lock(sma, sops, nsops);
1851 if (sma->sem_perm.deleted) 1851 /*
1852 * We eventually might perform the following check in a lockless
1853 * fashion, considering ipc_valid_object() locking constraints.
1854 * If nsops == 1 and there is no contention for sem_perm.lock, then
1855 * only a per-semaphore lock is held and it's OK to proceed with the
1856 * check below. More details on the fine grained locking scheme
1857 * entangled here and why it's RMID race safe on comments at sem_lock()
1858 */
1859 if (!ipc_valid_object(&sma->sem_perm))
1852 goto out_unlock_free; 1860 goto out_unlock_free;
1853 /* 1861 /*
1854 * semid identifiers are not unique - find_alloc_undo may have 1862 * semid identifiers are not unique - find_alloc_undo may have
@@ -2070,7 +2078,7 @@ void exit_sem(struct task_struct *tsk)
2070 2078
2071 sem_lock(sma, NULL, -1); 2079 sem_lock(sma, NULL, -1);
2072 /* exit_sem raced with IPC_RMID, nothing to do */ 2080 /* exit_sem raced with IPC_RMID, nothing to do */
2073 if (sma->sem_perm.deleted) { 2081 if (!ipc_valid_object(&sma->sem_perm)) {
2074 sem_unlock(sma, -1); 2082 sem_unlock(sma, -1);
2075 rcu_read_unlock(); 2083 rcu_read_unlock();
2076 continue; 2084 continue;
diff --git a/ipc/shm.c b/ipc/shm.c
index 7a51443a51d6..1bc68f115842 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -975,6 +975,13 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
975 goto out_unlock1; 975 goto out_unlock1;
976 976
977 ipc_lock_object(&shp->shm_perm); 977 ipc_lock_object(&shp->shm_perm);
978
979 /* check if shm_destroy() is tearing down shp */
980 if (!ipc_valid_object(&shp->shm_perm)) {
981 err = -EIDRM;
982 goto out_unlock0;
983 }
984
978 if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { 985 if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
979 kuid_t euid = current_euid(); 986 kuid_t euid = current_euid();
980 if (!uid_eq(euid, shp->shm_perm.uid) && 987 if (!uid_eq(euid, shp->shm_perm.uid) &&
@@ -989,13 +996,6 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
989 } 996 }
990 997
991 shm_file = shp->shm_file; 998 shm_file = shp->shm_file;
992
993 /* check if shm_destroy() is tearing down shp */
994 if (shm_file == NULL) {
995 err = -EIDRM;
996 goto out_unlock0;
997 }
998
999 if (is_file_hugepages(shm_file)) 999 if (is_file_hugepages(shm_file))
1000 goto out_unlock0; 1000 goto out_unlock0;
1001 1001
@@ -1116,7 +1116,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr,
1116 ipc_lock_object(&shp->shm_perm); 1116 ipc_lock_object(&shp->shm_perm);
1117 1117
1118 /* check if shm_destroy() is tearing down shp */ 1118 /* check if shm_destroy() is tearing down shp */
1119 if (shp->shm_file == NULL) { 1119 if (!ipc_valid_object(&shp->shm_perm)) {
1120 ipc_unlock_object(&shp->shm_perm); 1120 ipc_unlock_object(&shp->shm_perm);
1121 err = -EIDRM; 1121 err = -EIDRM;
1122 goto out_unlock; 1122 goto out_unlock;
diff --git a/ipc/util.h b/ipc/util.h
index 59d78aa94987..d05b7085a887 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -185,6 +185,19 @@ static inline void ipc_unlock(struct kern_ipc_perm *perm)
185 rcu_read_unlock(); 185 rcu_read_unlock();
186} 186}
187 187
188/*
189 * ipc_valid_object() - helper to sort out IPC_RMID races for codepaths
190 * where the respective ipc_ids.rwsem is not being held down.
191 * Checks whether the ipc object is still around or if it's gone already, as
192 * ipc_rmid() may have already freed the ID while the ipc lock was spinning.
193 * Needs to be called with kern_ipc_perm.lock held -- exception made for one
194 * checkpoint case at sys_semtimedop() as noted in code commentary.
195 */
196static inline bool ipc_valid_object(struct kern_ipc_perm *perm)
197{
198 return perm->deleted == 0;
199}
200
188struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id); 201struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id);
189int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, 202int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
190 struct ipc_ops *ops, struct ipc_params *params); 203 struct ipc_ops *ops, struct ipc_params *params);