diff options
author | Manfred Spraul <manfred@colorfullife.com> | 2013-10-16 16:46:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-10-17 00:35:52 -0400 |
commit | 6e224f94597842c5eb17f1fc2208d20b6f7f7d49 (patch) | |
tree | f2a2ed86aa847b16b0d952411aed975f3a978908 /ipc | |
parent | 18ccee263c7e250a57f01c9434658f11f4118a64 (diff) |
ipc/sem.c: synchronize semop and semctl with IPC_RMID
After acquiring the semlock spinlock, operations must test that the
array is still valid.
- semctl() and exit_sem() would walk stale linked lists (ugly, but
should be ok: all lists are empty)
- semtimedop() would sleep forever - and if woken up due to a signal -
access memory after free.
The patch also:
- standardizes the tests for .deleted, so that all tests in one
function leave the function with the same approach.
- unconditionally tests for .deleted immediately after every call to
sem_lock - even it it means that for semctl(GETALL), .deleted will be
tested twice.
Both changes make the review simpler: After every sem_lock, there must
be a test of .deleted, followed by a goto to the cleanup code (if the
function uses "goto cleanup").
The only exception is semctl_down(): If sem_ids().rwsem is locked, then
the presence in ids->ipcs_idr is equivalent to !.deleted, thus no
additional test is required.
Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
Cc: Mike Galbraith <efault@gmx.de>
Acked-by: Davidlohr Bueso <davidlohr@hp.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/sem.c | 42 |
1 files changed, 29 insertions, 13 deletions
@@ -1282,6 +1282,12 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, | |||
1282 | 1282 | ||
1283 | sem_lock(sma, NULL, -1); | 1283 | sem_lock(sma, NULL, -1); |
1284 | 1284 | ||
1285 | if (sma->sem_perm.deleted) { | ||
1286 | sem_unlock(sma, -1); | ||
1287 | rcu_read_unlock(); | ||
1288 | return -EIDRM; | ||
1289 | } | ||
1290 | |||
1285 | curr = &sma->sem_base[semnum]; | 1291 | curr = &sma->sem_base[semnum]; |
1286 | 1292 | ||
1287 | ipc_assert_locked_object(&sma->sem_perm); | 1293 | ipc_assert_locked_object(&sma->sem_perm); |
@@ -1336,12 +1342,14 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
1336 | int i; | 1342 | int i; |
1337 | 1343 | ||
1338 | sem_lock(sma, NULL, -1); | 1344 | sem_lock(sma, NULL, -1); |
1345 | if (sma->sem_perm.deleted) { | ||
1346 | err = -EIDRM; | ||
1347 | goto out_unlock; | ||
1348 | } | ||
1339 | if(nsems > SEMMSL_FAST) { | 1349 | if(nsems > SEMMSL_FAST) { |
1340 | if (!ipc_rcu_getref(sma)) { | 1350 | if (!ipc_rcu_getref(sma)) { |
1341 | sem_unlock(sma, -1); | ||
1342 | rcu_read_unlock(); | ||
1343 | err = -EIDRM; | 1351 | err = -EIDRM; |
1344 | goto out_free; | 1352 | goto out_unlock; |
1345 | } | 1353 | } |
1346 | sem_unlock(sma, -1); | 1354 | sem_unlock(sma, -1); |
1347 | rcu_read_unlock(); | 1355 | rcu_read_unlock(); |
@@ -1354,10 +1362,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
1354 | rcu_read_lock(); | 1362 | rcu_read_lock(); |
1355 | sem_lock_and_putref(sma); | 1363 | sem_lock_and_putref(sma); |
1356 | if (sma->sem_perm.deleted) { | 1364 | if (sma->sem_perm.deleted) { |
1357 | sem_unlock(sma, -1); | ||
1358 | rcu_read_unlock(); | ||
1359 | err = -EIDRM; | 1365 | err = -EIDRM; |
1360 | goto out_free; | 1366 | goto out_unlock; |
1361 | } | 1367 | } |
1362 | } | 1368 | } |
1363 | for (i = 0; i < sma->sem_nsems; i++) | 1369 | for (i = 0; i < sma->sem_nsems; i++) |
@@ -1375,8 +1381,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
1375 | struct sem_undo *un; | 1381 | struct sem_undo *un; |
1376 | 1382 | ||
1377 | if (!ipc_rcu_getref(sma)) { | 1383 | if (!ipc_rcu_getref(sma)) { |
1378 | rcu_read_unlock(); | 1384 | err = -EIDRM; |
1379 | return -EIDRM; | 1385 | goto out_rcu_wakeup; |
1380 | } | 1386 | } |
1381 | rcu_read_unlock(); | 1387 | rcu_read_unlock(); |
1382 | 1388 | ||
@@ -1404,10 +1410,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
1404 | rcu_read_lock(); | 1410 | rcu_read_lock(); |
1405 | sem_lock_and_putref(sma); | 1411 | sem_lock_and_putref(sma); |
1406 | if (sma->sem_perm.deleted) { | 1412 | if (sma->sem_perm.deleted) { |
1407 | sem_unlock(sma, -1); | ||
1408 | rcu_read_unlock(); | ||
1409 | err = -EIDRM; | 1413 | err = -EIDRM; |
1410 | goto out_free; | 1414 | goto out_unlock; |
1411 | } | 1415 | } |
1412 | 1416 | ||
1413 | for (i = 0; i < nsems; i++) | 1417 | for (i = 0; i < nsems; i++) |
@@ -1431,6 +1435,10 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
1431 | goto out_rcu_wakeup; | 1435 | goto out_rcu_wakeup; |
1432 | 1436 | ||
1433 | sem_lock(sma, NULL, -1); | 1437 | sem_lock(sma, NULL, -1); |
1438 | if (sma->sem_perm.deleted) { | ||
1439 | err = -EIDRM; | ||
1440 | goto out_unlock; | ||
1441 | } | ||
1434 | curr = &sma->sem_base[semnum]; | 1442 | curr = &sma->sem_base[semnum]; |
1435 | 1443 | ||
1436 | switch (cmd) { | 1444 | switch (cmd) { |
@@ -1836,6 +1844,10 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, | |||
1836 | if (error) | 1844 | if (error) |
1837 | goto out_rcu_wakeup; | 1845 | goto out_rcu_wakeup; |
1838 | 1846 | ||
1847 | error = -EIDRM; | ||
1848 | locknum = sem_lock(sma, sops, nsops); | ||
1849 | if (sma->sem_perm.deleted) | ||
1850 | goto out_unlock_free; | ||
1839 | /* | 1851 | /* |
1840 | * semid identifiers are not unique - find_alloc_undo may have | 1852 | * semid identifiers are not unique - find_alloc_undo may have |
1841 | * allocated an undo structure, it was invalidated by an RMID | 1853 | * allocated an undo structure, it was invalidated by an RMID |
@@ -1843,8 +1855,6 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, | |||
1843 | * This case can be detected checking un->semid. The existence of | 1855 | * This case can be detected checking un->semid. The existence of |
1844 | * "un" itself is guaranteed by rcu. | 1856 | * "un" itself is guaranteed by rcu. |
1845 | */ | 1857 | */ |
1846 | error = -EIDRM; | ||
1847 | locknum = sem_lock(sma, sops, nsops); | ||
1848 | if (un && un->semid == -1) | 1858 | if (un && un->semid == -1) |
1849 | goto out_unlock_free; | 1859 | goto out_unlock_free; |
1850 | 1860 | ||
@@ -2057,6 +2067,12 @@ void exit_sem(struct task_struct *tsk) | |||
2057 | } | 2067 | } |
2058 | 2068 | ||
2059 | sem_lock(sma, NULL, -1); | 2069 | sem_lock(sma, NULL, -1); |
2070 | /* exit_sem raced with IPC_RMID, nothing to do */ | ||
2071 | if (sma->sem_perm.deleted) { | ||
2072 | sem_unlock(sma, -1); | ||
2073 | rcu_read_unlock(); | ||
2074 | continue; | ||
2075 | } | ||
2060 | un = __lookup_undo(ulp, semid); | 2076 | un = __lookup_undo(ulp, semid); |
2061 | if (un == NULL) { | 2077 | if (un == NULL) { |
2062 | /* exit_sem raced with IPC_RMID+semget() that created | 2078 | /* exit_sem raced with IPC_RMID+semget() that created |