Skip to content

Commit d3de407

Browse files
committed
Merge: Add a test for execute-only memory
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/5386 Description: Add a test for execute-only memory JIRA: https://issues.redhat.com/browse/RHEL-52742 Build Info: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=64866266 Tested: Verified Brew build test kernel RPMs Signed-off-by: Mamatha Inamdar <minamdar@redhat.com> commit 98acee3 Author: Nicholas Miehlbradt <nicholas@linux.ibm.com> Date: Wed Aug 17 15:06:40 2022 +1000 selftests/powerpc: Add a test for execute-only memory This selftest is designed to cover execute-only protections on the Radix MMU but will also work with Hash. The tests are based on those found in pkey_exec_test with modifications to use the generic mprotect() instead of the pkey variants. Signed-off-by: Nicholas Miehlbradt <nicholas@linux.ibm.com> Signed-off-by: Russell Currey <ruscur@russell.cc> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20220817050640.406017-2-ruscur@russell.cc Signed-off-by: Mamatha Inamdar <minamdar@redhat.com> Approved-by: Steve Best <sbest@redhat.com> Approved-by: Tony Camuso <tcamuso@redhat.com> Approved-by: John B. Wyatt IV <jwyatt@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Rado Vrbovsky <rvrbovsk@redhat.com>
2 parents b1704f8 + fdeaba4 commit d3de407

File tree

2 files changed

+233
-1
lines changed

2 files changed

+233
-1
lines changed

tools/testing/selftests/powerpc/mm/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ noarg:
33
$(MAKE) -C ../
44

55
TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr \
6-
large_vm_fork_separation bad_accesses pkey_exec_prot \
6+
large_vm_fork_separation bad_accesses exec_prot pkey_exec_prot \
77
pkey_siginfo stack_expansion_signal stack_expansion_ldst
88
TEST_PROGS := stress_code_patching.sh
99

@@ -20,6 +20,7 @@ $(OUTPUT)/prot_sao: ../utils.c
2020
$(OUTPUT)/wild_bctr: CFLAGS += -m64
2121
$(OUTPUT)/large_vm_fork_separation: CFLAGS += -m64
2222
$(OUTPUT)/bad_accesses: CFLAGS += -m64
23+
$(OUTPUT)/exec_prot: CFLAGS += -m64
2324
$(OUTPUT)/pkey_exec_prot: CFLAGS += -m64
2425
$(OUTPUT)/pkey_siginfo: CFLAGS += -m64
2526

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* Copyright 2022, Nicholas Miehlbradt, IBM Corporation
5+
* based on pkey_exec_prot.c
6+
*
7+
* Test if applying execute protection on pages works as expected.
8+
*/
9+
10+
#define _GNU_SOURCE
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <string.h>
14+
#include <signal.h>
15+
16+
#include <unistd.h>
17+
#include <sys/mman.h>
18+
19+
#include "pkeys.h"
20+
21+
22+
#define PPC_INST_NOP 0x60000000
23+
#define PPC_INST_TRAP 0x7fe00008
24+
#define PPC_INST_BLR 0x4e800020
25+
26+
static volatile sig_atomic_t fault_code;
27+
static volatile sig_atomic_t remaining_faults;
28+
static volatile unsigned int *fault_addr;
29+
static unsigned long pgsize, numinsns;
30+
static unsigned int *insns;
31+
static bool pkeys_supported;
32+
33+
static bool is_fault_expected(int fault_code)
34+
{
35+
if (fault_code == SEGV_ACCERR)
36+
return true;
37+
38+
/* Assume any pkey error is fine since pkey_exec_prot test covers them */
39+
if (fault_code == SEGV_PKUERR && pkeys_supported)
40+
return true;
41+
42+
return false;
43+
}
44+
45+
static void trap_handler(int signum, siginfo_t *sinfo, void *ctx)
46+
{
47+
/* Check if this fault originated from the expected address */
48+
if (sinfo->si_addr != (void *)fault_addr)
49+
sigsafe_err("got a fault for an unexpected address\n");
50+
51+
_exit(1);
52+
}
53+
54+
static void segv_handler(int signum, siginfo_t *sinfo, void *ctx)
55+
{
56+
fault_code = sinfo->si_code;
57+
58+
/* Check if this fault originated from the expected address */
59+
if (sinfo->si_addr != (void *)fault_addr) {
60+
sigsafe_err("got a fault for an unexpected address\n");
61+
_exit(1);
62+
}
63+
64+
/* Check if too many faults have occurred for a single test case */
65+
if (!remaining_faults) {
66+
sigsafe_err("got too many faults for the same address\n");
67+
_exit(1);
68+
}
69+
70+
71+
/* Restore permissions in order to continue */
72+
if (is_fault_expected(fault_code)) {
73+
if (mprotect(insns, pgsize, PROT_READ | PROT_WRITE | PROT_EXEC)) {
74+
sigsafe_err("failed to set access permissions\n");
75+
_exit(1);
76+
}
77+
} else {
78+
sigsafe_err("got a fault with an unexpected code\n");
79+
_exit(1);
80+
}
81+
82+
remaining_faults--;
83+
}
84+
85+
static int check_exec_fault(int rights)
86+
{
87+
/*
88+
* Jump to the executable region.
89+
*
90+
* The first iteration also checks if the overwrite of the
91+
* first instruction word from a trap to a no-op succeeded.
92+
*/
93+
fault_code = -1;
94+
remaining_faults = 0;
95+
if (!(rights & PROT_EXEC))
96+
remaining_faults = 1;
97+
98+
FAIL_IF(mprotect(insns, pgsize, rights) != 0);
99+
asm volatile("mtctr %0; bctrl" : : "r"(insns));
100+
101+
FAIL_IF(remaining_faults != 0);
102+
if (!(rights & PROT_EXEC))
103+
FAIL_IF(!is_fault_expected(fault_code));
104+
105+
return 0;
106+
}
107+
108+
static int test(void)
109+
{
110+
struct sigaction segv_act, trap_act;
111+
int i;
112+
113+
/* Skip the test if the CPU doesn't support Radix */
114+
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00));
115+
116+
/* Check if pkeys are supported */
117+
pkeys_supported = pkeys_unsupported() == 0;
118+
119+
/* Setup SIGSEGV handler */
120+
segv_act.sa_handler = 0;
121+
segv_act.sa_sigaction = segv_handler;
122+
FAIL_IF(sigprocmask(SIG_SETMASK, 0, &segv_act.sa_mask) != 0);
123+
segv_act.sa_flags = SA_SIGINFO;
124+
segv_act.sa_restorer = 0;
125+
FAIL_IF(sigaction(SIGSEGV, &segv_act, NULL) != 0);
126+
127+
/* Setup SIGTRAP handler */
128+
trap_act.sa_handler = 0;
129+
trap_act.sa_sigaction = trap_handler;
130+
FAIL_IF(sigprocmask(SIG_SETMASK, 0, &trap_act.sa_mask) != 0);
131+
trap_act.sa_flags = SA_SIGINFO;
132+
trap_act.sa_restorer = 0;
133+
FAIL_IF(sigaction(SIGTRAP, &trap_act, NULL) != 0);
134+
135+
/* Setup executable region */
136+
pgsize = getpagesize();
137+
numinsns = pgsize / sizeof(unsigned int);
138+
insns = (unsigned int *)mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
139+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
140+
FAIL_IF(insns == MAP_FAILED);
141+
142+
/* Write the instruction words */
143+
for (i = 1; i < numinsns - 1; i++)
144+
insns[i] = PPC_INST_NOP;
145+
146+
/*
147+
* Set the first instruction as an unconditional trap. If
148+
* the last write to this address succeeds, this should
149+
* get overwritten by a no-op.
150+
*/
151+
insns[0] = PPC_INST_TRAP;
152+
153+
/*
154+
* Later, to jump to the executable region, we use a branch
155+
* and link instruction (bctrl) which sets the return address
156+
* automatically in LR. Use that to return back.
157+
*/
158+
insns[numinsns - 1] = PPC_INST_BLR;
159+
160+
/*
161+
* Pick the first instruction's address from the executable
162+
* region.
163+
*/
164+
fault_addr = insns;
165+
166+
/*
167+
* Read an instruction word from the address when the page
168+
* is execute only. This should generate an access fault.
169+
*/
170+
fault_code = -1;
171+
remaining_faults = 1;
172+
printf("Testing read on --x, should fault...");
173+
FAIL_IF(mprotect(insns, pgsize, PROT_EXEC) != 0);
174+
i = *fault_addr;
175+
FAIL_IF(remaining_faults != 0 || !is_fault_expected(fault_code));
176+
printf("ok!\n");
177+
178+
/*
179+
* Write an instruction word to the address when the page
180+
* execute only. This should also generate an access fault.
181+
*/
182+
fault_code = -1;
183+
remaining_faults = 1;
184+
printf("Testing write on --x, should fault...");
185+
FAIL_IF(mprotect(insns, pgsize, PROT_EXEC) != 0);
186+
*fault_addr = PPC_INST_NOP;
187+
FAIL_IF(remaining_faults != 0 || !is_fault_expected(fault_code));
188+
printf("ok!\n");
189+
190+
printf("Testing exec on ---, should fault...");
191+
FAIL_IF(check_exec_fault(PROT_NONE));
192+
printf("ok!\n");
193+
194+
printf("Testing exec on r--, should fault...");
195+
FAIL_IF(check_exec_fault(PROT_READ));
196+
printf("ok!\n");
197+
198+
printf("Testing exec on -w-, should fault...");
199+
FAIL_IF(check_exec_fault(PROT_WRITE));
200+
printf("ok!\n");
201+
202+
printf("Testing exec on rw-, should fault...");
203+
FAIL_IF(check_exec_fault(PROT_READ | PROT_WRITE));
204+
printf("ok!\n");
205+
206+
printf("Testing exec on --x, should succeed...");
207+
FAIL_IF(check_exec_fault(PROT_EXEC));
208+
printf("ok!\n");
209+
210+
printf("Testing exec on r-x, should succeed...");
211+
FAIL_IF(check_exec_fault(PROT_READ | PROT_EXEC));
212+
printf("ok!\n");
213+
214+
printf("Testing exec on -wx, should succeed...");
215+
FAIL_IF(check_exec_fault(PROT_WRITE | PROT_EXEC));
216+
printf("ok!\n");
217+
218+
printf("Testing exec on rwx, should succeed...");
219+
FAIL_IF(check_exec_fault(PROT_READ | PROT_WRITE | PROT_EXEC));
220+
printf("ok!\n");
221+
222+
/* Cleanup */
223+
FAIL_IF(munmap((void *)insns, pgsize));
224+
225+
return 0;
226+
}
227+
228+
int main(void)
229+
{
230+
return test_harness(test, "exec_prot");
231+
}

0 commit comments

Comments
 (0)