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