xref: /freebsd/tests/sys/vm/mmap_test.c (revision 9695459d02e4d3b85ae3d6ec40c75340842f424b)
142484f6fSEnji Cooper /*-
242484f6fSEnji Cooper  * Copyright (c) 2009	Simon L. Nielsen <simon@FreeBSD.org>,
342484f6fSEnji Cooper  * 			Bjoern A. Zeeb <bz@FreeBSD.org>
442484f6fSEnji Cooper  *
542484f6fSEnji Cooper  * Redistribution and use in source and binary forms, with or without
642484f6fSEnji Cooper  * modification, are permitted provided that the following conditions
742484f6fSEnji Cooper  * are met:
842484f6fSEnji Cooper  * 1. Redistributions of source code must retain the above copyright
942484f6fSEnji Cooper  *    notice, this list of conditions and the following disclaimer.
1042484f6fSEnji Cooper  * 2. Redistributions in binary form must reproduce the above copyright
1142484f6fSEnji Cooper  *    notice, this list of conditions and the following disclaimer in the
1242484f6fSEnji Cooper  *    documentation and/or other materials provided with the distribution.
1342484f6fSEnji Cooper  *
1442484f6fSEnji Cooper  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1542484f6fSEnji Cooper  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1642484f6fSEnji Cooper  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1742484f6fSEnji Cooper  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1842484f6fSEnji Cooper  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1942484f6fSEnji Cooper  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2042484f6fSEnji Cooper  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2142484f6fSEnji Cooper  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2242484f6fSEnji Cooper  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2342484f6fSEnji Cooper  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2442484f6fSEnji Cooper  * SUCH DAMAGE.
2542484f6fSEnji Cooper  *
2642484f6fSEnji Cooper  * $FreeBSD$
2742484f6fSEnji Cooper  */
2842484f6fSEnji Cooper 
2942484f6fSEnji Cooper #include <sys/param.h>
3042484f6fSEnji Cooper #include <sys/mman.h>
3142484f6fSEnji Cooper #include <sys/sysctl.h>
3242484f6fSEnji Cooper 
3395eee0d4SJohn Baldwin #include <atf-c.h>
3442484f6fSEnji Cooper #include <errno.h>
357f43ee0fSJohn Baldwin #include <fcntl.h>
367f43ee0fSJohn Baldwin #include <stdarg.h>
377f43ee0fSJohn Baldwin #include <stdio.h>
387f43ee0fSJohn Baldwin #include <stdlib.h>
3942484f6fSEnji Cooper 
4042484f6fSEnji Cooper static const struct {
4142484f6fSEnji Cooper 	void	*addr;
4242484f6fSEnji Cooper 	int	ok[2];	/* Depending on security.bsd.map_at_zero {0, !=0}. */
4395eee0d4SJohn Baldwin } map_at_zero_tests[] = {
4442484f6fSEnji Cooper 	{ (void *)0,			{ 0, 1 } }, /* Test sysctl. */
4542484f6fSEnji Cooper 	{ (void *)1,			{ 0, 0 } },
4642484f6fSEnji Cooper 	{ (void *)(PAGE_SIZE - 1),	{ 0, 0 } },
4742484f6fSEnji Cooper 	{ (void *)PAGE_SIZE,		{ 1, 1 } },
4842484f6fSEnji Cooper 	{ (void *)-1,			{ 0, 0 } },
4942484f6fSEnji Cooper 	{ (void *)(-PAGE_SIZE),		{ 0, 0 } },
5042484f6fSEnji Cooper 	{ (void *)(-1 - PAGE_SIZE),	{ 0, 0 } },
5142484f6fSEnji Cooper 	{ (void *)(-1 - PAGE_SIZE - 1),	{ 0, 0 } },
5242484f6fSEnji Cooper 	{ (void *)(0x1000 * PAGE_SIZE),	{ 1, 1 } },
5342484f6fSEnji Cooper };
5442484f6fSEnji Cooper 
5542484f6fSEnji Cooper #define	MAP_AT_ZERO	"security.bsd.map_at_zero"
5642484f6fSEnji Cooper 
5795eee0d4SJohn Baldwin ATF_TC_WITHOUT_HEAD(mmap__map_at_zero);
5895eee0d4SJohn Baldwin ATF_TC_BODY(mmap__map_at_zero, tc)
5942484f6fSEnji Cooper {
6042484f6fSEnji Cooper 	void *p;
6142484f6fSEnji Cooper 	size_t len;
6295eee0d4SJohn Baldwin 	unsigned int i;
6395eee0d4SJohn Baldwin 	int map_at_zero;
6442484f6fSEnji Cooper 
6542484f6fSEnji Cooper 	len = sizeof(map_at_zero);
6695eee0d4SJohn Baldwin 	if (sysctlbyname(MAP_AT_ZERO, &map_at_zero, &len, NULL, 0) == -1) {
6795eee0d4SJohn Baldwin 		atf_tc_skip("sysctl for %s failed: %s\n", MAP_AT_ZERO,
6842484f6fSEnji Cooper 		    strerror(errno));
6995eee0d4SJohn Baldwin 		return;
7042484f6fSEnji Cooper 	}
7142484f6fSEnji Cooper 
7242484f6fSEnji Cooper 	/* Normalize to 0 or 1 for array access. */
7342484f6fSEnji Cooper 	map_at_zero = !!map_at_zero;
7442484f6fSEnji Cooper 
7595eee0d4SJohn Baldwin 	for (i = 0; i < nitems(map_at_zero_tests); i++) {
7695eee0d4SJohn Baldwin 		p = mmap((void *)map_at_zero_tests[i].addr, PAGE_SIZE,
7742484f6fSEnji Cooper 		    PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_FIXED,
7842484f6fSEnji Cooper 		    -1, 0);
7942484f6fSEnji Cooper 		if (p == MAP_FAILED) {
8095eee0d4SJohn Baldwin 			ATF_CHECK_MSG(map_at_zero_tests[i].ok[map_at_zero] == 0,
8195eee0d4SJohn Baldwin 			    "mmap(%p, ...) failed", map_at_zero_tests[i].addr);
8242484f6fSEnji Cooper 		} else {
8395eee0d4SJohn Baldwin 			ATF_CHECK_MSG(map_at_zero_tests[i].ok[map_at_zero] == 1,
8495eee0d4SJohn Baldwin 			    "mmap(%p, ...) succeeded: p=%p\n",
8595eee0d4SJohn Baldwin 			    map_at_zero_tests[i].addr, p);
8695eee0d4SJohn Baldwin 		}
8742484f6fSEnji Cooper 	}
8842484f6fSEnji Cooper }
8942484f6fSEnji Cooper 
907f43ee0fSJohn Baldwin static void
917f43ee0fSJohn Baldwin checked_mmap(int prot, int flags, int fd, int error, const char *msg)
927f43ee0fSJohn Baldwin {
937f43ee0fSJohn Baldwin 	void *p;
947f43ee0fSJohn Baldwin 
957f43ee0fSJohn Baldwin 	p = mmap(NULL, getpagesize(), prot, flags, fd, 0);
967f43ee0fSJohn Baldwin 	if (p == MAP_FAILED) {
977f43ee0fSJohn Baldwin 		if (error == 0)
987f43ee0fSJohn Baldwin 			ATF_CHECK_MSG(0, "%s failed with errno %d", msg,
997f43ee0fSJohn Baldwin 			    errno);
1007f43ee0fSJohn Baldwin 		else
1017f43ee0fSJohn Baldwin 			ATF_CHECK_EQ_MSG(error, errno,
1027f43ee0fSJohn Baldwin 			    "%s failed with wrong errno %d (expected %d)", msg,
1037f43ee0fSJohn Baldwin 			    errno, error);
1047f43ee0fSJohn Baldwin 	} else {
1057f43ee0fSJohn Baldwin 		ATF_CHECK_MSG(error == 0, "%s succeeded", msg);
1067f43ee0fSJohn Baldwin 		munmap(p, getpagesize());
1077f43ee0fSJohn Baldwin 	}
1087f43ee0fSJohn Baldwin }
1097f43ee0fSJohn Baldwin 
1107f43ee0fSJohn Baldwin ATF_TC_WITHOUT_HEAD(mmap__bad_arguments);
1117f43ee0fSJohn Baldwin ATF_TC_BODY(mmap__bad_arguments, tc)
1127f43ee0fSJohn Baldwin {
113*9695459dSJohn Baldwin 	int devstatfd, shmfd, zerofd;
1147f43ee0fSJohn Baldwin 
115*9695459dSJohn Baldwin 	ATF_REQUIRE((devstatfd = open("/dev/devstat", O_RDONLY)) >= 0);
116*9695459dSJohn Baldwin 	ATF_REQUIRE((shmfd = shm_open(SHM_ANON, O_RDWR, 0644)) >= 0);
117*9695459dSJohn Baldwin 	ATF_REQUIRE(ftruncate(shmfd, getpagesize()) == 0);
118*9695459dSJohn Baldwin 	ATF_REQUIRE((zerofd = open("/dev/zero", O_RDONLY)) >= 0);
1197f43ee0fSJohn Baldwin 
1207f43ee0fSJohn Baldwin 	/* These should work. */
1217f43ee0fSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON, -1, 0,
1227f43ee0fSJohn Baldwin 	    "simple MAP_ANON");
123*9695459dSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0,
1247f43ee0fSJohn Baldwin 	    "simple shm fd shared");
125*9695459dSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_PRIVATE, shmfd, 0,
1267f43ee0fSJohn Baldwin 	    "simple shm fd private");
127*9695459dSJohn Baldwin 	checked_mmap(PROT_READ, MAP_SHARED, zerofd, 0,
128*9695459dSJohn Baldwin 	    "simple /dev/zero shared");
129*9695459dSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_PRIVATE, zerofd, 0,
130*9695459dSJohn Baldwin 	    "simple /dev/zero private");
131*9695459dSJohn Baldwin 	checked_mmap(PROT_READ, MAP_SHARED, devstatfd, 0,
132*9695459dSJohn Baldwin 	    "simple /dev/devstat shared");
1337f43ee0fSJohn Baldwin 
1347f43ee0fSJohn Baldwin 	/* Extra PROT flags. */
1357f43ee0fSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE | 0x100000, MAP_ANON, -1, EINVAL,
1367f43ee0fSJohn Baldwin 	    "MAP_ANON with extra PROT flags");
137*9695459dSJohn Baldwin 	checked_mmap(0xffff, MAP_SHARED, shmfd, EINVAL,
1387f43ee0fSJohn Baldwin 	    "shm fd with garbage PROT");
1397f43ee0fSJohn Baldwin 
1407f43ee0fSJohn Baldwin 	/* Undefined flag. */
1417f43ee0fSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_RESERVED0080, -1,
1427f43ee0fSJohn Baldwin 	    EINVAL, "Undefined flag");
1437f43ee0fSJohn Baldwin 
1447f43ee0fSJohn Baldwin 	/* Both MAP_SHARED and MAP_PRIVATE */
1457f43ee0fSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE |
1467f43ee0fSJohn Baldwin 	    MAP_SHARED, -1, EINVAL, "MAP_ANON with both SHARED and PRIVATE");
147*9695459dSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_SHARED, shmfd,
1487f43ee0fSJohn Baldwin 	    EINVAL, "shm fd with both SHARED and PRIVATE");
1497f43ee0fSJohn Baldwin 
1507f43ee0fSJohn Baldwin 	/* At least one of MAP_SHARED or MAP_PRIVATE without ANON */
151*9695459dSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, 0, shmfd, EINVAL,
1527f43ee0fSJohn Baldwin 	    "shm fd without sharing flag");
1537f43ee0fSJohn Baldwin 
1547f43ee0fSJohn Baldwin 	/* MAP_ANON with either sharing flag (impacts fork). */
1557f43ee0fSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0,
1567f43ee0fSJohn Baldwin 	    "shared MAP_ANON");
1577f43ee0fSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0,
1587f43ee0fSJohn Baldwin 	    "private MAP_ANON");
1597f43ee0fSJohn Baldwin 
1607f43ee0fSJohn Baldwin 	/* MAP_ANON should require an fd of -1. */
1617f43ee0fSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, EINVAL,
1627f43ee0fSJohn Baldwin 	    "MAP_ANON with fd != -1");
163*9695459dSJohn Baldwin 
164*9695459dSJohn Baldwin 	/* Writable MAP_SHARED should fail on read-only descriptors. */
165*9695459dSJohn Baldwin 	checked_mmap(PROT_READ | PROT_WRITE, MAP_SHARED, zerofd, EACCES,
166*9695459dSJohn Baldwin 	    "MAP_SHARED of read-only /dev/zero");
167*9695459dSJohn Baldwin 
168*9695459dSJohn Baldwin 	/*
169*9695459dSJohn Baldwin 	 * Character devices other than /dev/zero do not support private
170*9695459dSJohn Baldwin 	 * mappings.
171*9695459dSJohn Baldwin 	 */
172*9695459dSJohn Baldwin 	checked_mmap(PROT_READ, MAP_PRIVATE, devstatfd, EINVAL,
173*9695459dSJohn Baldwin 	    "MAP_PRIVATE of /dev/devstat");
174*9695459dSJohn Baldwin }
175*9695459dSJohn Baldwin 
176*9695459dSJohn Baldwin ATF_TC_WITHOUT_HEAD(mmap__dev_zero_private);
177*9695459dSJohn Baldwin ATF_TC_BODY(mmap__dev_zero_private, tc)
178*9695459dSJohn Baldwin {
179*9695459dSJohn Baldwin 	char *p1, *p2, *p3;
180*9695459dSJohn Baldwin 	size_t i;
181*9695459dSJohn Baldwin 	int fd;
182*9695459dSJohn Baldwin 
183*9695459dSJohn Baldwin 	ATF_REQUIRE((fd = open("/dev/zero", O_RDONLY)) >= 0);
184*9695459dSJohn Baldwin 
185*9695459dSJohn Baldwin 	p1 = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd,
186*9695459dSJohn Baldwin 	    0);
187*9695459dSJohn Baldwin 	ATF_REQUIRE(p1 != MAP_FAILED);
188*9695459dSJohn Baldwin 
189*9695459dSJohn Baldwin 	p2 = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd,
190*9695459dSJohn Baldwin 	    0);
191*9695459dSJohn Baldwin 	ATF_REQUIRE(p2 != MAP_FAILED);
192*9695459dSJohn Baldwin 
193*9695459dSJohn Baldwin 	for (i = 0; i < getpagesize(); i++)
194*9695459dSJohn Baldwin 		ATF_REQUIRE_EQ_MSG(0, p1[i], "byte at p1[%zu] is %x", i, p1[i]);
195*9695459dSJohn Baldwin 
196*9695459dSJohn Baldwin 	ATF_REQUIRE(memcmp(p1, p2, getpagesize()) == 0);
197*9695459dSJohn Baldwin 
198*9695459dSJohn Baldwin 	p1[0] = 1;
199*9695459dSJohn Baldwin 
200*9695459dSJohn Baldwin 	ATF_REQUIRE(p2[0] == 0);
201*9695459dSJohn Baldwin 
202*9695459dSJohn Baldwin 	p2[0] = 2;
203*9695459dSJohn Baldwin 
204*9695459dSJohn Baldwin 	ATF_REQUIRE(p1[0] == 1);
205*9695459dSJohn Baldwin 
206*9695459dSJohn Baldwin 	p3 = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd,
207*9695459dSJohn Baldwin 	    0);
208*9695459dSJohn Baldwin 	ATF_REQUIRE(p3 != MAP_FAILED);
209*9695459dSJohn Baldwin 
210*9695459dSJohn Baldwin 	ATF_REQUIRE(p3[0] == 0);
211*9695459dSJohn Baldwin }
212*9695459dSJohn Baldwin 
213*9695459dSJohn Baldwin ATF_TC_WITHOUT_HEAD(mmap__dev_zero_shared);
214*9695459dSJohn Baldwin ATF_TC_BODY(mmap__dev_zero_shared, tc)
215*9695459dSJohn Baldwin {
216*9695459dSJohn Baldwin 	char *p1, *p2, *p3;
217*9695459dSJohn Baldwin 	size_t i;
218*9695459dSJohn Baldwin 	int fd;
219*9695459dSJohn Baldwin 
220*9695459dSJohn Baldwin 	ATF_REQUIRE((fd = open("/dev/zero", O_RDWR)) >= 0);
221*9695459dSJohn Baldwin 
222*9695459dSJohn Baldwin 	p1 = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
223*9695459dSJohn Baldwin 	    0);
224*9695459dSJohn Baldwin 	ATF_REQUIRE(p1 != MAP_FAILED);
225*9695459dSJohn Baldwin 
226*9695459dSJohn Baldwin 	p2 = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
227*9695459dSJohn Baldwin 	    0);
228*9695459dSJohn Baldwin 	ATF_REQUIRE(p2 != MAP_FAILED);
229*9695459dSJohn Baldwin 
230*9695459dSJohn Baldwin 	for (i = 0; i < getpagesize(); i++)
231*9695459dSJohn Baldwin 		ATF_REQUIRE_EQ_MSG(0, p1[i], "byte at p1[%zu] is %x", i, p1[i]);
232*9695459dSJohn Baldwin 
233*9695459dSJohn Baldwin 	ATF_REQUIRE(memcmp(p1, p2, getpagesize()) == 0);
234*9695459dSJohn Baldwin 
235*9695459dSJohn Baldwin 	p1[0] = 1;
236*9695459dSJohn Baldwin 
237*9695459dSJohn Baldwin 	ATF_REQUIRE(p2[0] == 0);
238*9695459dSJohn Baldwin 
239*9695459dSJohn Baldwin 	p2[0] = 2;
240*9695459dSJohn Baldwin 
241*9695459dSJohn Baldwin 	ATF_REQUIRE(p1[0] == 1);
242*9695459dSJohn Baldwin 
243*9695459dSJohn Baldwin 	p3 = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
244*9695459dSJohn Baldwin 	    0);
245*9695459dSJohn Baldwin 	ATF_REQUIRE(p3 != MAP_FAILED);
246*9695459dSJohn Baldwin 
247*9695459dSJohn Baldwin 	ATF_REQUIRE(p3[0] == 0);
2487f43ee0fSJohn Baldwin }
2497f43ee0fSJohn Baldwin 
25095eee0d4SJohn Baldwin ATF_TP_ADD_TCS(tp)
25195eee0d4SJohn Baldwin {
25295eee0d4SJohn Baldwin 
25395eee0d4SJohn Baldwin 	ATF_TP_ADD_TC(tp, mmap__map_at_zero);
2547f43ee0fSJohn Baldwin 	ATF_TP_ADD_TC(tp, mmap__bad_arguments);
255*9695459dSJohn Baldwin 	ATF_TP_ADD_TC(tp, mmap__dev_zero_private);
256*9695459dSJohn Baldwin 	ATF_TP_ADD_TC(tp, mmap__dev_zero_shared);
25795eee0d4SJohn Baldwin 
25895eee0d4SJohn Baldwin 	return (atf_no_error());
25942484f6fSEnji Cooper }
260