diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-10-17 00:36:03 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-10-17 00:36:03 -0400 |
| commit | 056cdce0d3a214158f3a4ea40887b22639f855a8 (patch) | |
| tree | 8ced4ccf6c7bac7eef49710c3cdfb0745cc85102 /ipc | |
| parent | 0056019da4b7ee5ab51fb174fe0655278578516f (diff) | |
| parent | 57a8f0cdb87da776bf0e4ce7554a9133854fa779 (diff) | |
Merge branch 'akpm' (fixes from Andrew Morton)
Merge misc fixes from Andrew Morton.
* emailed patches from Andrew Morton <akpm@linux-foundation.org>: (21 commits)
mm: revert mremap pud_free anti-fix
mm: fix BUG in __split_huge_page_pmd
swap: fix set_blocksize race during swapon/swapoff
procfs: call default get_unmapped_area on MMU-present architectures
procfs: fix unintended truncation of returned mapped address
writeback: fix negative bdi max pause
percpu_refcount: export symbols
fs: buffer: move allocation failure loop into the allocator
mm: memcg: handle non-error OOM situations more gracefully
tools/testing/selftests: fix uninitialized variable
block/partitions/efi.c: treat size mismatch as a warning, not an error
mm: hugetlb: initialize PG_reserved for tail pages of gigantic compound pages
mm/zswap: bugfix: memory leak when re-swapon
mm: /proc/pid/pagemap: inspect _PAGE_SOFT_DIRTY only on present pages
mm: migration: do not lose soft dirty bit if page is in migration state
gcov: MAINTAINERS: Add an entry for gcov
mm/hugetlb.c: correct missing private flag clearing
mm/vmscan.c: don't forget to free shrinker->nr_deferred
ipc/sem.c: synchronize semop and semctl with IPC_RMID
ipc: update locking scheme comments
...
Diffstat (limited to 'ipc')
| -rw-r--r-- | ipc/sem.c | 42 | ||||
| -rw-r--r-- | ipc/util.c | 27 |
2 files changed, 50 insertions, 19 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 |
diff --git a/ipc/util.c b/ipc/util.c index fdb8ae740775..7684f41bce76 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
| @@ -17,12 +17,27 @@ | |||
| 17 | * Pavel Emelianov <xemul@openvz.org> | 17 | * Pavel Emelianov <xemul@openvz.org> |
| 18 | * | 18 | * |
| 19 | * General sysv ipc locking scheme: | 19 | * General sysv ipc locking scheme: |
| 20 | * when doing ipc id lookups, take the ids->rwsem | 20 | * rcu_read_lock() |
| 21 | * rcu_read_lock() | 21 | * obtain the ipc object (kern_ipc_perm) by looking up the id in an idr |
| 22 | * obtain the ipc object (kern_ipc_perm) | 22 | * tree. |
| 23 | * perform security, capabilities, auditing and permission checks, etc. | 23 | * - perform initial checks (capabilities, auditing and permission, |
| 24 | * acquire the ipc lock (kern_ipc_perm.lock) throught ipc_lock_object() | 24 | * etc). |
| 25 | * perform data updates (ie: SET, RMID, LOCK/UNLOCK commands) | 25 | * - perform read-only operations, such as STAT, INFO commands. |
| 26 | * acquire the ipc lock (kern_ipc_perm.lock) through | ||
| 27 | * ipc_lock_object() | ||
| 28 | * - perform data updates, such as SET, RMID commands and | ||
| 29 | * mechanism-specific operations (semop/semtimedop, | ||
| 30 | * msgsnd/msgrcv, shmat/shmdt). | ||
| 31 | * drop the ipc lock, through ipc_unlock_object(). | ||
| 32 | * rcu_read_unlock() | ||
| 33 | * | ||
| 34 | * The ids->rwsem must be taken when: | ||
| 35 | * - creating, removing and iterating the existing entries in ipc | ||
| 36 | * identifier sets. | ||
| 37 | * - iterating through files under /proc/sysvipc/ | ||
| 38 | * | ||
| 39 | * Note that sems have a special fast path that avoids kern_ipc_perm.lock - | ||
| 40 | * see sem_lock(). | ||
| 26 | */ | 41 | */ |
| 27 | 42 | ||
| 28 | #include <linux/mm.h> | 43 | #include <linux/mm.h> |
