1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef _PKEYS_HELPER_H |
3 | #define _PKEYS_HELPER_H |
4 | #define _GNU_SOURCE |
5 | #include <string.h> |
6 | #include <stdarg.h> |
7 | #include <stdio.h> |
8 | #include <stdint.h> |
9 | #include <stdbool.h> |
10 | #include <signal.h> |
11 | #include <assert.h> |
12 | #include <stdlib.h> |
13 | #include <ucontext.h> |
14 | #include <sys/mman.h> |
15 | |
16 | #include "../kselftest.h" |
17 | |
18 | /* Define some kernel-like types */ |
19 | #define u8 __u8 |
20 | #define u16 __u16 |
21 | #define u32 __u32 |
22 | #define u64 __u64 |
23 | |
24 | #define PTR_ERR_ENOTSUP ((void *)-ENOTSUP) |
25 | |
26 | #ifndef DEBUG_LEVEL |
27 | #define DEBUG_LEVEL 0 |
28 | #endif |
29 | #define DPRINT_IN_SIGNAL_BUF_SIZE 4096 |
30 | extern int dprint_in_signal; |
31 | extern char dprint_in_signal_buffer[DPRINT_IN_SIGNAL_BUF_SIZE]; |
32 | |
33 | extern int test_nr; |
34 | extern int iteration_nr; |
35 | |
36 | #ifdef __GNUC__ |
37 | __printf(1, 2) |
38 | #endif |
39 | static inline void sigsafe_printf(const char *format, ...) |
40 | { |
41 | va_list ap; |
42 | |
43 | if (!dprint_in_signal) { |
44 | va_start(ap, format); |
45 | vprintf(format, ap); |
46 | va_end(ap); |
47 | } else { |
48 | int ret; |
49 | /* |
50 | * No printf() functions are signal-safe. |
51 | * They deadlock easily. Write the format |
52 | * string to get some output, even if |
53 | * incomplete. |
54 | */ |
55 | ret = write(1, format, strlen(format)); |
56 | if (ret < 0) |
57 | exit(1); |
58 | } |
59 | } |
60 | #define dprintf_level(level, args...) do { \ |
61 | if (level <= DEBUG_LEVEL) \ |
62 | sigsafe_printf(args); \ |
63 | } while (0) |
64 | #define dprintf0(args...) dprintf_level(0, args) |
65 | #define dprintf1(args...) dprintf_level(1, args) |
66 | #define dprintf2(args...) dprintf_level(2, args) |
67 | #define dprintf3(args...) dprintf_level(3, args) |
68 | #define dprintf4(args...) dprintf_level(4, args) |
69 | |
70 | extern void abort_hooks(void); |
71 | #define pkey_assert(condition) do { \ |
72 | if (!(condition)) { \ |
73 | dprintf0("assert() at %s::%d test_nr: %d iteration: %d\n", \ |
74 | __FILE__, __LINE__, \ |
75 | test_nr, iteration_nr); \ |
76 | dprintf0("errno at assert: %d", errno); \ |
77 | abort_hooks(); \ |
78 | exit(__LINE__); \ |
79 | } \ |
80 | } while (0) |
81 | |
82 | __attribute__((noinline)) int read_ptr(int *ptr); |
83 | void expected_pkey_fault(int pkey); |
84 | int sys_pkey_alloc(unsigned long flags, unsigned long init_val); |
85 | int sys_pkey_free(unsigned long pkey); |
86 | int mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot, |
87 | unsigned long pkey); |
88 | void record_pkey_malloc(void *ptr, long size, int prot); |
89 | |
90 | #if defined(__i386__) || defined(__x86_64__) /* arch */ |
91 | #include "pkey-x86.h" |
92 | #elif defined(__powerpc64__) /* arch */ |
93 | #include "pkey-powerpc.h" |
94 | #else /* arch */ |
95 | #error Architecture not supported |
96 | #endif /* arch */ |
97 | |
98 | #define PKEY_MASK (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE) |
99 | |
100 | static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags) |
101 | { |
102 | u32 shift = pkey_bit_position(pkey); |
103 | /* mask out bits from pkey in old value */ |
104 | reg &= ~((u64)PKEY_MASK << shift); |
105 | /* OR in new bits for pkey */ |
106 | reg |= (flags & PKEY_MASK) << shift; |
107 | return reg; |
108 | } |
109 | |
110 | static inline u64 get_pkey_bits(u64 reg, int pkey) |
111 | { |
112 | u32 shift = pkey_bit_position(pkey); |
113 | /* |
114 | * shift down the relevant bits to the lowest two, then |
115 | * mask off all the other higher bits |
116 | */ |
117 | return ((reg >> shift) & PKEY_MASK); |
118 | } |
119 | |
120 | extern u64 shadow_pkey_reg; |
121 | |
122 | static inline u64 _read_pkey_reg(int line) |
123 | { |
124 | u64 pkey_reg = __read_pkey_reg(); |
125 | |
126 | dprintf4("read_pkey_reg(line=%d) pkey_reg: %016llx" |
127 | " shadow: %016llx\n" , |
128 | line, pkey_reg, shadow_pkey_reg); |
129 | assert(pkey_reg == shadow_pkey_reg); |
130 | |
131 | return pkey_reg; |
132 | } |
133 | |
134 | #define read_pkey_reg() _read_pkey_reg(__LINE__) |
135 | |
136 | static inline void write_pkey_reg(u64 pkey_reg) |
137 | { |
138 | dprintf4("%s() changing %016llx to %016llx\n" , __func__, |
139 | __read_pkey_reg(), pkey_reg); |
140 | /* will do the shadow check for us: */ |
141 | read_pkey_reg(); |
142 | __write_pkey_reg(pkey_reg); |
143 | shadow_pkey_reg = pkey_reg; |
144 | dprintf4("%s(%016llx) pkey_reg: %016llx\n" , __func__, |
145 | pkey_reg, __read_pkey_reg()); |
146 | } |
147 | |
148 | /* |
149 | * These are technically racy. since something could |
150 | * change PKEY register between the read and the write. |
151 | */ |
152 | static inline void __pkey_access_allow(int pkey, int do_allow) |
153 | { |
154 | u64 pkey_reg = read_pkey_reg(); |
155 | int bit = pkey * 2; |
156 | |
157 | if (do_allow) |
158 | pkey_reg &= (1<<bit); |
159 | else |
160 | pkey_reg |= (1<<bit); |
161 | |
162 | dprintf4("pkey_reg now: %016llx\n" , read_pkey_reg()); |
163 | write_pkey_reg(pkey_reg); |
164 | } |
165 | |
166 | static inline void __pkey_write_allow(int pkey, int do_allow_write) |
167 | { |
168 | u64 pkey_reg = read_pkey_reg(); |
169 | int bit = pkey * 2 + 1; |
170 | |
171 | if (do_allow_write) |
172 | pkey_reg &= (1<<bit); |
173 | else |
174 | pkey_reg |= (1<<bit); |
175 | |
176 | write_pkey_reg(pkey_reg); |
177 | dprintf4("pkey_reg now: %016llx\n" , read_pkey_reg()); |
178 | } |
179 | |
180 | #define ALIGN_UP(x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1)) |
181 | #define ALIGN_DOWN(x, align_to) ((x) & ~((align_to)-1)) |
182 | #define ALIGN_PTR_UP(p, ptr_align_to) \ |
183 | ((typeof(p))ALIGN_UP((unsigned long)(p), ptr_align_to)) |
184 | #define ALIGN_PTR_DOWN(p, ptr_align_to) \ |
185 | ((typeof(p))ALIGN_DOWN((unsigned long)(p), ptr_align_to)) |
186 | #define __stringify_1(x...) #x |
187 | #define __stringify(x...) __stringify_1(x) |
188 | |
189 | static inline u32 *siginfo_get_pkey_ptr(siginfo_t *si) |
190 | { |
191 | #ifdef si_pkey |
192 | return &si->si_pkey; |
193 | #else |
194 | return (u32 *)(((u8 *)si) + si_pkey_offset); |
195 | #endif |
196 | } |
197 | |
198 | static inline int kernel_has_pkeys(void) |
199 | { |
200 | /* try allocating a key and see if it succeeds */ |
201 | int ret = sys_pkey_alloc(flags: 0, init_val: 0); |
202 | if (ret <= 0) { |
203 | return 0; |
204 | } |
205 | sys_pkey_free(pkey: ret); |
206 | return 1; |
207 | } |
208 | |
209 | static inline int is_pkeys_supported(void) |
210 | { |
211 | /* check if the cpu supports pkeys */ |
212 | if (!cpu_has_pkeys()) { |
213 | dprintf1("SKIP: %s: no CPU support\n" , __func__); |
214 | return 0; |
215 | } |
216 | |
217 | /* check if the kernel supports pkeys */ |
218 | if (!kernel_has_pkeys()) { |
219 | dprintf1("SKIP: %s: no kernel support\n" , __func__); |
220 | return 0; |
221 | } |
222 | |
223 | return 1; |
224 | } |
225 | |
226 | #endif /* _PKEYS_HELPER_H */ |
227 | |