diff options
author | Hugh Dickins <hugh.dickins@tiscali.co.uk> | 2009-08-24 11:30:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-08-24 15:53:01 -0400 |
commit | 353d5c30c666580347515da609dd74a2b8e9b828 (patch) | |
tree | 03cf3b5c0bc2ce08a12af303b141503ad833178f /ipc/shm.c | |
parent | 0257a0c0c1997aac28420e784b3ef8f3ce17f093 (diff) |
mm: fix hugetlb bug due to user_shm_unlock call
2.6.30's commit 8a0bdec194c21c8fdef840989d0d7b742bb5d4bc removed
user_shm_lock() calls in hugetlb_file_setup() but left the
user_shm_unlock call in shm_destroy().
In detail:
Assume that can_do_hugetlb_shm() returns true and hence user_shm_lock()
is not called in hugetlb_file_setup(). However, user_shm_unlock() is
called in any case in shm_destroy() and in the following
atomic_dec_and_lock(&up->__count) in free_uid() is executed and if
up->__count gets zero, also cleanup_user_struct() is scheduled.
Note that sched_destroy_user() is empty if CONFIG_USER_SCHED is not set.
However, the ref counter up->__count gets unexpectedly non-positive and
the corresponding structs are freed even though there are live
references to them, resulting in a kernel oops after a lots of
shmget(SHM_HUGETLB)/shmctl(IPC_RMID) cycles and CONFIG_USER_SCHED set.
Hugh changed Stefan's suggested patch: can_do_hugetlb_shm() at the
time of shm_destroy() may give a different answer from at the time
of hugetlb_file_setup(). And fixed newseg()'s no_id error path,
which has missed user_shm_unlock() ever since it came in 2.6.9.
Reported-by: Stefan Huber <shuber2@gmail.com>
Signed-off-by: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Tested-by: Stefan Huber <shuber2@gmail.com>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc/shm.c')
-rw-r--r-- | ipc/shm.c | 8 |
1 files changed, 5 insertions, 3 deletions
@@ -174,7 +174,7 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) | |||
174 | shm_unlock(shp); | 174 | shm_unlock(shp); |
175 | if (!is_file_hugepages(shp->shm_file)) | 175 | if (!is_file_hugepages(shp->shm_file)) |
176 | shmem_lock(shp->shm_file, 0, shp->mlock_user); | 176 | shmem_lock(shp->shm_file, 0, shp->mlock_user); |
177 | else | 177 | else if (shp->mlock_user) |
178 | user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size, | 178 | user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size, |
179 | shp->mlock_user); | 179 | shp->mlock_user); |
180 | fput (shp->shm_file); | 180 | fput (shp->shm_file); |
@@ -369,8 +369,8 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) | |||
369 | /* hugetlb_file_setup applies strict accounting */ | 369 | /* hugetlb_file_setup applies strict accounting */ |
370 | if (shmflg & SHM_NORESERVE) | 370 | if (shmflg & SHM_NORESERVE) |
371 | acctflag = VM_NORESERVE; | 371 | acctflag = VM_NORESERVE; |
372 | file = hugetlb_file_setup(name, size, acctflag); | 372 | file = hugetlb_file_setup(name, size, acctflag, |
373 | shp->mlock_user = current_user(); | 373 | &shp->mlock_user); |
374 | } else { | 374 | } else { |
375 | /* | 375 | /* |
376 | * Do not allow no accounting for OVERCOMMIT_NEVER, even | 376 | * Do not allow no accounting for OVERCOMMIT_NEVER, even |
@@ -410,6 +410,8 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) | |||
410 | return error; | 410 | return error; |
411 | 411 | ||
412 | no_id: | 412 | no_id: |
413 | if (shp->mlock_user) /* shmflg & SHM_HUGETLB case */ | ||
414 | user_shm_unlock(size, shp->mlock_user); | ||
413 | fput(file); | 415 | fput(file); |
414 | no_file: | 416 | no_file: |
415 | security_shm_free(shp); | 417 | security_shm_free(shp); |