LCOV - code coverage report
Current view: top level - mm - mprotect.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 202 299 67.6 %
Date: 2022-04-01 14:58:12 Functions: 10 22 45.5 %
Branches: 85 192 44.3 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : /*
       3                 :            :  *  mm/mprotect.c
       4                 :            :  *
       5                 :            :  *  (C) Copyright 1994 Linus Torvalds
       6                 :            :  *  (C) Copyright 2002 Christoph Hellwig
       7                 :            :  *
       8                 :            :  *  Address space accounting code       <alan@lxorguk.ukuu.org.uk>
       9                 :            :  *  (C) Copyright 2002 Red Hat Inc, All Rights Reserved
      10                 :            :  */
      11                 :            : 
      12                 :            : #include <linux/pagewalk.h>
      13                 :            : #include <linux/hugetlb.h>
      14                 :            : #include <linux/shm.h>
      15                 :            : #include <linux/mman.h>
      16                 :            : #include <linux/fs.h>
      17                 :            : #include <linux/highmem.h>
      18                 :            : #include <linux/security.h>
      19                 :            : #include <linux/mempolicy.h>
      20                 :            : #include <linux/personality.h>
      21                 :            : #include <linux/syscalls.h>
      22                 :            : #include <linux/swap.h>
      23                 :            : #include <linux/swapops.h>
      24                 :            : #include <linux/mmu_notifier.h>
      25                 :            : #include <linux/migrate.h>
      26                 :            : #include <linux/perf_event.h>
      27                 :            : #include <linux/pkeys.h>
      28                 :            : #include <linux/ksm.h>
      29                 :            : #include <linux/uaccess.h>
      30                 :            : #include <linux/mm_inline.h>
      31                 :            : #include <asm/pgtable.h>
      32                 :            : #include <asm/cacheflush.h>
      33                 :            : #include <asm/mmu_context.h>
      34                 :            : #include <asm/tlbflush.h>
      35                 :            : 
      36                 :            : #include "internal.h"
      37                 :            : 
      38                 :      17954 : 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                 :      17954 :         pte_t *pte, oldpte;
      43                 :      17954 :         spinlock_t *ptl;
      44                 :      17954 :         unsigned long pages = 0;
      45                 :      17954 :         int target_node = NUMA_NO_NODE;
      46                 :            : 
      47                 :            :         /*
      48                 :            :          * Can be called with only the mmap_sem for reading by
      49                 :            :          * prot_numa so we must check the pmd isn't constantly
      50                 :            :          * changing from under us from pmd_none to pmd_trans_huge
      51                 :            :          * and/or the other way around.
      52                 :            :          */
      53         [ +  - ]:      17954 :         if (pmd_trans_unstable(pmd))
      54                 :            :                 return 0;
      55                 :            : 
      56                 :            :         /*
      57                 :            :          * The pmd points to a regular pte so the pmd can't change
      58                 :            :          * from under us even if the mmap_sem is only hold for
      59                 :            :          * reading.
      60                 :            :          */
      61         [ +  - ]:      35908 :         pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
      62                 :            : 
      63                 :            :         /* Get target node for single threaded private VMAs */
      64   [ -  +  -  -  :      17954 :         if (prot_numa && !(vma->vm_flags & VM_SHARED) &&
                   -  - ]
      65                 :          0 :             atomic_read(&vma->vm_mm->mm_users) == 1)
      66                 :          0 :                 target_node = numa_node_id();
      67                 :            : 
      68                 :      17954 :         flush_tlb_batched_pending(vma->vm_mm);
      69                 :     932137 :         arch_enter_lazy_mmu_mode();
      70                 :     932137 :         do {
      71                 :     932137 :                 oldpte = *pte;
      72         [ +  + ]:     932137 :                 if (pte_present(oldpte)) {
      73                 :      32641 :                         pte_t ptent;
      74   [ -  +  -  - ]:      32641 :                         bool preserve_write = prot_numa && pte_write(oldpte);
      75                 :            : 
      76                 :            :                         /*
      77                 :            :                          * Avoid trapping faults against the zero or KSM
      78                 :            :                          * pages. See similar comment in change_huge_pmd.
      79                 :            :                          */
      80         [ -  + ]:      32641 :                         if (prot_numa) {
      81                 :          0 :                                 struct page *page;
      82                 :            : 
      83                 :            :                                 /* Avoid TLB flush if possible */
      84                 :          0 :                                 if (pte_protnone(oldpte))
      85                 :            :                                         continue;
      86                 :            : 
      87                 :          0 :                                 page = vm_normal_page(vma, addr, oldpte);
      88         [ #  # ]:          0 :                                 if (!page || PageKsm(page))
      89                 :          0 :                                         continue;
      90                 :            : 
      91                 :            :                                 /* Also skip shared copy-on-write pages */
      92   [ #  #  #  # ]:          0 :                                 if (is_cow_mapping(vma->vm_flags) &&
      93                 :          0 :                                     page_mapcount(page) != 1)
      94                 :          0 :                                         continue;
      95                 :            : 
      96                 :            :                                 /*
      97                 :            :                                  * While migration can move some dirty pages,
      98                 :            :                                  * it cannot move them all from MIGRATE_ASYNC
      99                 :            :                                  * context.
     100                 :            :                                  */
     101   [ #  #  #  # ]:          0 :                                 if (page_is_file_cache(page) && PageDirty(page))
     102                 :          0 :                                         continue;
     103                 :            : 
     104                 :            :                                 /*
     105                 :            :                                  * Don't mess with PTEs if page is already on the node
     106                 :            :                                  * a single-threaded process is running on.
     107                 :            :                                  */
     108         [ #  # ]:          0 :                                 if (target_node == page_to_nid(page))
     109                 :          0 :                                         continue;
     110                 :            :                         }
     111                 :            : 
     112                 :      32641 :                         oldpte = ptep_modify_prot_start(vma, addr, pte);
     113                 :      32641 :                         ptent = pte_modify(oldpte, newprot);
     114         [ -  + ]:      32641 :                         if (preserve_write)
     115                 :          0 :                                 ptent = pte_mk_savedwrite(ptent);
     116                 :            : 
     117                 :            :                         /* Avoid taking write faults for known dirty pages */
     118   [ -  +  -  - ]:      32641 :                         if (dirty_accountable && pte_dirty(ptent) &&
     119                 :            :                                         (pte_soft_dirty(ptent) ||
     120                 :            :                                          !(vma->vm_flags & VM_SOFTDIRTY))) {
     121                 :          0 :                                 ptent = pte_mkwrite(ptent);
     122                 :            :                         }
     123                 :      32641 :                         ptep_modify_prot_commit(vma, addr, pte, oldpte, ptent);
     124                 :      32641 :                         pages++;
     125                 :     899496 :                 } else if (IS_ENABLED(CONFIG_MIGRATION)) {
     126         [ -  + ]:     899496 :                         swp_entry_t entry = pte_to_swp_entry(oldpte);
     127                 :            : 
     128         [ -  + ]:     899496 :                         if (is_write_migration_entry(entry)) {
     129                 :          0 :                                 pte_t newpte;
     130                 :            :                                 /*
     131                 :            :                                  * A protection check is difficult so
     132                 :            :                                  * just be safe and disable write
     133                 :            :                                  */
     134                 :          0 :                                 make_migration_entry_read(&entry);
     135                 :          0 :                                 newpte = swp_entry_to_pte(entry);
     136                 :          0 :                                 if (pte_swp_soft_dirty(oldpte))
     137                 :            :                                         newpte = pte_swp_mksoft_dirty(newpte);
     138                 :          0 :                                 set_pte_at(vma->vm_mm, addr, pte, newpte);
     139                 :            : 
     140                 :          0 :                                 pages++;
     141                 :            :                         }
     142                 :            : 
     143                 :            :                         if (is_write_device_private_entry(entry)) {
     144                 :            :                                 pte_t newpte;
     145                 :            : 
     146                 :            :                                 /*
     147                 :            :                                  * We do not preserve soft-dirtiness. See
     148                 :            :                                  * copy_one_pte() for explanation.
     149                 :            :                                  */
     150                 :            :                                 make_device_private_entry_read(&entry);
     151                 :            :                                 newpte = swp_entry_to_pte(entry);
     152                 :            :                                 set_pte_at(vma->vm_mm, addr, pte, newpte);
     153                 :            : 
     154                 :            :                                 pages++;
     155                 :            :                         }
     156                 :            :                 }
     157         [ +  + ]:     932137 :         } while (pte++, addr += PAGE_SIZE, addr != end);
     158                 :      17954 :         arch_leave_lazy_mmu_mode();
     159                 :      17954 :         pte_unmap_unlock(pte - 1, ptl);
     160                 :            : 
     161                 :      17954 :         return pages;
     162                 :            : }
     163                 :            : 
     164                 :            : /*
     165                 :            :  * Used when setting automatic NUMA hinting protection where it is
     166                 :            :  * critical that a numa hinting PMD is not confused with a bad PMD.
     167                 :            :  */
     168                 :      21238 : static inline int pmd_none_or_clear_bad_unless_trans_huge(pmd_t *pmd)
     169                 :            : {
     170         [ +  + ]:      21238 :         pmd_t pmdval = pmd_read_atomic(pmd);
     171                 :            : 
     172                 :            :         /* See pmd_none_or_trans_huge_or_clear_bad for info on barrier */
     173                 :            : #ifdef CONFIG_TRANSPARENT_HUGEPAGE
     174                 :            :         barrier();
     175                 :            : #endif
     176                 :            : 
     177         [ +  + ]:      21238 :         if (pmd_none(pmdval))
     178                 :            :                 return 1;
     179         [ +  - ]:      17954 :         if (pmd_trans_huge(pmdval))
     180                 :            :                 return 0;
     181   [ +  -  -  + ]:      35908 :         if (unlikely(pmd_bad(pmdval))) {
     182                 :          0 :                 pmd_clear_bad(pmd);
     183                 :          0 :                 return 1;
     184                 :            :         }
     185                 :            : 
     186                 :            :         return 0;
     187                 :            : }
     188                 :            : 
     189                 :      18016 : static inline unsigned long change_pmd_range(struct vm_area_struct *vma,
     190                 :            :                 pud_t *pud, unsigned long addr, unsigned long end,
     191                 :            :                 pgprot_t newprot, int dirty_accountable, int prot_numa)
     192                 :            : {
     193                 :      18016 :         pmd_t *pmd;
     194                 :      18016 :         unsigned long next;
     195                 :      18016 :         unsigned long pages = 0;
     196                 :      18016 :         unsigned long nr_huge_updates = 0;
     197                 :      18016 :         struct mmu_notifier_range range;
     198                 :            : 
     199                 :      18016 :         range.start = 0;
     200                 :            : 
     201         [ +  - ]:      18016 :         pmd = pmd_offset(pud, addr);
     202                 :      21238 :         do {
     203                 :      21238 :                 unsigned long this_pages;
     204                 :            : 
     205         [ +  + ]:      21238 :                 next = pmd_addr_end(addr, end);
     206                 :            : 
     207                 :            :                 /*
     208                 :            :                  * Automatic NUMA balancing walks the tables with mmap_sem
     209                 :            :                  * held for read. It's possible a parallel update to occur
     210                 :            :                  * between pmd_trans_huge() and a pmd_none_or_clear_bad()
     211                 :            :                  * check leading to a false positive and clearing.
     212                 :            :                  * Hence, it's necessary to atomically read the PMD value
     213                 :            :                  * for all the checks.
     214                 :            :                  */
     215         [ +  + ]:      21238 :                 if (!is_swap_pmd(*pmd) && !pmd_devmap(*pmd) &&
     216                 :      21238 :                      pmd_none_or_clear_bad_unless_trans_huge(pmd))
     217                 :       3284 :                         goto next;
     218                 :            : 
     219                 :            :                 /* invoke the mmu notifier if the pmd is populated */
     220         [ +  + ]:      17954 :                 if (!range.start) {
     221                 :      17902 :                         mmu_notifier_range_init(&range,
     222                 :            :                                 MMU_NOTIFY_PROTECTION_VMA, 0,
     223                 :            :                                 vma, vma->vm_mm, addr, end);
     224                 :      17902 :                         mmu_notifier_invalidate_range_start(&range);
     225                 :            :                 }
     226                 :            : 
     227                 :      17954 :                 if (is_swap_pmd(*pmd) || pmd_trans_huge(*pmd) || pmd_devmap(*pmd)) {
     228                 :            :                         if (next - addr != HPAGE_PMD_SIZE) {
     229                 :            :                                 __split_huge_pmd(vma, pmd, addr, false, NULL);
     230                 :            :                         } else {
     231                 :            :                                 int nr_ptes = change_huge_pmd(vma, pmd, addr,
     232                 :            :                                                 newprot, prot_numa);
     233                 :            : 
     234                 :            :                                 if (nr_ptes) {
     235                 :            :                                         if (nr_ptes == HPAGE_PMD_NR) {
     236                 :            :                                                 pages += HPAGE_PMD_NR;
     237                 :            :                                                 nr_huge_updates++;
     238                 :            :                                         }
     239                 :            : 
     240                 :            :                                         /* huge pmd was handled */
     241                 :            :                                         goto next;
     242                 :            :                                 }
     243                 :            :                         }
     244                 :            :                         /* fall through, the trans huge pmd just split */
     245                 :            :                 }
     246                 :      17954 :                 this_pages = change_pte_range(vma, pmd, addr, next, newprot,
     247                 :            :                                  dirty_accountable, prot_numa);
     248                 :      17954 :                 pages += this_pages;
     249                 :      21238 : next:
     250                 :      21238 :                 cond_resched();
     251         [ +  + ]:      21238 :         } while (pmd++, addr = next, addr != end);
     252                 :            : 
     253         [ +  + ]:      18016 :         if (range.start)
     254                 :      17902 :                 mmu_notifier_invalidate_range_end(&range);
     255                 :            : 
     256                 :      18016 :         if (nr_huge_updates)
     257                 :      18016 :                 count_vm_numa_events(NUMA_HUGE_PTE_UPDATES, nr_huge_updates);
     258                 :      18016 :         return pages;
     259                 :            : }
     260                 :            : 
     261                 :      18017 : static inline unsigned long change_pud_range(struct vm_area_struct *vma,
     262                 :            :                 p4d_t *p4d, unsigned long addr, unsigned long end,
     263                 :            :                 pgprot_t newprot, int dirty_accountable, int prot_numa)
     264                 :            : {
     265                 :      18017 :         pud_t *pud;
     266                 :      18017 :         unsigned long next;
     267                 :      18017 :         unsigned long pages = 0;
     268                 :            : 
     269                 :      18017 :         pud = pud_offset(p4d, addr);
     270                 :      18025 :         do {
     271         [ +  + ]:      18025 :                 next = pud_addr_end(addr, end);
     272         [ +  + ]:      18025 :                 if (pud_none_or_clear_bad(pud))
     273                 :          9 :                         continue;
     274                 :      18016 :                 pages += change_pmd_range(vma, pud, addr, next, newprot,
     275                 :            :                                  dirty_accountable, prot_numa);
     276         [ +  + ]:      18025 :         } while (pud++, addr = next, addr != end);
     277                 :            : 
     278                 :      18017 :         return pages;
     279                 :            : }
     280                 :            : 
     281                 :      18017 : static inline unsigned long change_p4d_range(struct vm_area_struct *vma,
     282                 :            :                 pgd_t *pgd, unsigned long addr, unsigned long end,
     283                 :            :                 pgprot_t newprot, int dirty_accountable, int prot_numa)
     284                 :            : {
     285                 :      18017 :         p4d_t *p4d;
     286                 :      18017 :         unsigned long next;
     287                 :      18017 :         unsigned long pages = 0;
     288                 :            : 
     289                 :      18017 :         p4d = p4d_offset(pgd, addr);
     290                 :      18017 :         do {
     291         [ -  + ]:      18017 :                 next = p4d_addr_end(addr, end);
     292         [ +  - ]:      18017 :                 if (p4d_none_or_clear_bad(p4d))
     293                 :          0 :                         continue;
     294                 :      18017 :                 pages += change_pud_range(vma, p4d, addr, next, newprot,
     295                 :            :                                  dirty_accountable, prot_numa);
     296         [ -  + ]:      18017 :         } while (p4d++, addr = next, addr != end);
     297                 :            : 
     298                 :      18017 :         return pages;
     299                 :            : }
     300                 :            : 
     301                 :      18017 : static unsigned long change_protection_range(struct vm_area_struct *vma,
     302                 :            :                 unsigned long addr, unsigned long end, pgprot_t newprot,
     303                 :            :                 int dirty_accountable, int prot_numa)
     304                 :            : {
     305                 :      18017 :         struct mm_struct *mm = vma->vm_mm;
     306                 :      18017 :         pgd_t *pgd;
     307                 :      18017 :         unsigned long next;
     308                 :      18017 :         unsigned long start = addr;
     309                 :      18017 :         unsigned long pages = 0;
     310                 :            : 
     311         [ -  + ]:      18017 :         BUG_ON(addr >= end);
     312                 :      18017 :         pgd = pgd_offset(mm, addr);
     313                 :      18017 :         flush_cache_range(vma, addr, end);
     314                 :      18017 :         inc_tlb_flush_pending(mm);
     315                 :      18017 :         do {
     316         [ +  - ]:      18017 :                 next = pgd_addr_end(addr, end);
     317         [ -  + ]:      18017 :                 if (pgd_none_or_clear_bad(pgd))
     318                 :          0 :                         continue;
     319                 :      18017 :                 pages += change_p4d_range(vma, pgd, addr, next, newprot,
     320                 :            :                                  dirty_accountable, prot_numa);
     321         [ -  + ]:      18017 :         } while (pgd++, addr = next, addr != end);
     322                 :            : 
     323                 :            :         /* Only flush the TLB if we actually modified any entries: */
     324         [ +  + ]:      18017 :         if (pages)
     325         [ -  + ]:      13464 :                 flush_tlb_range(vma, start, end);
     326                 :      18017 :         dec_tlb_flush_pending(mm);
     327                 :            : 
     328                 :      18017 :         return pages;
     329                 :            : }
     330                 :            : 
     331                 :      18017 : unsigned long change_protection(struct vm_area_struct *vma, unsigned long start,
     332                 :            :                        unsigned long end, pgprot_t newprot,
     333                 :            :                        int dirty_accountable, int prot_numa)
     334                 :            : {
     335                 :      18017 :         unsigned long pages;
     336                 :            : 
     337         [ -  + ]:      18017 :         if (is_vm_hugetlb_page(vma))
     338                 :          0 :                 pages = hugetlb_change_protection(vma, start, end, newprot);
     339                 :            :         else
     340                 :      18017 :                 pages = change_protection_range(vma, start, end, newprot, dirty_accountable, prot_numa);
     341                 :            : 
     342                 :      18017 :         return pages;
     343                 :            : }
     344                 :            : 
     345                 :          0 : static int prot_none_pte_entry(pte_t *pte, unsigned long addr,
     346                 :            :                                unsigned long next, struct mm_walk *walk)
     347                 :            : {
     348         [ #  # ]:          0 :         return pfn_modify_allowed(pte_pfn(*pte), *(pgprot_t *)(walk->private)) ?
     349         [ #  # ]:          0 :                 0 : -EACCES;
     350                 :            : }
     351                 :            : 
     352                 :          0 : static int prot_none_hugetlb_entry(pte_t *pte, unsigned long hmask,
     353                 :            :                                    unsigned long addr, unsigned long next,
     354                 :            :                                    struct mm_walk *walk)
     355                 :            : {
     356         [ #  # ]:          0 :         return pfn_modify_allowed(pte_pfn(*pte), *(pgprot_t *)(walk->private)) ?
     357         [ #  # ]:          0 :                 0 : -EACCES;
     358                 :            : }
     359                 :            : 
     360                 :          0 : static int prot_none_test(unsigned long addr, unsigned long next,
     361                 :            :                           struct mm_walk *walk)
     362                 :            : {
     363                 :          0 :         return 0;
     364                 :            : }
     365                 :            : 
     366                 :            : static const struct mm_walk_ops prot_none_walk_ops = {
     367                 :            :         .pte_entry              = prot_none_pte_entry,
     368                 :            :         .hugetlb_entry          = prot_none_hugetlb_entry,
     369                 :            :         .test_walk              = prot_none_test,
     370                 :            : };
     371                 :            : 
     372                 :            : int
     373                 :      20492 : mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
     374                 :            :         unsigned long start, unsigned long end, unsigned long newflags)
     375                 :            : {
     376                 :      20492 :         struct mm_struct *mm = vma->vm_mm;
     377                 :      20492 :         unsigned long oldflags = vma->vm_flags;
     378                 :      20492 :         long nrpages = (end - start) >> PAGE_SHIFT;
     379                 :      20492 :         unsigned long charged = 0;
     380                 :      20492 :         pgoff_t pgoff;
     381                 :      20492 :         int error;
     382                 :      20492 :         int dirty_accountable = 0;
     383                 :            : 
     384         [ +  + ]:      20492 :         if (newflags == oldflags) {
     385                 :       2475 :                 *pprev = vma;
     386                 :       2475 :                 return 0;
     387                 :            :         }
     388                 :            : 
     389                 :            :         /*
     390                 :            :          * Do PROT_NONE PFN permission checks here when we can still
     391                 :            :          * bail out without undoing a lot of state. This is a rather
     392                 :            :          * uncommon case, so doesn't need to be very optimized.
     393                 :            :          */
     394         [ -  + ]:      18017 :         if (arch_has_pfn_modify_check() &&
     395         [ #  # ]:          0 :             (vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) &&
     396         [ #  # ]:          0 :             (newflags & (VM_READ|VM_WRITE|VM_EXEC)) == 0) {
     397                 :          0 :                 pgprot_t new_pgprot = vm_get_page_prot(newflags);
     398                 :            : 
     399                 :          0 :                 error = walk_page_range(current->mm, start, end,
     400                 :            :                                 &prot_none_walk_ops, &new_pgprot);
     401         [ #  # ]:          0 :                 if (error)
     402                 :          0 :                         return error;
     403                 :            :         }
     404                 :            : 
     405                 :            :         /*
     406                 :            :          * If we make a private mapping writable we increase our commit;
     407                 :            :          * but (without finer accounting) cannot reduce our commit if we
     408                 :            :          * make it unwritable again. hugetlb mapping were accounted for
     409                 :            :          * even if read-only so there is no need to account for them here
     410                 :            :          */
     411         [ +  + ]:      18017 :         if (newflags & VM_WRITE) {
     412                 :            :                 /* Check space limits when area turns into data. */
     413   [ -  +  -  - ]:        206 :                 if (!may_expand_vm(mm, newflags, nrpages) &&
     414                 :          0 :                                 may_expand_vm(mm, oldflags, nrpages))
     415                 :            :                         return -ENOMEM;
     416         [ +  + ]:        206 :                 if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_HUGETLB|
     417                 :            :                                                 VM_SHARED|VM_NORESERVE))) {
     418                 :         12 :                         charged = nrpages;
     419         [ +  - ]:         12 :                         if (security_vm_enough_memory_mm(mm, charged))
     420                 :            :                                 return -ENOMEM;
     421                 :         12 :                         newflags |= VM_ACCOUNT;
     422                 :            :                 }
     423                 :            :         }
     424                 :            : 
     425                 :            :         /*
     426                 :            :          * First try to merge with previous and/or next vma.
     427                 :            :          */
     428                 :      18017 :         pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
     429                 :      18017 :         *pprev = vma_merge(mm, *pprev, start, end, newflags,
     430                 :            :                            vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma),
     431                 :            :                            vma->vm_userfaultfd_ctx);
     432         [ +  + ]:      18017 :         if (*pprev) {
     433                 :        188 :                 vma = *pprev;
     434                 :        188 :                 VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY);
     435                 :        188 :                 goto success;
     436                 :            :         }
     437                 :            : 
     438                 :      17829 :         *pprev = vma;
     439                 :            : 
     440         [ +  + ]:      17829 :         if (start != vma->vm_start) {
     441                 :       4356 :                 error = split_vma(mm, vma, start, 1);
     442         [ -  + ]:       4356 :                 if (error)
     443                 :          0 :                         goto fail;
     444                 :            :         }
     445                 :            : 
     446         [ +  - ]:      17829 :         if (end != vma->vm_end) {
     447                 :      17829 :                 error = split_vma(mm, vma, end, 0);
     448         [ +  - ]:      17829 :                 if (error)
     449                 :          0 :                         goto fail;
     450                 :            :         }
     451                 :            : 
     452                 :      17829 : success:
     453                 :            :         /*
     454                 :            :          * vm_flags and vm_page_prot are protected by the mmap_sem
     455                 :            :          * held in write mode.
     456                 :            :          */
     457                 :      18017 :         vma->vm_flags = newflags;
     458                 :      18017 :         dirty_accountable = vma_wants_writenotify(vma, vma->vm_page_prot);
     459                 :      18017 :         vma_set_page_prot(vma);
     460                 :            : 
     461                 :      18017 :         change_protection(vma, start, end, vma->vm_page_prot,
     462                 :            :                           dirty_accountable, 0);
     463                 :            : 
     464                 :            :         /*
     465                 :            :          * Private VM_LOCKED VMA becoming writable: trigger COW to avoid major
     466                 :            :          * fault on access.
     467                 :            :          */
     468         [ -  + ]:      18017 :         if ((oldflags & (VM_WRITE | VM_SHARED | VM_LOCKED)) == VM_LOCKED &&
     469         [ #  # ]:          0 :                         (newflags & VM_WRITE)) {
     470                 :          0 :                 populate_vma_page_range(vma, start, end, NULL);
     471                 :            :         }
     472                 :            : 
     473                 :      18017 :         vm_stat_account(mm, oldflags, -nrpages);
     474                 :      18017 :         vm_stat_account(mm, newflags, nrpages);
     475                 :      18017 :         perf_event_mmap(vma);
     476                 :      18017 :         return 0;
     477                 :            : 
     478                 :          0 : fail:
     479                 :          0 :         vm_unacct_memory(charged);
     480                 :          0 :         return error;
     481                 :            : }
     482                 :            : 
     483                 :            : /*
     484                 :            :  * pkey==-1 when doing a legacy mprotect()
     485                 :            :  */
     486                 :      18017 : static int do_mprotect_pkey(unsigned long start, size_t len,
     487                 :            :                 unsigned long prot, int pkey)
     488                 :            : {
     489                 :      18017 :         unsigned long nstart, end, tmp, reqprot;
     490                 :      18017 :         struct vm_area_struct *vma, *prev;
     491                 :      18017 :         int error = -EINVAL;
     492                 :      18017 :         const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP);
     493         [ -  + ]:      18017 :         const bool rier = (current->personality & READ_IMPLIES_EXEC) &&
     494         [ #  # ]:          0 :                                 (prot & PROT_READ);
     495                 :            : 
     496                 :      18017 :         start = untagged_addr(start);
     497                 :            : 
     498                 :      18017 :         prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP);
     499         [ +  - ]:      18017 :         if (grows == (PROT_GROWSDOWN|PROT_GROWSUP)) /* can't be both */
     500                 :            :                 return -EINVAL;
     501                 :            : 
     502         [ +  - ]:      18017 :         if (start & ~PAGE_MASK)
     503                 :            :                 return -EINVAL;
     504         [ +  - ]:      18017 :         if (!len)
     505                 :            :                 return 0;
     506                 :      18017 :         len = PAGE_ALIGN(len);
     507                 :      18017 :         end = start + len;
     508         [ +  - ]:      18017 :         if (end <= start)
     509                 :            :                 return -ENOMEM;
     510         [ +  - ]:      18017 :         if (!arch_validate_prot(prot, start))
     511                 :            :                 return -EINVAL;
     512                 :            : 
     513                 :      18017 :         reqprot = prot;
     514                 :            : 
     515         [ +  - ]:      18017 :         if (down_write_killable(&current->mm->mmap_sem))
     516                 :            :                 return -EINTR;
     517                 :            : 
     518                 :            :         /*
     519                 :            :          * If userspace did not allocate the pkey, do not let
     520                 :            :          * them use it here.
     521                 :            :          */
     522                 :      18017 :         error = -EINVAL;
     523   [ -  +  -  - ]:      18017 :         if ((pkey != -1) && !mm_pkey_is_allocated(current->mm, pkey))
     524                 :          0 :                 goto out;
     525                 :            : 
     526                 :      18017 :         vma = find_vma(current->mm, start);
     527                 :      18017 :         error = -ENOMEM;
     528         [ -  + ]:      18017 :         if (!vma)
     529                 :          0 :                 goto out;
     530                 :      18017 :         prev = vma->vm_prev;
     531         [ -  + ]:      18017 :         if (unlikely(grows & PROT_GROWSDOWN)) {
     532         [ #  # ]:          0 :                 if (vma->vm_start >= end)
     533                 :          0 :                         goto out;
     534                 :          0 :                 start = vma->vm_start;
     535                 :          0 :                 error = -EINVAL;
     536         [ #  # ]:          0 :                 if (!(vma->vm_flags & VM_GROWSDOWN))
     537                 :          0 :                         goto out;
     538                 :            :         } else {
     539         [ -  + ]:      18017 :                 if (vma->vm_start > start)
     540                 :          0 :                         goto out;
     541         [ -  + ]:      18017 :                 if (unlikely(grows & PROT_GROWSUP)) {
     542                 :          0 :                         end = vma->vm_end;
     543                 :          0 :                         error = -EINVAL;
     544                 :          0 :                         if (!(vma->vm_flags & VM_GROWSUP))
     545                 :          0 :                                 goto out;
     546                 :            :                 }
     547                 :            :         }
     548         [ +  + ]:      18017 :         if (start > vma->vm_start)
     549                 :       4362 :                 prev = vma;
     550                 :            : 
     551                 :            :         for (nstart = start ; ; ) {
     552                 :      18017 :                 unsigned long mask_off_old_flags;
     553                 :      18017 :                 unsigned long newflags;
     554                 :      18017 :                 int new_vma_pkey;
     555                 :            : 
     556                 :            :                 /* Here we know that vma->vm_start <= nstart < vma->vm_end. */
     557                 :            : 
     558                 :            :                 /* Does the application expect PROT_READ to imply PROT_EXEC */
     559   [ -  +  -  - ]:      18017 :                 if (rier && (vma->vm_flags & VM_MAYEXEC))
     560                 :          0 :                         prot |= PROT_EXEC;
     561                 :            : 
     562                 :            :                 /*
     563                 :            :                  * Each mprotect() call explicitly passes r/w/x permissions.
     564                 :            :                  * If a permission is not passed to mprotect(), it must be
     565                 :            :                  * cleared from the VMA.
     566                 :            :                  */
     567                 :      18017 :                 mask_off_old_flags = VM_READ | VM_WRITE | VM_EXEC |
     568                 :            :                                         VM_FLAGS_CLEAR;
     569                 :            : 
     570                 :      18017 :                 new_vma_pkey = arch_override_mprotect_pkey(vma, prot, pkey);
     571         [ -  + ]:      18017 :                 newflags = calc_vm_prot_bits(prot, new_vma_pkey);
     572                 :      18017 :                 newflags |= (vma->vm_flags & ~mask_off_old_flags);
     573                 :            : 
     574                 :            :                 /* newflags >> 4 shift VM_MAY% in place of VM_% */
     575         [ -  + ]:      18017 :                 if ((newflags & ~(newflags >> 4)) & (VM_READ | VM_WRITE | VM_EXEC)) {
     576                 :          0 :                         error = -EACCES;
     577                 :          0 :                         goto out;
     578                 :            :                 }
     579                 :            : 
     580                 :      18017 :                 error = security_file_mprotect(vma, reqprot, prot);
     581         [ -  + ]:      18017 :                 if (error)
     582                 :          0 :                         goto out;
     583                 :            : 
     584                 :      18017 :                 tmp = vma->vm_end;
     585                 :      18017 :                 if (tmp > end)
     586                 :            :                         tmp = end;
     587                 :      18017 :                 error = mprotect_fixup(vma, &prev, nstart, tmp, newflags);
     588         [ -  + ]:      18017 :                 if (error)
     589                 :          0 :                         goto out;
     590                 :      18017 :                 nstart = tmp;
     591                 :            : 
     592                 :      18017 :                 if (nstart < prev->vm_end)
     593                 :            :                         nstart = prev->vm_end;
     594         [ +  - ]:      18017 :                 if (nstart >= end)
     595                 :      18017 :                         goto out;
     596                 :            : 
     597                 :          0 :                 vma = prev->vm_next;
     598   [ #  #  #  # ]:          0 :                 if (!vma || vma->vm_start != nstart) {
     599                 :          0 :                         error = -ENOMEM;
     600                 :          0 :                         goto out;
     601                 :            :                 }
     602                 :            :                 prot = reqprot;
     603                 :            :         }
     604                 :      18017 : out:
     605                 :      18017 :         up_write(&current->mm->mmap_sem);
     606                 :      18017 :         return error;
     607                 :            : }
     608                 :            : 
     609                 :      36034 : SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
     610                 :            :                 unsigned long, prot)
     611                 :            : {
     612                 :      18017 :         return do_mprotect_pkey(start, len, prot, -1);
     613                 :            : }
     614                 :            : 
     615                 :            : #ifdef CONFIG_ARCH_HAS_PKEYS
     616                 :            : 
     617                 :          0 : SYSCALL_DEFINE4(pkey_mprotect, unsigned long, start, size_t, len,
     618                 :            :                 unsigned long, prot, int, pkey)
     619                 :            : {
     620                 :          0 :         return do_mprotect_pkey(start, len, prot, pkey);
     621                 :            : }
     622                 :            : 
     623                 :          0 : SYSCALL_DEFINE2(pkey_alloc, unsigned long, flags, unsigned long, init_val)
     624                 :            : {
     625                 :          0 :         int pkey;
     626                 :          0 :         int ret;
     627                 :            : 
     628                 :            :         /* No flags supported yet. */
     629         [ #  # ]:          0 :         if (flags)
     630                 :            :                 return -EINVAL;
     631                 :            :         /* check for unsupported init values */
     632         [ #  # ]:          0 :         if (init_val & ~PKEY_ACCESS_MASK)
     633                 :            :                 return -EINVAL;
     634                 :            : 
     635                 :          0 :         down_write(&current->mm->mmap_sem);
     636                 :          0 :         pkey = mm_pkey_alloc(current->mm);
     637                 :            : 
     638                 :          0 :         ret = -ENOSPC;
     639         [ #  # ]:          0 :         if (pkey == -1)
     640                 :          0 :                 goto out;
     641                 :            : 
     642                 :          0 :         ret = arch_set_user_pkey_access(current, pkey, init_val);
     643         [ #  # ]:          0 :         if (ret) {
     644                 :          0 :                 mm_pkey_free(current->mm, pkey);
     645                 :          0 :                 goto out;
     646                 :            :         }
     647                 :            :         ret = pkey;
     648                 :          0 : out:
     649                 :          0 :         up_write(&current->mm->mmap_sem);
     650                 :          0 :         return ret;
     651                 :            : }
     652                 :            : 
     653                 :          0 : SYSCALL_DEFINE1(pkey_free, int, pkey)
     654                 :            : {
     655                 :          0 :         int ret;
     656                 :            : 
     657                 :          0 :         down_write(&current->mm->mmap_sem);
     658                 :          0 :         ret = mm_pkey_free(current->mm, pkey);
     659                 :          0 :         up_write(&current->mm->mmap_sem);
     660                 :            : 
     661                 :            :         /*
     662                 :            :          * We could provie warnings or errors if any VMA still
     663                 :            :          * has the pkey set here.
     664                 :            :          */
     665                 :          0 :         return ret;
     666                 :            : }
     667                 :            : 
     668                 :            : #endif /* CONFIG_ARCH_HAS_PKEYS */

Generated by: LCOV version 1.14