Skip to content

Commit

Permalink
mm: make find_extend_vma() fail if write lock not held
Browse files Browse the repository at this point in the history
Make calls to extend_vma() and find_extend_vma() fail if the write lock
is required.

To avoid making this a flag-day event, this still allows the old
read-locking case for the trivial situations, and passes in a flag to
say "is it write-locked".  That way write-lockers can say "yes, I'm
being careful", and legacy users will continue to work in all the common
cases until they have been fully converted to the new world order.

Co-Developed-by: Matthew Wilcox (Oracle) <[email protected]>
Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
Signed-off-by: Liam R. Howlett <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
howlett authored and torvalds committed Jun 24, 2023
1 parent 2cd76c5 commit f440fa1
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 27 deletions.
6 changes: 3 additions & 3 deletions fs/binfmt_elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,10 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
* Grow the stack manually; some architectures have a limit on how
* far ahead a user-space access may be in order to grow the stack.
*/
if (mmap_read_lock_killable(mm))
if (mmap_write_lock_killable(mm))
return -EINTR;
vma = find_extend_vma(mm, bprm->p);
mmap_read_unlock(mm);
vma = find_extend_vma_locked(mm, bprm->p, true);
mmap_write_unlock(mm);
if (!vma)
return -EFAULT;

Expand Down
5 changes: 3 additions & 2 deletions fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,

#ifdef CONFIG_STACK_GROWSUP
if (write) {
ret = expand_downwards(bprm->vma, pos);
/* We claim to hold the lock - nobody to race with */
ret = expand_downwards(bprm->vma, pos, true);
if (ret < 0)
return NULL;
}
Expand Down Expand Up @@ -853,7 +854,7 @@ int setup_arg_pages(struct linux_binprm *bprm,
stack_base = vma->vm_end - stack_expand;
#endif
current->mm->start_stack = bprm->p;
ret = expand_stack(vma, stack_base);
ret = expand_stack_locked(vma, stack_base, true);
if (ret)
ret = -EFAULT;

Expand Down
10 changes: 7 additions & 3 deletions include/linux/mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -3192,11 +3192,13 @@ extern vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf);

extern unsigned long stack_guard_gap;
/* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */
extern int expand_stack(struct vm_area_struct *vma, unsigned long address);
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
bool write_locked);
#define expand_stack(vma,addr) expand_stack_locked(vma,addr,false)

/* CONFIG_STACK_GROWSUP still needs to grow downwards at some places */
extern int expand_downwards(struct vm_area_struct *vma,
unsigned long address);
int expand_downwards(struct vm_area_struct *vma, unsigned long address,
bool write_locked);
#if VM_GROWSUP
extern int expand_upwards(struct vm_area_struct *vma, unsigned long address);
#else
Expand Down Expand Up @@ -3297,6 +3299,8 @@ unsigned long change_prot_numa(struct vm_area_struct *vma,
#endif

struct vm_area_struct *find_extend_vma(struct mm_struct *, unsigned long addr);
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *,
unsigned long addr, bool write_locked);
int remap_pfn_range(struct vm_area_struct *, unsigned long addr,
unsigned long pfn, unsigned long size, pgprot_t);
int remap_pfn_range_notrack(struct vm_area_struct *vma, unsigned long addr,
Expand Down
2 changes: 1 addition & 1 deletion mm/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -5368,7 +5368,7 @@ struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,
goto fail;
}

if (expand_stack(vma, addr))
if (expand_stack_locked(vma, addr, true))
goto fail;

success:
Expand Down
50 changes: 33 additions & 17 deletions mm/mmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1935,7 +1935,8 @@ static int acct_stack_growth(struct vm_area_struct *vma,
* PA-RISC uses this for its stack; IA64 for its Register Backing Store.
* vma is the last one with address > vma->vm_end. Have to extend vma.
*/
int expand_upwards(struct vm_area_struct *vma, unsigned long address)
int expand_upwards(struct vm_area_struct *vma, unsigned long address,
bool write_locked)
{
struct mm_struct *mm = vma->vm_mm;
struct vm_area_struct *next;
Expand All @@ -1959,6 +1960,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
if (gap_addr < address || gap_addr > TASK_SIZE)
gap_addr = TASK_SIZE;

if (!write_locked)
return -EAGAIN;
next = find_vma_intersection(mm, vma->vm_end, gap_addr);
if (next && vma_is_accessible(next)) {
if (!(next->vm_flags & VM_GROWSUP))
Expand Down Expand Up @@ -2028,7 +2031,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
/*
* vma is the first one with address < vma->vm_start. Have to extend vma.
*/
int expand_downwards(struct vm_area_struct *vma, unsigned long address)
int expand_downwards(struct vm_area_struct *vma, unsigned long address,
bool write_locked)
{
struct mm_struct *mm = vma->vm_mm;
MA_STATE(mas, &mm->mm_mt, vma->vm_start, vma->vm_start);
Expand All @@ -2042,10 +2046,13 @@ int expand_downwards(struct vm_area_struct *vma, unsigned long address)
/* Enforce stack_guard_gap */
prev = mas_prev(&mas, 0);
/* Check that both stack segments have the same anon_vma? */
if (prev && !(prev->vm_flags & VM_GROWSDOWN) &&
vma_is_accessible(prev)) {
if (address - prev->vm_end < stack_guard_gap)
if (prev) {
if (!(prev->vm_flags & VM_GROWSDOWN) &&
vma_is_accessible(prev) &&
(address - prev->vm_end < stack_guard_gap))
return -ENOMEM;
if (!write_locked && (prev->vm_end == address))
return -EAGAIN;
}

if (mas_preallocate(&mas, GFP_KERNEL))
Expand Down Expand Up @@ -2124,34 +2131,40 @@ static int __init cmdline_parse_stack_guard_gap(char *p)
__setup("stack_guard_gap=", cmdline_parse_stack_guard_gap);

#ifdef CONFIG_STACK_GROWSUP
int expand_stack(struct vm_area_struct *vma, unsigned long address)
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
bool write_locked)
{
return expand_upwards(vma, address);
return expand_upwards(vma, address, write_locked);
}

struct vm_area_struct *
find_extend_vma(struct mm_struct *mm, unsigned long addr)
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm,
unsigned long addr, bool write_locked)
{
struct vm_area_struct *vma, *prev;

addr &= PAGE_MASK;
vma = find_vma_prev(mm, addr, &prev);
if (vma && (vma->vm_start <= addr))
return vma;
if (!prev || expand_stack(prev, addr))
if (!prev)
return NULL;
if (expand_stack_locked(prev, addr, write_locked))
return NULL;
if (prev->vm_flags & VM_LOCKED)
populate_vma_page_range(prev, addr, prev->vm_end, NULL);
return prev;
}
#else
int expand_stack(struct vm_area_struct *vma, unsigned long address)
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
bool write_locked)
{
return expand_downwards(vma, address);
if (unlikely(!(vma->vm_flags & VM_GROWSDOWN)))
return -EINVAL;
return expand_downwards(vma, address, write_locked);
}

struct vm_area_struct *
find_extend_vma(struct mm_struct *mm, unsigned long addr)
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm,
unsigned long addr, bool write_locked)
{
struct vm_area_struct *vma;
unsigned long start;
Expand All @@ -2162,17 +2175,20 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
return NULL;
if (vma->vm_start <= addr)
return vma;
if (!(vma->vm_flags & VM_GROWSDOWN))
return NULL;
start = vma->vm_start;
if (expand_stack(vma, addr))
if (expand_stack_locked(vma, addr, write_locked))
return NULL;
if (vma->vm_flags & VM_LOCKED)
populate_vma_page_range(vma, addr, start, NULL);
return vma;
}
#endif

struct vm_area_struct *find_extend_vma(struct mm_struct *mm,
unsigned long addr)
{
return find_extend_vma_locked(mm, addr, false);
}
EXPORT_SYMBOL_GPL(find_extend_vma);

/*
Expand Down
3 changes: 2 additions & 1 deletion mm/nommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,8 @@ struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr)
* expand a stack to a given address
* - not supported under NOMMU conditions
*/
int expand_stack(struct vm_area_struct *vma, unsigned long address)
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
bool write_locked)
{
return -ENOMEM;
}
Expand Down

0 comments on commit f440fa1

Please sign in to comment.