diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-01-11 16:09:20 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-01-11 16:09:20 -0800 |
commit | fa6e49a2497cb4298d81c0d384c1ade8bcf1f0a3 (patch) | |
tree | f57b43daf2a64a79d39d9c76ca6f8474ed8052b9 /mm | |
parent | 749e1c8ba98f98b5657e713b10e743581b5d32b2 (diff) | |
download | history-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.c | 38 |
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; |