]>
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 | ||
1b028f78 | 40 | unsigned long tasksize_32bit(void) |
8f3e474f DS |
41 | { |
42 | return IA32_PAGE_OFFSET; | |
43 | } | |
44 | ||
1b028f78 DS |
45 | unsigned long tasksize_64bit(void) |
46 | { | |
47 | return TASK_SIZE_MAX; | |
48 | } | |
49 | ||
8f3e474f | 50 | static unsigned long stack_maxrandom_size(unsigned long task_size) |
80938332 | 51 | { |
4e7c22d4 | 52 | unsigned long max = 0; |
80938332 MH |
53 | if ((current->flags & PF_RANDOMIZE) && |
54 | !(current->personality & ADDR_NO_RANDOMIZE)) { | |
8f3e474f DS |
55 | max = (-1UL) & __STACK_RND_MASK(task_size == tasksize_32bit()); |
56 | max <<= PAGE_SHIFT; | |
80938332 MH |
57 | } |
58 | ||
59 | return max; | |
60 | } | |
61 | ||
6a0b41d1 DS |
62 | #ifdef CONFIG_COMPAT |
63 | # define mmap32_rnd_bits mmap_rnd_compat_bits | |
64 | # define mmap64_rnd_bits mmap_rnd_bits | |
65 | #else | |
66 | # define mmap32_rnd_bits mmap_rnd_bits | |
67 | # define mmap64_rnd_bits mmap_rnd_bits | |
68 | #endif | |
69 | ||
8f3e474f DS |
70 | #define SIZE_128M (128 * 1024 * 1024UL) |
71 | ||
954683a2 | 72 | static int mmap_is_legacy(void) |
cc503c1b JK |
73 | { |
74 | if (current->personality & ADDR_COMPAT_LAYOUT) | |
75 | return 1; | |
76 | ||
cc503c1b JK |
77 | return sysctl_legacy_va_layout; |
78 | } | |
79 | ||
6a0b41d1 | 80 | static unsigned long arch_rnd(unsigned int rndbits) |
675a0813 | 81 | { |
6a0b41d1 DS |
82 | return (get_random_long() & ((1UL << rndbits) - 1)) << PAGE_SHIFT; |
83 | } | |
82168140 | 84 | |
6a0b41d1 DS |
85 | unsigned long arch_mmap_rnd(void) |
86 | { | |
1b028f78 DS |
87 | if (!(current->flags & PF_RANDOMIZE)) |
88 | return 0; | |
6a0b41d1 | 89 | return arch_rnd(mmap_is_ia32() ? mmap32_rnd_bits : mmap64_rnd_bits); |
675a0813 HH |
90 | } |
91 | ||
8f3e474f | 92 | static unsigned long mmap_base(unsigned long rnd, unsigned long task_size) |
675a0813 | 93 | { |
2854e72b | 94 | unsigned long gap = rlimit(RLIMIT_STACK); |
c204d21f | 95 | unsigned long pad = stack_maxrandom_size(task_size) + stack_guard_gap; |
8f3e474f DS |
96 | unsigned long gap_min, gap_max; |
97 | ||
c204d21f RR |
98 | /* Values close to RLIM_INFINITY can overflow. */ |
99 | if (gap + pad > gap) | |
100 | gap += pad; | |
101 | ||
8f3e474f DS |
102 | /* |
103 | * Top of mmap area (just below the process stack). | |
104 | * Leave an at least ~128 MB hole with possible stack randomization. | |
105 | */ | |
c204d21f | 106 | gap_min = SIZE_128M; |
8f3e474f | 107 | gap_max = (task_size / 6) * 5; |
675a0813 | 108 | |
8f3e474f DS |
109 | if (gap < gap_min) |
110 | gap = gap_min; | |
111 | else if (gap > gap_max) | |
112 | gap = gap_max; | |
675a0813 | 113 | |
8f3e474f DS |
114 | return PAGE_ALIGN(task_size - gap - rnd); |
115 | } | |
116 | ||
117 | static unsigned long mmap_legacy_base(unsigned long rnd, | |
118 | unsigned long task_size) | |
119 | { | |
120 | return __TASK_UNMAPPED_BASE(task_size) + rnd; | |
675a0813 HH |
121 | } |
122 | ||
cc503c1b JK |
123 | /* |
124 | * This function, called very early during the creation of a new | |
125 | * process VM image, sets up which VM layout function to use: | |
126 | */ | |
1b028f78 DS |
127 | static void arch_pick_mmap_base(unsigned long *base, unsigned long *legacy_base, |
128 | unsigned long random_factor, unsigned long task_size) | |
cc503c1b | 129 | { |
1b028f78 DS |
130 | *legacy_base = mmap_legacy_base(random_factor, task_size); |
131 | if (mmap_is_legacy()) | |
132 | *base = *legacy_base; | |
133 | else | |
134 | *base = mmap_base(random_factor, task_size); | |
135 | } | |
41aacc1e | 136 | |
1b028f78 DS |
137 | void arch_pick_mmap_layout(struct mm_struct *mm) |
138 | { | |
139 | if (mmap_is_legacy()) | |
cc503c1b | 140 | mm->get_unmapped_area = arch_get_unmapped_area; |
1b028f78 | 141 | else |
cc503c1b | 142 | mm->get_unmapped_area = arch_get_unmapped_area_topdown; |
1b028f78 DS |
143 | |
144 | arch_pick_mmap_base(&mm->mmap_base, &mm->mmap_legacy_base, | |
145 | arch_rnd(mmap64_rnd_bits), tasksize_64bit()); | |
146 | ||
147 | #ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES | |
148 | /* | |
149 | * The mmap syscall mapping base decision depends solely on the | |
150 | * syscall type (64-bit or compat). This applies for 64bit | |
151 | * applications and 32bit applications. The 64bit syscall uses | |
152 | * mmap_base, the compat syscall uses mmap_compat_base. | |
153 | */ | |
154 | arch_pick_mmap_base(&mm->mmap_compat_base, &mm->mmap_compat_legacy_base, | |
155 | arch_rnd(mmap32_rnd_bits), tasksize_32bit()); | |
156 | #endif | |
8817210d | 157 | } |
a8965276 | 158 | |
e13b73dd DS |
159 | unsigned long get_mmap_base(int is_legacy) |
160 | { | |
161 | struct mm_struct *mm = current->mm; | |
162 | ||
163 | #ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES | |
164 | if (in_compat_syscall()) { | |
165 | return is_legacy ? mm->mmap_compat_legacy_base | |
166 | : mm->mmap_compat_base; | |
167 | } | |
168 | #endif | |
169 | return is_legacy ? mm->mmap_legacy_base : mm->mmap_base; | |
170 | } | |
171 | ||
a8965276 KS |
172 | const char *arch_vma_name(struct vm_area_struct *vma) |
173 | { | |
174 | if (vma->vm_flags & VM_MPX) | |
175 | return "[mpx]"; | |
176 | return NULL; | |
177 | } |