18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mm/mprotect.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright 1994 Linus Torvalds 68c2ecf20Sopenharmony_ci * (C) Copyright 2002 Christoph Hellwig 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Address space accounting code <alan@lxorguk.ukuu.org.uk> 98c2ecf20Sopenharmony_ci * (C) Copyright 2002 Red Hat Inc, All Rights Reserved 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/pagewalk.h> 138c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 148c2ecf20Sopenharmony_ci#include <linux/shm.h> 158c2ecf20Sopenharmony_ci#include <linux/mman.h> 168c2ecf20Sopenharmony_ci#include <linux/fs.h> 178c2ecf20Sopenharmony_ci#include <linux/highmem.h> 188c2ecf20Sopenharmony_ci#include <linux/security.h> 198c2ecf20Sopenharmony_ci#include <linux/mempolicy.h> 208c2ecf20Sopenharmony_ci#include <linux/personality.h> 218c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 228c2ecf20Sopenharmony_ci#include <linux/swap.h> 238c2ecf20Sopenharmony_ci#include <linux/swapops.h> 248c2ecf20Sopenharmony_ci#include <linux/mmu_notifier.h> 258c2ecf20Sopenharmony_ci#include <linux/migrate.h> 268c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 278c2ecf20Sopenharmony_ci#include <linux/pkeys.h> 288c2ecf20Sopenharmony_ci#include <linux/ksm.h> 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci#include <linux/mm_inline.h> 318c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 328c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 338c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 348c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 358c2ecf20Sopenharmony_ci#include <linux/xpm.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <trace/hooks/mm.h> 388c2ecf20Sopenharmony_ci#include "internal.h" 398c2ecf20Sopenharmony_ci#include <linux/hck/lite_hck_jit_memory.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, 428c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, pgprot_t newprot, 438c2ecf20Sopenharmony_ci unsigned long cp_flags) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci pte_t *pte, oldpte; 468c2ecf20Sopenharmony_ci spinlock_t *ptl; 478c2ecf20Sopenharmony_ci unsigned long pages = 0; 488c2ecf20Sopenharmony_ci int target_node = NUMA_NO_NODE; 498c2ecf20Sopenharmony_ci bool dirty_accountable = cp_flags & MM_CP_DIRTY_ACCT; 508c2ecf20Sopenharmony_ci bool prot_numa = cp_flags & MM_CP_PROT_NUMA; 518c2ecf20Sopenharmony_ci bool uffd_wp = cp_flags & MM_CP_UFFD_WP; 528c2ecf20Sopenharmony_ci bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * Can be called with only the mmap_lock for reading by 568c2ecf20Sopenharmony_ci * prot_numa so we must check the pmd isn't constantly 578c2ecf20Sopenharmony_ci * changing from under us from pmd_none to pmd_trans_huge 588c2ecf20Sopenharmony_ci * and/or the other way around. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci if (pmd_trans_unstable(pmd)) 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* 648c2ecf20Sopenharmony_ci * The pmd points to a regular pte so the pmd can't change 658c2ecf20Sopenharmony_ci * from under us even if the mmap_lock is only hold for 668c2ecf20Sopenharmony_ci * reading. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Get target node for single threaded private VMAs */ 718c2ecf20Sopenharmony_ci if (prot_numa && !(vma->vm_flags & VM_SHARED) && 728c2ecf20Sopenharmony_ci atomic_read(&vma->vm_mm->mm_users) == 1) 738c2ecf20Sopenharmony_ci target_node = numa_node_id(); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci flush_tlb_batched_pending(vma->vm_mm); 768c2ecf20Sopenharmony_ci arch_enter_lazy_mmu_mode(); 778c2ecf20Sopenharmony_ci do { 788c2ecf20Sopenharmony_ci oldpte = *pte; 798c2ecf20Sopenharmony_ci if (pte_present(oldpte)) { 808c2ecf20Sopenharmony_ci pte_t ptent; 818c2ecf20Sopenharmony_ci bool preserve_write = prot_numa && pte_write(oldpte); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * Avoid trapping faults against the zero or KSM 858c2ecf20Sopenharmony_ci * pages. See similar comment in change_huge_pmd. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci if (prot_numa) { 888c2ecf20Sopenharmony_ci struct page *page; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Avoid TLB flush if possible */ 918c2ecf20Sopenharmony_ci if (pte_protnone(oldpte)) 928c2ecf20Sopenharmony_ci continue; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci page = vm_normal_page(vma, addr, oldpte); 958c2ecf20Sopenharmony_ci if (!page || PageKsm(page)) 968c2ecf20Sopenharmony_ci continue; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Also skip shared copy-on-write pages */ 998c2ecf20Sopenharmony_ci if (is_cow_mapping(vma->vm_flags) && 1008c2ecf20Sopenharmony_ci page_count(page) != 1) 1018c2ecf20Sopenharmony_ci continue; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * While migration can move some dirty pages, 1058c2ecf20Sopenharmony_ci * it cannot move them all from MIGRATE_ASYNC 1068c2ecf20Sopenharmony_ci * context. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci if (page_is_file_lru(page) && PageDirty(page)) 1098c2ecf20Sopenharmony_ci continue; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* 1128c2ecf20Sopenharmony_ci * Don't mess with PTEs if page is already on the node 1138c2ecf20Sopenharmony_ci * a single-threaded process is running on. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci if (target_node == page_to_nid(page)) 1168c2ecf20Sopenharmony_ci continue; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci oldpte = ptep_modify_prot_start(vma, addr, pte); 1208c2ecf20Sopenharmony_ci ptent = pte_modify(oldpte, newprot); 1218c2ecf20Sopenharmony_ci if (preserve_write) 1228c2ecf20Sopenharmony_ci ptent = pte_mk_savedwrite(ptent); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (uffd_wp) { 1258c2ecf20Sopenharmony_ci ptent = pte_wrprotect(ptent); 1268c2ecf20Sopenharmony_ci ptent = pte_mkuffd_wp(ptent); 1278c2ecf20Sopenharmony_ci } else if (uffd_wp_resolve) { 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * Leave the write bit to be handled 1308c2ecf20Sopenharmony_ci * by PF interrupt handler, then 1318c2ecf20Sopenharmony_ci * things like COW could be properly 1328c2ecf20Sopenharmony_ci * handled. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci ptent = pte_clear_uffd_wp(ptent); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Avoid taking write faults for known dirty pages */ 1388c2ecf20Sopenharmony_ci if (dirty_accountable && pte_dirty(ptent) && 1398c2ecf20Sopenharmony_ci (pte_soft_dirty(ptent) || 1408c2ecf20Sopenharmony_ci !(vma->vm_flags & VM_SOFTDIRTY))) { 1418c2ecf20Sopenharmony_ci ptent = pte_mkwrite(ptent); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* if exec added, check xpm integrity before set pte */ 1458c2ecf20Sopenharmony_ci if(pte_user_mkexec(oldpte, ptent) && 1468c2ecf20Sopenharmony_ci unlikely(xpm_integrity_validate_hook(vma, 0, addr, 1478c2ecf20Sopenharmony_ci vm_normal_page(vma, addr, oldpte)))) 1488c2ecf20Sopenharmony_ci continue; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ptep_modify_prot_commit(vma, addr, pte, oldpte, ptent); 1518c2ecf20Sopenharmony_ci pages++; 1528c2ecf20Sopenharmony_ci } else if (is_swap_pte(oldpte)) { 1538c2ecf20Sopenharmony_ci swp_entry_t entry = pte_to_swp_entry(oldpte); 1548c2ecf20Sopenharmony_ci pte_t newpte; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (is_write_migration_entry(entry)) { 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * A protection check is difficult so 1598c2ecf20Sopenharmony_ci * just be safe and disable write 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci make_migration_entry_read(&entry); 1628c2ecf20Sopenharmony_ci newpte = swp_entry_to_pte(entry); 1638c2ecf20Sopenharmony_ci if (pte_swp_soft_dirty(oldpte)) 1648c2ecf20Sopenharmony_ci newpte = pte_swp_mksoft_dirty(newpte); 1658c2ecf20Sopenharmony_ci if (pte_swp_uffd_wp(oldpte)) 1668c2ecf20Sopenharmony_ci newpte = pte_swp_mkuffd_wp(newpte); 1678c2ecf20Sopenharmony_ci } else if (is_write_device_private_entry(entry)) { 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * We do not preserve soft-dirtiness. See 1708c2ecf20Sopenharmony_ci * copy_one_pte() for explanation. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci make_device_private_entry_read(&entry); 1738c2ecf20Sopenharmony_ci newpte = swp_entry_to_pte(entry); 1748c2ecf20Sopenharmony_ci if (pte_swp_uffd_wp(oldpte)) 1758c2ecf20Sopenharmony_ci newpte = pte_swp_mkuffd_wp(newpte); 1768c2ecf20Sopenharmony_ci } else { 1778c2ecf20Sopenharmony_ci newpte = oldpte; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (uffd_wp) 1818c2ecf20Sopenharmony_ci newpte = pte_swp_mkuffd_wp(newpte); 1828c2ecf20Sopenharmony_ci else if (uffd_wp_resolve) 1838c2ecf20Sopenharmony_ci newpte = pte_swp_clear_uffd_wp(newpte); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!pte_same(oldpte, newpte)) { 1868c2ecf20Sopenharmony_ci set_pte_at(vma->vm_mm, addr, pte, newpte); 1878c2ecf20Sopenharmony_ci pages++; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci } while (pte++, addr += PAGE_SIZE, addr != end); 1918c2ecf20Sopenharmony_ci arch_leave_lazy_mmu_mode(); 1928c2ecf20Sopenharmony_ci pte_unmap_unlock(pte - 1, ptl); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return pages; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * Used when setting automatic NUMA hinting protection where it is 1998c2ecf20Sopenharmony_ci * critical that a numa hinting PMD is not confused with a bad PMD. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic inline int pmd_none_or_clear_bad_unless_trans_huge(pmd_t *pmd) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci pmd_t pmdval = pmd_read_atomic(pmd); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* See pmd_none_or_trans_huge_or_clear_bad for info on barrier */ 2068c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 2078c2ecf20Sopenharmony_ci barrier(); 2088c2ecf20Sopenharmony_ci#endif 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (pmd_none(pmdval)) 2118c2ecf20Sopenharmony_ci return 1; 2128c2ecf20Sopenharmony_ci if (pmd_trans_huge(pmdval)) 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci if (unlikely(pmd_bad(pmdval))) { 2158c2ecf20Sopenharmony_ci pmd_clear_bad(pmd); 2168c2ecf20Sopenharmony_ci return 1; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic inline unsigned long change_pmd_range(struct vm_area_struct *vma, 2238c2ecf20Sopenharmony_ci pud_t *pud, unsigned long addr, unsigned long end, 2248c2ecf20Sopenharmony_ci pgprot_t newprot, unsigned long cp_flags) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci pmd_t *pmd; 2278c2ecf20Sopenharmony_ci unsigned long next; 2288c2ecf20Sopenharmony_ci unsigned long pages = 0; 2298c2ecf20Sopenharmony_ci unsigned long nr_huge_updates = 0; 2308c2ecf20Sopenharmony_ci struct mmu_notifier_range range; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci range.start = 0; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, addr); 2358c2ecf20Sopenharmony_ci do { 2368c2ecf20Sopenharmony_ci unsigned long this_pages; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci next = pmd_addr_end(addr, end); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* 2418c2ecf20Sopenharmony_ci * Automatic NUMA balancing walks the tables with mmap_lock 2428c2ecf20Sopenharmony_ci * held for read. It's possible a parallel update to occur 2438c2ecf20Sopenharmony_ci * between pmd_trans_huge() and a pmd_none_or_clear_bad() 2448c2ecf20Sopenharmony_ci * check leading to a false positive and clearing. 2458c2ecf20Sopenharmony_ci * Hence, it's necessary to atomically read the PMD value 2468c2ecf20Sopenharmony_ci * for all the checks. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci if (!is_swap_pmd(*pmd) && !pmd_devmap(*pmd) && 2498c2ecf20Sopenharmony_ci pmd_none_or_clear_bad_unless_trans_huge(pmd)) 2508c2ecf20Sopenharmony_ci goto next; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* invoke the mmu notifier if the pmd is populated */ 2538c2ecf20Sopenharmony_ci if (!range.start) { 2548c2ecf20Sopenharmony_ci mmu_notifier_range_init(&range, 2558c2ecf20Sopenharmony_ci MMU_NOTIFY_PROTECTION_VMA, 0, 2568c2ecf20Sopenharmony_ci vma, vma->vm_mm, addr, end); 2578c2ecf20Sopenharmony_ci mmu_notifier_invalidate_range_start(&range); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (is_swap_pmd(*pmd) || pmd_trans_huge(*pmd) || pmd_devmap(*pmd)) { 2618c2ecf20Sopenharmony_ci if (next - addr != HPAGE_PMD_SIZE) { 2628c2ecf20Sopenharmony_ci __split_huge_pmd(vma, pmd, addr, false, NULL); 2638c2ecf20Sopenharmony_ci } else { 2648c2ecf20Sopenharmony_ci int nr_ptes = change_huge_pmd(vma, pmd, addr, 2658c2ecf20Sopenharmony_ci newprot, cp_flags); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (nr_ptes) { 2688c2ecf20Sopenharmony_ci if (nr_ptes == HPAGE_PMD_NR) { 2698c2ecf20Sopenharmony_ci pages += HPAGE_PMD_NR; 2708c2ecf20Sopenharmony_ci nr_huge_updates++; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* huge pmd was handled */ 2748c2ecf20Sopenharmony_ci goto next; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci /* fall through, the trans huge pmd just split */ 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci this_pages = change_pte_range(vma, pmd, addr, next, newprot, 2808c2ecf20Sopenharmony_ci cp_flags); 2818c2ecf20Sopenharmony_ci pages += this_pages; 2828c2ecf20Sopenharmony_cinext: 2838c2ecf20Sopenharmony_ci cond_resched(); 2848c2ecf20Sopenharmony_ci } while (pmd++, addr = next, addr != end); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (range.start) 2878c2ecf20Sopenharmony_ci mmu_notifier_invalidate_range_end(&range); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (nr_huge_updates) 2908c2ecf20Sopenharmony_ci count_vm_numa_events(NUMA_HUGE_PTE_UPDATES, nr_huge_updates); 2918c2ecf20Sopenharmony_ci return pages; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic inline unsigned long change_pud_range(struct vm_area_struct *vma, 2958c2ecf20Sopenharmony_ci p4d_t *p4d, unsigned long addr, unsigned long end, 2968c2ecf20Sopenharmony_ci pgprot_t newprot, unsigned long cp_flags) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci pud_t *pud; 2998c2ecf20Sopenharmony_ci unsigned long next; 3008c2ecf20Sopenharmony_ci unsigned long pages = 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci pud = pud_offset(p4d, addr); 3038c2ecf20Sopenharmony_ci do { 3048c2ecf20Sopenharmony_ci next = pud_addr_end(addr, end); 3058c2ecf20Sopenharmony_ci if (pud_none_or_clear_bad(pud)) 3068c2ecf20Sopenharmony_ci continue; 3078c2ecf20Sopenharmony_ci pages += change_pmd_range(vma, pud, addr, next, newprot, 3088c2ecf20Sopenharmony_ci cp_flags); 3098c2ecf20Sopenharmony_ci } while (pud++, addr = next, addr != end); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return pages; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic inline unsigned long change_p4d_range(struct vm_area_struct *vma, 3158c2ecf20Sopenharmony_ci pgd_t *pgd, unsigned long addr, unsigned long end, 3168c2ecf20Sopenharmony_ci pgprot_t newprot, unsigned long cp_flags) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci p4d_t *p4d; 3198c2ecf20Sopenharmony_ci unsigned long next; 3208c2ecf20Sopenharmony_ci unsigned long pages = 0; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 3238c2ecf20Sopenharmony_ci do { 3248c2ecf20Sopenharmony_ci next = p4d_addr_end(addr, end); 3258c2ecf20Sopenharmony_ci if (p4d_none_or_clear_bad(p4d)) 3268c2ecf20Sopenharmony_ci continue; 3278c2ecf20Sopenharmony_ci pages += change_pud_range(vma, p4d, addr, next, newprot, 3288c2ecf20Sopenharmony_ci cp_flags); 3298c2ecf20Sopenharmony_ci } while (p4d++, addr = next, addr != end); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return pages; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic unsigned long change_protection_range(struct vm_area_struct *vma, 3358c2ecf20Sopenharmony_ci unsigned long addr, unsigned long end, pgprot_t newprot, 3368c2ecf20Sopenharmony_ci unsigned long cp_flags) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 3398c2ecf20Sopenharmony_ci pgd_t *pgd; 3408c2ecf20Sopenharmony_ci unsigned long next; 3418c2ecf20Sopenharmony_ci unsigned long start = addr; 3428c2ecf20Sopenharmony_ci unsigned long pages = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci BUG_ON(addr >= end); 3458c2ecf20Sopenharmony_ci pgd = pgd_offset(mm, addr); 3468c2ecf20Sopenharmony_ci flush_cache_range(vma, addr, end); 3478c2ecf20Sopenharmony_ci inc_tlb_flush_pending(mm); 3488c2ecf20Sopenharmony_ci do { 3498c2ecf20Sopenharmony_ci next = pgd_addr_end(addr, end); 3508c2ecf20Sopenharmony_ci if (pgd_none_or_clear_bad(pgd)) 3518c2ecf20Sopenharmony_ci continue; 3528c2ecf20Sopenharmony_ci pages += change_p4d_range(vma, pgd, addr, next, newprot, 3538c2ecf20Sopenharmony_ci cp_flags); 3548c2ecf20Sopenharmony_ci } while (pgd++, addr = next, addr != end); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Only flush the TLB if we actually modified any entries: */ 3578c2ecf20Sopenharmony_ci if (pages) 3588c2ecf20Sopenharmony_ci flush_tlb_range(vma, start, end); 3598c2ecf20Sopenharmony_ci dec_tlb_flush_pending(mm); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return pages; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ciunsigned long change_protection(struct vm_area_struct *vma, unsigned long start, 3658c2ecf20Sopenharmony_ci unsigned long end, pgprot_t newprot, 3668c2ecf20Sopenharmony_ci unsigned long cp_flags) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci unsigned long pages; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci BUG_ON((cp_flags & MM_CP_UFFD_WP_ALL) == MM_CP_UFFD_WP_ALL); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (is_vm_hugetlb_page(vma)) 3738c2ecf20Sopenharmony_ci pages = hugetlb_change_protection(vma, start, end, newprot); 3748c2ecf20Sopenharmony_ci else 3758c2ecf20Sopenharmony_ci pages = change_protection_range(vma, start, end, newprot, 3768c2ecf20Sopenharmony_ci cp_flags); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return pages; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int prot_none_pte_entry(pte_t *pte, unsigned long addr, 3828c2ecf20Sopenharmony_ci unsigned long next, struct mm_walk *walk) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci return pfn_modify_allowed(pte_pfn(*pte), *(pgprot_t *)(walk->private)) ? 3858c2ecf20Sopenharmony_ci 0 : -EACCES; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int prot_none_hugetlb_entry(pte_t *pte, unsigned long hmask, 3898c2ecf20Sopenharmony_ci unsigned long addr, unsigned long next, 3908c2ecf20Sopenharmony_ci struct mm_walk *walk) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci return pfn_modify_allowed(pte_pfn(*pte), *(pgprot_t *)(walk->private)) ? 3938c2ecf20Sopenharmony_ci 0 : -EACCES; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int prot_none_test(unsigned long addr, unsigned long next, 3978c2ecf20Sopenharmony_ci struct mm_walk *walk) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const struct mm_walk_ops prot_none_walk_ops = { 4038c2ecf20Sopenharmony_ci .pte_entry = prot_none_pte_entry, 4048c2ecf20Sopenharmony_ci .hugetlb_entry = prot_none_hugetlb_entry, 4058c2ecf20Sopenharmony_ci .test_walk = prot_none_test, 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ciint 4098c2ecf20Sopenharmony_cimprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, 4108c2ecf20Sopenharmony_ci unsigned long start, unsigned long end, unsigned long newflags) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 4138c2ecf20Sopenharmony_ci unsigned long oldflags = vma->vm_flags; 4148c2ecf20Sopenharmony_ci long nrpages = (end - start) >> PAGE_SHIFT; 4158c2ecf20Sopenharmony_ci unsigned long charged = 0; 4168c2ecf20Sopenharmony_ci pgoff_t pgoff; 4178c2ecf20Sopenharmony_ci int error; 4188c2ecf20Sopenharmony_ci int dirty_accountable = 0; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (newflags == oldflags) { 4218c2ecf20Sopenharmony_ci *pprev = vma; 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* 4268c2ecf20Sopenharmony_ci * Do PROT_NONE PFN permission checks here when we can still 4278c2ecf20Sopenharmony_ci * bail out without undoing a lot of state. This is a rather 4288c2ecf20Sopenharmony_ci * uncommon case, so doesn't need to be very optimized. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci if (arch_has_pfn_modify_check() && 4318c2ecf20Sopenharmony_ci (vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) && 4328c2ecf20Sopenharmony_ci (newflags & VM_ACCESS_FLAGS) == 0) { 4338c2ecf20Sopenharmony_ci pgprot_t new_pgprot = vm_get_page_prot(newflags); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci error = walk_page_range(current->mm, start, end, 4368c2ecf20Sopenharmony_ci &prot_none_walk_ops, &new_pgprot); 4378c2ecf20Sopenharmony_ci if (error) 4388c2ecf20Sopenharmony_ci return error; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* 4428c2ecf20Sopenharmony_ci * If we make a private mapping writable we increase our commit; 4438c2ecf20Sopenharmony_ci * but (without finer accounting) cannot reduce our commit if we 4448c2ecf20Sopenharmony_ci * make it unwritable again. hugetlb mapping were accounted for 4458c2ecf20Sopenharmony_ci * even if read-only so there is no need to account for them here 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci if (newflags & VM_WRITE) { 4488c2ecf20Sopenharmony_ci /* Check space limits when area turns into data. */ 4498c2ecf20Sopenharmony_ci if (!may_expand_vm(mm, newflags, nrpages) && 4508c2ecf20Sopenharmony_ci may_expand_vm(mm, oldflags, nrpages)) 4518c2ecf20Sopenharmony_ci return -ENOMEM; 4528c2ecf20Sopenharmony_ci if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_HUGETLB| 4538c2ecf20Sopenharmony_ci VM_SHARED|VM_NORESERVE))) { 4548c2ecf20Sopenharmony_ci charged = nrpages; 4558c2ecf20Sopenharmony_ci if (security_vm_enough_memory_mm(mm, charged)) 4568c2ecf20Sopenharmony_ci return -ENOMEM; 4578c2ecf20Sopenharmony_ci newflags |= VM_ACCOUNT; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* 4628c2ecf20Sopenharmony_ci * First try to merge with previous and/or next vma. 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_ci pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); 4658c2ecf20Sopenharmony_ci *pprev = vma_merge(mm, *pprev, start, end, newflags, 4668c2ecf20Sopenharmony_ci vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), 4678c2ecf20Sopenharmony_ci vma->vm_userfaultfd_ctx, anon_vma_name(vma)); 4688c2ecf20Sopenharmony_ci if (*pprev) { 4698c2ecf20Sopenharmony_ci vma = *pprev; 4708c2ecf20Sopenharmony_ci VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY); 4718c2ecf20Sopenharmony_ci goto success; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci *pprev = vma; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (start != vma->vm_start) { 4778c2ecf20Sopenharmony_ci error = split_vma(mm, vma, start, 1); 4788c2ecf20Sopenharmony_ci if (error) 4798c2ecf20Sopenharmony_ci goto fail; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (end != vma->vm_end) { 4838c2ecf20Sopenharmony_ci error = split_vma(mm, vma, end, 0); 4848c2ecf20Sopenharmony_ci if (error) 4858c2ecf20Sopenharmony_ci goto fail; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cisuccess: 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * vm_flags and vm_page_prot are protected by the mmap_lock 4918c2ecf20Sopenharmony_ci * held in write mode. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci vma->vm_flags = newflags; 4948c2ecf20Sopenharmony_ci dirty_accountable = vma_wants_writenotify(vma, vma->vm_page_prot); 4958c2ecf20Sopenharmony_ci vma_set_page_prot(vma); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci change_protection(vma, start, end, vma->vm_page_prot, 4988c2ecf20Sopenharmony_ci dirty_accountable ? MM_CP_DIRTY_ACCT : 0); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* 5018c2ecf20Sopenharmony_ci * Private VM_LOCKED VMA becoming writable: trigger COW to avoid major 5028c2ecf20Sopenharmony_ci * fault on access. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci if ((oldflags & (VM_WRITE | VM_SHARED | VM_LOCKED)) == VM_LOCKED && 5058c2ecf20Sopenharmony_ci (newflags & VM_WRITE)) { 5068c2ecf20Sopenharmony_ci populate_vma_page_range(vma, start, end, NULL); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci vm_stat_account(mm, oldflags, -nrpages); 5108c2ecf20Sopenharmony_ci vm_stat_account(mm, newflags, nrpages); 5118c2ecf20Sopenharmony_ci perf_event_mmap(vma); 5128c2ecf20Sopenharmony_ci return 0; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cifail: 5158c2ecf20Sopenharmony_ci vm_unacct_memory(charged); 5168c2ecf20Sopenharmony_ci return error; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/* 5208c2ecf20Sopenharmony_ci * pkey==-1 when doing a legacy mprotect() 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic int do_mprotect_pkey(unsigned long start, size_t len, 5238c2ecf20Sopenharmony_ci unsigned long prot, int pkey) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci unsigned long nstart, end, tmp, reqprot; 5268c2ecf20Sopenharmony_ci struct vm_area_struct *vma, *prev; 5278c2ecf20Sopenharmony_ci int error = -EINVAL; 5288c2ecf20Sopenharmony_ci const int grows = prot & (PROT_GROWSDOWN|PROT_GROWSUP); 5298c2ecf20Sopenharmony_ci const bool rier = (current->personality & READ_IMPLIES_EXEC) && 5308c2ecf20Sopenharmony_ci (prot & PROT_READ); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci error = 0; 5338c2ecf20Sopenharmony_ci trace_vendor_do_mprotect_pkey(prot, &error); 5348c2ecf20Sopenharmony_ci if (error) 5358c2ecf20Sopenharmony_ci return error; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci start = untagged_addr(start); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (prot & PROT_EXEC) { 5408c2ecf20Sopenharmony_ci CALL_HCK_LITE_HOOK(find_jit_memory_lhck, current, start, len, &error); 5418c2ecf20Sopenharmony_ci if (error) { 5428c2ecf20Sopenharmony_ci pr_info("JITINFO: mprotect protection triggered"); 5438c2ecf20Sopenharmony_ci return error; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci prot &= ~(PROT_GROWSDOWN|PROT_GROWSUP); 5488c2ecf20Sopenharmony_ci if (grows == (PROT_GROWSDOWN|PROT_GROWSUP)) /* can't be both */ 5498c2ecf20Sopenharmony_ci return -EINVAL; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (start & ~PAGE_MASK) 5528c2ecf20Sopenharmony_ci return -EINVAL; 5538c2ecf20Sopenharmony_ci if (!len) 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci len = PAGE_ALIGN(len); 5568c2ecf20Sopenharmony_ci end = start + len; 5578c2ecf20Sopenharmony_ci if (end <= start) 5588c2ecf20Sopenharmony_ci return -ENOMEM; 5598c2ecf20Sopenharmony_ci if (!arch_validate_prot(prot, start)) 5608c2ecf20Sopenharmony_ci return -EINVAL; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci reqprot = prot; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (mmap_write_lock_killable(current->mm)) 5658c2ecf20Sopenharmony_ci return -EINTR; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* 5688c2ecf20Sopenharmony_ci * If userspace did not allocate the pkey, do not let 5698c2ecf20Sopenharmony_ci * them use it here. 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_ci error = -EINVAL; 5728c2ecf20Sopenharmony_ci if ((pkey != -1) && !mm_pkey_is_allocated(current->mm, pkey)) 5738c2ecf20Sopenharmony_ci goto out; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci vma = find_vma(current->mm, start); 5768c2ecf20Sopenharmony_ci error = -ENOMEM; 5778c2ecf20Sopenharmony_ci if (!vma) 5788c2ecf20Sopenharmony_ci goto out; 5798c2ecf20Sopenharmony_ci prev = vma->vm_prev; 5808c2ecf20Sopenharmony_ci if (unlikely(grows & PROT_GROWSDOWN)) { 5818c2ecf20Sopenharmony_ci if (vma->vm_start >= end) 5828c2ecf20Sopenharmony_ci goto out; 5838c2ecf20Sopenharmony_ci start = vma->vm_start; 5848c2ecf20Sopenharmony_ci error = -EINVAL; 5858c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_GROWSDOWN)) 5868c2ecf20Sopenharmony_ci goto out; 5878c2ecf20Sopenharmony_ci } else { 5888c2ecf20Sopenharmony_ci if (vma->vm_start > start) 5898c2ecf20Sopenharmony_ci goto out; 5908c2ecf20Sopenharmony_ci if (unlikely(grows & PROT_GROWSUP)) { 5918c2ecf20Sopenharmony_ci end = vma->vm_end; 5928c2ecf20Sopenharmony_ci error = -EINVAL; 5938c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_GROWSUP)) 5948c2ecf20Sopenharmony_ci goto out; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci if (start > vma->vm_start) 5988c2ecf20Sopenharmony_ci prev = vma; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci for (nstart = start ; ; ) { 6018c2ecf20Sopenharmony_ci unsigned long mask_off_old_flags; 6028c2ecf20Sopenharmony_ci unsigned long newflags; 6038c2ecf20Sopenharmony_ci int new_vma_pkey; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* Here we know that vma->vm_start <= nstart < vma->vm_end. */ 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Does the application expect PROT_READ to imply PROT_EXEC */ 6088c2ecf20Sopenharmony_ci if (rier && (vma->vm_flags & VM_MAYEXEC)) 6098c2ecf20Sopenharmony_ci prot |= PROT_EXEC; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* 6128c2ecf20Sopenharmony_ci * Each mprotect() call explicitly passes r/w/x permissions. 6138c2ecf20Sopenharmony_ci * If a permission is not passed to mprotect(), it must be 6148c2ecf20Sopenharmony_ci * cleared from the VMA. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci mask_off_old_flags = VM_READ | VM_WRITE | VM_EXEC | 6178c2ecf20Sopenharmony_ci VM_FLAGS_CLEAR; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci new_vma_pkey = arch_override_mprotect_pkey(vma, prot, pkey); 6208c2ecf20Sopenharmony_ci newflags = calc_vm_prot_bits(prot, new_vma_pkey); 6218c2ecf20Sopenharmony_ci newflags |= (vma->vm_flags & ~mask_off_old_flags); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* newflags >> 4 shift VM_MAY% in place of VM_% */ 6248c2ecf20Sopenharmony_ci if ((newflags & ~(newflags >> 4)) & VM_ACCESS_FLAGS) { 6258c2ecf20Sopenharmony_ci error = -EACCES; 6268c2ecf20Sopenharmony_ci goto out; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Allow architectures to sanity-check the new flags */ 6308c2ecf20Sopenharmony_ci if (!arch_validate_flags(newflags)) { 6318c2ecf20Sopenharmony_ci error = -EINVAL; 6328c2ecf20Sopenharmony_ci goto out; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci error = security_file_mprotect(vma, reqprot, prot); 6368c2ecf20Sopenharmony_ci if (error) 6378c2ecf20Sopenharmony_ci goto out; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci tmp = vma->vm_end; 6408c2ecf20Sopenharmony_ci if (tmp > end) 6418c2ecf20Sopenharmony_ci tmp = end; 6428c2ecf20Sopenharmony_ci error = mprotect_fixup(vma, &prev, nstart, tmp, newflags); 6438c2ecf20Sopenharmony_ci if (error) 6448c2ecf20Sopenharmony_ci goto out; 6458c2ecf20Sopenharmony_ci nstart = tmp; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (nstart < prev->vm_end) 6488c2ecf20Sopenharmony_ci nstart = prev->vm_end; 6498c2ecf20Sopenharmony_ci if (nstart >= end) 6508c2ecf20Sopenharmony_ci goto out; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci vma = prev->vm_next; 6538c2ecf20Sopenharmony_ci if (!vma || vma->vm_start != nstart) { 6548c2ecf20Sopenharmony_ci error = -ENOMEM; 6558c2ecf20Sopenharmony_ci goto out; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci prot = reqprot; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ciout: 6608c2ecf20Sopenharmony_ci mmap_write_unlock(current->mm); 6618c2ecf20Sopenharmony_ci return error; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len, 6658c2ecf20Sopenharmony_ci unsigned long, prot) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci return do_mprotect_pkey(start, len, prot, -1); 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_HAS_PKEYS 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ciSYSCALL_DEFINE4(pkey_mprotect, unsigned long, start, size_t, len, 6738c2ecf20Sopenharmony_ci unsigned long, prot, int, pkey) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci return do_mprotect_pkey(start, len, prot, pkey); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ciSYSCALL_DEFINE2(pkey_alloc, unsigned long, flags, unsigned long, init_val) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci int pkey; 6818c2ecf20Sopenharmony_ci int ret; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* No flags supported yet. */ 6848c2ecf20Sopenharmony_ci if (flags) 6858c2ecf20Sopenharmony_ci return -EINVAL; 6868c2ecf20Sopenharmony_ci /* check for unsupported init values */ 6878c2ecf20Sopenharmony_ci if (init_val & ~PKEY_ACCESS_MASK) 6888c2ecf20Sopenharmony_ci return -EINVAL; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci mmap_write_lock(current->mm); 6918c2ecf20Sopenharmony_ci pkey = mm_pkey_alloc(current->mm); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci ret = -ENOSPC; 6948c2ecf20Sopenharmony_ci if (pkey == -1) 6958c2ecf20Sopenharmony_ci goto out; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci ret = arch_set_user_pkey_access(current, pkey, init_val); 6988c2ecf20Sopenharmony_ci if (ret) { 6998c2ecf20Sopenharmony_ci mm_pkey_free(current->mm, pkey); 7008c2ecf20Sopenharmony_ci goto out; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci ret = pkey; 7038c2ecf20Sopenharmony_ciout: 7048c2ecf20Sopenharmony_ci mmap_write_unlock(current->mm); 7058c2ecf20Sopenharmony_ci return ret; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ciSYSCALL_DEFINE1(pkey_free, int, pkey) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci int ret; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci mmap_write_lock(current->mm); 7138c2ecf20Sopenharmony_ci ret = mm_pkey_free(current->mm, pkey); 7148c2ecf20Sopenharmony_ci mmap_write_unlock(current->mm); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* 7178c2ecf20Sopenharmony_ci * We could provie warnings or errors if any VMA still 7188c2ecf20Sopenharmony_ci * has the pkey set here. 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_ci return ret; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci#endif /* CONFIG_ARCH_HAS_PKEYS */ 724