diff options
| author | Rafael Aquini <aquini@redhat.com> | 2014-01-27 20:07:01 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-28 00:02:39 -0500 |
| commit | 0f3d2b0135f4bdbfe47a99753923a64efd373d11 (patch) | |
| tree | 4b211b7502e2412aedd94400bdcb012a2b5814b3 /ipc | |
| parent | 78f5009cc35eb5e52d276a046d90ee2f41b60f8c (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.c | 7 | ||||
| -rw-r--r-- | ipc/sem.c | 24 | ||||
| -rw-r--r-- | ipc/shm.c | 16 | ||||
| -rw-r--r-- | ipc/util.h | 13 |
4 files changed, 41 insertions, 19 deletions
| @@ -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 | } |
| @@ -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; |
| @@ -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 | */ | ||
| 196 | static inline bool ipc_valid_object(struct kern_ipc_perm *perm) | ||
| 197 | { | ||
| 198 | return perm->deleted == 0; | ||
| 199 | } | ||
| 200 | |||
| 188 | struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id); | 201 | struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id); |
| 189 | int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, | 202 | int 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); |
