From: Dave Hansen <hidden> Date: 2015-11-17 03:35:15
Memory Protection Keys for User pages is a CPU feature which will
first appear on Skylake Servers, but will also be supported on
future non-server parts. It provides a mechanism for enforcing
page-based protections, but without requiring modification of the
page tables when an application changes protection domains. See
the Documentation/ patch for more details.
Changes from RFCv3:
* Added 'current' and 'foreign' variants of get_user_pages() to
help indicate whether protection keys should be enforced.
Thanks to Jerome Glisse for pointing out this issue.
* Added "allocation" and set/get system calls so that we can do
management of proection keys in the kernel. This opens the
door to use of specific protection keys for kernel use in the
future, such as for execute-only memory.
* Removed the kselftest code for the moment. It will be
submitted separately.
Thanks Ingo and Thomas for most of these):
Changes from RFCv2 (Thanks Ingo and Thomas for most of these):
* few minor compile warnings
* changed 'nopku' interaction with cpuid bits. Now, we do not
clear the PKU cpuid bit, we just skip enabling it.
* changed __pkru_allows_write() to also check access disable bit
* removed the unused write_pkru()
* made si_pkey a u64 and added some patch description details.
Also made it share space in siginfo with MPX and clarified
comments.
* give some real text for the Processor Trace xsave state
* made vma_pkey() less ugly (and much more optimized actually)
* added SEGV_PKUERR to copy_siginfo_to_user()
* remove page table walk when filling in si_pkey, added some
big fat comments about it being inherently racy.
* added self test code
This code is not runnable to anyone outside of Intel unless they
have some special hardware or a fancy simulator. If you are
interested in running this for real, please get in touch with me.
Hardware is available to a very small but nonzero number of
people.
This set is also available here (with the new syscall):
git://git.kernel.org/pub/scm/linux/kernel/git/daveh/x86-pkeys.git pkeys-v010
=== diffstat ===
Documentation/kernel-parameters.txt | 3 +
Documentation/x86/protection-keys.txt | 53 +++++
arch/mips/mm/gup.c | 3 +-
arch/powerpc/include/asm/mman.h | 5 +-
arch/powerpc/include/asm/mmu_context.h | 12 +
arch/s390/include/asm/mmu_context.h | 12 +
arch/s390/mm/gup.c | 3 +-
arch/sh/mm/gup.c | 2 +-
arch/sparc/mm/gup.c | 2 +-
arch/unicore32/include/asm/mmu_context.h | 12 +
arch/x86/Kconfig | 16 ++
arch/x86/entry/syscalls/syscall_32.tbl | 5 +
arch/x86/entry/syscalls/syscall_64.tbl | 5 +
arch/x86/include/asm/cpufeature.h | 54 +++--
arch/x86/include/asm/disabled-features.h | 12 +
arch/x86/include/asm/fpu/types.h | 12 +
arch/x86/include/asm/fpu/xstate.h | 4 +-
arch/x86/include/asm/mmu.h | 7 +
arch/x86/include/asm/mmu_context.h | 110 ++++++++-
arch/x86/include/asm/pgtable.h | 38 +++
arch/x86/include/asm/pgtable_types.h | 34 ++-
arch/x86/include/asm/pkeys.h | 68 ++++++
arch/x86/include/asm/required-features.h | 4 +
arch/x86/include/asm/special_insns.h | 20 ++
arch/x86/include/uapi/asm/mman.h | 22 ++
arch/x86/include/uapi/asm/processor-flags.h | 2 +
arch/x86/kernel/cpu/common.c | 42 ++++
arch/x86/kernel/fpu/xstate.c | 250 +++++++++++++++++++-
arch/x86/kernel/ldt.c | 4 +-
arch/x86/kernel/process_64.c | 2 +
arch/x86/kernel/setup.c | 9 +
arch/x86/mm/fault.c | 153 ++++++++++--
arch/x86/mm/gup.c | 52 ++--
arch/x86/mm/mpx.c | 4 +-
drivers/char/agp/frontend.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 4 +-
drivers/gpu/drm/i915/i915_gem_userptr.c | 2 +-
drivers/gpu/drm/radeon/radeon_ttm.c | 4 +-
drivers/gpu/drm/via/via_dmablit.c | 3 +-
drivers/infiniband/core/umem.c | 2 +-
drivers/infiniband/core/umem_odp.c | 8 +-
drivers/infiniband/hw/mthca/mthca_memfree.c | 3 +-
drivers/infiniband/hw/qib/qib_user_pages.c | 3 +-
drivers/infiniband/hw/usnic/usnic_uiom.c | 2 +-
drivers/iommu/amd_iommu_v2.c | 8 +-
drivers/media/pci/ivtv/ivtv-udma.c | 4 +-
drivers/media/pci/ivtv/ivtv-yuv.c | 10 +-
drivers/media/v4l2-core/videobuf-dma-sg.c | 3 +-
drivers/misc/sgi-gru/grufault.c | 3 +-
drivers/scsi/st.c | 2 -
drivers/staging/android/ashmem.c | 4 +-
drivers/video/fbdev/pvr2fb.c | 4 +-
drivers/virt/fsl_hypervisor.c | 5 +-
fs/exec.c | 8 +-
fs/proc/task_mmu.c | 5 +
include/asm-generic/mm_hooks.h | 13 +
include/linux/mm.h | 52 +++-
include/linux/mman.h | 6 +-
include/linux/pkeys.h | 59 +++++
include/uapi/asm-generic/mman-common.h | 5 +
include/uapi/asm-generic/siginfo.h | 17 +-
kernel/events/uprobes.c | 8 +-
kernel/signal.c | 4 +
mm/Kconfig | 13 +
mm/frame_vector.c | 9 +-
mm/gup.c | 153 ++++++------
mm/ksm.c | 10 +-
mm/memory.c | 8 +-
mm/mempolicy.c | 6 +-
mm/mmap.c | 2 +-
mm/mprotect.c | 134 ++++++++++-
mm/nommu.c | 35 ++-
mm/process_vm_access.c | 6 +-
mm/util.c | 4 +-
net/ceph/pagevec.c | 2 +-
security/tomoyo/domain.c | 9 +-
virt/kvm/async_pf.c | 2 +-
virt/kvm/kvm_main.c | 13 +-
78 files changed, 1410 insertions(+), 285 deletions(-)
Cc: linux-api@vger.kernel.org
Cc: linux-arch@vger.kernel.org
Cc: aarcange@redhat.com
Cc: akpm@linux-foundation.org
Cc: jack@suse.cz
Cc: kirill.shutemov@linux.intel.com
Cc: linux-api@vger.kernel.org
Cc: linux-arch@vger.kernel.org
Cc: n-horiguchi@ah.jp.nec.com
Cc: x86@kernel.org
From: Dave Hansen <hidden> Date: 2015-11-17 03:36:08
From: Dave Hansen <dave.hansen@linux.intel.com>
mprotect_key() is just like mprotect, except it also takes a
protection key as an argument. On systems that do not support
protection keys, it still works, but requires that key=0.
Otherwise it does exactly what mprotect does.
I expect it to get used like this, if you want to guarantee that
any mapping you create can *never* be accessed without the right
protection keys set up.
pkey_deny_access(11); // random pkey
int real_prot = PROT_READ|PROT_WRITE;
ptr = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
ret = mprotect_key(ptr, PAGE_SIZE, real_prot, 11);
This way, there is *no* window where the mapping is accessible
since it was always either PROT_NONE or had a protection key set.
We settled on 'unsigned long' for the type of the key here. We
only need 4 bits on x86 today, but I figured that other
architectures might need some more space.
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: linux-api@vger.kernel.org
---
b/arch/x86/include/asm/mmu_context.h | 10 +++++++--
b/include/linux/pkeys.h | 7 +++++-
b/mm/Kconfig | 7 ++++++
b/mm/mprotect.c | 36 +++++++++++++++++++++++++++++------
4 files changed, 51 insertions(+), 9 deletions(-)
diff -puN arch/x86/include/asm/mmu_context.h~pkeys-85-mprotect_pkey arch/x86/include/asm/mmu_context.h
@@ -673,3 +673,10 @@ config ARCH_USES_HIGH_VMA_FLAGSboolconfigARCH_HAS_PKEYSbool++configNR_PROTECTION_KEYS+int+# Everything supports a _single_ key, so allow folks to+# at least call APIs that take keys, but require that the+# key be 0.+default1
@@ -407,7 +409,14 @@ SYSCALL_DEFINE3(mprotect, unsigned long,/* Here we know that vma->vm_start <= nstart < vma->vm_end. */-newflags=vm_flags;+/*+*Ifthisisavanilla,non-pkeymprotect,inheritthe+*pkeyfromtheVMAweareworkingon.+*/+if(pkey==-1)+newflags=calc_vm_prot_bits(prot,vma_pkey(vma));+else+newflags=calc_vm_prot_bits(prot,pkey);newflags|=(vma->vm_flags&~(VM_READ|VM_WRITE|VM_EXEC));/* newflags >> 4 shift VM_MAY% in place of VM_% */
From: Dave Hansen <hidden> Date: 2015-11-17 03:38:06
From: Dave Hansen <dave.hansen@linux.intel.com>
This patch adds two new system calls:
int pkey_alloc(unsigned long flags, unsigned long init_access_rights)
int pkey_free(int pkey);
These establish which protection keys are valid for use by
userspace. A key which was not obtained by pkey_alloc() may not
be passed to pkey_mprotect().
In addition, the 'init_access_rights' argument to pkey_alloc() specifies
the rights that will be established for the returned pkey. For instance
pkey = pkey_alloc(flags, PKEY_DENY_WRITE);
will return with the bits set in PKRU such that writing to 'pkey' is
already denied. This keeps userspace from needing to have knowledge
about manipulating PKRU. It is still free to do so if it wishes, but
it is no longer required.
The kernel does _not_ enforce that this interface must be used for
changes to PKRU, even for keys it does not control.
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: linux-api@vger.kernel.org
---
b/arch/x86/entry/syscalls/syscall_32.tbl | 2
b/arch/x86/entry/syscalls/syscall_64.tbl | 2
b/arch/x86/include/asm/mmu.h | 7 ++
b/arch/x86/include/asm/mmu_context.h | 8 +++
b/arch/x86/include/asm/pgtable.h | 5 +-
b/arch/x86/include/asm/pkeys.h | 55 ++++++++++++++++++++++
b/arch/x86/kernel/fpu/xstate.c | 75 +++++++++++++++++++++++++++++++
b/include/asm-generic/mm_hooks.h | 1
b/include/linux/pkeys.h | 23 +++++++++
b/include/uapi/asm-generic/mman-common.h | 5 ++
b/mm/mprotect.c | 55 ++++++++++++++++++++++
11 files changed, 236 insertions(+), 2 deletions(-)
diff -puN arch/x86/entry/syscalls/syscall_32.tbl~pkey-allocation-syscalls arch/x86/entry/syscalls/syscall_32.tbl
@@ -333,6 +333,8 @@ 324 common membarrier sys_membarrier 325 common mlock2 sys_mlock2 326 common pkey_mprotect sys_pkey_mprotect+327 common pkey_alloc sys_pkey_alloc+328 common pkey_free sys_pkey_free # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -864,6 +867,78 @@ static void fpu__xfeature_set_state(intpreempt_disable();fpu__restore(fpu);preempt_enable();+}++#define NR_VALID_PKRU_BITS (CONFIG_NR_PROTECTION_KEYS * 2)+#define PKRU_VALID_MASK (NR_VALID_PKRU_BITS - 1)++/*+*ThiswillgooutandmodifytheXSAVEbuffersothatPKRUis+*settoaparticularstateforaccessto'pkey'.+*+*PKRUstatedoesaffectkernelaccesstousermemory.Wedo+*notmodfiyPKRU*itself*here,onlytheXSAVEstatethatwill+*berestoredintoPKRUwhenwereturnbacktouserspace.+*/+intarch_set_user_pkey_access(structtask_struct*tsk,intpkey,+unsignedlonginit_val)+{+structxregs_state*xsave=&tsk->thread.fpu.state.xsave;+structpkru_state*old_pkru_state;+structpkru_statenew_pkru_state;+intpkey_shift=(pkey*PKRU_BITS_PER_PKEY);+u32new_pkru_bits=0;++if(!arch_validate_pkey(pkey))+return-EINVAL;+/*+*ThischeckimpliesXSAVEsupport.OSPKEonlygets+*setifweenableXSAVEandweenablePKUinXCR0.+*/+if(!boot_cpu_has(X86_FEATURE_OSPKE))+return-EINVAL;++/* Set the bits we need in PKRU */+if(init_val&PKEY_DISABLE_ACCESS)+new_pkru_bits|=PKRU_AD_BIT;+if(init_val&PKEY_DISABLE_WRITE)+new_pkru_bits|=PKRU_WD_BIT;++/* Shift the bits in to the correct place in PKRU for pkey. */+new_pkru_bits<<=pkey_shift;++/* Locate old copy of the state in the xsave buffer */+old_pkru_state=get_xsave_addr(xsave,XFEATURE_MASK_PKRU);++/*+*Whenstateisnotinthebuffer,itisintheinit+*state,setitmanually.Otherwise,copyouttheold+*state.+*/+if(!old_pkru_state)+new_pkru_state.pkru=0;+else+new_pkru_state.pkru=old_pkru_state->pkru;++/* mask off any old bits in place */+new_pkru_state.pkru&=~((PKRU_AD_BIT|PKRU_WD_BIT)<<pkey_shift);+/* Set the newly-requested bits */+new_pkru_state.pkru|=new_pkru_bits;++/*+*Wecouldtheoreticallylivewithoutzeroingpkru.pad.+*ThecurrentXSAVEfeaturestatedefinitionsaysthat+*onlybytes0->3areused.Butwedonotwantto+*chanceleakingkernelstackouttouserspaceincasea+*memcpy()ofthewholexsavebufferwasdone.+*+*They'reinthesamecachelineanyway.+*/+new_pkru_state.pad=0;++fpu__xfeature_set_state(XFEATURE_MASK_PKRU,&new_pkru_state,+sizeof(new_pkru_state));return0;}+#endif /* CONFIG_ARCH_HAS_PKEYS */
From: Dave Hansen <hidden> Date: 2015-11-17 03:39:00
From: Dave Hansen <dave.hansen@linux.intel.com>
This establishes two more system calls for protection key management:
unsigned long pkey_get(int pkey);
int pkey_set(int pkey, unsigned long access_rights);
The return value from pkey_get() and the 'access_rights' passed
to pkey_set() are the same format: a bitmask containing
PKEY_DENY_WRITE and/or PKEY_DENY_ACCESS, or nothing set at all.
These replace userspace's direct use of rdpkru/wrpkru.
With current hardware, the kernel can not enforce that it has
control over a given key. But, this at least allows the kernel
to indicate to userspace that userspace does not control a given
protection key.
The kernel does _not_ enforce that this interface must be used for
changes to PKRU, even for keys it has not "allocated".
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: linux-api@vger.kernel.org
---
b/arch/x86/entry/syscalls/syscall_32.tbl | 2 +
b/arch/x86/entry/syscalls/syscall_64.tbl | 2 +
b/arch/x86/include/asm/mmu_context.h | 2 +
b/arch/x86/include/asm/pkeys.h | 2 -
b/arch/x86/kernel/fpu/xstate.c | 55 +++++++++++++++++++++++++++++--
b/include/linux/pkeys.h | 8 ++++
b/mm/mprotect.c | 34 +++++++++++++++++++
7 files changed, 102 insertions(+), 3 deletions(-)
diff -puN arch/x86/entry/syscalls/syscall_32.tbl~pkey-syscalls-set-get arch/x86/entry/syscalls/syscall_32.tbl
@@ -335,6 +335,8 @@ 326 common pkey_mprotect sys_pkey_mprotect 327 common pkey_alloc sys_pkey_alloc 328 common pkey_free sys_pkey_free+329 common pkey_get sys_pkey_get+330 common pkey_set sys_pkey_set # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -889,6 +890,9 @@ int arch_set_user_pkey_access(struct tasintpkey_shift=(pkey*PKRU_BITS_PER_PKEY);u32new_pkru_bits=0;+/* Only support manipulating current task for now */+if(tsk!=current)+return-EINVAL;if(!arch_validate_pkey(pkey))return-EINVAL;/*
@@ -916,7 +920,7 @@ int arch_set_user_pkey_access(struct tas*state.*/if(!old_pkru_state)-new_pkru_state.pkru=0;+new_pkru_state.pkru=PKRU_INIT_STATE;elsenew_pkru_state.pkru=old_pkru_state->pkru;
@@ -941,4 +945,51 @@ int arch_set_user_pkey_access(struct tasreturn0;}++/*+*Figuresoutwhattherightsarecurrentlyfor'pkey'.+*ConvertsfromPKRU'sformattotheuser-visiblePKEY_DISABLE_*+*format.+*/+unsignedlongarch_get_user_pkey_access(structtask_struct*tsk,intpkey)+{+structfpu*fpu=¤t->thread.fpu;+u32pkru_reg;+intret=0;++/* Only support manipulating current task for now */+if(tsk!=current)+return-1;+if(!boot_cpu_has(X86_FEATURE_OSPKE))+return-1;+/*+*ThecontentsofPKRUitselfareinvalid.Consultthe+*task'sXSAVEbufferforPKRUcontents.Thisismuch+*moreexpensivethanreadingPKRUdirectly,butshould+*berareorimpossiblewitheagerfpumode.+*/+if(!fpu->fpregs_active){+structxregs_state*xsave=&fpu->state.xsave;+structpkru_state*pkru_state=+get_xsave_addr(xsave,XFEATURE_MASK_PKRU);+/*+*PKRUisinitsinitstateandnotpresentin+*thebufferinasavedform.+*/+if(!pkru_state)+returnPKRU_INIT_STATE;++returnpkru_state->pkru;+}+/*+*Consulttheuserregisterdirectly.+*/+pkru_reg=read_pkru();+if(!__pkru_allows_read(pkru_reg,pkey))+ret|=PKEY_DISABLE_ACCESS;+if(!__pkru_allows_write(pkru_reg,pkey))+ret|=PKEY_DISABLE_WRITE;++returnret;+}#endif /* CONFIG_ARCH_HAS_PKEYS */
From: Dave Hansen <hidden> Date: 2015-11-17 03:39:26
From: Dave Hansen <dave.hansen@linux.intel.com>
This is all that we need to get the new system call itself
working on x86.
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: linux-api@vger.kernel.org
---
b/arch/x86/entry/syscalls/syscall_32.tbl | 1 +
b/arch/x86/entry/syscalls/syscall_64.tbl | 1 +
b/arch/x86/include/uapi/asm/mman.h | 6 ++++++
b/mm/Kconfig | 1 +
4 files changed, 9 insertions(+)
diff -puN arch/x86/entry/syscalls/syscall_32.tbl~pkeys-16-x86-mprotect_key arch/x86/entry/syscalls/syscall_32.tbl
@@ -332,6 +332,7 @@ 323 common userfaultfd sys_userfaultfd 324 common membarrier sys_membarrier 325 common mlock2 sys_mlock2+326 common pkey_mprotect sys_pkey_mprotect # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -679,4 +679,5 @@ config NR_PROTECTION_KEYS# Everything supports a _single_ key, so allow folks to# at least call APIs that take keys, but require that the# key be 0.+default16ifX86_INTEL_MEMORY_PROTECTION_KEYSdefault1
From: Dave Hansen <hidden> Date: 2015-11-17 03:40:39
From: Dave Hansen <dave.hansen@linux.intel.com>
This plumbs a protection key through calc_vm_flag_bits(). We
could have done this in calc_vm_prot_bits(), but I did not feel
super strongly which way to go. It was pretty arbitrary which
one to use.
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: linux-api@vger.kernel.org
Cc: linux-arch@vger.kernel.org
---
b/arch/powerpc/include/asm/mman.h | 5 +++--
b/drivers/char/agp/frontend.c | 2 +-
b/drivers/staging/android/ashmem.c | 4 ++--
b/include/linux/mman.h | 6 +++---
b/mm/mmap.c | 2 +-
b/mm/mprotect.c | 2 +-
b/mm/nommu.c | 2 +-
7 files changed, 12 insertions(+), 11 deletions(-)
diff -puN arch/powerpc/include/asm/mman.h~pkeys-84-calc_vm_prot_bits arch/powerpc/include/asm/mman.h