aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Hansen <dave.hansen@linux.intel.com>2016-02-12 16:02:36 -0500
committerIngo Molnar <mingo@kernel.org>2016-02-18 13:46:32 -0500
commit8459429693395ca9e8d18101300b120ad9171795 (patch)
tree56a67afee6292c137696a31dabe873777304adb8
parentb8b9b6ba9dec3f155c7555cb208ba4078e97aedb (diff)
x86/mm/pkeys: Allow kernel to modify user pkey rights register
The Protection Key Rights for User memory (PKRU) is a 32-bit user-accessible register. It contains two bits for each protection key: one to write-disable (WD) access to memory covered by the key and another to access-disable (AD). Userspace can read/write the register with the RDPKRU and WRPKRU instructions. But, the register is saved and restored with the XSAVE family of instructions, which means we have to treat it like a floating point register. The kernel needs to write to the register if it wants to implement execute-only memory or if it implements a system call to change PKRU. To do this, we need to create a 'pkru_state' buffer, read the old contents in to it, modify it, and then tell the FPU code that there is modified data in there so it can (possibly) move the buffer back in to the registers. This uses the fpu__xfeature_set_state() function that we defined in the previous patch. Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Dave Hansen <dave@sr71.net> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rik van Riel <riel@redhat.com> Cc: linux-mm@kvack.org Link: http://lkml.kernel.org/r/20160212210236.0BE13217@viggo.jf.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/include/asm/pgtable.h5
-rw-r--r--arch/x86/include/asm/pkeys.h3
-rw-r--r--arch/x86/kernel/fpu/xstate.c74
-rw-r--r--include/linux/pkeys.h5
4 files changed, 85 insertions, 2 deletions
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 3cbfae80abb2..1ff49ec29ece 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -921,16 +921,17 @@ static inline pte_t pte_swp_clear_soft_dirty(pte_t pte)
921 921
922#define PKRU_AD_BIT 0x1 922#define PKRU_AD_BIT 0x1
923#define PKRU_WD_BIT 0x2 923#define PKRU_WD_BIT 0x2
924#define PKRU_BITS_PER_PKEY 2
924 925
925static inline bool __pkru_allows_read(u32 pkru, u16 pkey) 926static inline bool __pkru_allows_read(u32 pkru, u16 pkey)
926{ 927{
927 int pkru_pkey_bits = pkey * 2; 928 int pkru_pkey_bits = pkey * PKRU_BITS_PER_PKEY;
928 return !(pkru & (PKRU_AD_BIT << pkru_pkey_bits)); 929 return !(pkru & (PKRU_AD_BIT << pkru_pkey_bits));
929} 930}
930 931
931static inline bool __pkru_allows_write(u32 pkru, u16 pkey) 932static inline bool __pkru_allows_write(u32 pkru, u16 pkey)
932{ 933{
933 int pkru_pkey_bits = pkey * 2; 934 int pkru_pkey_bits = pkey * PKRU_BITS_PER_PKEY;
934 /* 935 /*
935 * Access-disable disables writes too so we need to check 936 * Access-disable disables writes too so we need to check
936 * both bits here. 937 * both bits here.
diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h
index 04243c23380c..5061aec2ed5e 100644
--- a/arch/x86/include/asm/pkeys.h
+++ b/arch/x86/include/asm/pkeys.h
@@ -3,4 +3,7 @@
3 3
4#define arch_max_pkey() (boot_cpu_has(X86_FEATURE_OSPKE) ? 16 : 1) 4#define arch_max_pkey() (boot_cpu_has(X86_FEATURE_OSPKE) ? 16 : 1)
5 5
6extern int arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
7 unsigned long init_val);
8
6#endif /*_ASM_X86_PKEYS_H */ 9#endif /*_ASM_X86_PKEYS_H */
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index 30d144f01eb9..50813c35e9d9 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -5,6 +5,7 @@
5 */ 5 */
6#include <linux/compat.h> 6#include <linux/compat.h>
7#include <linux/cpu.h> 7#include <linux/cpu.h>
8#include <linux/pkeys.h>
8 9
9#include <asm/fpu/api.h> 10#include <asm/fpu/api.h>
10#include <asm/fpu/internal.h> 11#include <asm/fpu/internal.h>
@@ -855,3 +856,76 @@ out:
855 */ 856 */
856 fpu__current_fpstate_write_end(); 857 fpu__current_fpstate_write_end();
857} 858}
859
860#define NR_VALID_PKRU_BITS (CONFIG_NR_PROTECTION_KEYS * 2)
861#define PKRU_VALID_MASK (NR_VALID_PKRU_BITS - 1)
862
863/*
864 * This will go out and modify the XSAVE buffer so that PKRU is
865 * set to a particular state for access to 'pkey'.
866 *
867 * PKRU state does affect kernel access to user memory. We do
868 * not modfiy PKRU *itself* here, only the XSAVE state that will
869 * be restored in to PKRU when we return back to userspace.
870 */
871int arch_set_user_pkey_access(struct task_struct *tsk, int pkey,
872 unsigned long init_val)
873{
874 struct xregs_state *xsave = &tsk->thread.fpu.state.xsave;
875 struct pkru_state *old_pkru_state;
876 struct pkru_state new_pkru_state;
877 int pkey_shift = (pkey * PKRU_BITS_PER_PKEY);
878 u32 new_pkru_bits = 0;
879
880 if (!validate_pkey(pkey))
881 return -EINVAL;
882 /*
883 * This check implies XSAVE support. OSPKE only gets
884 * set if we enable XSAVE and we enable PKU in XCR0.
885 */
886 if (!boot_cpu_has(X86_FEATURE_OSPKE))
887 return -EINVAL;
888
889 /* Set the bits we need in PKRU */
890 if (init_val & PKEY_DISABLE_ACCESS)
891 new_pkru_bits |= PKRU_AD_BIT;
892 if (init_val & PKEY_DISABLE_WRITE)
893 new_pkru_bits |= PKRU_WD_BIT;
894
895 /* Shift the bits in to the correct place in PKRU for pkey. */
896 new_pkru_bits <<= pkey_shift;
897
898 /* Locate old copy of the state in the xsave buffer */
899 old_pkru_state = get_xsave_addr(xsave, XFEATURE_MASK_PKRU);
900
901 /*
902 * When state is not in the buffer, it is in the init
903 * state, set it manually. Otherwise, copy out the old
904 * state.
905 */
906 if (!old_pkru_state)
907 new_pkru_state.pkru = 0;
908 else
909 new_pkru_state.pkru = old_pkru_state->pkru;
910
911 /* mask off any old bits in place */
912 new_pkru_state.pkru &= ~((PKRU_AD_BIT|PKRU_WD_BIT) << pkey_shift);
913 /* Set the newly-requested bits */
914 new_pkru_state.pkru |= new_pkru_bits;
915
916 /*
917 * We could theoretically live without zeroing pkru.pad.
918 * The current XSAVE feature state definition says that
919 * only bytes 0->3 are used. But we do not want to
920 * chance leaking kernel stack out to userspace in case a
921 * memcpy() of the whole xsave buffer was done.
922 *
923 * They're in the same cacheline anyway.
924 */
925 new_pkru_state.pad = 0;
926
927 fpu__xfeature_set_state(XFEATURE_MASK_PKRU, &new_pkru_state,
928 sizeof(new_pkru_state));
929
930 return 0;
931}
diff --git a/include/linux/pkeys.h b/include/linux/pkeys.h
index 55e465f93a28..fc325b367bd0 100644
--- a/include/linux/pkeys.h
+++ b/include/linux/pkeys.h
@@ -4,6 +4,11 @@
4#include <linux/mm_types.h> 4#include <linux/mm_types.h>
5#include <asm/mmu_context.h> 5#include <asm/mmu_context.h>
6 6
7#define PKEY_DISABLE_ACCESS 0x1
8#define PKEY_DISABLE_WRITE 0x2
9#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\
10 PKEY_DISABLE_WRITE)
11
7#ifdef CONFIG_ARCH_HAS_PKEYS 12#ifdef CONFIG_ARCH_HAS_PKEYS
8#include <asm/pkeys.h> 13#include <asm/pkeys.h>
9#else /* ! CONFIG_ARCH_HAS_PKEYS */ 14#else /* ! CONFIG_ARCH_HAS_PKEYS */