Skip to content

Commit f440fa1

Browse files
howletttorvalds
authored andcommitted
mm: make find_extend_vma() fail if write lock not held
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]>
1 parent 2cd76c5 commit f440fa1

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
@@ -320,10 +320,10 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
320320
* Grow the stack manually; some architectures have a limit on how
321321
* far ahead a user-space access may be in order to grow the stack.
322322
*/
323-
if (mmap_read_lock_killable(mm))
323+
if (mmap_write_lock_killable(mm))
324324
return -EINTR;
325-
vma = find_extend_vma(mm, bprm->p);
326-
mmap_read_unlock(mm);
325+
vma = find_extend_vma_locked(mm, bprm->p, true);
326+
mmap_write_unlock(mm);
327327
if (!vma)
328328
return -EFAULT;
329329

fs/exec.c

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

206206
#ifdef CONFIG_STACK_GROWSUP
207207
if (write) {
208-
ret = expand_downwards(bprm->vma, pos);
208+
/* We claim to hold the lock - nobody to race with */
209+
ret = expand_downwards(bprm->vma, pos, true);
209210
if (ret < 0)
210211
return NULL;
211212
}
@@ -853,7 +854,7 @@ int setup_arg_pages(struct linux_binprm *bprm,
853854
stack_base = vma->vm_end - stack_expand;
854855
#endif
855856
current->mm->start_stack = bprm->p;
856-
ret = expand_stack(vma, stack_base);
857+
ret = expand_stack_locked(vma, stack_base, true);
857858
if (ret)
858859
ret = -EFAULT;
859860

include/linux/mm.h

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

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

31973199
/* CONFIG_STACK_GROWSUP still needs to grow downwards at some places */
3198-
extern int expand_downwards(struct vm_area_struct *vma,
3199-
unsigned long address);
3200+
int expand_downwards(struct vm_area_struct *vma, unsigned long address,
3201+
bool write_locked);
32003202
#if VM_GROWSUP
32013203
extern int expand_upwards(struct vm_area_struct *vma, unsigned long address);
32023204
#else
@@ -3297,6 +3299,8 @@ unsigned long change_prot_numa(struct vm_area_struct *vma,
32973299
#endif
32983300

32993301
struct vm_area_struct *find_extend_vma(struct mm_struct *, unsigned long addr);
3302+
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *,
3303+
unsigned long addr, bool write_locked);
33003304
int remap_pfn_range(struct vm_area_struct *, unsigned long addr,
33013305
unsigned long pfn, unsigned long size, pgprot_t);
33023306
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
@@ -5368,7 +5368,7 @@ struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm,
53685368
goto fail;
53695369
}
53705370

5371-
if (expand_stack(vma, addr))
5371+
if (expand_stack_locked(vma, addr, true))
53725372
goto fail;
53735373

53745374
success:

mm/mmap.c

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,7 +1935,8 @@ static int acct_stack_growth(struct vm_area_struct *vma,
19351935
* PA-RISC uses this for its stack; IA64 for its Register Backing Store.
19361936
* vma is the last one with address > vma->vm_end. Have to extend vma.
19371937
*/
1938-
int expand_upwards(struct vm_area_struct *vma, unsigned long address)
1938+
int expand_upwards(struct vm_area_struct *vma, unsigned long address,
1939+
bool write_locked)
19391940
{
19401941
struct mm_struct *mm = vma->vm_mm;
19411942
struct vm_area_struct *next;
@@ -1959,6 +1960,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
19591960
if (gap_addr < address || gap_addr > TASK_SIZE)
19601961
gap_addr = TASK_SIZE;
19611962

1963+
if (!write_locked)
1964+
return -EAGAIN;
19621965
next = find_vma_intersection(mm, vma->vm_end, gap_addr);
19631966
if (next && vma_is_accessible(next)) {
19641967
if (!(next->vm_flags & VM_GROWSUP))
@@ -2028,7 +2031,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
20282031
/*
20292032
* vma is the first one with address < vma->vm_start. Have to extend vma.
20302033
*/
2031-
int expand_downwards(struct vm_area_struct *vma, unsigned long address)
2034+
int expand_downwards(struct vm_area_struct *vma, unsigned long address,
2035+
bool write_locked)
20322036
{
20332037
struct mm_struct *mm = vma->vm_mm;
20342038
MA_STATE(mas, &mm->mm_mt, vma->vm_start, vma->vm_start);
@@ -2042,10 +2046,13 @@ int expand_downwards(struct vm_area_struct *vma, unsigned long address)
20422046
/* Enforce stack_guard_gap */
20432047
prev = mas_prev(&mas, 0);
20442048
/* Check that both stack segments have the same anon_vma? */
2045-
if (prev && !(prev->vm_flags & VM_GROWSDOWN) &&
2046-
vma_is_accessible(prev)) {
2047-
if (address - prev->vm_end < stack_guard_gap)
2049+
if (prev) {
2050+
if (!(prev->vm_flags & VM_GROWSDOWN) &&
2051+
vma_is_accessible(prev) &&
2052+
(address - prev->vm_end < stack_guard_gap))
20482053
return -ENOMEM;
2054+
if (!write_locked && (prev->vm_end == address))
2055+
return -EAGAIN;
20492056
}
20502057

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

21262133
#ifdef CONFIG_STACK_GROWSUP
2127-
int expand_stack(struct vm_area_struct *vma, unsigned long address)
2134+
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
2135+
bool write_locked)
21282136
{
2129-
return expand_upwards(vma, address);
2137+
return expand_upwards(vma, address, write_locked);
21302138
}
21312139

2132-
struct vm_area_struct *
2133-
find_extend_vma(struct mm_struct *mm, unsigned long addr)
2140+
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm,
2141+
unsigned long addr, bool write_locked)
21342142
{
21352143
struct vm_area_struct *vma, *prev;
21362144

21372145
addr &= PAGE_MASK;
21382146
vma = find_vma_prev(mm, addr, &prev);
21392147
if (vma && (vma->vm_start <= addr))
21402148
return vma;
2141-
if (!prev || expand_stack(prev, addr))
2149+
if (!prev)
2150+
return NULL;
2151+
if (expand_stack_locked(prev, addr, write_locked))
21422152
return NULL;
21432153
if (prev->vm_flags & VM_LOCKED)
21442154
populate_vma_page_range(prev, addr, prev->vm_end, NULL);
21452155
return prev;
21462156
}
21472157
#else
2148-
int expand_stack(struct vm_area_struct *vma, unsigned long address)
2158+
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
2159+
bool write_locked)
21492160
{
2150-
return expand_downwards(vma, address);
2161+
if (unlikely(!(vma->vm_flags & VM_GROWSDOWN)))
2162+
return -EINVAL;
2163+
return expand_downwards(vma, address, write_locked);
21512164
}
21522165

2153-
struct vm_area_struct *
2154-
find_extend_vma(struct mm_struct *mm, unsigned long addr)
2166+
struct vm_area_struct *find_extend_vma_locked(struct mm_struct *mm,
2167+
unsigned long addr, bool write_locked)
21552168
{
21562169
struct vm_area_struct *vma;
21572170
unsigned long start;
@@ -2162,17 +2175,20 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
21622175
return NULL;
21632176
if (vma->vm_start <= addr)
21642177
return vma;
2165-
if (!(vma->vm_flags & VM_GROWSDOWN))
2166-
return NULL;
21672178
start = vma->vm_start;
2168-
if (expand_stack(vma, addr))
2179+
if (expand_stack_locked(vma, addr, write_locked))
21692180
return NULL;
21702181
if (vma->vm_flags & VM_LOCKED)
21712182
populate_vma_page_range(vma, addr, start, NULL);
21722183
return vma;
21732184
}
21742185
#endif
21752186

2187+
struct vm_area_struct *find_extend_vma(struct mm_struct *mm,
2188+
unsigned long addr)
2189+
{
2190+
return find_extend_vma_locked(mm, addr, false);
2191+
}
21762192
EXPORT_SYMBOL_GPL(find_extend_vma);
21772193

21782194
/*

mm/nommu.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,8 @@ struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr)
643643
* expand a stack to a given address
644644
* - not supported under NOMMU conditions
645645
*/
646-
int expand_stack(struct vm_area_struct *vma, unsigned long address)
646+
int expand_stack_locked(struct vm_area_struct *vma, unsigned long address,
647+
bool write_locked)
647648
{
648649
return -ENOMEM;
649650
}

0 commit comments

Comments
 (0)