Skip to content

Commit d892454

Browse files
hansendctorvalds
authored andcommitted
selftests/vm/pkeys: exercise x86 XSAVE init state
On x86, there is a set of instructions used to save and restore register state collectively known as the XSAVE architecture. There are about a dozen different features managed with XSAVE. The protection keys register, PKRU, is one of those features. The hardware optimizes XSAVE by tracking when the state has not changed from its initial (init) state. In this case, it can avoid the cost of writing state to memory (it would usually just be a bunch of 0's). When the pkey register is 0x0 the hardware optionally choose to track the register as being in the init state (optimize away the writes). AMD CPUs do this more aggressively compared to Intel. On x86, PKRU is rarely in its (very permissive) init state. Instead, the value defaults to something very restrictive. It is not surprising that bugs have popped up in the rare cases when PKRU reaches its init state. Add a protection key selftest which gets the protection keys register into its init state in a way that should work on Intel and AMD. Then, do a bunch of pkey register reads to watch for inadvertent changes. This adds "-mxsave" to CFLAGS for all the x86 vm selftests in order to allow use of the XSAVE instruction __builtin functions. This will make the builtins available on all of the vm selftests, but is expected to be harmless. Link: https://lkml.kernel.org/r/20210611164202.1849B712@viggo.jf.intel.com Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com> Cc: Ram Pai <linuxram@us.ibm.com> Cc: Sandipan Das <sandipan@linux.ibm.com> Cc: Florian Weimer <fweimer@redhat.com> Cc: "Desnes A. Nunes do Rosario" <desnesn@linux.vnet.ibm.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Thiago Jung Bauermann <bauerman@linux.ibm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Michal Hocko <mhocko@kernel.org> Cc: Michal Suchanek <msuchanek@suse.de> Cc: Shuah Khan <shuah@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 6039ca2 commit d892454

File tree

3 files changed

+76
-2
lines changed

3 files changed

+76
-2
lines changed

tools/testing/selftests/vm/Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ $(1) $(1)_64: $(OUTPUT)/$(1)_64
101101
endef
102102

103103
ifeq ($(CAN_BUILD_I386),1)
104-
$(BINARIES_32): CFLAGS += -m32
104+
$(BINARIES_32): CFLAGS += -m32 -mxsave
105105
$(BINARIES_32): LDLIBS += -lrt -ldl -lm
106106
$(BINARIES_32): $(OUTPUT)/%_32: %.c
107107
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@
108108
$(foreach t,$(TARGETS),$(eval $(call gen-target-rule-32,$(t))))
109109
endif
110110

111111
ifeq ($(CAN_BUILD_X86_64),1)
112-
$(BINARIES_64): CFLAGS += -m64
112+
$(BINARIES_64): CFLAGS += -m64 -mxsave
113113
$(BINARIES_64): LDLIBS += -lrt -ldl
114114
$(BINARIES_64): $(OUTPUT)/%_64: %.c
115115
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@

tools/testing/selftests/vm/pkey-x86.h

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ static inline u32 pkey_bit_position(int pkey)
126126

127127
#define XSTATE_PKEY_BIT (9)
128128
#define XSTATE_PKEY 0x200
129+
#define XSTATE_BV_OFFSET 512
129130

130131
int pkey_reg_xstate_offset(void)
131132
{

tools/testing/selftests/vm/protection_keys.c

+73
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,78 @@ void test_pkey_alloc_exhaust(int *ptr, u16 pkey)
12771277
}
12781278
}
12791279

1280+
void arch_force_pkey_reg_init(void)
1281+
{
1282+
#if defined(__i386__) || defined(__x86_64__) /* arch */
1283+
u64 *buf;
1284+
1285+
/*
1286+
* All keys should be allocated and set to allow reads and
1287+
* writes, so the register should be all 0. If not, just
1288+
* skip the test.
1289+
*/
1290+
if (read_pkey_reg())
1291+
return;
1292+
1293+
/*
1294+
* Just allocate an absurd about of memory rather than
1295+
* doing the XSAVE size enumeration dance.
1296+
*/
1297+
buf = mmap(NULL, 1*MB, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
1298+
1299+
/* These __builtins require compiling with -mxsave */
1300+
1301+
/* XSAVE to build a valid buffer: */
1302+
__builtin_ia32_xsave(buf, XSTATE_PKEY);
1303+
/* Clear XSTATE_BV[PKRU]: */
1304+
buf[XSTATE_BV_OFFSET/sizeof(u64)] &= ~XSTATE_PKEY;
1305+
/* XRSTOR will likely get PKRU back to the init state: */
1306+
__builtin_ia32_xrstor(buf, XSTATE_PKEY);
1307+
1308+
munmap(buf, 1*MB);
1309+
#endif
1310+
}
1311+
1312+
1313+
/*
1314+
* This is mostly useless on ppc for now. But it will not
1315+
* hurt anything and should give some better coverage as
1316+
* a long-running test that continually checks the pkey
1317+
* register.
1318+
*/
1319+
void test_pkey_init_state(int *ptr, u16 pkey)
1320+
{
1321+
int err;
1322+
int allocated_pkeys[NR_PKEYS] = {0};
1323+
int nr_allocated_pkeys = 0;
1324+
int i;
1325+
1326+
for (i = 0; i < NR_PKEYS; i++) {
1327+
int new_pkey = alloc_pkey();
1328+
1329+
if (new_pkey < 0)
1330+
continue;
1331+
allocated_pkeys[nr_allocated_pkeys++] = new_pkey;
1332+
}
1333+
1334+
dprintf3("%s()::%d\n", __func__, __LINE__);
1335+
1336+
arch_force_pkey_reg_init();
1337+
1338+
/*
1339+
* Loop for a bit, hoping to get exercise the kernel
1340+
* context switch code.
1341+
*/
1342+
for (i = 0; i < 1000000; i++)
1343+
read_pkey_reg();
1344+
1345+
for (i = 0; i < nr_allocated_pkeys; i++) {
1346+
err = sys_pkey_free(allocated_pkeys[i]);
1347+
pkey_assert(!err);
1348+
read_pkey_reg(); /* for shadow checking */
1349+
}
1350+
}
1351+
12801352
/*
12811353
* pkey 0 is special. It is allocated by default, so you do not
12821354
* have to call pkey_alloc() to use it first. Make sure that it
@@ -1508,6 +1580,7 @@ void (*pkey_tests[])(int *ptr, u16 pkey) = {
15081580
test_implicit_mprotect_exec_only_memory,
15091581
test_mprotect_with_pkey_0,
15101582
test_ptrace_of_child,
1583+
test_pkey_init_state,
15111584
test_pkey_syscalls_on_non_allocated_pkey,
15121585
test_pkey_syscalls_bad_args,
15131586
test_pkey_alloc_exhaust,

0 commit comments

Comments
 (0)