1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3 * Copyright (C) 2005-2006 IBM Corporation.
4 * Author: David Gibson & Adam Litke
5 */
6
7 /*\
8 * [Description]
9 *
10 * This test uses mprotect to change protection of hugepage mapping and
11 * perform read/write operation. It checks if the operation results in
12 * expected behaviour as per the protection.
13 */
14 #include <setjmp.h>
15 #include "hugetlb.h"
16
17 #define MNTPOINT "hugetlbfs/"
18 #define RANDOM_CONSTANT 0x1234ABCD
19
20 static int fd = -1;
21 static sigjmp_buf sig_escape;
22 static void *sig_expected = MAP_FAILED;
23 static long hpage_size;
24 static void *addr;
25
26 static struct tcase {
27 char *tname;
28 unsigned long len1;
29 int prot1;
30 char *prot1_str;
31 unsigned long len2;
32 int prot2;
33 char *prot2_str;
34 } tcases[] = {
35 {"R->RW", 1, PROT_READ, "PROT_READ",
36 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
37
38 {"RW->R", 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE",
39 1, PROT_READ, "PROT_READ"},
40
41 {"R->RW 1/2", 2, PROT_READ, "PROT_READ",
42 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
43
44 {"RW->R 1/2", 2, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE",
45 1, PROT_READ, "PROT_READ"},
46
47 {"NONE->R", 1, PROT_NONE, "PROT_NONE",
48 1, PROT_READ, "PROT_READ"},
49
50 {"NONE->RW", 1, PROT_NONE, "PROT_NONE",
51 1, PROT_READ|PROT_WRITE, "PROT_READ|PROT_WRITE"},
52 };
53
sig_handler(int signum, siginfo_t *si, void *uc)54 static void sig_handler(int signum, siginfo_t *si, void *uc)
55 {
56 (void)uc;
57
58 if (signum == SIGSEGV) {
59 tst_res(TINFO, "SIGSEGV at %p (sig_expected=%p)", si->si_addr,
60 sig_expected);
61 if (si->si_addr == sig_expected)
62 siglongjmp(sig_escape, 1);
63 tst_res(TFAIL, "SIGSEGV somewhere unexpected");
64 } else {
65 tst_res(TFAIL, "Unexpected signal %s", strsignal(signum));
66 }
67 }
68
test_read(void *p)69 static int test_read(void *p)
70 {
71 volatile unsigned long *pl = p;
72 unsigned long x;
73
74 if (sigsetjmp(sig_escape, 1)) {
75 /* We got a SEGV */
76 sig_expected = MAP_FAILED;
77 return -1;
78 }
79
80 sig_expected = p;
81 barrier();
82 x = *pl;
83 tst_res(TINFO, "Read back %lu", x);
84 barrier();
85 sig_expected = MAP_FAILED;
86 return 0;
87 }
88
test_write(void *p, unsigned long val)89 static int test_write(void *p, unsigned long val)
90 {
91 volatile unsigned long *pl = p;
92 unsigned long x;
93
94 if (sigsetjmp(sig_escape, 1)) {
95 /* We got a SEGV */
96 sig_expected = MAP_FAILED;
97 return -1;
98 }
99
100 sig_expected = p;
101 barrier();
102 *pl = val;
103 x = *pl;
104 barrier();
105 sig_expected = MAP_FAILED;
106
107 return (x != val);
108 }
109
test_prot(void *p, int prot, char *prot_str)110 static int test_prot(void *p, int prot, char *prot_str)
111 {
112 int r, w;
113
114 r = test_read(p);
115 tst_res(TINFO, "On Read: %d", r);
116 w = test_write(p, RANDOM_CONSTANT);
117 tst_res(TINFO, "On Write: %d", w);
118
119 if (prot & PROT_READ) {
120 if (r != 0) {
121 tst_res(TFAIL, "read failed on mmap(prot %s)", prot_str);
122 return -1;
123 }
124 } else {
125 if (r != -1) {
126 tst_res(TFAIL, "read succeeded on mmap(prot %s)", prot_str);
127 return -1;
128 }
129 }
130
131 if (prot & PROT_WRITE) {
132 switch (w) {
133 case -1:
134 tst_res(TFAIL, "write failed on mmap(prot %s)", prot_str);
135 return -1;
136 case 0:
137 break;
138 case 1:
139 tst_res(TFAIL, "write mismatch on mmap(prot %s)", prot_str);
140 return -1;
141 default:
142 tst_res(TWARN, "Bug in test");
143 return -1;
144 }
145 } else {
146 switch (w) {
147 case -1:
148 break;
149 case 0:
150 tst_res(TFAIL, "write succeeded on mmap(prot %s)", prot_str);
151 return -1;
152 case 1:
153 tst_res(TFAIL, "write mismatch on mmap(prot %s)", prot_str);
154 return -1;
155 default:
156 tst_res(TWARN, "Bug in test");
157 break;
158 }
159 }
160
161 return 0;
162 }
163
run_test(unsigned int i)164 static void run_test(unsigned int i)
165 {
166 void *p;
167 int ret;
168 struct tcase *tc = &tcases[i];
169
170 tst_res(TINFO, "Test Name: %s", tc->tname);
171
172 p = SAFE_MMAP(NULL, tc->len1*hpage_size, tc->prot1, MAP_SHARED, fd, 0);
173
174 ret = test_prot(p, tc->prot1, tc->prot1_str);
175 if (ret)
176 goto cleanup;
177
178 ret = mprotect(p, tc->len2*hpage_size, tc->prot2);
179 if (ret != 0) {
180 tst_res(TFAIL|TERRNO, "%s: mprotect(prot %s)",
181 tc->tname, tc->prot2_str);
182 goto cleanup;
183 }
184
185 ret = test_prot(p, tc->prot2, tc->prot2_str);
186 if (ret)
187 goto cleanup;
188
189 if (tc->len2 < tc->len1)
190 ret = test_prot(p + tc->len2*hpage_size, tc->prot1, tc->prot1_str);
191
192 tst_res(TPASS, "Successfully tested mprotect %s", tc->tname);
193
194 cleanup:
195 SAFE_MUNMAP(p, tc->len1*hpage_size);
196 }
197
setup(void)198 static void setup(void)
199 {
200 struct sigaction sa = {
201 .sa_sigaction = sig_handler,
202 .sa_flags = SA_SIGINFO,
203 };
204
205 hpage_size = tst_get_hugepage_size();
206 SAFE_SIGACTION(SIGSEGV, &sa, NULL);
207
208 fd = tst_creat_unlinked(MNTPOINT, 0);
209 addr = SAFE_MMAP(NULL, 2*hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
210 memset(addr, 0, hpage_size);
211 SAFE_MUNMAP(addr, hpage_size);
212 }
213
cleanup(void)214 static void cleanup(void)
215 {
216 SAFE_MUNMAP(addr+hpage_size, hpage_size);
217 if (fd >= 0)
218 SAFE_CLOSE(fd);
219 }
220
221 static struct tst_test test = {
222 .tcnt = ARRAY_SIZE(tcases),
223 .needs_root = 1,
224 .mntpoint = MNTPOINT,
225 .needs_hugetlbfs = 1,
226 .needs_tmpdir = 1,
227 .setup = setup,
228 .cleanup = cleanup,
229 .test = run_test,
230 .hugepages = {2, TST_NEEDS},
231 };
232