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/sem.c | |
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/sem.c')
-rw-r--r-- | ipc/sem.c | 24 |
1 files changed, 16 insertions, 8 deletions
@@ -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; |