]>
Commit | Line | Data |
---|---|---|
ac41aaee | 1 | // SPDX-License-Identifier: GPL-2.0+ |
1da177e4 | 2 | /* |
1da177e4 LT |
3 | * flexible mmap layout support |
4 | * | |
5 | * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. | |
6 | * All Rights Reserved. | |
7 | * | |
1da177e4 LT |
8 | * Started by Ingo Molnar <mingo@elte.hu> |
9 | */ | |
10 | ||
ca21872e | 11 | #include <linux/elf-randomize.h> |
1da177e4 LT |
12 | #include <linux/personality.h> |
13 | #include <linux/mm.h> | |
638ad34a | 14 | #include <linux/mman.h> |
3f07c014 | 15 | #include <linux/sched/signal.h> |
01042607 | 16 | #include <linux/sched/mm.h> |
df1ca53c | 17 | #include <linux/random.h> |
048cd4e5 | 18 | #include <linux/compat.h> |
1f6b83e5 | 19 | #include <linux/security.h> |
6252d702 | 20 | #include <asm/pgalloc.h> |
ff24b07a | 21 | #include <asm/elf.h> |
1da177e4 | 22 | |
9046e401 HC |
23 | static unsigned long stack_maxrandom_size(void) |
24 | { | |
25 | if (!(current->flags & PF_RANDOMIZE)) | |
26 | return 0; | |
27 | if (current->personality & ADDR_NO_RANDOMIZE) | |
28 | return 0; | |
29 | return STACK_RND_MASK << PAGE_SHIFT; | |
30 | } | |
31 | ||
1da177e4 LT |
32 | /* |
33 | * Top of mmap area (just below the process stack). | |
34 | * | |
9e78a13b | 35 | * Leave at least a ~32 MB hole. |
1da177e4 | 36 | */ |
9e78a13b | 37 | #define MIN_GAP (32*1024*1024) |
f481bfaf | 38 | #define MAX_GAP (STACK_TOP/6*5) |
1da177e4 | 39 | |
1060f62e HC |
40 | static inline int mmap_is_legacy(void) |
41 | { | |
42 | if (current->personality & ADDR_COMPAT_LAYOUT) | |
43 | return 1; | |
44 | if (rlimit(RLIMIT_STACK) == RLIM_INFINITY) | |
45 | return 1; | |
46 | return sysctl_legacy_va_layout; | |
47 | } | |
48 | ||
2b68f6ca | 49 | unsigned long arch_mmap_rnd(void) |
df1ca53c | 50 | { |
c7e8b2c2 | 51 | return (get_random_int() & MMAP_RND_MASK) << PAGE_SHIFT; |
df1ca53c HC |
52 | } |
53 | ||
8e89a356 | 54 | static unsigned long mmap_base_legacy(unsigned long rnd) |
7aba842f | 55 | { |
8e89a356 | 56 | return TASK_UNMAPPED_BASE + rnd; |
7aba842f HC |
57 | } |
58 | ||
8e89a356 | 59 | static inline unsigned long mmap_base(unsigned long rnd) |
1da177e4 | 60 | { |
a58c26bb | 61 | unsigned long gap = rlimit(RLIMIT_STACK); |
1da177e4 LT |
62 | |
63 | if (gap < MIN_GAP) | |
64 | gap = MIN_GAP; | |
65 | else if (gap > MAX_GAP) | |
66 | gap = MAX_GAP; | |
df1ca53c | 67 | gap &= PAGE_MASK; |
8e89a356 | 68 | return STACK_TOP - stack_maxrandom_size() - rnd - gap; |
1da177e4 LT |
69 | } |
70 | ||
1f6b83e5 MS |
71 | unsigned long |
72 | arch_get_unmapped_area(struct file *filp, unsigned long addr, | |
73 | unsigned long len, unsigned long pgoff, unsigned long flags) | |
74 | { | |
75 | struct mm_struct *mm = current->mm; | |
76 | struct vm_area_struct *vma; | |
77 | struct vm_unmapped_area_info info; | |
9b11c791 | 78 | int rc; |
1f6b83e5 | 79 | |
9b11c791 | 80 | if (len > TASK_SIZE - mmap_min_addr) |
1f6b83e5 MS |
81 | return -ENOMEM; |
82 | ||
83 | if (flags & MAP_FIXED) | |
9b11c791 | 84 | goto check_asce_limit; |
1f6b83e5 MS |
85 | |
86 | if (addr) { | |
87 | addr = PAGE_ALIGN(addr); | |
88 | vma = find_vma(mm, addr); | |
9b11c791 | 89 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && |
1be7107f | 90 | (!vma || addr + len <= vm_start_gap(vma))) |
9b11c791 | 91 | goto check_asce_limit; |
1f6b83e5 MS |
92 | } |
93 | ||
1f6b83e5 MS |
94 | info.flags = 0; |
95 | info.length = len; | |
96 | info.low_limit = mm->mmap_base; | |
9b11c791 | 97 | info.high_limit = TASK_SIZE; |
c7e8b2c2 MS |
98 | if (filp || (flags & MAP_SHARED)) |
99 | info.align_mask = MMAP_ALIGN_MASK << PAGE_SHIFT; | |
100 | else | |
101 | info.align_mask = 0; | |
1f6b83e5 | 102 | info.align_offset = pgoff << PAGE_SHIFT; |
9b11c791 MS |
103 | addr = vm_unmapped_area(&info); |
104 | if (addr & ~PAGE_MASK) | |
105 | return addr; | |
106 | ||
107 | check_asce_limit: | |
8ab867cb MS |
108 | if (addr + len > current->mm->context.asce_limit && |
109 | addr + len <= TASK_SIZE) { | |
1aea9b3f | 110 | rc = crst_table_upgrade(mm, addr + len); |
9b11c791 MS |
111 | if (rc) |
112 | return (unsigned long) rc; | |
113 | } | |
114 | ||
115 | return addr; | |
1f6b83e5 MS |
116 | } |
117 | ||
118 | unsigned long | |
119 | arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, | |
120 | const unsigned long len, const unsigned long pgoff, | |
121 | const unsigned long flags) | |
122 | { | |
123 | struct vm_area_struct *vma; | |
124 | struct mm_struct *mm = current->mm; | |
125 | unsigned long addr = addr0; | |
126 | struct vm_unmapped_area_info info; | |
9b11c791 | 127 | int rc; |
1f6b83e5 MS |
128 | |
129 | /* requested length too big for entire address space */ | |
9b11c791 | 130 | if (len > TASK_SIZE - mmap_min_addr) |
1f6b83e5 MS |
131 | return -ENOMEM; |
132 | ||
133 | if (flags & MAP_FIXED) | |
9b11c791 | 134 | goto check_asce_limit; |
1f6b83e5 MS |
135 | |
136 | /* requesting a specific address */ | |
137 | if (addr) { | |
138 | addr = PAGE_ALIGN(addr); | |
139 | vma = find_vma(mm, addr); | |
9b11c791 | 140 | if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && |
1be7107f | 141 | (!vma || addr + len <= vm_start_gap(vma))) |
9b11c791 | 142 | goto check_asce_limit; |
1f6b83e5 MS |
143 | } |
144 | ||
1f6b83e5 MS |
145 | info.flags = VM_UNMAPPED_AREA_TOPDOWN; |
146 | info.length = len; | |
147 | info.low_limit = max(PAGE_SIZE, mmap_min_addr); | |
148 | info.high_limit = mm->mmap_base; | |
c7e8b2c2 MS |
149 | if (filp || (flags & MAP_SHARED)) |
150 | info.align_mask = MMAP_ALIGN_MASK << PAGE_SHIFT; | |
151 | else | |
152 | info.align_mask = 0; | |
1f6b83e5 MS |
153 | info.align_offset = pgoff << PAGE_SHIFT; |
154 | addr = vm_unmapped_area(&info); | |
155 | ||
156 | /* | |
157 | * A failed mmap() very likely causes application failure, | |
158 | * so fall back to the bottom-up function here. This scenario | |
159 | * can happen with large stack limits and large mmap() | |
160 | * allocations. | |
161 | */ | |
162 | if (addr & ~PAGE_MASK) { | |
163 | VM_BUG_ON(addr != -ENOMEM); | |
164 | info.flags = 0; | |
165 | info.low_limit = TASK_UNMAPPED_BASE; | |
9b11c791 | 166 | info.high_limit = TASK_SIZE; |
1f6b83e5 | 167 | addr = vm_unmapped_area(&info); |
9b11c791 MS |
168 | if (addr & ~PAGE_MASK) |
169 | return addr; | |
1f6b83e5 MS |
170 | } |
171 | ||
9b11c791 | 172 | check_asce_limit: |
8ab867cb MS |
173 | if (addr + len > current->mm->context.asce_limit && |
174 | addr + len <= TASK_SIZE) { | |
1aea9b3f | 175 | rc = crst_table_upgrade(mm, addr + len); |
6252d702 MS |
176 | if (rc) |
177 | return (unsigned long) rc; | |
178 | } | |
6252d702 | 179 | |
9b11c791 | 180 | return addr; |
6252d702 | 181 | } |
9b11c791 | 182 | |
6252d702 MS |
183 | /* |
184 | * This function, called very early during the creation of a new | |
185 | * process VM image, sets up which VM layout function to use: | |
186 | */ | |
187 | void arch_pick_mmap_layout(struct mm_struct *mm) | |
188 | { | |
8e89a356 KC |
189 | unsigned long random_factor = 0UL; |
190 | ||
191 | if (current->flags & PF_RANDOMIZE) | |
2b68f6ca | 192 | random_factor = arch_mmap_rnd(); |
8e89a356 | 193 | |
6252d702 MS |
194 | /* |
195 | * Fall back to the standard layout if the personality | |
196 | * bit is set, or if the expected stack growth is unlimited: | |
197 | */ | |
198 | if (mmap_is_legacy()) { | |
8e89a356 | 199 | mm->mmap_base = mmap_base_legacy(random_factor); |
9b11c791 | 200 | mm->get_unmapped_area = arch_get_unmapped_area; |
6252d702 | 201 | } else { |
8e89a356 | 202 | mm->mmap_base = mmap_base(random_factor); |
9b11c791 | 203 | mm->get_unmapped_area = arch_get_unmapped_area_topdown; |
6252d702 MS |
204 | } |
205 | } |