Skip to content

Commit 5f23f6d

Browse files
hansendcKAGA-KOKO
authored andcommitted
x86/pkeys: Add self-tests
This code should be a good demonstration of how to use the new system calls as well as how to use protection keys in general. This code shows how to: 1. Manipulate the Protection Keys Rights User (PKRU) register 2. Set a protection key on memory 3. Fetch and/or modify PKRU from the signal XSAVE state 4. Read the kernel-provided protection key in the siginfo 5. Set up an execute-only mapping There are currently 13 tests: test_read_of_write_disabled_region test_read_of_access_disabled_region test_write_of_write_disabled_region test_write_of_access_disabled_region test_kernel_write_of_access_disabled_region test_kernel_write_of_write_disabled_region test_kernel_gup_of_access_disabled_region test_kernel_gup_write_to_write_disabled_region test_executing_on_unreadable_memory test_ptrace_of_child test_pkey_syscalls_on_non_allocated_pkey test_pkey_syscalls_bad_args test_pkey_alloc_exhaust Each of the tests is run with plain memory (via mmap(MAP_ANON)), transparent huge pages, and hugetlb. Signed-off-by: Dave Hansen <[email protected]> Cc: [email protected] Cc: Dave Hansen <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 76de993 commit 5f23f6d

File tree

3 files changed

+1631
-1
lines changed

3 files changed

+1631
-1
lines changed

tools/testing/selftests/x86/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ include ../lib.mk
55
.PHONY: all all_32 all_64 warn_32bit_failure clean
66

77
TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \
8-
check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test
8+
check_initial_reg_state sigreturn ldt_gdt iopl \
9+
protection_keys
910
TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
1011
test_FCMOV test_FCOMI test_FISTTP \
1112
vdso_restorer
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
#ifndef _PKEYS_HELPER_H
2+
#define _PKEYS_HELPER_H
3+
#define _GNU_SOURCE
4+
#include <string.h>
5+
#include <stdarg.h>
6+
#include <stdio.h>
7+
#include <stdint.h>
8+
#include <stdbool.h>
9+
#include <signal.h>
10+
#include <assert.h>
11+
#include <stdlib.h>
12+
#include <ucontext.h>
13+
#include <sys/mman.h>
14+
15+
#define NR_PKEYS 16
16+
#define PKRU_BITS_PER_PKEY 2
17+
18+
#ifndef DEBUG_LEVEL
19+
#define DEBUG_LEVEL 0
20+
#endif
21+
#define DPRINT_IN_SIGNAL_BUF_SIZE 4096
22+
extern int dprint_in_signal;
23+
extern char dprint_in_signal_buffer[DPRINT_IN_SIGNAL_BUF_SIZE];
24+
static inline void sigsafe_printf(const char *format, ...)
25+
{
26+
va_list ap;
27+
28+
va_start(ap, format);
29+
if (!dprint_in_signal) {
30+
vprintf(format, ap);
31+
} else {
32+
int len = vsnprintf(dprint_in_signal_buffer,
33+
DPRINT_IN_SIGNAL_BUF_SIZE,
34+
format, ap);
35+
/*
36+
* len is amount that would have been printed,
37+
* but actual write is truncated at BUF_SIZE.
38+
*/
39+
if (len > DPRINT_IN_SIGNAL_BUF_SIZE)
40+
len = DPRINT_IN_SIGNAL_BUF_SIZE;
41+
write(1, dprint_in_signal_buffer, len);
42+
}
43+
va_end(ap);
44+
}
45+
#define dprintf_level(level, args...) do { \
46+
if (level <= DEBUG_LEVEL) \
47+
sigsafe_printf(args); \
48+
fflush(NULL); \
49+
} while (0)
50+
#define dprintf0(args...) dprintf_level(0, args)
51+
#define dprintf1(args...) dprintf_level(1, args)
52+
#define dprintf2(args...) dprintf_level(2, args)
53+
#define dprintf3(args...) dprintf_level(3, args)
54+
#define dprintf4(args...) dprintf_level(4, args)
55+
56+
extern unsigned int shadow_pkru;
57+
static inline unsigned int __rdpkru(void)
58+
{
59+
unsigned int eax, edx;
60+
unsigned int ecx = 0;
61+
unsigned int pkru;
62+
63+
asm volatile(".byte 0x0f,0x01,0xee\n\t"
64+
: "=a" (eax), "=d" (edx)
65+
: "c" (ecx));
66+
pkru = eax;
67+
return pkru;
68+
}
69+
70+
static inline unsigned int _rdpkru(int line)
71+
{
72+
unsigned int pkru = __rdpkru();
73+
74+
dprintf4("rdpkru(line=%d) pkru: %x shadow: %x\n",
75+
line, pkru, shadow_pkru);
76+
assert(pkru == shadow_pkru);
77+
78+
return pkru;
79+
}
80+
81+
#define rdpkru() _rdpkru(__LINE__)
82+
83+
static inline void __wrpkru(unsigned int pkru)
84+
{
85+
unsigned int eax = pkru;
86+
unsigned int ecx = 0;
87+
unsigned int edx = 0;
88+
89+
dprintf4("%s() changing %08x to %08x\n", __func__, __rdpkru(), pkru);
90+
asm volatile(".byte 0x0f,0x01,0xef\n\t"
91+
: : "a" (eax), "c" (ecx), "d" (edx));
92+
assert(pkru == __rdpkru());
93+
}
94+
95+
static inline void wrpkru(unsigned int pkru)
96+
{
97+
dprintf4("%s() changing %08x to %08x\n", __func__, __rdpkru(), pkru);
98+
/* will do the shadow check for us: */
99+
rdpkru();
100+
__wrpkru(pkru);
101+
shadow_pkru = pkru;
102+
dprintf4("%s(%08x) pkru: %08x\n", __func__, pkru, __rdpkru());
103+
}
104+
105+
/*
106+
* These are technically racy. since something could
107+
* change PKRU between the read and the write.
108+
*/
109+
static inline void __pkey_access_allow(int pkey, int do_allow)
110+
{
111+
unsigned int pkru = rdpkru();
112+
int bit = pkey * 2;
113+
114+
if (do_allow)
115+
pkru &= (1<<bit);
116+
else
117+
pkru |= (1<<bit);
118+
119+
dprintf4("pkru now: %08x\n", rdpkru());
120+
wrpkru(pkru);
121+
}
122+
123+
static inline void __pkey_write_allow(int pkey, int do_allow_write)
124+
{
125+
long pkru = rdpkru();
126+
int bit = pkey * 2 + 1;
127+
128+
if (do_allow_write)
129+
pkru &= (1<<bit);
130+
else
131+
pkru |= (1<<bit);
132+
133+
wrpkru(pkru);
134+
dprintf4("pkru now: %08x\n", rdpkru());
135+
}
136+
137+
#define PROT_PKEY0 0x10 /* protection key value (bit 0) */
138+
#define PROT_PKEY1 0x20 /* protection key value (bit 1) */
139+
#define PROT_PKEY2 0x40 /* protection key value (bit 2) */
140+
#define PROT_PKEY3 0x80 /* protection key value (bit 3) */
141+
142+
#define PAGE_SIZE 4096
143+
#define MB (1<<20)
144+
145+
static inline void __cpuid(unsigned int *eax, unsigned int *ebx,
146+
unsigned int *ecx, unsigned int *edx)
147+
{
148+
/* ecx is often an input as well as an output. */
149+
asm volatile(
150+
"cpuid;"
151+
: "=a" (*eax),
152+
"=b" (*ebx),
153+
"=c" (*ecx),
154+
"=d" (*edx)
155+
: "0" (*eax), "2" (*ecx));
156+
}
157+
158+
/* Intel-defined CPU features, CPUID level 0x00000007:0 (ecx) */
159+
#define X86_FEATURE_PKU (1<<3) /* Protection Keys for Userspace */
160+
#define X86_FEATURE_OSPKE (1<<4) /* OS Protection Keys Enable */
161+
162+
static inline int cpu_has_pku(void)
163+
{
164+
unsigned int eax;
165+
unsigned int ebx;
166+
unsigned int ecx;
167+
unsigned int edx;
168+
169+
eax = 0x7;
170+
ecx = 0x0;
171+
__cpuid(&eax, &ebx, &ecx, &edx);
172+
173+
if (!(ecx & X86_FEATURE_PKU)) {
174+
dprintf2("cpu does not have PKU\n");
175+
return 0;
176+
}
177+
if (!(ecx & X86_FEATURE_OSPKE)) {
178+
dprintf2("cpu does not have OSPKE\n");
179+
return 0;
180+
}
181+
return 1;
182+
}
183+
184+
#define XSTATE_PKRU_BIT (9)
185+
#define XSTATE_PKRU 0x200
186+
187+
int pkru_xstate_offset(void)
188+
{
189+
unsigned int eax;
190+
unsigned int ebx;
191+
unsigned int ecx;
192+
unsigned int edx;
193+
int xstate_offset;
194+
int xstate_size;
195+
unsigned long XSTATE_CPUID = 0xd;
196+
int leaf;
197+
198+
/* assume that XSTATE_PKRU is set in XCR0 */
199+
leaf = XSTATE_PKRU_BIT;
200+
{
201+
eax = XSTATE_CPUID;
202+
ecx = leaf;
203+
__cpuid(&eax, &ebx, &ecx, &edx);
204+
205+
if (leaf == XSTATE_PKRU_BIT) {
206+
xstate_offset = ebx;
207+
xstate_size = eax;
208+
}
209+
}
210+
211+
if (xstate_size == 0) {
212+
printf("could not find size/offset of PKRU in xsave state\n");
213+
return 0;
214+
}
215+
216+
return xstate_offset;
217+
}
218+
219+
#endif /* _PKEYS_HELPER_H */

0 commit comments

Comments
 (0)