Skip to content

Commit 6a6b561

Browse files
howlettgregkh
authored andcommitted
mm: make find_extend_vma() fail if write lock not held
commit f440fa1 upstream. 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]> Signed-off-by: Samuel Mendoza-Jonas <[email protected]> Signed-off-by: David Woodhouse <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 48c2328 commit 6a6b561

File tree

6 files changed

+49
-27
lines changed

6 files changed

+49
-27
lines changed

fs/binfmt_elf.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,10 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
315315
* Grow the stack manually; some architectures have a limit on how
316316
* far ahead a user-space access may be in order to grow the stack.
317317
*/
318-
if (mmap_read_lock_killable(mm))
318+
if (mmap_write_lock_killable(mm))
319319
return -EINTR;
320-
vma = find_extend_vma(mm, bprm->p);
321-
mmap_read_unlock(mm);
320+
vma = find_extend_vma_locked(mm, bprm->p, true);
321+
mmap_write_unlock(mm);
322322
if (!vma)
323323
return -EFAULT;
324324

fs/exec.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
203203

204204
#ifdef CONFIG_STACK_GROWSUP
205205
if (write) {
206-
ret = expand_downwards(bprm->vma, pos);
206+
/* We claim to hold the lock - nobody to race with */
207+
ret = expand_downwards(bprm->vma, pos, true);
207208
if (ret < 0)
208209
return NULL;
209210
}
@@ -854,7 +855,7 @@ int setup_arg_pages(struct linux_binprm *bprm,
854855
stack_base = vma->vm_start - stack_expand;
855856
#endif
856857
current->mm->start_stack = bprm->p;
857-
ret = expand_stack(vma, stack_base);
858+
ret = expand_stack_locked(vma, stack_base, true);
858859
if (ret)
859860
ret = -EFAULT;
860861

include/linux/mm.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,11 +2810,13 @@ extern vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf);
28102810

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

28152817
/* CONFIG_STACK_GROWSUP still needs to grow downwards at some places */
2816-
extern int expand_downwards(struct vm_area_struct *vma,
2817-
unsigned long address);
2818+
int expand_downwards(struct vm_area_struct *vma, unsigned long address,
2819+
bool write_locked);
28182820
#if VM_GROWSUP
28192821
extern int expand_upwards(struct vm_area_struct *vma, unsigned long address);
28202822
#else
@@ -2915,6 +2917,8 @@ unsigned long change_prot_numa(struct vm_area_struct *vma,
29152917
#endif
29162918

29172919
struct vm_area_struct *find_extend_vma(struct mm_struct *, unsigned long addr);
2920+
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *,
2921+
unsigned long addr, bool write_locked);
29182922
int remap_pfn_range(struct vm_area_struct *, unsigned long addr,
29192923
unsigned long pfn, unsigned long size, pgprot_t);
29202924
int remap_pfn_range_notrack(struct vm_area_struct *vma, unsigned long addr,

mm/memory.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5364,7 +5364,7 @@ struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,
53645364
goto fail;
53655365
}
53665366

5367-
if (expand_stack(vma, addr))
5367+
if (expand_stack_locked(vma, addr, true))
53685368
goto fail;
53695369

53705370
success:

mm/mmap.c

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,7 +1945,8 @@ static int acct_stack_growth(struct vm_area_struct *vma,
19451945
* PA-RISC uses this for its stack; IA64 for its Register Backing Store.
19461946
* vma is the last one with address > vma->vm_end. Have to extend vma.
19471947
*/
1948-
int expand_upwards(struct vm_area_struct *vma, unsigned long address)
1948+
int expand_upwards(struct vm_area_struct *vma, unsigned long address,
1949+
bool write_locked)
19491950
{
19501951
struct mm_struct *mm = vma->vm_mm;
19511952
struct vm_area_struct *next;
@@ -1969,6 +1970,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
19691970
if (gap_addr < address || gap_addr > TASK_SIZE)
19701971
gap_addr = TASK_SIZE;
19711972

1973+
if (!write_locked)
1974+
return -EAGAIN;
19721975
next = find_vma_intersection(mm, vma->vm_end, gap_addr);
19731976
if (next && vma_is_accessible(next)) {
19741977
if (!(next->vm_flags & VM_GROWSUP))
@@ -2037,7 +2040,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
20372040
/*
20382041
* vma is the first one with address < vma->vm_start. Have to extend vma.
20392042
*/
2040-
int expand_downwards(struct vm_area_struct *vma, unsigned long address)
2043+
int expand_downwards(struct vm_area_struct *vma, unsigned long address,
2044+
bool write_locked)
20412045
{
20422046
struct mm_struct *mm = vma->vm_mm;
20432047
MA_STATE(mas, &mm->mm_mt, vma->vm_start, vma->vm_start);
@@ -2051,10 +2055,13 @@ int expand_downwards(struct vm_area_struct *vma, unsigned long address)
20512055
/* Enforce stack_guard_gap */
20522056
prev = mas_prev(&mas, 0);
20532057
/* Check that both stack segments have the same anon_vma? */
2054-
if (prev && !(prev->vm_flags & VM_GROWSDOWN) &&
2055-
vma_is_accessible(prev)) {
2056-
if (address - prev->vm_end < stack_guard_gap)
2058+
if (prev) {
2059+
if (!(prev->vm_flags & VM_GROWSDOWN) &&
2060+
vma_is_accessible(prev) &&
2061+
(address - prev->vm_end < stack_guard_gap))
20572062
return -ENOMEM;
2063+
if (!write_locked && (prev->vm_end == address))
2064+
return -EAGAIN;
20582065
}
20592066

20602067
if (mas_preallocate(&mas, vma, GFP_KERNEL))
@@ -2132,34 +2139,40 @@ static int __init cmdline_parse_stack_guard_gap(char *p)
21322139
__setup("stack_guard_gap=", cmdline_parse_stack_guard_gap);
21332140

21342141
#ifdef CONFIG_STACK_GROWSUP
2135-
int expand_stack(struct vm_area_struct *vma, unsigned long address)
2142+
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
2143+
bool write_locked)
21362144
{
2137-
return expand_upwards(vma, address);
2145+
return expand_upwards(vma, address, write_locked);
21382146
}
21392147

2140-
struct vm_area_struct *
2141-
find_extend_vma(struct mm_struct *mm, unsigned long addr)
2148+
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm,
2149+
unsigned long addr, bool write_locked)
21422150
{
21432151
struct vm_area_struct *vma, *prev;
21442152

21452153
addr &= PAGE_MASK;
21462154
vma = find_vma_prev(mm, addr, &prev);
21472155
if (vma && (vma->vm_start <= addr))
21482156
return vma;
2149-
if (!prev || expand_stack(prev, addr))
2157+
if (!prev)
2158+
return NULL;
2159+
if (expand_stack_locked(prev, addr, write_locked))
21502160
return NULL;
21512161
if (prev->vm_flags & VM_LOCKED)
21522162
populate_vma_page_range(prev, addr, prev->vm_end, NULL);
21532163
return prev;
21542164
}
21552165
#else
2156-
int expand_stack(struct vm_area_struct *vma, unsigned long address)
2166+
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
2167+
bool write_locked)
21572168
{
2158-
return expand_downwards(vma, address);
2169+
if (unlikely(!(vma->vm_flags & VM_GROWSDOWN)))
2170+
return -EINVAL;
2171+
return expand_downwards(vma, address, write_locked);
21592172
}
21602173

2161-
struct vm_area_struct *
2162-
find_extend_vma(struct mm_struct *mm, unsigned long addr)
2174+
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm,
2175+
unsigned long addr, bool write_locked)
21632176
{
21642177
struct vm_area_struct *vma;
21652178
unsigned long start;
@@ -2170,17 +2183,20 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
21702183
return NULL;
21712184
if (vma->vm_start <= addr)
21722185
return vma;
2173-
if (!(vma->vm_flags & VM_GROWSDOWN))
2174-
return NULL;
21752186
start = vma->vm_start;
2176-
if (expand_stack(vma, addr))
2187+
if (expand_stack_locked(vma, addr, write_locked))
21772188
return NULL;
21782189
if (vma->vm_flags & VM_LOCKED)
21792190
populate_vma_page_range(vma, addr, start, NULL);
21802191
return vma;
21812192
}
21822193
#endif
21832194

2195+
struct vm_area_struct *find_extend_vma(struct mm_struct *mm,
2196+
unsigned long addr)
2197+
{
2198+
return find_extend_vma_locked(mm, addr, false);
2199+
}
21842200
EXPORT_SYMBOL_GPL(find_extend_vma);
21852201

21862202
/*

mm/nommu.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,8 @@ struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr)
694694
* expand a stack to a given address
695695
* - not supported under NOMMU conditions
696696
*/
697-
int expand_stack(struct vm_area_struct *vma, unsigned long address)
697+
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
698+
bool write_locked)
698699
{
699700
return -ENOMEM;
700701
}

0 commit comments

Comments
 (0)