| /* |
| * Intel Memory Protection Keys management |
| * Copyright (c) 2015, Intel Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| */ |
| #include <linux/mm_types.h> /* mm_struct, vma, etc... */ |
| #include <linux/pkeys.h> /* PKEY_* */ |
| #include <uapi/asm-generic/mman-common.h> |
| |
| #include <asm/cpufeature.h> /* boot_cpu_has, ... */ |
| #include <asm/mmu_context.h> /* vma_pkey() */ |
| #include <asm/fpu/internal.h> /* fpregs_active() */ |
| |
| int __execute_only_pkey(struct mm_struct *mm) |
| { |
| bool need_to_set_mm_pkey = false; |
| int execute_only_pkey = mm->context.execute_only_pkey; |
| int ret; |
| |
| /* Do we need to assign a pkey for mm's execute-only maps? */ |
| if (execute_only_pkey == -1) { |
| /* Go allocate one to use, which might fail */ |
| execute_only_pkey = mm_pkey_alloc(mm); |
| if (execute_only_pkey < 0) |
| return -1; |
| need_to_set_mm_pkey = true; |
| } |
| |
| /* |
| * We do not want to go through the relatively costly |
| * dance to set PKRU if we do not need to. Check it |
| * first and assume that if the execute-only pkey is |
| * write-disabled that we do not have to set it |
| * ourselves. We need preempt off so that nobody |
| * can make fpregs inactive. |
| */ |
| preempt_disable(); |
| if (!need_to_set_mm_pkey && |
| fpregs_active() && |
| !__pkru_allows_read(read_pkru(), execute_only_pkey)) { |
| preempt_enable(); |
| return execute_only_pkey; |
| } |
| preempt_enable(); |
| |
| /* |
| * Set up PKRU so that it denies access for everything |
| * other than execution. |
| */ |
| ret = arch_set_user_pkey_access(current, execute_only_pkey, |
| PKEY_DISABLE_ACCESS); |
| /* |
| * If the PKRU-set operation failed somehow, just return |
| * 0 and effectively disable execute-only support. |
| */ |
| if (ret) { |
| mm_set_pkey_free(mm, execute_only_pkey); |
| return -1; |
| } |
| |
| /* We got one, store it and use it from here on out */ |
| if (need_to_set_mm_pkey) |
| mm->context.execute_only_pkey = execute_only_pkey; |
| return execute_only_pkey; |
| } |
| |
| static inline bool vma_is_pkey_exec_only(struct vm_area_struct *vma) |
| { |
| /* Do this check first since the vm_flags should be hot */ |
| if ((vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)) != VM_EXEC) |
| return false; |
| if (vma_pkey(vma) != vma->vm_mm->context.execute_only_pkey) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| * This is only called for *plain* mprotect calls. |
| */ |
| int __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, int pkey) |
| { |
| /* |
| * Is this an mprotect_pkey() call? If so, never |
| * override the value that came from the user. |
| */ |
| if (pkey != -1) |
| return pkey; |
| /* |
| * Look for a protection-key-drive execute-only mapping |
| * which is now being given permissions that are not |
| * execute-only. Move it back to the default pkey. |
| */ |
| if (vma_is_pkey_exec_only(vma) && |
| (prot & (PROT_READ|PROT_WRITE))) { |
| return 0; |
| } |
| /* |
| * The mapping is execute-only. Go try to get the |
| * execute-only protection key. If we fail to do that, |
| * fall through as if we do not have execute-only |
| * support. |
| */ |
| if (prot == PROT_EXEC) { |
| pkey = execute_only_pkey(vma->vm_mm); |
| if (pkey > 0) |
| return pkey; |
| } |
| /* |
| * This is a vanilla, non-pkey mprotect (or we failed to |
| * setup execute-only), inherit the pkey from the VMA we |
| * are working on. |
| */ |
| return vma_pkey(vma); |
| } |