diff options
author | Sona Sarmadi <sona.sarmadi@enea.com> | 2017-11-16 09:38:47 +0100 |
---|---|---|
committer | Adrian Dudau <adrian.dudau@enea.com> | 2017-11-16 12:00:54 +0100 |
commit | 63e1243bb63d15cbf58cafa7caab00599ed8f46c (patch) | |
tree | 6ce141ad9d3ba5712fac9b6de3f9b5fdc885a261 /recipes-kernel/linux/linux-intel/CVE-2017-1000364.patch | |
parent | cf3664b57f0dc010c27bce1103c89c22dc359641 (diff) | |
download | meta-enea-bsp-x86-63e1243bb63d15cbf58cafa7caab00599ed8f46c.tar.gz |
linu-intel: CVE-2017-1000364pyro
Fixes an issue in the size of the stack guard page on Linux,
specifically a 4k stack guard page is not sufficiently large
and can be "jumped" over (the stack guard page is bypassed),
this affects Linux Kernel versions 4.11.5 and earlier.
References:
https://nvd.nist.gov/vuln/detail/CVE-2017-1000364
https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2017-1000364
https://blogs.oracle.com/wim/cve-2017-1000364
Upstream patch
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit/?h=v4.9.50&id=cfc0eb403816c5c4f9667d959de5e22789b5421e
Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com>
Signed-off-by: Adrian Dudau <adrian.dudau@enea.com>
Diffstat (limited to 'recipes-kernel/linux/linux-intel/CVE-2017-1000364.patch')
-rw-r--r-- | recipes-kernel/linux/linux-intel/CVE-2017-1000364.patch | 939 |
1 files changed, 939 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-intel/CVE-2017-1000364.patch b/recipes-kernel/linux/linux-intel/CVE-2017-1000364.patch new file mode 100644 index 0000000..a25dd45 --- /dev/null +++ b/recipes-kernel/linux/linux-intel/CVE-2017-1000364.patch | |||
@@ -0,0 +1,939 @@ | |||
1 | From cfc0eb403816c5c4f9667d959de5e22789b5421e Mon Sep 17 00:00:00 2001 | ||
2 | From: Hugh Dickins <hughd@google.com> | ||
3 | Date: Mon, 19 Jun 2017 04:03:24 -0700 | ||
4 | Subject: [PATCH] mm: larger stack guard gap, between vmas | ||
5 | |||
6 | commit 1be7107fbe18eed3e319a6c3e83c78254b693acb upstream. | ||
7 | |||
8 | Stack guard page is a useful feature to reduce a risk of stack smashing | ||
9 | into a different mapping. We have been using a single page gap which | ||
10 | is sufficient to prevent having stack adjacent to a different mapping. | ||
11 | But this seems to be insufficient in the light of the stack usage in | ||
12 | userspace. E.g. glibc uses as large as 64kB alloca() in many commonly | ||
13 | used functions. Others use constructs liks gid_t buffer[NGROUPS_MAX] | ||
14 | which is 256kB or stack strings with MAX_ARG_STRLEN. | ||
15 | |||
16 | This will become especially dangerous for suid binaries and the default | ||
17 | no limit for the stack size limit because those applications can be | ||
18 | tricked to consume a large portion of the stack and a single glibc call | ||
19 | could jump over the guard page. These attacks are not theoretical, | ||
20 | unfortunatelly. | ||
21 | |||
22 | Make those attacks less probable by increasing the stack guard gap | ||
23 | to 1MB (on systems with 4k pages; but make it depend on the page size | ||
24 | because systems with larger base pages might cap stack allocations in | ||
25 | the PAGE_SIZE units) which should cover larger alloca() and VLA stack | ||
26 | allocations. It is obviously not a full fix because the problem is | ||
27 | somehow inherent, but it should reduce attack space a lot. | ||
28 | |||
29 | One could argue that the gap size should be configurable from userspace, | ||
30 | but that can be done later when somebody finds that the new 1MB is wrong | ||
31 | for some special case applications. For now, add a kernel command line | ||
32 | option (stack_guard_gap) to specify the stack gap size (in page units). | ||
33 | |||
34 | Implementation wise, first delete all the old code for stack guard page: | ||
35 | because although we could get away with accounting one extra page in a | ||
36 | stack vma, accounting a larger gap can break userspace - case in point, | ||
37 | a program run with "ulimit -S -v 20000" failed when the 1MB gap was | ||
38 | counted for RLIMIT_AS; similar problems could come with RLIMIT_MLOCK | ||
39 | and strict non-overcommit mode. | ||
40 | |||
41 | Instead of keeping gap inside the stack vma, maintain the stack guard | ||
42 | gap as a gap between vmas: using vm_start_gap() in place of vm_start | ||
43 | (or vm_end_gap() in place of vm_end if VM_GROWSUP) in just those few | ||
44 | places which need to respect the gap - mainly arch_get_unmapped_area(), | ||
45 | and and the vma tree's subtree_gap support for that. | ||
46 | |||
47 | CVE: CVE-2017-1000364 | ||
48 | Upstream-Status: Backport [backport from https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit/?h=v4.9.50&id=cfc0eb403816c5c4f9667d959de5e22789b5421e] | ||
49 | |||
50 | Original-patch-by: Oleg Nesterov <oleg@redhat.com> | ||
51 | Original-patch-by: Michal Hocko <mhocko@suse.com> | ||
52 | Signed-off-by: Hugh Dickins <hughd@google.com> | ||
53 | Acked-by: Michal Hocko <mhocko@suse.com> | ||
54 | Tested-by: Helge Deller <deller@gmx.de> # parisc | ||
55 | Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> | ||
56 | [wt: backport to 4.11: adjust context] | ||
57 | [wt: backport to 4.9: adjust context ; kernel doc was not in admin-guide] | ||
58 | Signed-off-by: Willy Tarreau <w@1wt.eu> | ||
59 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||
60 | Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com> | ||
61 | --- | ||
62 | Documentation/kernel-parameters.txt | 7 ++ | ||
63 | arch/arc/mm/mmap.c | 2 +- | ||
64 | arch/arm/mm/mmap.c | 4 +- | ||
65 | arch/frv/mm/elf-fdpic.c | 2 +- | ||
66 | arch/mips/mm/mmap.c | 2 +- | ||
67 | arch/parisc/kernel/sys_parisc.c | 15 ++-- | ||
68 | arch/powerpc/mm/hugetlbpage-radix.c | 2 +- | ||
69 | arch/powerpc/mm/mmap.c | 4 +- | ||
70 | arch/powerpc/mm/slice.c | 2 +- | ||
71 | arch/s390/mm/mmap.c | 4 +- | ||
72 | arch/sh/mm/mmap.c | 4 +- | ||
73 | arch/sparc/kernel/sys_sparc_64.c | 4 +- | ||
74 | arch/sparc/mm/hugetlbpage.c | 2 +- | ||
75 | arch/tile/mm/hugetlbpage.c | 2 +- | ||
76 | arch/x86/kernel/sys_x86_64.c | 4 +- | ||
77 | arch/x86/mm/hugetlbpage.c | 2 +- | ||
78 | arch/xtensa/kernel/syscall.c | 2 +- | ||
79 | fs/hugetlbfs/inode.c | 2 +- | ||
80 | fs/proc/task_mmu.c | 4 - | ||
81 | include/linux/mm.h | 53 ++++++------- | ||
82 | mm/gup.c | 5 -- | ||
83 | mm/memory.c | 38 --------- | ||
84 | mm/mmap.c | 149 +++++++++++++++++++++--------------- | ||
85 | 23 files changed, 152 insertions(+), 163 deletions(-) | ||
86 | |||
87 | diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt | ||
88 | index a6fadef..86a6746 100644 | ||
89 | --- a/Documentation/kernel-parameters.txt | ||
90 | +++ b/Documentation/kernel-parameters.txt | ||
91 | @@ -3932,6 +3932,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | ||
92 | spia_pedr= | ||
93 | spia_peddr= | ||
94 | |||
95 | + stack_guard_gap= [MM] | ||
96 | + override the default stack gap protection. The value | ||
97 | + is in page units and it defines how many pages prior | ||
98 | + to (for stacks growing down) resp. after (for stacks | ||
99 | + growing up) the main stack are reserved for no other | ||
100 | + mapping. Default value is 256 pages. | ||
101 | + | ||
102 | stacktrace [FTRACE] | ||
103 | Enabled the stack tracer on boot up. | ||
104 | |||
105 | diff --git a/arch/arc/mm/mmap.c b/arch/arc/mm/mmap.c | ||
106 | index 2e06d56..cf4ae69 100644 | ||
107 | --- a/arch/arc/mm/mmap.c | ||
108 | +++ b/arch/arc/mm/mmap.c | ||
109 | @@ -64,7 +64,7 @@ | ||
110 | |||
111 | vma = find_vma(mm, addr); | ||
112 | if (TASK_SIZE - len >= addr && | ||
113 | - (!vma || addr + len <= vma->vm_start)) | ||
114 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
115 | return addr; | ||
116 | } | ||
117 | |||
118 | diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c | ||
119 | index 66353ca..641334e 100644 | ||
120 | --- a/arch/arm/mm/mmap.c | ||
121 | +++ b/arch/arm/mm/mmap.c | ||
122 | @@ -89,7 +89,7 @@ static unsigned long mmap_base(unsigned long rnd) | ||
123 | |||
124 | vma = find_vma(mm, addr); | ||
125 | if (TASK_SIZE - len >= addr && | ||
126 | - (!vma || addr + len <= vma->vm_start)) | ||
127 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
128 | return addr; | ||
129 | } | ||
130 | |||
131 | @@ -140,7 +140,7 @@ static unsigned long mmap_base(unsigned long rnd) | ||
132 | addr = PAGE_ALIGN(addr); | ||
133 | vma = find_vma(mm, addr); | ||
134 | if (TASK_SIZE - len >= addr && | ||
135 | - (!vma || addr + len <= vma->vm_start)) | ||
136 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
137 | return addr; | ||
138 | } | ||
139 | |||
140 | diff --git a/arch/frv/mm/elf-fdpic.c b/arch/frv/mm/elf-fdpic.c | ||
141 | index 836f1470..efa59f1 100644 | ||
142 | --- a/arch/frv/mm/elf-fdpic.c | ||
143 | +++ b/arch/frv/mm/elf-fdpic.c | ||
144 | @@ -74,7 +74,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi | ||
145 | addr = PAGE_ALIGN(addr); | ||
146 | vma = find_vma(current->mm, addr); | ||
147 | if (TASK_SIZE - len >= addr && | ||
148 | - (!vma || addr + len <= vma->vm_start)) | ||
149 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
150 | goto success; | ||
151 | } | ||
152 | |||
153 | diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c | ||
154 | index d08ea3f..a44052c 100644 | ||
155 | --- a/arch/mips/mm/mmap.c | ||
156 | +++ b/arch/mips/mm/mmap.c | ||
157 | @@ -92,7 +92,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, | ||
158 | |||
159 | vma = find_vma(mm, addr); | ||
160 | if (TASK_SIZE - len >= addr && | ||
161 | - (!vma || addr + len <= vma->vm_start)) | ||
162 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
163 | return addr; | ||
164 | } | ||
165 | |||
166 | diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c | ||
167 | index 0a393a0..1d7691f 100644 | ||
168 | --- a/arch/parisc/kernel/sys_parisc.c | ||
169 | +++ b/arch/parisc/kernel/sys_parisc.c | ||
170 | @@ -88,7 +88,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
171 | unsigned long len, unsigned long pgoff, unsigned long flags) | ||
172 | { | ||
173 | struct mm_struct *mm = current->mm; | ||
174 | - struct vm_area_struct *vma; | ||
175 | + struct vm_area_struct *vma, *prev; | ||
176 | unsigned long task_size = TASK_SIZE; | ||
177 | int do_color_align, last_mmap; | ||
178 | struct vm_unmapped_area_info info; | ||
179 | @@ -115,9 +115,10 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
180 | else | ||
181 | addr = PAGE_ALIGN(addr); | ||
182 | |||
183 | - vma = find_vma(mm, addr); | ||
184 | + vma = find_vma_prev(mm, addr, &prev); | ||
185 | if (task_size - len >= addr && | ||
186 | - (!vma || addr + len <= vma->vm_start)) | ||
187 | + (!vma || addr + len <= vm_start_gap(vma)) && | ||
188 | + (!prev || addr >= vm_end_gap(prev))) | ||
189 | goto found_addr; | ||
190 | } | ||
191 | |||
192 | @@ -141,7 +142,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
193 | const unsigned long len, const unsigned long pgoff, | ||
194 | const unsigned long flags) | ||
195 | { | ||
196 | - struct vm_area_struct *vma; | ||
197 | + struct vm_area_struct *vma, *prev; | ||
198 | struct mm_struct *mm = current->mm; | ||
199 | unsigned long addr = addr0; | ||
200 | int do_color_align, last_mmap; | ||
201 | @@ -175,9 +176,11 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
202 | addr = COLOR_ALIGN(addr, last_mmap, pgoff); | ||
203 | else | ||
204 | addr = PAGE_ALIGN(addr); | ||
205 | - vma = find_vma(mm, addr); | ||
206 | + | ||
207 | + vma = find_vma_prev(mm, addr, &prev); | ||
208 | if (TASK_SIZE - len >= addr && | ||
209 | - (!vma || addr + len <= vma->vm_start)) | ||
210 | + (!vma || addr + len <= vm_start_gap(vma)) && | ||
211 | + (!prev || addr >= vm_end_gap(prev))) | ||
212 | goto found_addr; | ||
213 | } | ||
214 | |||
215 | diff --git a/arch/powerpc/mm/hugetlbpage-radix.c b/arch/powerpc/mm/hugetlbpage-radix.c | ||
216 | index 35254a6..a2b2d97 100644 | ||
217 | --- a/arch/powerpc/mm/hugetlbpage-radix.c | ||
218 | +++ b/arch/powerpc/mm/hugetlbpage-radix.c | ||
219 | @@ -65,7 +65,7 @@ void radix__flush_hugetlb_tlb_range(struct vm_area_struct *vma, unsigned long st | ||
220 | addr = ALIGN(addr, huge_page_size(h)); | ||
221 | vma = find_vma(mm, addr); | ||
222 | if (TASK_SIZE - len >= addr && | ||
223 | - (!vma || addr + len <= vma->vm_start)) | ||
224 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
225 | return addr; | ||
226 | } | ||
227 | /* | ||
228 | diff --git a/arch/powerpc/mm/mmap.c b/arch/powerpc/mm/mmap.c | ||
229 | index 2f1e443..5bc2845 100644 | ||
230 | --- a/arch/powerpc/mm/mmap.c | ||
231 | +++ b/arch/powerpc/mm/mmap.c | ||
232 | @@ -106,7 +106,7 @@ static inline unsigned long mmap_base(unsigned long rnd) | ||
233 | addr = PAGE_ALIGN(addr); | ||
234 | vma = find_vma(mm, addr); | ||
235 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && | ||
236 | - (!vma || addr + len <= vma->vm_start)) | ||
237 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
238 | return addr; | ||
239 | } | ||
240 | |||
241 | @@ -142,7 +142,7 @@ static inline unsigned long mmap_base(unsigned long rnd) | ||
242 | addr = PAGE_ALIGN(addr); | ||
243 | vma = find_vma(mm, addr); | ||
244 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && | ||
245 | - (!vma || addr + len <= vma->vm_start)) | ||
246 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
247 | return addr; | ||
248 | } | ||
249 | |||
250 | diff --git a/arch/powerpc/mm/slice.c b/arch/powerpc/mm/slice.c | ||
251 | index 2b27458..c4d5c9c 100644 | ||
252 | --- a/arch/powerpc/mm/slice.c | ||
253 | +++ b/arch/powerpc/mm/slice.c | ||
254 | @@ -105,7 +105,7 @@ static int slice_area_is_free(struct mm_struct *mm, unsigned long addr, | ||
255 | if ((mm->task_size - len) < addr) | ||
256 | return 0; | ||
257 | vma = find_vma(mm, addr); | ||
258 | - return (!vma || (addr + len) <= vma->vm_start); | ||
259 | + return (!vma || (addr + len) <= vm_start_gap(vma)); | ||
260 | } | ||
261 | |||
262 | static int slice_low_has_vma(struct mm_struct *mm, unsigned long slice) | ||
263 | diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c | ||
264 | index eb9df28..812368f 100644 | ||
265 | --- a/arch/s390/mm/mmap.c | ||
266 | +++ b/arch/s390/mm/mmap.c | ||
267 | @@ -98,7 +98,7 @@ static inline unsigned long mmap_base(unsigned long rnd) | ||
268 | addr = PAGE_ALIGN(addr); | ||
269 | vma = find_vma(mm, addr); | ||
270 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && | ||
271 | - (!vma || addr + len <= vma->vm_start)) | ||
272 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
273 | return addr; | ||
274 | } | ||
275 | |||
276 | @@ -136,7 +136,7 @@ static inline unsigned long mmap_base(unsigned long rnd) | ||
277 | addr = PAGE_ALIGN(addr); | ||
278 | vma = find_vma(mm, addr); | ||
279 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && | ||
280 | - (!vma || addr + len <= vma->vm_start)) | ||
281 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
282 | return addr; | ||
283 | } | ||
284 | |||
285 | diff --git a/arch/sh/mm/mmap.c b/arch/sh/mm/mmap.c | ||
286 | index 6777177..7df7d59 100644 | ||
287 | --- a/arch/sh/mm/mmap.c | ||
288 | +++ b/arch/sh/mm/mmap.c | ||
289 | @@ -63,7 +63,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
290 | |||
291 | vma = find_vma(mm, addr); | ||
292 | if (TASK_SIZE - len >= addr && | ||
293 | - (!vma || addr + len <= vma->vm_start)) | ||
294 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
295 | return addr; | ||
296 | } | ||
297 | |||
298 | @@ -113,7 +113,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
299 | |||
300 | vma = find_vma(mm, addr); | ||
301 | if (TASK_SIZE - len >= addr && | ||
302 | - (!vma || addr + len <= vma->vm_start)) | ||
303 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
304 | return addr; | ||
305 | } | ||
306 | |||
307 | diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c | ||
308 | index fe8b8ee..02e05e2 100644 | ||
309 | --- a/arch/sparc/kernel/sys_sparc_64.c | ||
310 | +++ b/arch/sparc/kernel/sys_sparc_64.c | ||
311 | @@ -118,7 +118,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi | ||
312 | |||
313 | vma = find_vma(mm, addr); | ||
314 | if (task_size - len >= addr && | ||
315 | - (!vma || addr + len <= vma->vm_start)) | ||
316 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
317 | return addr; | ||
318 | } | ||
319 | |||
320 | @@ -181,7 +181,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi | ||
321 | |||
322 | vma = find_vma(mm, addr); | ||
323 | if (task_size - len >= addr && | ||
324 | - (!vma || addr + len <= vma->vm_start)) | ||
325 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
326 | return addr; | ||
327 | } | ||
328 | |||
329 | diff --git a/arch/sparc/mm/hugetlbpage.c b/arch/sparc/mm/hugetlbpage.c | ||
330 | index 988acc8b..58cde8d 100644 | ||
331 | --- a/arch/sparc/mm/hugetlbpage.c | ||
332 | +++ b/arch/sparc/mm/hugetlbpage.c | ||
333 | @@ -116,7 +116,7 @@ static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *filp, | ||
334 | addr = ALIGN(addr, HPAGE_SIZE); | ||
335 | vma = find_vma(mm, addr); | ||
336 | if (task_size - len >= addr && | ||
337 | - (!vma || addr + len <= vma->vm_start)) | ||
338 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
339 | return addr; | ||
340 | } | ||
341 | if (mm->get_unmapped_area == arch_get_unmapped_area) | ||
342 | diff --git a/arch/tile/mm/hugetlbpage.c b/arch/tile/mm/hugetlbpage.c | ||
343 | index 77ceaa3..67508b2 100644 | ||
344 | --- a/arch/tile/mm/hugetlbpage.c | ||
345 | +++ b/arch/tile/mm/hugetlbpage.c | ||
346 | @@ -232,7 +232,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, | ||
347 | addr = ALIGN(addr, huge_page_size(h)); | ||
348 | vma = find_vma(mm, addr); | ||
349 | if (TASK_SIZE - len >= addr && | ||
350 | - (!vma || addr + len <= vma->vm_start)) | ||
351 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
352 | return addr; | ||
353 | } | ||
354 | if (current->mm->get_unmapped_area == arch_get_unmapped_area) | ||
355 | diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c | ||
356 | index a55ed63..1119414 100644 | ||
357 | --- a/arch/x86/kernel/sys_x86_64.c | ||
358 | +++ b/arch/x86/kernel/sys_x86_64.c | ||
359 | @@ -140,7 +140,7 @@ static void find_start_end(unsigned long flags, unsigned long *begin, | ||
360 | addr = PAGE_ALIGN(addr); | ||
361 | vma = find_vma(mm, addr); | ||
362 | if (end - len >= addr && | ||
363 | - (!vma || addr + len <= vma->vm_start)) | ||
364 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
365 | return addr; | ||
366 | } | ||
367 | |||
368 | @@ -183,7 +183,7 @@ static void find_start_end(unsigned long flags, unsigned long *begin, | ||
369 | addr = PAGE_ALIGN(addr); | ||
370 | vma = find_vma(mm, addr); | ||
371 | if (TASK_SIZE - len >= addr && | ||
372 | - (!vma || addr + len <= vma->vm_start)) | ||
373 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
374 | return addr; | ||
375 | } | ||
376 | |||
377 | diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c | ||
378 | index 2ae8584..fe342e8 100644 | ||
379 | --- a/arch/x86/mm/hugetlbpage.c | ||
380 | +++ b/arch/x86/mm/hugetlbpage.c | ||
381 | @@ -144,7 +144,7 @@ static unsigned long hugetlb_get_unmapped_area_topdown(struct file *file, | ||
382 | addr = ALIGN(addr, huge_page_size(h)); | ||
383 | vma = find_vma(mm, addr); | ||
384 | if (TASK_SIZE - len >= addr && | ||
385 | - (!vma || addr + len <= vma->vm_start)) | ||
386 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
387 | return addr; | ||
388 | } | ||
389 | if (mm->get_unmapped_area == arch_get_unmapped_area) | ||
390 | diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c | ||
391 | index 83cf496..3aaaae1 100644 | ||
392 | --- a/arch/xtensa/kernel/syscall.c | ||
393 | +++ b/arch/xtensa/kernel/syscall.c | ||
394 | @@ -87,7 +87,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
395 | /* At this point: (!vmm || addr < vmm->vm_end). */ | ||
396 | if (TASK_SIZE - len < addr) | ||
397 | return -ENOMEM; | ||
398 | - if (!vmm || addr + len <= vmm->vm_start) | ||
399 | + if (!vmm || addr + len <= vm_start_gap(vmm)) | ||
400 | return addr; | ||
401 | addr = vmm->vm_end; | ||
402 | if (flags & MAP_SHARED) | ||
403 | diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c | ||
404 | index 4fb7b10..704fa0b 100644 | ||
405 | --- a/fs/hugetlbfs/inode.c | ||
406 | +++ b/fs/hugetlbfs/inode.c | ||
407 | @@ -191,7 +191,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) | ||
408 | addr = ALIGN(addr, huge_page_size(h)); | ||
409 | vma = find_vma(mm, addr); | ||
410 | if (TASK_SIZE - len >= addr && | ||
411 | - (!vma || addr + len <= vma->vm_start)) | ||
412 | + (!vma || addr + len <= vm_start_gap(vma))) | ||
413 | return addr; | ||
414 | } | ||
415 | |||
416 | diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c | ||
417 | index b1517b6..5138e78 100644 | ||
418 | --- a/fs/proc/task_mmu.c | ||
419 | +++ b/fs/proc/task_mmu.c | ||
420 | @@ -299,11 +299,7 @@ static int is_stack(struct proc_maps_private *priv, | ||
421 | |||
422 | /* We don't show the stack guard page in /proc/maps */ | ||
423 | start = vma->vm_start; | ||
424 | - if (stack_guard_page_start(vma, start)) | ||
425 | - start += PAGE_SIZE; | ||
426 | end = vma->vm_end; | ||
427 | - if (stack_guard_page_end(vma, end)) | ||
428 | - end -= PAGE_SIZE; | ||
429 | |||
430 | seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); | ||
431 | seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", | ||
432 | diff --git a/include/linux/mm.h b/include/linux/mm.h | ||
433 | index 0b5b2e4..6c9e1ad 100644 | ||
434 | --- a/include/linux/mm.h | ||
435 | +++ b/include/linux/mm.h | ||
436 | @@ -1356,39 +1356,11 @@ void account_page_cleaned(struct page *page, struct address_space *mapping, | ||
437 | |||
438 | int get_cmdline(struct task_struct *task, char *buffer, int buflen); | ||
439 | |||
440 | -/* Is the vma a continuation of the stack vma above it? */ | ||
441 | -static inline int vma_growsdown(struct vm_area_struct *vma, unsigned long addr) | ||
442 | -{ | ||
443 | - return vma && (vma->vm_end == addr) && (vma->vm_flags & VM_GROWSDOWN); | ||
444 | -} | ||
445 | - | ||
446 | static inline bool vma_is_anonymous(struct vm_area_struct *vma) | ||
447 | { | ||
448 | return !vma->vm_ops; | ||
449 | } | ||
450 | |||
451 | -static inline int stack_guard_page_start(struct vm_area_struct *vma, | ||
452 | - unsigned long addr) | ||
453 | -{ | ||
454 | - return (vma->vm_flags & VM_GROWSDOWN) && | ||
455 | - (vma->vm_start == addr) && | ||
456 | - !vma_growsdown(vma->vm_prev, addr); | ||
457 | -} | ||
458 | - | ||
459 | -/* Is the vma a continuation of the stack vma below it? */ | ||
460 | -static inline int vma_growsup(struct vm_area_struct *vma, unsigned long addr) | ||
461 | -{ | ||
462 | - return vma && (vma->vm_start == addr) && (vma->vm_flags & VM_GROWSUP); | ||
463 | -} | ||
464 | - | ||
465 | -static inline int stack_guard_page_end(struct vm_area_struct *vma, | ||
466 | - unsigned long addr) | ||
467 | -{ | ||
468 | - return (vma->vm_flags & VM_GROWSUP) && | ||
469 | - (vma->vm_end == addr) && | ||
470 | - !vma_growsup(vma->vm_next, addr); | ||
471 | -} | ||
472 | - | ||
473 | int vma_is_stack_for_current(struct vm_area_struct *vma); | ||
474 | |||
475 | extern unsigned long move_page_tables(struct vm_area_struct *vma, | ||
476 | @@ -2127,6 +2099,7 @@ void page_cache_async_readahead(struct address_space *mapping, | ||
477 | pgoff_t offset, | ||
478 | unsigned long size); | ||
479 | |||
480 | +extern unsigned long stack_guard_gap; | ||
481 | /* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */ | ||
482 | extern int expand_stack(struct vm_area_struct *vma, unsigned long address); | ||
483 | |||
484 | @@ -2155,6 +2128,30 @@ static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * m | ||
485 | return vma; | ||
486 | } | ||
487 | |||
488 | +static inline unsigned long vm_start_gap(struct vm_area_struct *vma) | ||
489 | +{ | ||
490 | + unsigned long vm_start = vma->vm_start; | ||
491 | + | ||
492 | + if (vma->vm_flags & VM_GROWSDOWN) { | ||
493 | + vm_start -= stack_guard_gap; | ||
494 | + if (vm_start > vma->vm_start) | ||
495 | + vm_start = 0; | ||
496 | + } | ||
497 | + return vm_start; | ||
498 | +} | ||
499 | + | ||
500 | +static inline unsigned long vm_end_gap(struct vm_area_struct *vma) | ||
501 | +{ | ||
502 | + unsigned long vm_end = vma->vm_end; | ||
503 | + | ||
504 | + if (vma->vm_flags & VM_GROWSUP) { | ||
505 | + vm_end += stack_guard_gap; | ||
506 | + if (vm_end < vma->vm_end) | ||
507 | + vm_end = -PAGE_SIZE; | ||
508 | + } | ||
509 | + return vm_end; | ||
510 | +} | ||
511 | + | ||
512 | static inline unsigned long vma_pages(struct vm_area_struct *vma) | ||
513 | { | ||
514 | return (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||
515 | diff --git a/mm/gup.c b/mm/gup.c | ||
516 | index ec4f827..c63a034 100644 | ||
517 | --- a/mm/gup.c | ||
518 | +++ b/mm/gup.c | ||
519 | @@ -370,11 +370,6 @@ static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma, | ||
520 | /* mlock all present pages, but do not fault in new pages */ | ||
521 | if ((*flags & (FOLL_POPULATE | FOLL_MLOCK)) == FOLL_MLOCK) | ||
522 | return -ENOENT; | ||
523 | - /* For mm_populate(), just skip the stack guard page. */ | ||
524 | - if ((*flags & FOLL_POPULATE) && | ||
525 | - (stack_guard_page_start(vma, address) || | ||
526 | - stack_guard_page_end(vma, address + PAGE_SIZE))) | ||
527 | - return -ENOENT; | ||
528 | if (*flags & FOLL_WRITE) | ||
529 | fault_flags |= FAULT_FLAG_WRITE; | ||
530 | if (*flags & FOLL_REMOTE) | ||
531 | diff --git a/mm/memory.c b/mm/memory.c | ||
532 | index cbb1e5e..e6a5a1f 100644 | ||
533 | --- a/mm/memory.c | ||
534 | +++ b/mm/memory.c | ||
535 | @@ -2699,40 +2699,6 @@ int do_swap_page(struct fault_env *fe, pte_t orig_pte) | ||
536 | } | ||
537 | |||
538 | /* | ||
539 | - * This is like a special single-page "expand_{down|up}wards()", | ||
540 | - * except we must first make sure that 'address{-|+}PAGE_SIZE' | ||
541 | - * doesn't hit another vma. | ||
542 | - */ | ||
543 | -static inline int check_stack_guard_page(struct vm_area_struct *vma, unsigned long address) | ||
544 | -{ | ||
545 | - address &= PAGE_MASK; | ||
546 | - if ((vma->vm_flags & VM_GROWSDOWN) && address == vma->vm_start) { | ||
547 | - struct vm_area_struct *prev = vma->vm_prev; | ||
548 | - | ||
549 | - /* | ||
550 | - * Is there a mapping abutting this one below? | ||
551 | - * | ||
552 | - * That's only ok if it's the same stack mapping | ||
553 | - * that has gotten split.. | ||
554 | - */ | ||
555 | - if (prev && prev->vm_end == address) | ||
556 | - return prev->vm_flags & VM_GROWSDOWN ? 0 : -ENOMEM; | ||
557 | - | ||
558 | - return expand_downwards(vma, address - PAGE_SIZE); | ||
559 | - } | ||
560 | - if ((vma->vm_flags & VM_GROWSUP) && address + PAGE_SIZE == vma->vm_end) { | ||
561 | - struct vm_area_struct *next = vma->vm_next; | ||
562 | - | ||
563 | - /* As VM_GROWSDOWN but s/below/above/ */ | ||
564 | - if (next && next->vm_start == address + PAGE_SIZE) | ||
565 | - return next->vm_flags & VM_GROWSUP ? 0 : -ENOMEM; | ||
566 | - | ||
567 | - return expand_upwards(vma, address + PAGE_SIZE); | ||
568 | - } | ||
569 | - return 0; | ||
570 | -} | ||
571 | - | ||
572 | -/* | ||
573 | * We enter with non-exclusive mmap_sem (to exclude vma changes, | ||
574 | * but allow concurrent faults), and pte mapped but not yet locked. | ||
575 | * We return with mmap_sem still held, but pte unmapped and unlocked. | ||
576 | @@ -2748,10 +2714,6 @@ static int do_anonymous_page(struct fault_env *fe) | ||
577 | if (vma->vm_flags & VM_SHARED) | ||
578 | return VM_FAULT_SIGBUS; | ||
579 | |||
580 | - /* Check if we need to add a guard page to the stack */ | ||
581 | - if (check_stack_guard_page(vma, fe->address) < 0) | ||
582 | - return VM_FAULT_SIGSEGV; | ||
583 | - | ||
584 | /* | ||
585 | * Use pte_alloc() instead of pte_alloc_map(). We can't run | ||
586 | * pte_offset_map() on pmds where a huge pmd might be created | ||
587 | diff --git a/mm/mmap.c b/mm/mmap.c | ||
588 | index 1af87c1..26542b3 100644 | ||
589 | --- a/mm/mmap.c | ||
590 | +++ b/mm/mmap.c | ||
591 | @@ -183,6 +183,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) | ||
592 | unsigned long retval; | ||
593 | unsigned long newbrk, oldbrk; | ||
594 | struct mm_struct *mm = current->mm; | ||
595 | + struct vm_area_struct *next; | ||
596 | unsigned long min_brk; | ||
597 | bool populate; | ||
598 | |||
599 | @@ -228,7 +229,8 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) | ||
600 | } | ||
601 | |||
602 | /* Check against existing mmap mappings. */ | ||
603 | - if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE)) | ||
604 | + next = find_vma(mm, oldbrk); | ||
605 | + if (next && newbrk + PAGE_SIZE > vm_start_gap(next)) | ||
606 | goto out; | ||
607 | |||
608 | /* Ok, looks good - let it rip. */ | ||
609 | @@ -251,10 +253,22 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) | ||
610 | |||
611 | static long vma_compute_subtree_gap(struct vm_area_struct *vma) | ||
612 | { | ||
613 | - unsigned long max, subtree_gap; | ||
614 | - max = vma->vm_start; | ||
615 | - if (vma->vm_prev) | ||
616 | - max -= vma->vm_prev->vm_end; | ||
617 | + unsigned long max, prev_end, subtree_gap; | ||
618 | + | ||
619 | + /* | ||
620 | + * Note: in the rare case of a VM_GROWSDOWN above a VM_GROWSUP, we | ||
621 | + * allow two stack_guard_gaps between them here, and when choosing | ||
622 | + * an unmapped area; whereas when expanding we only require one. | ||
623 | + * That's a little inconsistent, but keeps the code here simpler. | ||
624 | + */ | ||
625 | + max = vm_start_gap(vma); | ||
626 | + if (vma->vm_prev) { | ||
627 | + prev_end = vm_end_gap(vma->vm_prev); | ||
628 | + if (max > prev_end) | ||
629 | + max -= prev_end; | ||
630 | + else | ||
631 | + max = 0; | ||
632 | + } | ||
633 | if (vma->vm_rb.rb_left) { | ||
634 | subtree_gap = rb_entry(vma->vm_rb.rb_left, | ||
635 | struct vm_area_struct, vm_rb)->rb_subtree_gap; | ||
636 | @@ -350,7 +364,7 @@ static void validate_mm(struct mm_struct *mm) | ||
637 | anon_vma_unlock_read(anon_vma); | ||
638 | } | ||
639 | |||
640 | - highest_address = vma->vm_end; | ||
641 | + highest_address = vm_end_gap(vma); | ||
642 | vma = vma->vm_next; | ||
643 | i++; | ||
644 | } | ||
645 | @@ -539,7 +553,7 @@ void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma, | ||
646 | if (vma->vm_next) | ||
647 | vma_gap_update(vma->vm_next); | ||
648 | else | ||
649 | - mm->highest_vm_end = vma->vm_end; | ||
650 | + mm->highest_vm_end = vm_end_gap(vma); | ||
651 | |||
652 | /* | ||
653 | * vma->vm_prev wasn't known when we followed the rbtree to find the | ||
654 | @@ -854,7 +868,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start, | ||
655 | vma_gap_update(vma); | ||
656 | if (end_changed) { | ||
657 | if (!next) | ||
658 | - mm->highest_vm_end = end; | ||
659 | + mm->highest_vm_end = vm_end_gap(vma); | ||
660 | else if (!adjust_next) | ||
661 | vma_gap_update(next); | ||
662 | } | ||
663 | @@ -939,7 +953,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start, | ||
664 | * mm->highest_vm_end doesn't need any update | ||
665 | * in remove_next == 1 case. | ||
666 | */ | ||
667 | - VM_WARN_ON(mm->highest_vm_end != end); | ||
668 | + VM_WARN_ON(mm->highest_vm_end != vm_end_gap(vma)); | ||
669 | } | ||
670 | } | ||
671 | if (insert && file) | ||
672 | @@ -1783,7 +1797,7 @@ unsigned long unmapped_area(struct vm_unmapped_area_info *info) | ||
673 | |||
674 | while (true) { | ||
675 | /* Visit left subtree if it looks promising */ | ||
676 | - gap_end = vma->vm_start; | ||
677 | + gap_end = vm_start_gap(vma); | ||
678 | if (gap_end >= low_limit && vma->vm_rb.rb_left) { | ||
679 | struct vm_area_struct *left = | ||
680 | rb_entry(vma->vm_rb.rb_left, | ||
681 | @@ -1794,7 +1808,7 @@ unsigned long unmapped_area(struct vm_unmapped_area_info *info) | ||
682 | } | ||
683 | } | ||
684 | |||
685 | - gap_start = vma->vm_prev ? vma->vm_prev->vm_end : 0; | ||
686 | + gap_start = vma->vm_prev ? vm_end_gap(vma->vm_prev) : 0; | ||
687 | check_current: | ||
688 | /* Check if current node has a suitable gap */ | ||
689 | if (gap_start > high_limit) | ||
690 | @@ -1821,8 +1835,8 @@ unsigned long unmapped_area(struct vm_unmapped_area_info *info) | ||
691 | vma = rb_entry(rb_parent(prev), | ||
692 | struct vm_area_struct, vm_rb); | ||
693 | if (prev == vma->vm_rb.rb_left) { | ||
694 | - gap_start = vma->vm_prev->vm_end; | ||
695 | - gap_end = vma->vm_start; | ||
696 | + gap_start = vm_end_gap(vma->vm_prev); | ||
697 | + gap_end = vm_start_gap(vma); | ||
698 | goto check_current; | ||
699 | } | ||
700 | } | ||
701 | @@ -1886,7 +1900,7 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) | ||
702 | |||
703 | while (true) { | ||
704 | /* Visit right subtree if it looks promising */ | ||
705 | - gap_start = vma->vm_prev ? vma->vm_prev->vm_end : 0; | ||
706 | + gap_start = vma->vm_prev ? vm_end_gap(vma->vm_prev) : 0; | ||
707 | if (gap_start <= high_limit && vma->vm_rb.rb_right) { | ||
708 | struct vm_area_struct *right = | ||
709 | rb_entry(vma->vm_rb.rb_right, | ||
710 | @@ -1899,7 +1913,7 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) | ||
711 | |||
712 | check_current: | ||
713 | /* Check if current node has a suitable gap */ | ||
714 | - gap_end = vma->vm_start; | ||
715 | + gap_end = vm_start_gap(vma); | ||
716 | if (gap_end < low_limit) | ||
717 | return -ENOMEM; | ||
718 | if (gap_start <= high_limit && gap_end - gap_start >= length) | ||
719 | @@ -1925,7 +1939,7 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) | ||
720 | struct vm_area_struct, vm_rb); | ||
721 | if (prev == vma->vm_rb.rb_right) { | ||
722 | gap_start = vma->vm_prev ? | ||
723 | - vma->vm_prev->vm_end : 0; | ||
724 | + vm_end_gap(vma->vm_prev) : 0; | ||
725 | goto check_current; | ||
726 | } | ||
727 | } | ||
728 | @@ -1963,7 +1977,7 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) | ||
729 | unsigned long len, unsigned long pgoff, unsigned long flags) | ||
730 | { | ||
731 | struct mm_struct *mm = current->mm; | ||
732 | - struct vm_area_struct *vma; | ||
733 | + struct vm_area_struct *vma, *prev; | ||
734 | struct vm_unmapped_area_info info; | ||
735 | |||
736 | if (len > TASK_SIZE - mmap_min_addr) | ||
737 | @@ -1974,9 +1988,10 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) | ||
738 | |||
739 | if (addr) { | ||
740 | addr = PAGE_ALIGN(addr); | ||
741 | - vma = find_vma(mm, addr); | ||
742 | + vma = find_vma_prev(mm, addr, &prev); | ||
743 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && | ||
744 | - (!vma || addr + len <= vma->vm_start)) | ||
745 | + (!vma || addr + len <= vm_start_gap(vma)) && | ||
746 | + (!prev || addr >= vm_end_gap(prev))) | ||
747 | return addr; | ||
748 | } | ||
749 | |||
750 | @@ -1999,7 +2014,7 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) | ||
751 | const unsigned long len, const unsigned long pgoff, | ||
752 | const unsigned long flags) | ||
753 | { | ||
754 | - struct vm_area_struct *vma; | ||
755 | + struct vm_area_struct *vma, *prev; | ||
756 | struct mm_struct *mm = current->mm; | ||
757 | unsigned long addr = addr0; | ||
758 | struct vm_unmapped_area_info info; | ||
759 | @@ -2014,9 +2029,10 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) | ||
760 | /* requesting a specific address */ | ||
761 | if (addr) { | ||
762 | addr = PAGE_ALIGN(addr); | ||
763 | - vma = find_vma(mm, addr); | ||
764 | + vma = find_vma_prev(mm, addr, &prev); | ||
765 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && | ||
766 | - (!vma || addr + len <= vma->vm_start)) | ||
767 | + (!vma || addr + len <= vm_start_gap(vma)) && | ||
768 | + (!prev || addr >= vm_end_gap(prev))) | ||
769 | return addr; | ||
770 | } | ||
771 | |||
772 | @@ -2151,21 +2167,19 @@ struct vm_area_struct * | ||
773 | * update accounting. This is shared with both the | ||
774 | * grow-up and grow-down cases. | ||
775 | */ | ||
776 | -static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, unsigned long grow) | ||
777 | +static int acct_stack_growth(struct vm_area_struct *vma, | ||
778 | + unsigned long size, unsigned long grow) | ||
779 | { | ||
780 | struct mm_struct *mm = vma->vm_mm; | ||
781 | struct rlimit *rlim = current->signal->rlim; | ||
782 | - unsigned long new_start, actual_size; | ||
783 | + unsigned long new_start; | ||
784 | |||
785 | /* address space limit tests */ | ||
786 | if (!may_expand_vm(mm, vma->vm_flags, grow)) | ||
787 | return -ENOMEM; | ||
788 | |||
789 | /* Stack limit test */ | ||
790 | - actual_size = size; | ||
791 | - if (size && (vma->vm_flags & (VM_GROWSUP | VM_GROWSDOWN))) | ||
792 | - actual_size -= PAGE_SIZE; | ||
793 | - if (actual_size > READ_ONCE(rlim[RLIMIT_STACK].rlim_cur)) | ||
794 | + if (size > READ_ONCE(rlim[RLIMIT_STACK].rlim_cur)) | ||
795 | return -ENOMEM; | ||
796 | |||
797 | /* mlock limit tests */ | ||
798 | @@ -2203,17 +2217,30 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns | ||
799 | int expand_upwards(struct vm_area_struct *vma, unsigned long address) | ||
800 | { | ||
801 | struct mm_struct *mm = vma->vm_mm; | ||
802 | + struct vm_area_struct *next; | ||
803 | + unsigned long gap_addr; | ||
804 | int error = 0; | ||
805 | |||
806 | if (!(vma->vm_flags & VM_GROWSUP)) | ||
807 | return -EFAULT; | ||
808 | |||
809 | /* Guard against wrapping around to address 0. */ | ||
810 | - if (address < PAGE_ALIGN(address+4)) | ||
811 | - address = PAGE_ALIGN(address+4); | ||
812 | - else | ||
813 | + address &= PAGE_MASK; | ||
814 | + address += PAGE_SIZE; | ||
815 | + if (!address) | ||
816 | return -ENOMEM; | ||
817 | |||
818 | + /* Enforce stack_guard_gap */ | ||
819 | + gap_addr = address + stack_guard_gap; | ||
820 | + if (gap_addr < address) | ||
821 | + return -ENOMEM; | ||
822 | + next = vma->vm_next; | ||
823 | + if (next && next->vm_start < gap_addr) { | ||
824 | + if (!(next->vm_flags & VM_GROWSUP)) | ||
825 | + return -ENOMEM; | ||
826 | + /* Check that both stack segments have the same anon_vma? */ | ||
827 | + } | ||
828 | + | ||
829 | /* We must make sure the anon_vma is allocated. */ | ||
830 | if (unlikely(anon_vma_prepare(vma))) | ||
831 | return -ENOMEM; | ||
832 | @@ -2257,7 +2284,7 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) | ||
833 | if (vma->vm_next) | ||
834 | vma_gap_update(vma->vm_next); | ||
835 | else | ||
836 | - mm->highest_vm_end = address; | ||
837 | + mm->highest_vm_end = vm_end_gap(vma); | ||
838 | spin_unlock(&mm->page_table_lock); | ||
839 | |||
840 | perf_event_mmap(vma); | ||
841 | @@ -2278,6 +2305,8 @@ int expand_downwards(struct vm_area_struct *vma, | ||
842 | unsigned long address) | ||
843 | { | ||
844 | struct mm_struct *mm = vma->vm_mm; | ||
845 | + struct vm_area_struct *prev; | ||
846 | + unsigned long gap_addr; | ||
847 | int error; | ||
848 | |||
849 | address &= PAGE_MASK; | ||
850 | @@ -2285,6 +2314,17 @@ int expand_downwards(struct vm_area_struct *vma, | ||
851 | if (error) | ||
852 | return error; | ||
853 | |||
854 | + /* Enforce stack_guard_gap */ | ||
855 | + gap_addr = address - stack_guard_gap; | ||
856 | + if (gap_addr > address) | ||
857 | + return -ENOMEM; | ||
858 | + prev = vma->vm_prev; | ||
859 | + if (prev && prev->vm_end > gap_addr) { | ||
860 | + if (!(prev->vm_flags & VM_GROWSDOWN)) | ||
861 | + return -ENOMEM; | ||
862 | + /* Check that both stack segments have the same anon_vma? */ | ||
863 | + } | ||
864 | + | ||
865 | /* We must make sure the anon_vma is allocated. */ | ||
866 | if (unlikely(anon_vma_prepare(vma))) | ||
867 | return -ENOMEM; | ||
868 | @@ -2339,28 +2379,25 @@ int expand_downwards(struct vm_area_struct *vma, | ||
869 | return error; | ||
870 | } | ||
871 | |||
872 | -/* | ||
873 | - * Note how expand_stack() refuses to expand the stack all the way to | ||
874 | - * abut the next virtual mapping, *unless* that mapping itself is also | ||
875 | - * a stack mapping. We want to leave room for a guard page, after all | ||
876 | - * (the guard page itself is not added here, that is done by the | ||
877 | - * actual page faulting logic) | ||
878 | - * | ||
879 | - * This matches the behavior of the guard page logic (see mm/memory.c: | ||
880 | - * check_stack_guard_page()), which only allows the guard page to be | ||
881 | - * removed under these circumstances. | ||
882 | - */ | ||
883 | +/* enforced gap between the expanding stack and other mappings. */ | ||
884 | +unsigned long stack_guard_gap = 256UL<<PAGE_SHIFT; | ||
885 | + | ||
886 | +static int __init cmdline_parse_stack_guard_gap(char *p) | ||
887 | +{ | ||
888 | + unsigned long val; | ||
889 | + char *endptr; | ||
890 | + | ||
891 | + val = simple_strtoul(p, &endptr, 10); | ||
892 | + if (!*endptr) | ||
893 | + stack_guard_gap = val << PAGE_SHIFT; | ||
894 | + | ||
895 | + return 0; | ||
896 | +} | ||
897 | +__setup("stack_guard_gap=", cmdline_parse_stack_guard_gap); | ||
898 | + | ||
899 | #ifdef CONFIG_STACK_GROWSUP | ||
900 | int expand_stack(struct vm_area_struct *vma, unsigned long address) | ||
901 | { | ||
902 | - struct vm_area_struct *next; | ||
903 | - | ||
904 | - address &= PAGE_MASK; | ||
905 | - next = vma->vm_next; | ||
906 | - if (next && next->vm_start == address + PAGE_SIZE) { | ||
907 | - if (!(next->vm_flags & VM_GROWSUP)) | ||
908 | - return -ENOMEM; | ||
909 | - } | ||
910 | return expand_upwards(vma, address); | ||
911 | } | ||
912 | |||
913 | @@ -2382,14 +2419,6 @@ struct vm_area_struct * | ||
914 | #else | ||
915 | int expand_stack(struct vm_area_struct *vma, unsigned long address) | ||
916 | { | ||
917 | - struct vm_area_struct *prev; | ||
918 | - | ||
919 | - address &= PAGE_MASK; | ||
920 | - prev = vma->vm_prev; | ||
921 | - if (prev && prev->vm_end == address) { | ||
922 | - if (!(prev->vm_flags & VM_GROWSDOWN)) | ||
923 | - return -ENOMEM; | ||
924 | - } | ||
925 | return expand_downwards(vma, address); | ||
926 | } | ||
927 | |||
928 | @@ -2487,7 +2516,7 @@ static void unmap_region(struct mm_struct *mm, | ||
929 | vma->vm_prev = prev; | ||
930 | vma_gap_update(vma); | ||
931 | } else | ||
932 | - mm->highest_vm_end = prev ? prev->vm_end : 0; | ||
933 | + mm->highest_vm_end = prev ? vm_end_gap(prev) : 0; | ||
934 | tail_vma->vm_next = NULL; | ||
935 | |||
936 | /* Kill the cache */ | ||
937 | -- | ||
938 | 1.9.1 | ||
939 | |||