]>
Commit | Line | Data |
---|---|---|
cc503c1b | 1 | /* |
675a0813 | 2 | * Flexible mmap layout support |
cc503c1b JK |
3 | * |
4 | * Based on code by Ingo Molnar and Andi Kleen, copyrighted | |
5 | * as follows: | |
6 | * | |
8f47e163 | 7 | * Copyright 2003-2009 Red Hat Inc. |
cc503c1b JK |
8 | * All Rights Reserved. |
9 | * Copyright 2005 Andi Kleen, SUSE Labs. | |
10 | * Copyright 2007 Jiri Kosina, SUSE Labs. | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
8817210d | 25 | */ |
cc503c1b JK |
26 | |
27 | #include <linux/personality.h> | |
8817210d | 28 | #include <linux/mm.h> |
8817210d | 29 | #include <linux/random.h> |
cc503c1b | 30 | #include <linux/limits.h> |
3f07c014 | 31 | #include <linux/sched/signal.h> |
01042607 | 32 | #include <linux/sched/mm.h> |
e13b73dd | 33 | #include <linux/compat.h> |
80938332 MH |
34 | #include <asm/elf.h> |
35 | ||
cc99535e | 36 | struct va_alignment __read_mostly va_align = { |
9387f774 BP |
37 | .flags = -1, |
38 | }; | |
39 | ||
e8f01a8d | 40 | unsigned long task_size_32bit(void) |
8f3e474f DS |
41 | { |
42 | return IA32_PAGE_OFFSET; | |
43 | } | |
44 | ||
b569bab7 | 45 | unsigned long task_size_64bit(int full_addr_space) |
1b028f78 | 46 | { |
b569bab7 | 47 | return full_addr_space ? TASK_SIZE_MAX : DEFAULT_MAP_WINDOW; |
1b028f78 DS |
48 | } |
49 | ||
8f3e474f | 50 | static unsigned long stack_maxrandom_size(unsigned long task_size) |
80938332 | 51 | { |
4e7c22d4 | 52 | unsigned long max = 0; |
01578e36 | 53 | if (current->flags & PF_RANDOMIZE) { |
e8f01a8d | 54 | max = (-1UL) & __STACK_RND_MASK(task_size == task_size_32bit()); |
8f3e474f | 55 | max <<= PAGE_SHIFT; |
80938332 MH |
56 | } |
57 | ||
58 | return max; | |
59 | } | |
60 | ||
6a0b41d1 DS |
61 | #ifdef CONFIG_COMPAT |
62 | # define mmap32_rnd_bits mmap_rnd_compat_bits | |
63 | # define mmap64_rnd_bits mmap_rnd_bits | |
64 | #else | |
65 | # define mmap32_rnd_bits mmap_rnd_bits | |
66 | # define mmap64_rnd_bits mmap_rnd_bits | |
67 | #endif | |
68 | ||
8f3e474f DS |
69 | #define SIZE_128M (128 * 1024 * 1024UL) |
70 | ||
954683a2 | 71 | static int mmap_is_legacy(void) |
cc503c1b JK |
72 | { |
73 | if (current->personality & ADDR_COMPAT_LAYOUT) | |
74 | return 1; | |
75 | ||
cc503c1b JK |
76 | return sysctl_legacy_va_layout; |
77 | } | |
78 | ||
6a0b41d1 | 79 | static unsigned long arch_rnd(unsigned int rndbits) |
675a0813 | 80 | { |
47ac5484 ON |
81 | if (!(current->flags & PF_RANDOMIZE)) |
82 | return 0; | |
6a0b41d1 DS |
83 | return (get_random_long() & ((1UL << rndbits) - 1)) << PAGE_SHIFT; |
84 | } | |
82168140 | 85 | |
6a0b41d1 DS |
86 | unsigned long arch_mmap_rnd(void) |
87 | { | |
88 | return arch_rnd(mmap_is_ia32() ? mmap32_rnd_bits : mmap64_rnd_bits); | |
675a0813 HH |
89 | } |
90 | ||
8f3e474f | 91 | static unsigned long mmap_base(unsigned long rnd, unsigned long task_size) |
675a0813 | 92 | { |
2854e72b | 93 | unsigned long gap = rlimit(RLIMIT_STACK); |
c204d21f | 94 | unsigned long pad = stack_maxrandom_size(task_size) + stack_guard_gap; |
8f3e474f DS |
95 | unsigned long gap_min, gap_max; |
96 | ||
c204d21f RR |
97 | /* Values close to RLIM_INFINITY can overflow. */ |
98 | if (gap + pad > gap) | |
99 | gap += pad; | |
100 | ||
8f3e474f DS |
101 | /* |
102 | * Top of mmap area (just below the process stack). | |
103 | * Leave an at least ~128 MB hole with possible stack randomization. | |
104 | */ | |
c204d21f | 105 | gap_min = SIZE_128M; |
8f3e474f | 106 | gap_max = (task_size / 6) * 5; |
675a0813 | 107 | |
8f3e474f DS |
108 | if (gap < gap_min) |
109 | gap = gap_min; | |
110 | else if (gap > gap_max) | |
111 | gap = gap_max; | |
675a0813 | 112 | |
8f3e474f DS |
113 | return PAGE_ALIGN(task_size - gap - rnd); |
114 | } | |
115 | ||
116 | static unsigned long mmap_legacy_base(unsigned long rnd, | |
117 | unsigned long task_size) | |
118 | { | |
119 | return __TASK_UNMAPPED_BASE(task_size) + rnd; | |
675a0813 HH |
120 | } |
121 | ||
cc503c1b JK |
122 | /* |
123 | * This function, called very early during the creation of a new | |
124 | * process VM image, sets up which VM layout function to use: | |
125 | */ | |
1b028f78 DS |
126 | static void arch_pick_mmap_base(unsigned long *base, unsigned long *legacy_base, |
127 | unsigned long random_factor, unsigned long task_size) | |
cc503c1b | 128 | { |
1b028f78 DS |
129 | *legacy_base = mmap_legacy_base(random_factor, task_size); |
130 | if (mmap_is_legacy()) | |
131 | *base = *legacy_base; | |
132 | else | |
133 | *base = mmap_base(random_factor, task_size); | |
134 | } | |
41aacc1e | 135 | |
1b028f78 DS |
136 | void arch_pick_mmap_layout(struct mm_struct *mm) |
137 | { | |
138 | if (mmap_is_legacy()) | |
cc503c1b | 139 | mm->get_unmapped_area = arch_get_unmapped_area; |
1b028f78 | 140 | else |
cc503c1b | 141 | mm->get_unmapped_area = arch_get_unmapped_area_topdown; |
1b028f78 DS |
142 | |
143 | arch_pick_mmap_base(&mm->mmap_base, &mm->mmap_legacy_base, | |
b569bab7 | 144 | arch_rnd(mmap64_rnd_bits), task_size_64bit(0)); |
1b028f78 DS |
145 | |
146 | #ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES | |
147 | /* | |
148 | * The mmap syscall mapping base decision depends solely on the | |
149 | * syscall type (64-bit or compat). This applies for 64bit | |
150 | * applications and 32bit applications. The 64bit syscall uses | |
151 | * mmap_base, the compat syscall uses mmap_compat_base. | |
152 | */ | |
153 | arch_pick_mmap_base(&mm->mmap_compat_base, &mm->mmap_compat_legacy_base, | |
e8f01a8d | 154 | arch_rnd(mmap32_rnd_bits), task_size_32bit()); |
1b028f78 | 155 | #endif |
8817210d | 156 | } |
a8965276 | 157 | |
e13b73dd DS |
158 | unsigned long get_mmap_base(int is_legacy) |
159 | { | |
160 | struct mm_struct *mm = current->mm; | |
161 | ||
162 | #ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES | |
163 | if (in_compat_syscall()) { | |
164 | return is_legacy ? mm->mmap_compat_legacy_base | |
165 | : mm->mmap_compat_base; | |
166 | } | |
167 | #endif | |
168 | return is_legacy ? mm->mmap_legacy_base : mm->mmap_base; | |
169 | } | |
170 | ||
a8965276 KS |
171 | const char *arch_vma_name(struct vm_area_struct *vma) |
172 | { | |
173 | if (vma->vm_flags & VM_MPX) | |
174 | return "[mpx]"; | |
175 | return NULL; | |
176 | } |