1 // SPDX-License-Identifier: GPL-2.0
2 
3 #ifdef __aarch64__
4 #include <asm/hwcap.h>
5 #endif
6 
7 #include <linux/mman.h>
8 #include <linux/prctl.h>
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/auxv.h>
13 #include <sys/prctl.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 
17 #include "../kselftest_harness.h"
18 
19 #ifndef __aarch64__
20 # define PROT_BTI	0
21 #endif
22 
23 TEST(prctl_flags)
24 {
25 	EXPECT_LT(prctl(PR_SET_MDWE, 7L, 0L, 0L, 0L), 0);
26 	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 7L, 0L, 0L), 0);
27 	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 7L, 0L), 0);
28 	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 0L, 7L), 0);
29 
30 	EXPECT_LT(prctl(PR_GET_MDWE, 7L, 0L, 0L, 0L), 0);
31 	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 7L, 0L, 0L), 0);
32 	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 7L, 0L), 0);
33 	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 0L, 7L), 0);
34 }
35 
36 FIXTURE(mdwe)
37 {
38 	void *p;
39 	int flags;
40 	size_t size;
41 	pid_t pid;
42 };
43 
44 FIXTURE_VARIANT(mdwe)
45 {
46 	bool enabled;
47 	bool forked;
48 };
49 
50 FIXTURE_VARIANT_ADD(mdwe, stock)
51 {
52         .enabled = false,
53 	.forked = false,
54 };
55 
56 FIXTURE_VARIANT_ADD(mdwe, enabled)
57 {
58         .enabled = true,
59 	.forked = false,
60 };
61 
62 FIXTURE_VARIANT_ADD(mdwe, forked)
63 {
64         .enabled = true,
65 	.forked = true,
66 };
67 
68 FIXTURE_SETUP(mdwe)
69 {
70 	int ret, status;
71 
72 	self->p = NULL;
73 	self->flags = MAP_SHARED | MAP_ANONYMOUS;
74 	self->size = getpagesize();
75 
76 	if (!variant->enabled)
77 		return;
78 
79 	ret = prctl(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0L, 0L, 0L);
80 	ASSERT_EQ(ret, 0) {
81 		TH_LOG("PR_SET_MDWE failed or unsupported");
82 	}
83 
84 	ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L);
85 	ASSERT_EQ(ret, 1);
86 
87 	if (variant->forked) {
88 		self->pid = fork();
89 		ASSERT_GE(self->pid, 0) {
90 			TH_LOG("fork failed\n");
91 		}
92 
93 		if (self->pid > 0) {
94 			ret = waitpid(self->pid, &status, 0);
95 			ASSERT_TRUE(WIFEXITED(status));
96 			exit(WEXITSTATUS(status));
97 		}
98 	}
99 }
100 
101 FIXTURE_TEARDOWN(mdwe)
102 {
103 	if (self->p && self->p != MAP_FAILED)
104 		munmap(self->p, self->size);
105 }
106 
107 TEST_F(mdwe, mmap_READ_EXEC)
108 {
109 	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
110 	EXPECT_NE(self->p, MAP_FAILED);
111 }
112 
113 TEST_F(mdwe, mmap_WRITE_EXEC)
114 {
115 	self->p = mmap(NULL, self->size, PROT_WRITE | PROT_EXEC, self->flags, 0, 0);
116 	if (variant->enabled) {
117 		EXPECT_EQ(self->p, MAP_FAILED);
118 	} else {
119 		EXPECT_NE(self->p, MAP_FAILED);
120 	}
121 }
122 
123 TEST_F(mdwe, mprotect_stay_EXEC)
124 {
125 	int ret;
126 
127 	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
128 	ASSERT_NE(self->p, MAP_FAILED);
129 
130 	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
131 	EXPECT_EQ(ret, 0);
132 }
133 
134 TEST_F(mdwe, mprotect_add_EXEC)
135 {
136 	int ret;
137 
138 	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
139 	ASSERT_NE(self->p, MAP_FAILED);
140 
141 	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
142 	if (variant->enabled) {
143 		EXPECT_LT(ret, 0);
144 	} else {
145 		EXPECT_EQ(ret, 0);
146 	}
147 }
148 
149 TEST_F(mdwe, mprotect_WRITE_EXEC)
150 {
151 	int ret;
152 
153 	self->p = mmap(NULL, self->size, PROT_WRITE, self->flags, 0, 0);
154 	ASSERT_NE(self->p, MAP_FAILED);
155 
156 	ret = mprotect(self->p, self->size, PROT_WRITE | PROT_EXEC);
157 	if (variant->enabled) {
158 		EXPECT_LT(ret, 0);
159 	} else {
160 		EXPECT_EQ(ret, 0);
161 	}
162 }
163 
164 TEST_F(mdwe, mmap_FIXED)
165 {
166 	void *p;
167 
168 	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
169 	ASSERT_NE(self->p, MAP_FAILED);
170 
171 	/* MAP_FIXED unmaps the existing page before mapping which is allowed */
172 	p = mmap(self->p, self->size, PROT_READ | PROT_EXEC,
173 		 self->flags | MAP_FIXED, 0, 0);
174 	EXPECT_EQ(p, self->p);
175 }
176 
177 TEST_F(mdwe, arm64_BTI)
178 {
179 	int ret;
180 
181 #ifdef __aarch64__
182 	if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI))
183 #endif
184 		SKIP(return, "HWCAP2_BTI not supported");
185 
186 	self->p = mmap(NULL, self->size, PROT_EXEC, self->flags, 0, 0);
187 	ASSERT_NE(self->p, MAP_FAILED);
188 
189 	ret = mprotect(self->p, self->size, PROT_EXEC | PROT_BTI);
190 	EXPECT_EQ(ret, 0);
191 }
192 
193 TEST_HARNESS_MAIN
194