t_mmap.c revision 1.17 1 1.17 gson /* $NetBSD: t_mmap.c,v 1.17 2022/04/06 10:02:55 gson Exp $ */
2 1.1 jruoho
3 1.1 jruoho /*-
4 1.1 jruoho * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 1.1 jruoho * All rights reserved.
6 1.1 jruoho *
7 1.1 jruoho * This code is derived from software contributed to The NetBSD Foundation
8 1.1 jruoho * by Jukka Ruohonen.
9 1.1 jruoho *
10 1.1 jruoho * Redistribution and use in source and binary forms, with or without
11 1.1 jruoho * modification, are permitted provided that the following conditions
12 1.1 jruoho * are met:
13 1.1 jruoho * 1. Redistributions of source code must retain the above copyright
14 1.1 jruoho * notice, this list of conditions and the following disclaimer.
15 1.1 jruoho * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 jruoho * notice, this list of conditions and the following disclaimer in the
17 1.1 jruoho * documentation and/or other materials provided with the distribution.
18 1.1 jruoho *
19 1.1 jruoho * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 jruoho * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 jruoho * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 jruoho * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 jruoho * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 jruoho * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 jruoho * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 jruoho * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 jruoho * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 jruoho * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 jruoho * POSSIBILITY OF SUCH DAMAGE.
30 1.1 jruoho */
31 1.2 jruoho
32 1.2 jruoho /*-
33 1.2 jruoho * Copyright (c)2004 YAMAMOTO Takashi,
34 1.2 jruoho * All rights reserved.
35 1.2 jruoho *
36 1.2 jruoho * Redistribution and use in source and binary forms, with or without
37 1.2 jruoho * modification, are permitted provided that the following conditions
38 1.2 jruoho * are met:
39 1.2 jruoho * 1. Redistributions of source code must retain the above copyright
40 1.2 jruoho * notice, this list of conditions and the following disclaimer.
41 1.2 jruoho * 2. Redistributions in binary form must reproduce the above copyright
42 1.2 jruoho * notice, this list of conditions and the following disclaimer in the
43 1.2 jruoho * documentation and/or other materials provided with the distribution.
44 1.2 jruoho *
45 1.2 jruoho * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46 1.2 jruoho * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 1.2 jruoho * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 1.2 jruoho * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49 1.2 jruoho * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 1.2 jruoho * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 1.2 jruoho * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 1.2 jruoho * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 1.2 jruoho * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 1.2 jruoho * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 1.2 jruoho * SUCH DAMAGE.
56 1.2 jruoho */
57 1.1 jruoho #include <sys/cdefs.h>
58 1.17 gson __RCSID("$NetBSD: t_mmap.c,v 1.17 2022/04/06 10:02:55 gson Exp $");
59 1.1 jruoho
60 1.1 jruoho #include <sys/param.h>
61 1.12 christos #include <sys/disklabel.h>
62 1.1 jruoho #include <sys/mman.h>
63 1.10 christos #include <sys/stat.h>
64 1.2 jruoho #include <sys/socket.h>
65 1.1 jruoho #include <sys/sysctl.h>
66 1.1 jruoho #include <sys/wait.h>
67 1.1 jruoho
68 1.2 jruoho #include <atf-c.h>
69 1.1 jruoho #include <errno.h>
70 1.1 jruoho #include <fcntl.h>
71 1.1 jruoho #include <signal.h>
72 1.3 jruoho #include <stdio.h>
73 1.1 jruoho #include <stdlib.h>
74 1.1 jruoho #include <string.h>
75 1.1 jruoho #include <unistd.h>
76 1.5 martin #include <paths.h>
77 1.17 gson #include <pthread.h>
78 1.1 jruoho
79 1.1 jruoho static long page = 0;
80 1.1 jruoho static char path[] = "mmap";
81 1.1 jruoho static void map_check(void *, int);
82 1.1 jruoho static void map_sighandler(int);
83 1.2 jruoho static void testloan(void *, void *, char, int);
84 1.2 jruoho
85 1.2 jruoho #define BUFSIZE (32 * 1024) /* enough size to trigger sosend_loan */
86 1.1 jruoho
87 1.1 jruoho static void
88 1.1 jruoho map_check(void *map, int flag)
89 1.1 jruoho {
90 1.1 jruoho
91 1.1 jruoho if (flag != 0) {
92 1.1 jruoho ATF_REQUIRE(map == MAP_FAILED);
93 1.1 jruoho return;
94 1.1 jruoho }
95 1.1 jruoho
96 1.1 jruoho ATF_REQUIRE(map != MAP_FAILED);
97 1.1 jruoho ATF_REQUIRE(munmap(map, page) == 0);
98 1.1 jruoho }
99 1.1 jruoho
100 1.2 jruoho void
101 1.2 jruoho testloan(void *vp, void *vp2, char pat, int docheck)
102 1.2 jruoho {
103 1.2 jruoho char buf[BUFSIZE];
104 1.2 jruoho char backup[BUFSIZE];
105 1.2 jruoho ssize_t nwritten;
106 1.2 jruoho ssize_t nread;
107 1.2 jruoho int fds[2];
108 1.2 jruoho int val;
109 1.2 jruoho
110 1.2 jruoho val = BUFSIZE;
111 1.2 jruoho
112 1.2 jruoho if (docheck != 0)
113 1.2 jruoho (void)memcpy(backup, vp, BUFSIZE);
114 1.2 jruoho
115 1.2 jruoho if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, fds) != 0)
116 1.2 jruoho atf_tc_fail("socketpair() failed");
117 1.2 jruoho
118 1.2 jruoho val = BUFSIZE;
119 1.2 jruoho
120 1.2 jruoho if (setsockopt(fds[1], SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) != 0)
121 1.2 jruoho atf_tc_fail("setsockopt() failed, SO_RCVBUF");
122 1.2 jruoho
123 1.2 jruoho val = BUFSIZE;
124 1.2 jruoho
125 1.2 jruoho if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) != 0)
126 1.2 jruoho atf_tc_fail("setsockopt() failed, SO_SNDBUF");
127 1.2 jruoho
128 1.2 jruoho if (fcntl(fds[0], F_SETFL, O_NONBLOCK) != 0)
129 1.2 jruoho atf_tc_fail("fcntl() failed");
130 1.2 jruoho
131 1.2 jruoho nwritten = write(fds[0], (char *)vp + page, BUFSIZE - page);
132 1.2 jruoho
133 1.2 jruoho if (nwritten == -1)
134 1.2 jruoho atf_tc_fail("write() failed");
135 1.2 jruoho
136 1.2 jruoho /* Break loan. */
137 1.2 jruoho (void)memset(vp2, pat, BUFSIZE);
138 1.2 jruoho
139 1.2 jruoho nread = read(fds[1], buf + page, BUFSIZE - page);
140 1.2 jruoho
141 1.2 jruoho if (nread == -1)
142 1.2 jruoho atf_tc_fail("read() failed");
143 1.2 jruoho
144 1.2 jruoho if (nread != nwritten)
145 1.2 jruoho atf_tc_fail("too short read");
146 1.2 jruoho
147 1.2 jruoho if (docheck != 0 && memcmp(backup, buf + page, nread) != 0)
148 1.2 jruoho atf_tc_fail("data mismatch");
149 1.2 jruoho
150 1.2 jruoho ATF_REQUIRE(close(fds[0]) == 0);
151 1.2 jruoho ATF_REQUIRE(close(fds[1]) == 0);
152 1.2 jruoho }
153 1.2 jruoho
154 1.1 jruoho static void
155 1.1 jruoho map_sighandler(int signo)
156 1.1 jruoho {
157 1.1 jruoho _exit(signo);
158 1.1 jruoho }
159 1.1 jruoho
160 1.3 jruoho ATF_TC(mmap_block);
161 1.3 jruoho ATF_TC_HEAD(mmap_block, tc)
162 1.3 jruoho {
163 1.3 jruoho atf_tc_set_md_var(tc, "descr", "Test mmap(2) with a block device");
164 1.5 martin atf_tc_set_md_var(tc, "require.user", "root");
165 1.3 jruoho }
166 1.3 jruoho
167 1.3 jruoho ATF_TC_BODY(mmap_block, tc)
168 1.3 jruoho {
169 1.5 martin static const int mib[] = { CTL_HW, HW_DISKNAMES };
170 1.5 martin static const unsigned int miblen = __arraycount(mib);
171 1.5 martin char *map, *dk, *drives, dev[PATH_MAX];
172 1.5 martin size_t len;
173 1.5 martin int fd = -1;
174 1.3 jruoho
175 1.14 jruoho atf_tc_skip("The test case causes a panic " \
176 1.14 jruoho "(PR kern/38889, PR kern/46592)");
177 1.3 jruoho
178 1.5 martin ATF_REQUIRE(sysctl(mib, miblen, NULL, &len, NULL, 0) == 0);
179 1.5 martin drives = malloc(len);
180 1.5 martin ATF_REQUIRE(drives != NULL);
181 1.5 martin ATF_REQUIRE(sysctl(mib, miblen, drives, &len, NULL, 0) == 0);
182 1.5 martin for (dk = strtok(drives, " "); dk != NULL; dk = strtok(NULL, " ")) {
183 1.13 christos if (strncmp(dk, "dk", 2) == 0)
184 1.13 christos snprintf(dev, sizeof(dev), _PATH_DEV "%s", dk);
185 1.13 christos else
186 1.13 christos snprintf(dev, sizeof(dev), _PATH_DEV "%s%c", dk,
187 1.13 christos 'a' + RAW_PART);
188 1.5 martin fprintf(stderr, "trying: %s\n", dev);
189 1.3 jruoho
190 1.5 martin if ((fd = open(dev, O_RDONLY)) >= 0) {
191 1.5 martin (void)fprintf(stderr, "using %s\n", dev);
192 1.3 jruoho break;
193 1.13 christos } else
194 1.13 christos (void)fprintf(stderr, "%s: %s\n", dev, strerror(errno));
195 1.3 jruoho }
196 1.5 martin free(drives);
197 1.3 jruoho
198 1.5 martin if (fd < 0)
199 1.4 jruoho atf_tc_skip("failed to find suitable block device");
200 1.3 jruoho
201 1.3 jruoho map = mmap(NULL, 4096, PROT_READ, MAP_FILE, fd, 0);
202 1.13 christos ATF_REQUIRE_MSG(map != MAP_FAILED, "mmap: %s", strerror(errno));
203 1.3 jruoho
204 1.3 jruoho (void)fprintf(stderr, "first byte %x\n", *map);
205 1.3 jruoho ATF_REQUIRE(close(fd) == 0);
206 1.3 jruoho (void)fprintf(stderr, "first byte %x\n", *map);
207 1.3 jruoho
208 1.3 jruoho ATF_REQUIRE(munmap(map, 4096) == 0);
209 1.3 jruoho }
210 1.3 jruoho
211 1.1 jruoho ATF_TC(mmap_err);
212 1.1 jruoho ATF_TC_HEAD(mmap_err, tc)
213 1.1 jruoho {
214 1.1 jruoho atf_tc_set_md_var(tc, "descr", "Test error conditions of mmap(2)");
215 1.1 jruoho }
216 1.1 jruoho
217 1.1 jruoho ATF_TC_BODY(mmap_err, tc)
218 1.1 jruoho {
219 1.16 gson void *addr = (void *)-1;
220 1.1 jruoho void *map;
221 1.1 jruoho
222 1.1 jruoho errno = 0;
223 1.1 jruoho map = mmap(NULL, 3, PROT_READ, MAP_FILE|MAP_PRIVATE, -1, 0);
224 1.1 jruoho
225 1.1 jruoho ATF_REQUIRE(map == MAP_FAILED);
226 1.1 jruoho ATF_REQUIRE(errno == EBADF);
227 1.1 jruoho
228 1.1 jruoho errno = 0;
229 1.16 gson map = mmap(addr, page, PROT_READ, MAP_FIXED|MAP_PRIVATE, -1, 0);
230 1.1 jruoho
231 1.1 jruoho ATF_REQUIRE(map == MAP_FAILED);
232 1.15 gson ATF_REQUIRE_MSG(errno == EINVAL, "errno %d != EINVAL", errno);
233 1.1 jruoho
234 1.1 jruoho errno = 0;
235 1.1 jruoho map = mmap(NULL, page, PROT_READ, MAP_ANON|MAP_PRIVATE, INT_MAX, 0);
236 1.1 jruoho
237 1.1 jruoho ATF_REQUIRE(map == MAP_FAILED);
238 1.1 jruoho ATF_REQUIRE(errno == EINVAL);
239 1.1 jruoho }
240 1.1 jruoho
241 1.2 jruoho ATF_TC_WITH_CLEANUP(mmap_loan);
242 1.2 jruoho ATF_TC_HEAD(mmap_loan, tc)
243 1.2 jruoho {
244 1.2 jruoho atf_tc_set_md_var(tc, "descr", "Test uvm page loanout with mmap(2)");
245 1.2 jruoho }
246 1.2 jruoho
247 1.2 jruoho ATF_TC_BODY(mmap_loan, tc)
248 1.2 jruoho {
249 1.2 jruoho char buf[BUFSIZE];
250 1.2 jruoho char *vp, *vp2;
251 1.2 jruoho int fd;
252 1.2 jruoho
253 1.2 jruoho fd = open(path, O_RDWR | O_CREAT, 0600);
254 1.2 jruoho ATF_REQUIRE(fd >= 0);
255 1.2 jruoho
256 1.2 jruoho (void)memset(buf, 'x', sizeof(buf));
257 1.2 jruoho (void)write(fd, buf, sizeof(buf));
258 1.2 jruoho
259 1.2 jruoho vp = mmap(NULL, BUFSIZE, PROT_READ | PROT_WRITE,
260 1.2 jruoho MAP_FILE | MAP_PRIVATE, fd, 0);
261 1.2 jruoho
262 1.2 jruoho ATF_REQUIRE(vp != MAP_FAILED);
263 1.2 jruoho
264 1.2 jruoho vp2 = vp;
265 1.2 jruoho
266 1.2 jruoho testloan(vp, vp2, 'A', 0);
267 1.2 jruoho testloan(vp, vp2, 'B', 1);
268 1.2 jruoho
269 1.2 jruoho ATF_REQUIRE(munmap(vp, BUFSIZE) == 0);
270 1.2 jruoho
271 1.2 jruoho vp = mmap(NULL, BUFSIZE, PROT_READ | PROT_WRITE,
272 1.2 jruoho MAP_FILE | MAP_SHARED, fd, 0);
273 1.2 jruoho
274 1.2 jruoho vp2 = mmap(NULL, BUFSIZE, PROT_READ | PROT_WRITE,
275 1.2 jruoho MAP_FILE | MAP_SHARED, fd, 0);
276 1.2 jruoho
277 1.2 jruoho ATF_REQUIRE(vp != MAP_FAILED);
278 1.2 jruoho ATF_REQUIRE(vp2 != MAP_FAILED);
279 1.2 jruoho
280 1.2 jruoho testloan(vp, vp2, 'E', 1);
281 1.2 jruoho
282 1.2 jruoho ATF_REQUIRE(munmap(vp, BUFSIZE) == 0);
283 1.2 jruoho ATF_REQUIRE(munmap(vp2, BUFSIZE) == 0);
284 1.2 jruoho }
285 1.2 jruoho
286 1.2 jruoho ATF_TC_CLEANUP(mmap_loan, tc)
287 1.2 jruoho {
288 1.2 jruoho (void)unlink(path);
289 1.2 jruoho }
290 1.2 jruoho
291 1.1 jruoho ATF_TC_WITH_CLEANUP(mmap_prot_1);
292 1.1 jruoho ATF_TC_HEAD(mmap_prot_1, tc)
293 1.1 jruoho {
294 1.1 jruoho atf_tc_set_md_var(tc, "descr", "Test mmap(2) protections, #1");
295 1.1 jruoho }
296 1.1 jruoho
297 1.1 jruoho ATF_TC_BODY(mmap_prot_1, tc)
298 1.1 jruoho {
299 1.1 jruoho void *map;
300 1.1 jruoho int fd;
301 1.1 jruoho
302 1.1 jruoho /*
303 1.1 jruoho * Open a file write-only and try to
304 1.1 jruoho * map it read-only. This should fail.
305 1.1 jruoho */
306 1.1 jruoho fd = open(path, O_WRONLY | O_CREAT, 0700);
307 1.1 jruoho
308 1.1 jruoho if (fd < 0)
309 1.1 jruoho return;
310 1.1 jruoho
311 1.1 jruoho ATF_REQUIRE(write(fd, "XXX", 3) == 3);
312 1.1 jruoho
313 1.1 jruoho map = mmap(NULL, 3, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
314 1.1 jruoho map_check(map, 1);
315 1.1 jruoho
316 1.1 jruoho map = mmap(NULL, 3, PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0);
317 1.1 jruoho map_check(map, 0);
318 1.1 jruoho
319 1.1 jruoho ATF_REQUIRE(close(fd) == 0);
320 1.1 jruoho }
321 1.1 jruoho
322 1.1 jruoho ATF_TC_CLEANUP(mmap_prot_1, tc)
323 1.1 jruoho {
324 1.1 jruoho (void)unlink(path);
325 1.1 jruoho }
326 1.1 jruoho
327 1.1 jruoho ATF_TC(mmap_prot_2);
328 1.1 jruoho ATF_TC_HEAD(mmap_prot_2, tc)
329 1.1 jruoho {
330 1.1 jruoho atf_tc_set_md_var(tc, "descr", "Test mmap(2) protections, #2");
331 1.1 jruoho }
332 1.1 jruoho
333 1.1 jruoho ATF_TC_BODY(mmap_prot_2, tc)
334 1.1 jruoho {
335 1.1 jruoho char buf[2];
336 1.1 jruoho void *map;
337 1.1 jruoho pid_t pid;
338 1.1 jruoho int sta;
339 1.1 jruoho
340 1.1 jruoho /*
341 1.1 jruoho * Make a PROT_NONE mapping and try to access it.
342 1.1 jruoho * If we catch a SIGSEGV, all works as expected.
343 1.1 jruoho */
344 1.1 jruoho map = mmap(NULL, page, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
345 1.1 jruoho ATF_REQUIRE(map != MAP_FAILED);
346 1.1 jruoho
347 1.1 jruoho pid = fork();
348 1.1 jruoho ATF_REQUIRE(pid >= 0);
349 1.1 jruoho
350 1.1 jruoho if (pid == 0) {
351 1.1 jruoho ATF_REQUIRE(signal(SIGSEGV, map_sighandler) != SIG_ERR);
352 1.1 jruoho ATF_REQUIRE(strlcpy(buf, map, sizeof(buf)) != 0);
353 1.1 jruoho }
354 1.1 jruoho
355 1.1 jruoho (void)wait(&sta);
356 1.1 jruoho
357 1.1 jruoho ATF_REQUIRE(WIFEXITED(sta) != 0);
358 1.1 jruoho ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
359 1.1 jruoho ATF_REQUIRE(munmap(map, page) == 0);
360 1.1 jruoho }
361 1.1 jruoho
362 1.1 jruoho ATF_TC_WITH_CLEANUP(mmap_prot_3);
363 1.1 jruoho ATF_TC_HEAD(mmap_prot_3, tc)
364 1.1 jruoho {
365 1.1 jruoho atf_tc_set_md_var(tc, "descr", "Test mmap(2) protections, #3");
366 1.1 jruoho }
367 1.1 jruoho
368 1.1 jruoho ATF_TC_BODY(mmap_prot_3, tc)
369 1.1 jruoho {
370 1.1 jruoho char buf[2];
371 1.1 jruoho int fd, sta;
372 1.1 jruoho void *map;
373 1.1 jruoho pid_t pid;
374 1.1 jruoho
375 1.1 jruoho /*
376 1.1 jruoho * Open a file, change the permissions
377 1.1 jruoho * to read-only, and try to map it as
378 1.1 jruoho * PROT_NONE. This should succeed, but
379 1.1 jruoho * the access should generate SIGSEGV.
380 1.1 jruoho */
381 1.1 jruoho fd = open(path, O_RDWR | O_CREAT, 0700);
382 1.1 jruoho
383 1.1 jruoho if (fd < 0)
384 1.1 jruoho return;
385 1.1 jruoho
386 1.1 jruoho ATF_REQUIRE(write(fd, "XXX", 3) == 3);
387 1.1 jruoho ATF_REQUIRE(close(fd) == 0);
388 1.1 jruoho ATF_REQUIRE(chmod(path, 0444) == 0);
389 1.1 jruoho
390 1.1 jruoho fd = open(path, O_RDONLY);
391 1.1 jruoho ATF_REQUIRE(fd != -1);
392 1.1 jruoho
393 1.1 jruoho map = mmap(NULL, 3, PROT_NONE, MAP_FILE | MAP_SHARED, fd, 0);
394 1.1 jruoho ATF_REQUIRE(map != MAP_FAILED);
395 1.1 jruoho
396 1.1 jruoho pid = fork();
397 1.1 jruoho
398 1.1 jruoho ATF_REQUIRE(pid >= 0);
399 1.1 jruoho
400 1.1 jruoho if (pid == 0) {
401 1.1 jruoho ATF_REQUIRE(signal(SIGSEGV, map_sighandler) != SIG_ERR);
402 1.1 jruoho ATF_REQUIRE(strlcpy(buf, map, sizeof(buf)) != 0);
403 1.1 jruoho }
404 1.1 jruoho
405 1.1 jruoho (void)wait(&sta);
406 1.1 jruoho
407 1.1 jruoho ATF_REQUIRE(WIFEXITED(sta) != 0);
408 1.1 jruoho ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
409 1.1 jruoho ATF_REQUIRE(munmap(map, 3) == 0);
410 1.1 jruoho }
411 1.1 jruoho
412 1.1 jruoho ATF_TC_CLEANUP(mmap_prot_3, tc)
413 1.1 jruoho {
414 1.1 jruoho (void)unlink(path);
415 1.1 jruoho }
416 1.1 jruoho
417 1.17 gson ATF_TC(mmap_reprotect_race);
418 1.17 gson
419 1.17 gson ATF_TC_HEAD(mmap_reprotect_race, tc)
420 1.17 gson {
421 1.17 gson atf_tc_set_md_var(tc, "descr", "Test for the race condition of PR 52239");
422 1.17 gson }
423 1.17 gson
424 1.17 gson const int mmap_reprotect_race_npages = 13;
425 1.17 gson const int mmap_reprotect_iterations = 1000000;
426 1.17 gson
427 1.17 gson static void *
428 1.17 gson mmap_reprotect_race_thread(void *arg)
429 1.17 gson {
430 1.17 gson int i, r;
431 1.17 gson void *p;
432 1.17 gson
433 1.17 gson for (i = 0; i < mmap_reprotect_iterations; i++) {
434 1.17 gson /* Get some unrelated memory */
435 1.17 gson p = mmap(0, mmap_reprotect_race_npages * page,
436 1.17 gson PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
437 1.17 gson ATF_REQUIRE(p);
438 1.17 gson r = munmap(p, mmap_reprotect_race_npages * page);
439 1.17 gson ATF_REQUIRE(r == 0);
440 1.17 gson }
441 1.17 gson return 0;
442 1.17 gson }
443 1.17 gson
444 1.17 gson ATF_TC_BODY(mmap_reprotect_race, tc)
445 1.17 gson {
446 1.17 gson pthread_t thread;
447 1.17 gson void *p, *q;
448 1.17 gson int i, r;
449 1.17 gson
450 1.17 gson r = pthread_create(&thread, 0, mmap_reprotect_race_thread, 0);
451 1.17 gson ATF_REQUIRE(r == 0);
452 1.17 gson
453 1.17 gson for (i = 0; i < mmap_reprotect_iterations; i++) {
454 1.17 gson /* Get a placeholder region */
455 1.17 gson p = mmap(0, mmap_reprotect_race_npages * page,
456 1.17 gson PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0);
457 1.17 gson if (p == MAP_FAILED)
458 1.17 gson atf_tc_fail("mmap: %s", strerror(errno));
459 1.17 gson
460 1.17 gson /* Upgrade placeholder to read/write access */
461 1.17 gson q = mmap(p, mmap_reprotect_race_npages * page,
462 1.17 gson PROT_READ|PROT_WRITE,
463 1.17 gson MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0);
464 1.17 gson if (q == MAP_FAILED)
465 1.17 gson atf_tc_fail("update mmap: %s", strerror(errno));
466 1.17 gson ATF_REQUIRE(q == p);
467 1.17 gson
468 1.17 gson /* Free it */
469 1.17 gson r = munmap(q, mmap_reprotect_race_npages * page);
470 1.17 gson if (r != 0)
471 1.17 gson atf_tc_fail("munmap: %s", strerror(errno));
472 1.17 gson }
473 1.17 gson pthread_join(thread, NULL);
474 1.17 gson }
475 1.17 gson
476 1.1 jruoho ATF_TC_WITH_CLEANUP(mmap_truncate);
477 1.1 jruoho ATF_TC_HEAD(mmap_truncate, tc)
478 1.1 jruoho {
479 1.1 jruoho atf_tc_set_md_var(tc, "descr", "Test mmap(2) and ftruncate(2)");
480 1.1 jruoho }
481 1.1 jruoho
482 1.1 jruoho ATF_TC_BODY(mmap_truncate, tc)
483 1.1 jruoho {
484 1.1 jruoho char *map;
485 1.1 jruoho long i;
486 1.1 jruoho int fd;
487 1.1 jruoho
488 1.1 jruoho fd = open(path, O_RDWR | O_CREAT, 0700);
489 1.1 jruoho
490 1.1 jruoho if (fd < 0)
491 1.1 jruoho return;
492 1.1 jruoho
493 1.1 jruoho /*
494 1.1 jruoho * See that ftruncate(2) works
495 1.1 jruoho * while the file is mapped.
496 1.1 jruoho */
497 1.1 jruoho ATF_REQUIRE(ftruncate(fd, page) == 0);
498 1.1 jruoho
499 1.1 jruoho map = mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_FILE|MAP_PRIVATE,
500 1.1 jruoho fd, 0);
501 1.1 jruoho ATF_REQUIRE(map != MAP_FAILED);
502 1.1 jruoho
503 1.1 jruoho for (i = 0; i < page; i++)
504 1.1 jruoho map[i] = 'x';
505 1.1 jruoho
506 1.1 jruoho ATF_REQUIRE(ftruncate(fd, 0) == 0);
507 1.1 jruoho ATF_REQUIRE(ftruncate(fd, page / 8) == 0);
508 1.1 jruoho ATF_REQUIRE(ftruncate(fd, page / 4) == 0);
509 1.1 jruoho ATF_REQUIRE(ftruncate(fd, page / 2) == 0);
510 1.1 jruoho ATF_REQUIRE(ftruncate(fd, page / 12) == 0);
511 1.1 jruoho ATF_REQUIRE(ftruncate(fd, page / 64) == 0);
512 1.1 jruoho
513 1.11 christos (void)munmap(map, page);
514 1.1 jruoho ATF_REQUIRE(close(fd) == 0);
515 1.1 jruoho }
516 1.1 jruoho
517 1.1 jruoho ATF_TC_CLEANUP(mmap_truncate, tc)
518 1.1 jruoho {
519 1.1 jruoho (void)unlink(path);
520 1.1 jruoho }
521 1.1 jruoho
522 1.8 christos ATF_TC_WITH_CLEANUP(mmap_truncate_signal);
523 1.8 christos ATF_TC_HEAD(mmap_truncate_signal, tc)
524 1.8 christos {
525 1.8 christos atf_tc_set_md_var(tc, "descr",
526 1.8 christos "Test mmap(2) ftruncate(2) causing signal");
527 1.8 christos }
528 1.8 christos
529 1.8 christos ATF_TC_BODY(mmap_truncate_signal, tc)
530 1.8 christos {
531 1.8 christos char *map;
532 1.8 christos long i;
533 1.8 christos int fd, sta;
534 1.8 christos pid_t pid;
535 1.8 christos
536 1.8 christos fd = open(path, O_RDWR | O_CREAT, 0700);
537 1.8 christos
538 1.8 christos if (fd < 0)
539 1.8 christos return;
540 1.8 christos
541 1.8 christos ATF_REQUIRE(write(fd, "foo\n", 5) == 5);
542 1.8 christos
543 1.8 christos map = mmap(NULL, page, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
544 1.8 christos ATF_REQUIRE(map != MAP_FAILED);
545 1.8 christos
546 1.8 christos sta = 0;
547 1.8 christos for (i = 0; i < 5; i++)
548 1.8 christos sta += map[i];
549 1.8 christos ATF_REQUIRE(sta == 334);
550 1.8 christos
551 1.8 christos ATF_REQUIRE(ftruncate(fd, 0) == 0);
552 1.8 christos pid = fork();
553 1.8 christos ATF_REQUIRE(pid >= 0);
554 1.8 christos
555 1.8 christos if (pid == 0) {
556 1.8 christos ATF_REQUIRE(signal(SIGBUS, map_sighandler) != SIG_ERR);
557 1.9 martin ATF_REQUIRE(signal(SIGSEGV, map_sighandler) != SIG_ERR);
558 1.8 christos sta = 0;
559 1.8 christos for (i = 0; i < page; i++)
560 1.8 christos sta += map[i];
561 1.9 martin /* child never will get this far, but the compiler will
562 1.9 martin not know, so better use the values calculated to
563 1.9 martin prevent the access to be optimized out */
564 1.8 christos ATF_REQUIRE(i == 0);
565 1.9 martin ATF_REQUIRE(sta == 0);
566 1.11 christos (void)munmap(map, page);
567 1.11 christos (void)close(fd);
568 1.9 martin return;
569 1.8 christos }
570 1.8 christos
571 1.8 christos (void)wait(&sta);
572 1.8 christos
573 1.8 christos ATF_REQUIRE(WIFEXITED(sta) != 0);
574 1.9 martin if (WEXITSTATUS(sta) == SIGSEGV)
575 1.9 martin atf_tc_fail("child process got SIGSEGV instead of SIGBUS");
576 1.8 christos ATF_REQUIRE(WEXITSTATUS(sta) == SIGBUS);
577 1.8 christos ATF_REQUIRE(munmap(map, page) == 0);
578 1.8 christos ATF_REQUIRE(close(fd) == 0);
579 1.8 christos }
580 1.8 christos
581 1.8 christos ATF_TC_CLEANUP(mmap_truncate_signal, tc)
582 1.8 christos {
583 1.8 christos (void)unlink(path);
584 1.8 christos }
585 1.8 christos
586 1.1 jruoho ATF_TC(mmap_va0);
587 1.1 jruoho ATF_TC_HEAD(mmap_va0, tc)
588 1.1 jruoho {
589 1.1 jruoho atf_tc_set_md_var(tc, "descr", "Test mmap(2) and vm.user_va0_disable");
590 1.1 jruoho }
591 1.1 jruoho
592 1.1 jruoho ATF_TC_BODY(mmap_va0, tc)
593 1.1 jruoho {
594 1.1 jruoho int flags = MAP_ANON | MAP_FIXED | MAP_PRIVATE;
595 1.1 jruoho size_t len = sizeof(int);
596 1.1 jruoho void *map;
597 1.1 jruoho int val;
598 1.1 jruoho
599 1.1 jruoho /*
600 1.1 jruoho * Make an anonymous fixed mapping at zero address. If the address
601 1.1 jruoho * is restricted as noted in security(7), the syscall should fail.
602 1.1 jruoho */
603 1.1 jruoho if (sysctlbyname("vm.user_va0_disable", &val, &len, NULL, 0) != 0)
604 1.1 jruoho atf_tc_fail("failed to read vm.user_va0_disable");
605 1.1 jruoho
606 1.1 jruoho map = mmap(NULL, page, PROT_EXEC, flags, -1, 0);
607 1.1 jruoho map_check(map, val);
608 1.1 jruoho
609 1.1 jruoho map = mmap(NULL, page, PROT_READ, flags, -1, 0);
610 1.1 jruoho map_check(map, val);
611 1.1 jruoho
612 1.1 jruoho map = mmap(NULL, page, PROT_WRITE, flags, -1, 0);
613 1.1 jruoho map_check(map, val);
614 1.1 jruoho
615 1.1 jruoho map = mmap(NULL, page, PROT_READ|PROT_WRITE, flags, -1, 0);
616 1.1 jruoho map_check(map, val);
617 1.1 jruoho
618 1.1 jruoho map = mmap(NULL, page, PROT_EXEC|PROT_READ|PROT_WRITE, flags, -1, 0);
619 1.1 jruoho map_check(map, val);
620 1.1 jruoho }
621 1.1 jruoho
622 1.1 jruoho ATF_TP_ADD_TCS(tp)
623 1.1 jruoho {
624 1.1 jruoho page = sysconf(_SC_PAGESIZE);
625 1.1 jruoho ATF_REQUIRE(page >= 0);
626 1.1 jruoho
627 1.3 jruoho ATF_TP_ADD_TC(tp, mmap_block);
628 1.1 jruoho ATF_TP_ADD_TC(tp, mmap_err);
629 1.2 jruoho ATF_TP_ADD_TC(tp, mmap_loan);
630 1.1 jruoho ATF_TP_ADD_TC(tp, mmap_prot_1);
631 1.1 jruoho ATF_TP_ADD_TC(tp, mmap_prot_2);
632 1.1 jruoho ATF_TP_ADD_TC(tp, mmap_prot_3);
633 1.17 gson ATF_TP_ADD_TC(tp, mmap_reprotect_race);
634 1.1 jruoho ATF_TP_ADD_TC(tp, mmap_truncate);
635 1.8 christos ATF_TP_ADD_TC(tp, mmap_truncate_signal);
636 1.1 jruoho ATF_TP_ADD_TC(tp, mmap_va0);
637 1.1 jruoho
638 1.1 jruoho return atf_no_error();
639 1.1 jruoho }
640