1 // SPDX-License-Identifier: GPL-2.0+
4 * Copyright 2020, Sandipan Das, IBM Corp.
6 * Test if applying execute protection on pages using memory
7 * protection keys works as expected.
20 #define PPC_INST_NOP 0x60000000
21 #define PPC_INST_TRAP 0x7fe00008
22 #define PPC_INST_BLR 0x4e800020
24 static volatile sig_atomic_t fault_pkey
, fault_code
, fault_type
;
25 static volatile sig_atomic_t remaining_faults
;
26 static volatile unsigned int *fault_addr
;
27 static unsigned long pgsize
, numinsns
;
28 static unsigned int *insns
;
30 static void trap_handler(int signum
, siginfo_t
*sinfo
, void *ctx
)
32 /* Check if this fault originated from the expected address */
33 if (sinfo
->si_addr
!= (void *) fault_addr
)
34 sigsafe_err("got a fault for an unexpected address\n");
39 static void segv_handler(int signum
, siginfo_t
*sinfo
, void *ctx
)
43 signal_pkey
= siginfo_pkey(sinfo
);
44 fault_code
= sinfo
->si_code
;
46 /* Check if this fault originated from the expected address */
47 if (sinfo
->si_addr
!= (void *) fault_addr
) {
48 sigsafe_err("got a fault for an unexpected address\n");
52 /* Check if too many faults have occurred for a single test case */
53 if (!remaining_faults
) {
54 sigsafe_err("got too many faults for the same address\n");
59 /* Restore permissions in order to continue */
62 if (mprotect(insns
, pgsize
, PROT_READ
| PROT_WRITE
)) {
63 sigsafe_err("failed to set access permissions\n");
68 if (signal_pkey
!= fault_pkey
) {
69 sigsafe_err("got a fault for an unexpected pkey\n");
74 case PKEY_DISABLE_ACCESS
:
75 pkey_set_rights(fault_pkey
, 0);
77 case PKEY_DISABLE_EXECUTE
:
79 * Reassociate the exec-only pkey with the region
80 * to be able to continue. Unlike AMR, we cannot
81 * set IAMR directly from userspace to restore the
84 if (mprotect(insns
, pgsize
, PROT_EXEC
)) {
85 sigsafe_err("failed to set execute permissions\n");
90 sigsafe_err("got a fault with an unexpected type\n");
95 sigsafe_err("got a fault with an unexpected code\n");
102 static int test(void)
104 struct sigaction segv_act
, trap_act
;
105 unsigned long rights
;
108 ret
= pkeys_unsupported();
112 /* Setup SIGSEGV handler */
113 segv_act
.sa_handler
= 0;
114 segv_act
.sa_sigaction
= segv_handler
;
115 FAIL_IF(sigprocmask(SIG_SETMASK
, 0, &segv_act
.sa_mask
) != 0);
116 segv_act
.sa_flags
= SA_SIGINFO
;
117 segv_act
.sa_restorer
= 0;
118 FAIL_IF(sigaction(SIGSEGV
, &segv_act
, NULL
) != 0);
120 /* Setup SIGTRAP handler */
121 trap_act
.sa_handler
= 0;
122 trap_act
.sa_sigaction
= trap_handler
;
123 FAIL_IF(sigprocmask(SIG_SETMASK
, 0, &trap_act
.sa_mask
) != 0);
124 trap_act
.sa_flags
= SA_SIGINFO
;
125 trap_act
.sa_restorer
= 0;
126 FAIL_IF(sigaction(SIGTRAP
, &trap_act
, NULL
) != 0);
128 /* Setup executable region */
129 pgsize
= getpagesize();
130 numinsns
= pgsize
/ sizeof(unsigned int);
131 insns
= (unsigned int *) mmap(NULL
, pgsize
, PROT_READ
| PROT_WRITE
,
132 MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0);
133 FAIL_IF(insns
== MAP_FAILED
);
135 /* Write the instruction words */
136 for (i
= 1; i
< numinsns
- 1; i
++)
137 insns
[i
] = PPC_INST_NOP
;
140 * Set the first instruction as an unconditional trap. If
141 * the last write to this address succeeds, this should
142 * get overwritten by a no-op.
144 insns
[0] = PPC_INST_TRAP
;
147 * Later, to jump to the executable region, we use a branch
148 * and link instruction (bctrl) which sets the return address
149 * automatically in LR. Use that to return back.
151 insns
[numinsns
- 1] = PPC_INST_BLR
;
153 /* Allocate a pkey that restricts execution */
154 rights
= PKEY_DISABLE_EXECUTE
;
155 pkey
= sys_pkey_alloc(0, rights
);
159 * Pick the first instruction's address from the executable
164 /* The following two cases will avoid SEGV_PKUERR */
169 * Read an instruction word from the address when AMR bits
170 * are not set i.e. the pkey permits both read and write
173 * This should not generate a fault as having PROT_EXEC
174 * implies PROT_READ on GNU systems. The pkey currently
175 * restricts execution only based on the IAMR bits. The
176 * AMR bits are cleared.
178 remaining_faults
= 0;
179 FAIL_IF(sys_pkey_mprotect(insns
, pgsize
, PROT_EXEC
, pkey
) != 0);
180 printf("read from %p, pkey permissions are %s\n", fault_addr
,
181 pkey_rights(rights
));
183 FAIL_IF(remaining_faults
!= 0);
186 * Write an instruction word to the address when AMR bits
187 * are not set i.e. the pkey permits both read and write
190 * This should generate an access fault as having just
191 * PROT_EXEC also restricts writes. The pkey currently
192 * restricts execution only based on the IAMR bits. The
193 * AMR bits are cleared.
195 remaining_faults
= 1;
196 FAIL_IF(sys_pkey_mprotect(insns
, pgsize
, PROT_EXEC
, pkey
) != 0);
197 printf("write to %p, pkey permissions are %s\n", fault_addr
,
198 pkey_rights(rights
));
199 *fault_addr
= PPC_INST_TRAP
;
200 FAIL_IF(remaining_faults
!= 0 || fault_code
!= SEGV_ACCERR
);
202 /* The following three cases will generate SEGV_PKUERR */
203 rights
|= PKEY_DISABLE_ACCESS
;
204 fault_type
= PKEY_DISABLE_ACCESS
;
208 * Read an instruction word from the address when AMR bits
209 * are set i.e. the pkey permits neither read nor write
212 * This should generate a pkey fault based on AMR bits only
213 * as having PROT_EXEC implicitly allows reads.
215 remaining_faults
= 1;
216 FAIL_IF(sys_pkey_mprotect(insns
, pgsize
, PROT_EXEC
, pkey
) != 0);
217 pkey_set_rights(pkey
, rights
);
218 printf("read from %p, pkey permissions are %s\n", fault_addr
,
219 pkey_rights(rights
));
221 FAIL_IF(remaining_faults
!= 0 || fault_code
!= SEGV_PKUERR
);
224 * Write an instruction word to the address when AMR bits
225 * are set i.e. the pkey permits neither read nor write
228 * This should generate two faults. First, a pkey fault
229 * based on AMR bits and then an access fault since
230 * PROT_EXEC does not allow writes.
232 remaining_faults
= 2;
233 FAIL_IF(sys_pkey_mprotect(insns
, pgsize
, PROT_EXEC
, pkey
) != 0);
234 pkey_set_rights(pkey
, rights
);
235 printf("write to %p, pkey permissions are %s\n", fault_addr
,
236 pkey_rights(rights
));
237 *fault_addr
= PPC_INST_NOP
;
238 FAIL_IF(remaining_faults
!= 0 || fault_code
!= SEGV_ACCERR
);
240 /* Free the current pkey */
246 * Allocate pkeys with all valid combinations of read,
247 * write and execute restrictions.
249 pkey
= sys_pkey_alloc(0, rights
);
253 * Jump to the executable region. AMR bits may or may not
254 * be set but they should not affect execution.
256 * This should generate pkey faults based on IAMR bits which
257 * may be set to restrict execution.
259 * The first iteration also checks if the overwrite of the
260 * first instruction word from a trap to a no-op succeeded.
264 remaining_faults
= 0;
265 if (rights
& PKEY_DISABLE_EXECUTE
) {
266 fault_type
= PKEY_DISABLE_EXECUTE
;
267 remaining_faults
= 1;
270 FAIL_IF(sys_pkey_mprotect(insns
, pgsize
, PROT_EXEC
, pkey
) != 0);
271 printf("execute at %p, pkey permissions are %s\n", fault_addr
,
272 pkey_rights(rights
));
273 asm volatile("mtctr %0; bctrl" : : "r"(insns
));
274 FAIL_IF(remaining_faults
!= 0);
275 if (rights
& PKEY_DISABLE_EXECUTE
)
276 FAIL_IF(fault_code
!= SEGV_PKUERR
);
278 /* Free the current pkey */
281 /* Find next valid combination of pkey rights */
282 rights
= next_pkey_rights(rights
);
286 munmap((void *) insns
, pgsize
);
293 test_harness(test
, "pkey_exec_prot");