aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-11 16:09:20 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-11 16:09:20 -0800
commitfa6e49a2497cb4298d81c0d384c1ade8bcf1f0a3 (patch)
treef57b43daf2a64a79d39d9c76ca6f8474ed8052b9 /mm
parent749e1c8ba98f98b5657e713b10e743581b5d32b2 (diff)
downloadhistory-fa6e49a2497cb4298d81c0d384c1ade8bcf1f0a3.tar.gz
Handle two threads both trying to expand their stack simultaneously.
We had all the locking right, but we didn't check whether one of the threads now no longer needed to expand, so we could incorrectly _shrink_ the stack in the other thread instead (not only causing segfaults, but since we didn't do a proper unmap, we'd possibly leak pages too). So re-check the need for expand after getting the lock. Noticed by Paul Starzetz.
Diffstat (limited to 'mm')
-rw-r--r--mm/mmap.c38
1 files changed, 25 insertions, 13 deletions
diff --git a/mm/mmap.c b/mm/mmap.c
index f0d1f464ee0019..9ccd3146bfdcc5 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1475,7 +1475,6 @@ static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, un
int expand_stack(struct vm_area_struct * vma, unsigned long address)
{
int error;
- unsigned long size, grow;
if (!(vma->vm_flags & VM_GROWSUP))
return -EFAULT;
@@ -1495,12 +1494,19 @@ int expand_stack(struct vm_area_struct * vma, unsigned long address)
*/
address += 4 + PAGE_SIZE - 1;
address &= PAGE_MASK;
- size = address - vma->vm_start;
- grow = (address - vma->vm_end) >> PAGE_SHIFT;
+ error = 0;
- error = acct_stack_growth(vma, size, grow);
- if (!error)
- vma->vm_end = address;
+ /* Somebody else might have raced and expanded it already */
+ if (address > vma->vm_end) {
+ unsigned long size, grow;
+
+ size = address - vma->vm_start;
+ grow = (address - vma->vm_end) >> PAGE_SHIFT;
+
+ error = acct_stack_growth(vma, size, grow);
+ if (!error)
+ vma->vm_end = address;
+ }
anon_vma_unlock(vma);
return error;
}
@@ -1528,7 +1534,6 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
int expand_stack(struct vm_area_struct *vma, unsigned long address)
{
int error;
- unsigned long size, grow;
/*
* We must make sure the anon_vma is allocated
@@ -1544,13 +1549,20 @@ int expand_stack(struct vm_area_struct *vma, unsigned long address)
* anon_vma lock to serialize against concurrent expand_stacks.
*/
address &= PAGE_MASK;
- size = vma->vm_end - address;
- grow = (vma->vm_start - address) >> PAGE_SHIFT;
+ error = 0;
- error = acct_stack_growth(vma, size, grow);
- if (!error) {
- vma->vm_start = address;
- vma->vm_pgoff -= grow;
+ /* Somebody else might have raced and expanded it already */
+ if (address < vma->vm_start) {
+ unsigned long size, grow;
+
+ size = vma->vm_end - address;
+ grow = (vma->vm_start - address) >> PAGE_SHIFT;
+
+ error = acct_stack_growth(vma, size, grow);
+ if (!error) {
+ vma->vm_start = address;
+ vma->vm_pgoff -= grow;
+ }
}
anon_vma_unlock(vma);
return error;