Branch data Line data Source code
1 : : /*
2 : : * mm/mprotect.c
3 : : *
4 : : * (C) Copyright 1994 Linus Torvalds
5 : : * (C) Copyright 2002 Christoph Hellwig
6 : : *
7 : : * Address space accounting code <alan@lxorguk.ukuu.org.uk>
8 : : * (C) Copyright 2002 Red Hat Inc, All Rights Reserved
9 : : */
10 : :
11 : : #include <linux/mm.h>
12 : : #include <linux/hugetlb.h>
13 : : #include <linux/shm.h>
14 : : #include <linux/mman.h>
15 : : #include <linux/fs.h>
16 : : #include <linux/highmem.h>
17 : : #include <linux/security.h>
18 : : #include <linux/mempolicy.h>
19 : : #include <linux/personality.h>
20 : : #include <linux/syscalls.h>
21 : : #include <linux/swap.h>
22 : : #include <linux/swapops.h>
23 : : #include <linux/mmu_notifier.h>
24 : : #include <linux/migrate.h>
25 : : #include <linux/perf_event.h>
26 : : #include <asm/uaccess.h>
27 : : #include <asm/pgtable.h>
28 : : #include <asm/cacheflush.h>
29 : : #include <asm/tlbflush.h>
30 : :
31 : : #ifndef pgprot_modify
32 : : static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
33 : : {
34 : : return newprot;
35 : : }
36 : : #endif
37 : :
38 : 0 : static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
39 : : unsigned long addr, unsigned long end, pgprot_t newprot,
40 : : int dirty_accountable, int prot_numa)
41 : : {
42 : : struct mm_struct *mm = vma->vm_mm;
43 : : pte_t *pte, oldpte;
44 : : spinlock_t *ptl;
45 : : unsigned long pages = 0;
46 : :
47 : 590088 : pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
48 : : arch_enter_lazy_mmu_mode();
49 : : do {
50 : 2106379 : oldpte = *pte;
51 [ + + ]: 2106379 : if (pte_present(oldpte)) {
52 : : pte_t ptent;
53 : : bool updated = false;
54 : :
55 [ + - ]: 469904 : if (!prot_numa) {
56 : : ptent = ptep_modify_prot_start(mm, addr, pte);
57 : : if (pte_numa(ptent))
58 : : ptent = pte_mknonnuma(ptent);
59 : : ptent = pte_modify(ptent, newprot);
60 : : updated = true;
61 : : } else {
62 : : struct page *page;
63 : :
64 : : ptent = *pte;
65 : 0 : page = vm_normal_page(vma, addr, oldpte);
66 [ # # ]: 0 : if (page) {
67 : : if (!pte_numa(oldpte)) {
68 : : ptent = pte_mknuma(ptent);
69 : : set_pte_at(mm, addr, pte, ptent);
70 : : updated = true;
71 : : }
72 : : }
73 : : }
74 : :
75 : : /*
76 : : * Avoid taking write faults for pages we know to be
77 : : * dirty.
78 : : */
79 [ - + ][ # # ]: 1059972 : if (dirty_accountable && pte_dirty(ptent)) {
80 : : ptent = pte_mkwrite(ptent);
81 : : updated = true;
82 : : }
83 : :
84 [ + ]: 1059972 : if (updated)
85 : 469904 : pages++;
86 : :
87 : : /* Only !prot_numa always clears the pte */
88 [ + ]: 469884 : if (!prot_numa)
89 : : ptep_modify_prot_commit(mm, addr, pte, ptent);
90 [ + + ]: 1636475 : } else if (IS_ENABLED(CONFIG_MIGRATION) && !pte_file(oldpte)) {
91 : : swp_entry_t entry = pte_to_swp_entry(oldpte);
92 : :
93 [ - + ]: 1636457 : if (is_write_migration_entry(entry)) {
94 : : pte_t newpte;
95 : : /*
96 : : * A protection check is difficult so
97 : : * just be safe and disable write
98 : : */
99 : : make_migration_entry_read(&entry);
100 : : newpte = swp_entry_to_pte(entry);
101 : : if (pte_swp_soft_dirty(oldpte))
102 : : newpte = pte_swp_mksoft_dirty(newpte);
103 : : set_pte_at(mm, addr, pte, newpte);
104 : :
105 : 0 : pages++;
106 : : }
107 : : }
108 [ + + ]: 2106359 : } while (pte++, addr += PAGE_SIZE, addr != end);
109 : : arch_leave_lazy_mmu_mode();
110 : 590107 : pte_unmap_unlock(pte - 1, ptl);
111 : :
112 : 590114 : return pages;
113 : : }
114 : :
115 : : static inline unsigned long change_pmd_range(struct vm_area_struct *vma,
116 : : pud_t *pud, unsigned long addr, unsigned long end,
117 : : pgprot_t newprot, int dirty_accountable, int prot_numa)
118 : : {
119 : : pmd_t *pmd;
120 : : unsigned long next;
121 : : unsigned long pages = 0;
122 : : unsigned long nr_huge_updates = 0;
123 : :
124 : : pmd = pmd_offset(pud, addr);
125 : : do {
126 : : unsigned long this_pages;
127 : :
128 : : next = pmd_addr_end(addr, end);
129 : : if (pmd_trans_huge(*pmd)) {
130 : : if (next - addr != HPAGE_PMD_SIZE)
131 : : split_huge_page_pmd(vma, addr, pmd);
132 : : else {
133 : : int nr_ptes = change_huge_pmd(vma, pmd, addr,
134 : : newprot, prot_numa);
135 : :
136 : : if (nr_ptes) {
137 : : if (nr_ptes == HPAGE_PMD_NR) {
138 : : pages += HPAGE_PMD_NR;
139 : : nr_huge_updates++;
140 : : }
141 : : continue;
142 : : }
143 : : }
144 : : /* fall through */
145 : : }
146 [ + + ]: 602963 : if (pmd_none_or_clear_bad(pmd))
147 : 12874 : continue;
148 : 590089 : this_pages = change_pte_range(vma, pmd, addr, next, newprot,
149 : : dirty_accountable, prot_numa);
150 : : pages += this_pages;
151 : : } while (pmd++, addr = next, addr != end);
152 : :
153 : : if (nr_huge_updates)
154 : : count_vm_numa_events(NUMA_HUGE_PTE_UPDATES, nr_huge_updates);
155 : : return pages;
156 : : }
157 : :
158 : : static inline unsigned long change_pud_range(struct vm_area_struct *vma,
159 : : pgd_t *pgd, unsigned long addr, unsigned long end,
160 : : pgprot_t newprot, int dirty_accountable, int prot_numa)
161 : : {
162 : : pud_t *pud;
163 : : unsigned long next;
164 : : unsigned long pages = 0;
165 : :
166 : : pud = pud_offset(pgd, addr);
167 : : do {
168 : : next = pud_addr_end(addr, end);
169 : : if (pud_none_or_clear_bad(pud))
170 : : continue;
171 : : pages += change_pmd_range(vma, pud, addr, next, newprot,
172 : : dirty_accountable, prot_numa);
173 : : } while (pud++, addr = next, addr != end);
174 : :
175 : : return pages;
176 : : }
177 : :
178 : 0 : static unsigned long change_protection_range(struct vm_area_struct *vma,
179 : : unsigned long addr, unsigned long end, pgprot_t newprot,
180 : : int dirty_accountable, int prot_numa)
181 : : {
182 : 601116 : struct mm_struct *mm = vma->vm_mm;
183 : : pgd_t *pgd;
184 : : unsigned long next;
185 : : unsigned long start = addr;
186 : : unsigned long pages = 0;
187 : :
188 [ - + ]: 601116 : BUG_ON(addr >= end);
189 : 601116 : pgd = pgd_offset(mm, addr);
190 : 601116 : flush_cache_range(vma, addr, end);
191 : : set_tlb_flush_pending(mm);
192 : : do {
193 [ + + ]: 1204079 : next = pgd_addr_end(addr, end);
194 : : if (pgd_none_or_clear_bad(pgd))
195 : : continue;
196 : 602967 : pages += change_pud_range(vma, pgd, addr, next, newprot,
197 : : dirty_accountable, prot_numa);
198 [ + + ]: 602967 : } while (pgd++, addr = next, addr != end);
199 : :
200 : : /* Only flush the TLB if we actually modified any entries: */
201 [ + + ]: 601097 : if (pages)
202 : 358252 : flush_tlb_range(vma, start, end);
203 : : clear_tlb_flush_pending(mm);
204 : :
205 : 601096 : return pages;
206 : : }
207 : :
208 : 0 : unsigned long change_protection(struct vm_area_struct *vma, unsigned long start,
209 : : unsigned long end, pgprot_t newprot,
210 : : int dirty_accountable, int prot_numa)
211 : : {
212 : : struct mm_struct *mm = vma->vm_mm;
213 : : unsigned long pages;
214 : :
215 : : mmu_notifier_invalidate_range_start(mm, start, end);
216 : : if (is_vm_hugetlb_page(vma))
217 : : pages = hugetlb_change_protection(vma, start, end, newprot);
218 : : else
219 : 601112 : pages = change_protection_range(vma, start, end, newprot, dirty_accountable, prot_numa);
220 : : mmu_notifier_invalidate_range_end(mm, start, end);
221 : :
222 : 0 : return pages;
223 : : }
224 : :
225 : : int
226 : 0 : mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
227 : : unsigned long start, unsigned long end, unsigned long newflags)
228 : : {
229 : 660092 : struct mm_struct *mm = vma->vm_mm;
230 : 660092 : unsigned long oldflags = vma->vm_flags;
231 : 660092 : long nrpages = (end - start) >> PAGE_SHIFT;
232 : : unsigned long charged = 0;
233 : : pgoff_t pgoff;
234 : : int error;
235 : : int dirty_accountable = 0;
236 : :
237 [ + + ]: 660092 : if (newflags == oldflags) {
238 : 58976 : *pprev = vma;
239 : 58976 : return 0;
240 : : }
241 : :
242 : : /*
243 : : * If we make a private mapping writable we increase our commit;
244 : : * but (without finer accounting) cannot reduce our commit if we
245 : : * make it unwritable again. hugetlb mapping were accounted for
246 : : * even if read-only so there is no need to account for them here
247 : : */
248 [ + + ]: 601116 : if (newflags & VM_WRITE) {
249 [ - + ]: 890 : if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_HUGETLB|
250 : : VM_SHARED|VM_NORESERVE))) {
251 : : charged = nrpages;
252 [ # # ]: 0 : if (security_vm_enough_memory_mm(mm, charged))
253 : : return -ENOMEM;
254 : 0 : newflags |= VM_ACCOUNT;
255 : : }
256 : : }
257 : :
258 : : /*
259 : : * First try to merge with previous and/or next vma.
260 : : */
261 : 601116 : pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
262 : 601116 : *pprev = vma_merge(mm, *pprev, start, end, newflags,
263 : : vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma),
264 : : vma_get_anon_name(vma));
265 [ + + ]: 601113 : if (*pprev) {
266 : : vma = *pprev;
267 : : goto success;
268 : : }
269 : :
270 : 600486 : *pprev = vma;
271 : :
272 [ + + ]: 600486 : if (start != vma->vm_start) {
273 : 240045 : error = split_vma(mm, vma, start, 1);
274 [ + ]: 240049 : if (error)
275 : : goto fail;
276 : : }
277 : :
278 [ + + ]: 600500 : if (end != vma->vm_end) {
279 : 600360 : error = split_vma(mm, vma, end, 0);
280 [ + - ]: 600391 : if (error)
281 : : goto fail;
282 : : }
283 : :
284 : : success:
285 : : /*
286 : : * vm_flags and vm_page_prot are protected by the mmap_sem
287 : : * held in write mode.
288 : : */
289 : 601158 : vma->vm_flags = newflags;
290 : 601158 : vma->vm_page_prot = pgprot_modify(vma->vm_page_prot,
291 : : vm_get_page_prot(newflags));
292 : :
293 [ + + ]: 601114 : if (vma_wants_writenotify(vma)) {
294 : 1 : vma->vm_page_prot = vm_get_page_prot(newflags & ~VM_SHARED);
295 : : dirty_accountable = 1;
296 : : }
297 : :
298 : 601112 : change_protection(vma, start, end, vma->vm_page_prot,
299 : : dirty_accountable, 0);
300 : :
301 : 601099 : vm_stat_account(mm, oldflags, vma->vm_file, -nrpages);
302 : 601113 : vm_stat_account(mm, newflags, vma->vm_file, nrpages);
303 : 601113 : perf_event_mmap(vma);
304 : 601114 : return 0;
305 : :
306 : : fail:
307 : 0 : vm_unacct_memory(charged);
308 : 0 : return error;
309 : : }
310 : :
311 : 0 : SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
312 : : unsigned long, prot)
313 : : {
314 : : unsigned long vm_flags, nstart, end, tmp, reqprot;
315 : : struct vm_area_struct *vma, *prev;
316 : : int error = -EINVAL;
317 : 601110 : const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP);
318 : 601110 : prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP);
319 [ + ]: 601110 : if (grows == (PROT_GROWSDOWN|PROT_GROWSUP)) /* can't be both */
320 : : return -EINVAL;
321 : :
322 [ + + ]: 601111 : if (start & ~PAGE_MASK)
323 : : return -EINVAL;
324 [ + + ]: 601096 : if (!len)
325 : : return 0;
326 : 601091 : len = PAGE_ALIGN(len);
327 : 601091 : end = start + len;
328 [ + + ]: 601091 : if (end <= start)
329 : : return -ENOMEM;
330 [ + + ]: 601087 : if (!arch_validate_prot(prot))
331 : : return -EINVAL;
332 : :
333 : : reqprot = prot;
334 : : /*
335 : : * Does the application expect PROT_READ to imply PROT_EXEC:
336 : : */
337 [ + + ][ - + ]: 601081 : if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
338 : 0 : prot |= PROT_EXEC;
339 : :
340 : : vm_flags = calc_vm_prot_bits(prot);
341 : :
342 : 601081 : down_write(¤t->mm->mmap_sem);
343 : :
344 : 601113 : vma = find_vma(current->mm, start);
345 : : error = -ENOMEM;
346 [ + + ]: 601101 : if (!vma)
347 : : goto out;
348 : 601097 : prev = vma->vm_prev;
349 [ - + ]: 601097 : if (unlikely(grows & PROT_GROWSDOWN)) {
350 [ # # ]: 0 : if (vma->vm_start >= end)
351 : : goto out;
352 : : start = vma->vm_start;
353 : : error = -EINVAL;
354 [ # # ]: 0 : if (!(vma->vm_flags & VM_GROWSDOWN))
355 : : goto out;
356 : : } else {
357 [ + - ]: 601097 : if (vma->vm_start > start)
358 : : goto out;
359 [ + + ]: 601097 : if (unlikely(grows & PROT_GROWSUP)) {
360 : : end = vma->vm_end;
361 : : error = -EINVAL;
362 : : if (!(vma->vm_flags & VM_GROWSUP))
363 : : goto out;
364 : : }
365 : : }
366 [ + + ]: 601080 : if (start > vma->vm_start)
367 : 601080 : prev = vma;
368 : :
369 : : for (nstart = start ; ; ) {
370 : : unsigned long newflags;
371 : :
372 : : /* Here we know that vma->vm_start <= nstart < vma->vm_end. */
373 : :
374 : : newflags = vm_flags;
375 : 601080 : newflags |= (vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC));
376 : :
377 : : /* newflags >> 4 shift VM_MAY% in place of VM_% */
378 [ + ]: 601080 : if ((newflags & ~(newflags >> 4)) & (VM_READ | VM_WRITE | VM_EXEC)) {
379 : : error = -EACCES;
380 : : goto out;
381 : : }
382 : :
383 : 601102 : error = security_file_mprotect(vma, reqprot, prot);
384 [ + ]: 601103 : if (error)
385 : : goto out;
386 : :
387 : 601105 : tmp = vma->vm_end;
388 [ + + ]: 601105 : if (tmp > end)
389 : : tmp = end;
390 : 601105 : error = mprotect_fixup(vma, &prev, nstart, tmp, newflags);
391 [ + ]: 601113 : if (error)
392 : : goto out;
393 : : nstart = tmp;
394 : :
395 [ - + ]: 601117 : if (nstart < prev->vm_end)
396 : : nstart = prev->vm_end;
397 [ - + ]: 601117 : if (nstart >= end)
398 : : goto out;
399 : :
400 : 0 : vma = prev->vm_next;
401 [ # # ][ # # ]: 0 : if (!vma || vma->vm_start != nstart) {
402 : : error = -ENOMEM;
403 : : goto out;
404 : : }
405 : : }
406 : : out:
407 : 601110 : up_write(¤t->mm->mmap_sem);
408 : : return error;
409 : : }
|