diff options
Diffstat (limited to 'mm/mmap.c')
| -rw-r--r-- | mm/mmap.c | 54 |
1 files changed, 41 insertions, 13 deletions
diff --git a/mm/mmap.c b/mm/mmap.c index f0d1f464ee00..21329bf3dea3 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; + + /* Somebody else might have raced and expanded it already */ + if (address < vma->vm_start) { + unsigned long size, grow; - error = acct_stack_growth(vma, size, grow); - if (!error) { - vma->vm_start = address; - vma->vm_pgoff -= 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; @@ -1858,6 +1870,16 @@ asmlinkage long sys_munmap(unsigned long addr, size_t len) return ret; } +static inline void verify_mm_writelocked(struct mm_struct *mm) +{ +#ifdef CONFIG_DEBUG_KERNEL + if (unlikely(down_read_trylock(&mm->mmap_sem))) { + WARN_ON(1); + up_read(&mm->mmap_sem); + } +#endif +} + /* * this is really a simplified "do_mmap". it only handles * anonymous maps. eventually we may be able to do some @@ -1891,6 +1913,12 @@ unsigned long do_brk(unsigned long addr, unsigned long len) } /* + * mm->mmap_sem is required to protect against another thread + * changing the mappings in case we sleep. + */ + verify_mm_writelocked(mm); + + /* * Clear old maps. this also does some error checking for us */ munmap_back: |
