aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/sem.c
diff options
context:
space:
mode:
authorManfred Spraul <manfred@colorfullife.com>2013-10-16 16:46:45 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-10-17 00:35:52 -0400
commit6e224f94597842c5eb17f1fc2208d20b6f7f7d49 (patch)
treef2a2ed86aa847b16b0d952411aed975f3a978908 /ipc/sem.c
parent18ccee263c7e250a57f01c9434658f11f4118a64 (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/sem.c')
-rw-r--r--ipc/sem.c42
1 files changed, 29 insertions, 13 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index 8c4f59b0204a..db9d241af133 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -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