Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * flexible mmap layout support |
3 | * | |
4 | * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. | |
5 | * All Rights Reserved. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | * | |
21 | * | |
22 | * Started by Ingo Molnar <mingo@elte.hu> | |
23 | */ | |
24 | ||
25 | #include <linux/personality.h> | |
26 | #include <linux/mm.h> | |
638ad34a | 27 | #include <linux/mman.h> |
1da177e4 | 28 | #include <linux/module.h> |
df1ca53c | 29 | #include <linux/random.h> |
048cd4e5 | 30 | #include <linux/compat.h> |
1f6b83e5 | 31 | #include <linux/security.h> |
6252d702 | 32 | #include <asm/pgalloc.h> |
1da177e4 | 33 | |
1f6b83e5 MS |
34 | unsigned long mmap_rnd_mask; |
35 | unsigned long mmap_align_mask; | |
36 | ||
9046e401 HC |
37 | static unsigned long stack_maxrandom_size(void) |
38 | { | |
39 | if (!(current->flags & PF_RANDOMIZE)) | |
40 | return 0; | |
41 | if (current->personality & ADDR_NO_RANDOMIZE) | |
42 | return 0; | |
43 | return STACK_RND_MASK << PAGE_SHIFT; | |
44 | } | |
45 | ||
1da177e4 LT |
46 | /* |
47 | * Top of mmap area (just below the process stack). | |
48 | * | |
9e78a13b | 49 | * Leave at least a ~32 MB hole. |
1da177e4 | 50 | */ |
9e78a13b | 51 | #define MIN_GAP (32*1024*1024) |
f481bfaf | 52 | #define MAX_GAP (STACK_TOP/6*5) |
1da177e4 | 53 | |
1060f62e HC |
54 | static inline int mmap_is_legacy(void) |
55 | { | |
56 | if (current->personality & ADDR_COMPAT_LAYOUT) | |
57 | return 1; | |
58 | if (rlimit(RLIMIT_STACK) == RLIM_INFINITY) | |
59 | return 1; | |
60 | return sysctl_legacy_va_layout; | |
61 | } | |
62 | ||
2b68f6ca | 63 | unsigned long arch_mmap_rnd(void) |
df1ca53c | 64 | { |
1f6b83e5 MS |
65 | if (is_32bit_task()) |
66 | return (get_random_int() & 0x7ff) << PAGE_SHIFT; | |
67 | else | |
68 | return (get_random_int() & mmap_rnd_mask) << PAGE_SHIFT; | |
df1ca53c HC |
69 | } |
70 | ||
8e89a356 | 71 | static unsigned long mmap_base_legacy(unsigned long rnd) |
7aba842f | 72 | { |
8e89a356 | 73 | return TASK_UNMAPPED_BASE + rnd; |
7aba842f HC |
74 | } |
75 | ||
8e89a356 | 76 | static inline unsigned long mmap_base(unsigned long rnd) |
1da177e4 | 77 | { |
a58c26bb | 78 | unsigned long gap = rlimit(RLIMIT_STACK); |
1da177e4 LT |
79 | |
80 | if (gap < MIN_GAP) | |
81 | gap = MIN_GAP; | |
82 | else if (gap > MAX_GAP) | |
83 | gap = MAX_GAP; | |
df1ca53c | 84 | gap &= PAGE_MASK; |
8e89a356 | 85 | return STACK_TOP - stack_maxrandom_size() - rnd - gap; |
1da177e4 LT |
86 | } |
87 | ||
1f6b83e5 MS |
88 | unsigned long |
89 | arch_get_unmapped_area(struct file *filp, unsigned long addr, | |
90 | unsigned long len, unsigned long pgoff, unsigned long flags) | |
91 | { | |
92 | struct mm_struct *mm = current->mm; | |
93 | struct vm_area_struct *vma; | |
94 | struct vm_unmapped_area_info info; | |
95 | int do_color_align; | |
96 | ||
97 | if (len > TASK_SIZE - mmap_min_addr) | |
98 | return -ENOMEM; | |
99 | ||
100 | if (flags & MAP_FIXED) | |
101 | return addr; | |
102 | ||
103 | if (addr) { | |
104 | addr = PAGE_ALIGN(addr); | |
105 | vma = find_vma(mm, addr); | |
106 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && | |
107 | (!vma || addr + len <= vma->vm_start)) | |
108 | return addr; | |
109 | } | |
110 | ||
111 | do_color_align = 0; | |
112 | if (filp || (flags & MAP_SHARED)) | |
113 | do_color_align = !is_32bit_task(); | |
114 | ||
115 | info.flags = 0; | |
116 | info.length = len; | |
117 | info.low_limit = mm->mmap_base; | |
118 | info.high_limit = TASK_SIZE; | |
119 | info.align_mask = do_color_align ? (mmap_align_mask << PAGE_SHIFT) : 0; | |
120 | info.align_offset = pgoff << PAGE_SHIFT; | |
121 | return vm_unmapped_area(&info); | |
122 | } | |
123 | ||
124 | unsigned long | |
125 | arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, | |
126 | const unsigned long len, const unsigned long pgoff, | |
127 | const unsigned long flags) | |
128 | { | |
129 | struct vm_area_struct *vma; | |
130 | struct mm_struct *mm = current->mm; | |
131 | unsigned long addr = addr0; | |
132 | struct vm_unmapped_area_info info; | |
133 | int do_color_align; | |
134 | ||
135 | /* requested length too big for entire address space */ | |
136 | if (len > TASK_SIZE - mmap_min_addr) | |
137 | return -ENOMEM; | |
138 | ||
139 | if (flags & MAP_FIXED) | |
140 | return addr; | |
141 | ||
142 | /* requesting a specific address */ | |
143 | if (addr) { | |
144 | addr = PAGE_ALIGN(addr); | |
145 | vma = find_vma(mm, addr); | |
146 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && | |
147 | (!vma || addr + len <= vma->vm_start)) | |
148 | return addr; | |
149 | } | |
150 | ||
151 | do_color_align = 0; | |
152 | if (filp || (flags & MAP_SHARED)) | |
153 | do_color_align = !is_32bit_task(); | |
154 | ||
155 | info.flags = VM_UNMAPPED_AREA_TOPDOWN; | |
156 | info.length = len; | |
157 | info.low_limit = max(PAGE_SIZE, mmap_min_addr); | |
158 | info.high_limit = mm->mmap_base; | |
159 | info.align_mask = do_color_align ? (mmap_align_mask << PAGE_SHIFT) : 0; | |
160 | info.align_offset = pgoff << PAGE_SHIFT; | |
161 | addr = vm_unmapped_area(&info); | |
162 | ||
163 | /* | |
164 | * A failed mmap() very likely causes application failure, | |
165 | * so fall back to the bottom-up function here. This scenario | |
166 | * can happen with large stack limits and large mmap() | |
167 | * allocations. | |
168 | */ | |
169 | if (addr & ~PAGE_MASK) { | |
170 | VM_BUG_ON(addr != -ENOMEM); | |
171 | info.flags = 0; | |
172 | info.low_limit = TASK_UNMAPPED_BASE; | |
173 | info.high_limit = TASK_SIZE; | |
174 | addr = vm_unmapped_area(&info); | |
175 | } | |
176 | ||
177 | return addr; | |
178 | } | |
179 | ||
180 | unsigned long randomize_et_dyn(void) | |
181 | { | |
182 | unsigned long base; | |
183 | ||
4ba2815d MS |
184 | base = STACK_TOP / 3 * 2; |
185 | if (!is_32bit_task()) | |
186 | /* Align to 4GB */ | |
187 | base &= ~((1UL << 32) - 1); | |
8e89a356 KC |
188 | |
189 | if (current->flags & PF_RANDOMIZE) | |
2b68f6ca | 190 | base += arch_mmap_rnd(); |
8e89a356 KC |
191 | |
192 | return base; | |
1f6b83e5 MS |
193 | } |
194 | ||
6252d702 MS |
195 | #ifndef CONFIG_64BIT |
196 | ||
1da177e4 LT |
197 | /* |
198 | * This function, called very early during the creation of a new | |
199 | * process VM image, sets up which VM layout function to use: | |
200 | */ | |
201 | void arch_pick_mmap_layout(struct mm_struct *mm) | |
202 | { | |
8e89a356 KC |
203 | unsigned long random_factor = 0UL; |
204 | ||
205 | if (current->flags & PF_RANDOMIZE) | |
2b68f6ca | 206 | random_factor = arch_mmap_rnd(); |
8e89a356 | 207 | |
1da177e4 LT |
208 | /* |
209 | * Fall back to the standard layout if the personality | |
210 | * bit is set, or if the expected stack growth is unlimited: | |
211 | */ | |
212 | if (mmap_is_legacy()) { | |
8e89a356 | 213 | mm->mmap_base = mmap_base_legacy(random_factor); |
1da177e4 | 214 | mm->get_unmapped_area = arch_get_unmapped_area; |
1da177e4 | 215 | } else { |
8e89a356 | 216 | mm->mmap_base = mmap_base(random_factor); |
1da177e4 | 217 | mm->get_unmapped_area = arch_get_unmapped_area_topdown; |
1da177e4 LT |
218 | } |
219 | } | |
1da177e4 | 220 | |
6252d702 MS |
221 | #else |
222 | ||
486c0a0b | 223 | int s390_mmap_check(unsigned long addr, unsigned long len, unsigned long flags) |
0fb1d9bc | 224 | { |
486c0a0b HB |
225 | if (is_compat_task() || (TASK_SIZE >= (1UL << 53))) |
226 | return 0; | |
227 | if (!(flags & MAP_FIXED)) | |
228 | addr = 0; | |
10607864 MS |
229 | if ((addr + len) >= TASK_SIZE) |
230 | return crst_table_upgrade(current->mm, 1UL << 53); | |
0fb1d9bc MS |
231 | return 0; |
232 | } | |
233 | ||
6252d702 MS |
234 | static unsigned long |
235 | s390_get_unmapped_area(struct file *filp, unsigned long addr, | |
236 | unsigned long len, unsigned long pgoff, unsigned long flags) | |
237 | { | |
238 | struct mm_struct *mm = current->mm; | |
0fb1d9bc | 239 | unsigned long area; |
6252d702 MS |
240 | int rc; |
241 | ||
0fb1d9bc MS |
242 | area = arch_get_unmapped_area(filp, addr, len, pgoff, flags); |
243 | if (!(area & ~PAGE_MASK)) | |
244 | return area; | |
7757591a | 245 | if (area == -ENOMEM && !is_compat_task() && TASK_SIZE < (1UL << 53)) { |
0fb1d9bc MS |
246 | /* Upgrade the page table to 4 levels and retry. */ |
247 | rc = crst_table_upgrade(mm, 1UL << 53); | |
6252d702 MS |
248 | if (rc) |
249 | return (unsigned long) rc; | |
0fb1d9bc | 250 | area = arch_get_unmapped_area(filp, addr, len, pgoff, flags); |
6252d702 | 251 | } |
0fb1d9bc | 252 | return area; |
6252d702 MS |
253 | } |
254 | ||
255 | static unsigned long | |
0fb1d9bc | 256 | s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr, |
6252d702 MS |
257 | const unsigned long len, const unsigned long pgoff, |
258 | const unsigned long flags) | |
259 | { | |
260 | struct mm_struct *mm = current->mm; | |
0fb1d9bc | 261 | unsigned long area; |
6252d702 MS |
262 | int rc; |
263 | ||
0fb1d9bc MS |
264 | area = arch_get_unmapped_area_topdown(filp, addr, len, pgoff, flags); |
265 | if (!(area & ~PAGE_MASK)) | |
266 | return area; | |
7757591a | 267 | if (area == -ENOMEM && !is_compat_task() && TASK_SIZE < (1UL << 53)) { |
0fb1d9bc MS |
268 | /* Upgrade the page table to 4 levels and retry. */ |
269 | rc = crst_table_upgrade(mm, 1UL << 53); | |
6252d702 MS |
270 | if (rc) |
271 | return (unsigned long) rc; | |
0fb1d9bc MS |
272 | area = arch_get_unmapped_area_topdown(filp, addr, len, |
273 | pgoff, flags); | |
6252d702 | 274 | } |
0fb1d9bc | 275 | return area; |
6252d702 MS |
276 | } |
277 | /* | |
278 | * This function, called very early during the creation of a new | |
279 | * process VM image, sets up which VM layout function to use: | |
280 | */ | |
281 | void arch_pick_mmap_layout(struct mm_struct *mm) | |
282 | { | |
8e89a356 KC |
283 | unsigned long random_factor = 0UL; |
284 | ||
285 | if (current->flags & PF_RANDOMIZE) | |
2b68f6ca | 286 | random_factor = arch_mmap_rnd(); |
8e89a356 | 287 | |
6252d702 MS |
288 | /* |
289 | * Fall back to the standard layout if the personality | |
290 | * bit is set, or if the expected stack growth is unlimited: | |
291 | */ | |
292 | if (mmap_is_legacy()) { | |
8e89a356 | 293 | mm->mmap_base = mmap_base_legacy(random_factor); |
6252d702 | 294 | mm->get_unmapped_area = s390_get_unmapped_area; |
6252d702 | 295 | } else { |
8e89a356 | 296 | mm->mmap_base = mmap_base(random_factor); |
6252d702 | 297 | mm->get_unmapped_area = s390_get_unmapped_area_topdown; |
6252d702 MS |
298 | } |
299 | } | |
6252d702 | 300 | |
1f6b83e5 MS |
301 | static int __init setup_mmap_rnd(void) |
302 | { | |
303 | struct cpuid cpu_id; | |
304 | ||
305 | get_cpu_id(&cpu_id); | |
306 | switch (cpu_id.machine) { | |
307 | case 0x9672: | |
308 | case 0x2064: | |
309 | case 0x2066: | |
310 | case 0x2084: | |
311 | case 0x2086: | |
312 | case 0x2094: | |
313 | case 0x2096: | |
314 | case 0x2097: | |
315 | case 0x2098: | |
316 | case 0x2817: | |
317 | case 0x2818: | |
318 | case 0x2827: | |
319 | case 0x2828: | |
320 | mmap_rnd_mask = 0x7ffUL; | |
321 | mmap_align_mask = 0UL; | |
322 | break; | |
323 | case 0x2964: /* z13 */ | |
324 | default: | |
325 | mmap_rnd_mask = 0x3ff80UL; | |
326 | mmap_align_mask = 0x7fUL; | |
327 | break; | |
328 | } | |
329 | return 0; | |
330 | } | |
331 | early_initcall(setup_mmap_rnd); | |
332 | ||
6252d702 | 333 | #endif |