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