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