xref: /netbsd-src/tests/lib/libc/sys/t_mprotect.c (revision 37baba21c7b9c4cbd52515d9eb97bab48e45fce5)
1*37baba21Schristos /* $NetBSD: t_mprotect.c,v 1.9 2020/04/18 17:44:53 christos Exp $ */
2068fb4f1Sjruoho 
3068fb4f1Sjruoho /*-
4*37baba21Schristos  * Copyright (c) 2011, 2020 The NetBSD Foundation, Inc.
5068fb4f1Sjruoho  * All rights reserved.
6068fb4f1Sjruoho  *
7068fb4f1Sjruoho  * This code is derived from software contributed to The NetBSD Foundation
8068fb4f1Sjruoho  * by Jukka Ruohonen.
9068fb4f1Sjruoho  *
10068fb4f1Sjruoho  * Redistribution and use in source and binary forms, with or without
11068fb4f1Sjruoho  * modification, are permitted provided that the following conditions
12068fb4f1Sjruoho  * are met:
13068fb4f1Sjruoho  * 1. Redistributions of source code must retain the above copyright
14068fb4f1Sjruoho  *    notice, this list of conditions and the following disclaimer.
15068fb4f1Sjruoho  * 2. Redistributions in binary form must reproduce the above copyright
16068fb4f1Sjruoho  *    notice, this list of conditions and the following disclaimer in the
17068fb4f1Sjruoho  *    documentation and/or other materials provided with the distribution.
18068fb4f1Sjruoho  *
19068fb4f1Sjruoho  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20068fb4f1Sjruoho  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21068fb4f1Sjruoho  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22068fb4f1Sjruoho  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23068fb4f1Sjruoho  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24068fb4f1Sjruoho  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25068fb4f1Sjruoho  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26068fb4f1Sjruoho  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27068fb4f1Sjruoho  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28068fb4f1Sjruoho  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29068fb4f1Sjruoho  * POSSIBILITY OF SUCH DAMAGE.
30068fb4f1Sjruoho  */
31068fb4f1Sjruoho #include <sys/cdefs.h>
32*37baba21Schristos __RCSID("$NetBSD: t_mprotect.c,v 1.9 2020/04/18 17:44:53 christos Exp $");
33068fb4f1Sjruoho 
34068fb4f1Sjruoho #include <sys/param.h>
35068fb4f1Sjruoho #include <sys/mman.h>
36068fb4f1Sjruoho #include <sys/sysctl.h>
37068fb4f1Sjruoho #include <sys/wait.h>
38068fb4f1Sjruoho 
39068fb4f1Sjruoho #include <errno.h>
40068fb4f1Sjruoho #include <fcntl.h>
41068fb4f1Sjruoho #include <stdlib.h>
42068fb4f1Sjruoho #include <string.h>
43068fb4f1Sjruoho #include <unistd.h>
44068fb4f1Sjruoho 
45068fb4f1Sjruoho #include <atf-c.h>
46068fb4f1Sjruoho 
47ca453df6Sjym #include "../common/exec_prot.h"
48*37baba21Schristos #include "t_mprotect_helper.h"
49ca453df6Sjym 
50068fb4f1Sjruoho static long	page = 0;
51068fb4f1Sjruoho static char	path[] = "mmap";
52068fb4f1Sjruoho 
53068fb4f1Sjruoho static void	sighandler(int);
54068fb4f1Sjruoho static bool	paxinit(void);
55068fb4f1Sjruoho 
56068fb4f1Sjruoho static void
sighandler(int signo)57068fb4f1Sjruoho sighandler(int signo)
58068fb4f1Sjruoho {
59068fb4f1Sjruoho 	_exit(signo);
60068fb4f1Sjruoho }
61068fb4f1Sjruoho 
62068fb4f1Sjruoho static bool
paxinit(void)63068fb4f1Sjruoho paxinit(void)
64068fb4f1Sjruoho {
65068fb4f1Sjruoho 	size_t len = sizeof(int);
66b57579c7Spgoyette 	int pax_flags;
67068fb4f1Sjruoho 	int rv;
68068fb4f1Sjruoho 
69b57579c7Spgoyette 	rv = sysctlbyname("proc.curproc.paxflags",
70b57579c7Spgoyette 	    &pax_flags, &len, NULL, 0);
71068fb4f1Sjruoho 
72068fb4f1Sjruoho 	if (rv != 0)
73068fb4f1Sjruoho 		return false;
74068fb4f1Sjruoho 
75b57579c7Spgoyette 	return ((pax_flags & CTL_PROC_PAXFLAGS_MPROTECT) != 0);
76068fb4f1Sjruoho }
77068fb4f1Sjruoho 
78068fb4f1Sjruoho ATF_TC_WITH_CLEANUP(mprotect_access);
ATF_TC_HEAD(mprotect_access,tc)79068fb4f1Sjruoho ATF_TC_HEAD(mprotect_access, tc)
80068fb4f1Sjruoho {
81068fb4f1Sjruoho 	atf_tc_set_md_var(tc, "descr", "Test for EACCES from mprotect(2)");
82068fb4f1Sjruoho }
83068fb4f1Sjruoho 
ATF_TC_BODY(mprotect_access,tc)84068fb4f1Sjruoho ATF_TC_BODY(mprotect_access, tc)
85068fb4f1Sjruoho {
86068fb4f1Sjruoho 	int prot[2] = { PROT_NONE, PROT_READ };
87068fb4f1Sjruoho 	void *map;
88068fb4f1Sjruoho 	size_t i;
89068fb4f1Sjruoho 	int fd;
90068fb4f1Sjruoho 
91653f037eSmartin 	fd = open(path, O_RDONLY | O_CREAT, 0600);
92068fb4f1Sjruoho 	ATF_REQUIRE(fd >= 0);
93068fb4f1Sjruoho 
94068fb4f1Sjruoho 	/*
95068fb4f1Sjruoho 	 * The call should fail with EACCES if we try to mark
96068fb4f1Sjruoho 	 * a PROT_NONE or PROT_READ file/section as PROT_WRITE.
97068fb4f1Sjruoho 	 */
98068fb4f1Sjruoho 	for (i = 0; i < __arraycount(prot); i++) {
99068fb4f1Sjruoho 
100068fb4f1Sjruoho 		map = mmap(NULL, page, prot[i], MAP_SHARED, fd, 0);
101068fb4f1Sjruoho 
102068fb4f1Sjruoho 		if (map == MAP_FAILED)
103068fb4f1Sjruoho 			continue;
104068fb4f1Sjruoho 
105068fb4f1Sjruoho 		errno = 0;
106068fb4f1Sjruoho 
107068fb4f1Sjruoho 		ATF_REQUIRE(mprotect(map, page, PROT_WRITE) != 0);
108068fb4f1Sjruoho 		ATF_REQUIRE(errno == EACCES);
109068fb4f1Sjruoho 		ATF_REQUIRE(munmap(map, page) == 0);
110068fb4f1Sjruoho 	}
111068fb4f1Sjruoho 
112068fb4f1Sjruoho 	ATF_REQUIRE(close(fd) == 0);
113068fb4f1Sjruoho }
114068fb4f1Sjruoho 
ATF_TC_CLEANUP(mprotect_access,tc)115068fb4f1Sjruoho ATF_TC_CLEANUP(mprotect_access, tc)
116068fb4f1Sjruoho {
117068fb4f1Sjruoho 	(void)unlink(path);
118068fb4f1Sjruoho }
119068fb4f1Sjruoho 
120068fb4f1Sjruoho ATF_TC(mprotect_err);
ATF_TC_HEAD(mprotect_err,tc)121068fb4f1Sjruoho ATF_TC_HEAD(mprotect_err, tc)
122068fb4f1Sjruoho {
123068fb4f1Sjruoho 	atf_tc_set_md_var(tc, "descr", "Test error conditions of mprotect(2)");
124068fb4f1Sjruoho }
125068fb4f1Sjruoho 
ATF_TC_BODY(mprotect_err,tc)126068fb4f1Sjruoho ATF_TC_BODY(mprotect_err, tc)
127068fb4f1Sjruoho {
128068fb4f1Sjruoho 	errno = 0;
129068fb4f1Sjruoho 
130068fb4f1Sjruoho 	ATF_REQUIRE(mprotect((char *)-1, 1, PROT_READ) != 0);
131068fb4f1Sjruoho 	ATF_REQUIRE(errno == EINVAL);
132068fb4f1Sjruoho }
133068fb4f1Sjruoho 
134ca453df6Sjym ATF_TC(mprotect_exec);
ATF_TC_HEAD(mprotect_exec,tc)135ca453df6Sjym ATF_TC_HEAD(mprotect_exec, tc)
136ca453df6Sjym {
137ca453df6Sjym 	atf_tc_set_md_var(tc, "descr",
138ca453df6Sjym 	    "Test mprotect(2) executable space protections");
139ca453df6Sjym }
140ca453df6Sjym 
141ca453df6Sjym /*
142ca453df6Sjym  * Trivial function -- should fit into a page
143ca453df6Sjym  */
ATF_TC_BODY(mprotect_exec,tc)144ca453df6Sjym ATF_TC_BODY(mprotect_exec, tc)
145ca453df6Sjym {
146ca453df6Sjym 	pid_t pid;
147ca453df6Sjym 	void *map;
148ca453df6Sjym 	int sta, xp_support;
149ca453df6Sjym 
150ca453df6Sjym 	xp_support = exec_prot_support();
151ca453df6Sjym 
152ca453df6Sjym 	switch (xp_support) {
153ca453df6Sjym 	case NOTIMPL:
154ca453df6Sjym 		atf_tc_skip(
155ca453df6Sjym 		    "Execute protection callback check not implemented");
156ca453df6Sjym 		break;
157ca453df6Sjym 	case NO_XP:
158ca453df6Sjym 		atf_tc_skip(
159ca453df6Sjym 		    "Host does not support executable space protection");
160ca453df6Sjym 		break;
161ca453df6Sjym 	case PARTIAL_XP: case PERPAGE_XP: default:
162ca453df6Sjym 		break;
163ca453df6Sjym 	}
164ca453df6Sjym 
165ca453df6Sjym 	/*
166ca453df6Sjym 	 * Map a page read/write and copy a trivial assembly function inside.
167ca453df6Sjym 	 * We will then change the mapping rights:
168ca453df6Sjym 	 * - first by setting the execution right, and check that we can
169ca453df6Sjym 	 *   call the code found in the allocated page.
170ca453df6Sjym 	 * - second by removing the execution right. This should generate
171ca453df6Sjym 	 *   a SIGSEGV on architectures that can enforce --x permissions.
172ca453df6Sjym 	 */
173ca453df6Sjym 
1744f77b889Sjoerg 	map = mmap(NULL, page, PROT_MPROTECT(PROT_EXEC)|PROT_WRITE|PROT_READ,
1754f77b889Sjoerg 	    MAP_ANON, -1, 0);
176ca453df6Sjym 	ATF_REQUIRE(map != MAP_FAILED);
177ca453df6Sjym 
178ca453df6Sjym 	memcpy(map, (void *)return_one,
179ca453df6Sjym 	    (uintptr_t)return_one_end - (uintptr_t)return_one);
180ca453df6Sjym 
181ca453df6Sjym 	/* give r-x rights then call code in page */
182ca453df6Sjym 	ATF_REQUIRE(mprotect(map, page, PROT_EXEC|PROT_READ) == 0);
183ca453df6Sjym 	ATF_REQUIRE(((int (*)(void))map)() == 1);
184ca453df6Sjym 
185ca453df6Sjym 	/* remove --x right */
186ca453df6Sjym 	ATF_REQUIRE(mprotect(map, page, PROT_READ) == 0);
187ca453df6Sjym 
188ca453df6Sjym 	pid = fork();
189ca453df6Sjym 	ATF_REQUIRE(pid >= 0);
190ca453df6Sjym 
191ca453df6Sjym 	if (pid == 0) {
192ca453df6Sjym 		ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
193ca453df6Sjym 		ATF_CHECK(((int (*)(void))map)() == 1);
194ca453df6Sjym 		_exit(0);
195ca453df6Sjym 	}
196ca453df6Sjym 
197ca453df6Sjym 	(void)wait(&sta);
198ca453df6Sjym 
1993c92ae04Sjym 	ATF_REQUIRE(munmap(map, page) == 0);
2003c92ae04Sjym 
201ca453df6Sjym 	ATF_REQUIRE(WIFEXITED(sta) != 0);
202ca453df6Sjym 
203ca453df6Sjym 	switch (xp_support) {
204ca453df6Sjym 	case PARTIAL_XP:
2053c92ae04Sjym 		/* Partial protection might fail; skip the test when it does */
2063c92ae04Sjym 		if (WEXITSTATUS(sta) != SIGSEGV) {
2073c92ae04Sjym 			atf_tc_skip("Host only supports "
2083c92ae04Sjym 			    "partial executable space protection");
2093c92ae04Sjym 		}
210ca453df6Sjym 		break;
211ca453df6Sjym 	case PERPAGE_XP: default:
212ca453df6Sjym 		/* Per-page --x protection should not fail */
213ca453df6Sjym 		ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
214ca453df6Sjym 		break;
215ca453df6Sjym 	}
216ca453df6Sjym }
217ca453df6Sjym 
218068fb4f1Sjruoho ATF_TC(mprotect_pax);
ATF_TC_HEAD(mprotect_pax,tc)219068fb4f1Sjruoho ATF_TC_HEAD(mprotect_pax, tc)
220068fb4f1Sjruoho {
221ca453df6Sjym 	atf_tc_set_md_var(tc, "descr", "PaX restrictions and mprotect(2)");
222068fb4f1Sjruoho 	atf_tc_set_md_var(tc, "require.user", "root");
223068fb4f1Sjruoho }
224068fb4f1Sjruoho 
ATF_TC_BODY(mprotect_pax,tc)225068fb4f1Sjruoho ATF_TC_BODY(mprotect_pax, tc)
226068fb4f1Sjruoho {
227068fb4f1Sjruoho 	const int prot[4] = { PROT_NONE, PROT_READ, PROT_WRITE };
228068fb4f1Sjruoho 	const char *str = NULL;
229068fb4f1Sjruoho 	void *map;
230068fb4f1Sjruoho 	size_t i;
231068fb4f1Sjruoho 	int rv;
232068fb4f1Sjruoho 
233426774e5Smartin 	if (!paxinit())
234426774e5Smartin 		atf_tc_skip("PaX MPROTECT restrictions not enabled");
235068fb4f1Sjruoho 
236068fb4f1Sjruoho 	/*
237068fb4f1Sjruoho 	 * As noted in the original PaX documentation [1],
238068fb4f1Sjruoho 	 * the following restrictions should apply:
239068fb4f1Sjruoho 	 *
240068fb4f1Sjruoho 	 *   (1) creating executable anonymous mappings
241068fb4f1Sjruoho 	 *
242068fb4f1Sjruoho 	 *   (2) creating executable/writable file mappings
243068fb4f1Sjruoho 	 *
244068fb4f1Sjruoho 	 *   (3) making a non-executable mapping executable
245068fb4f1Sjruoho 	 *
246068fb4f1Sjruoho 	 *   (4) making an executable/read-only file mapping
247068fb4f1Sjruoho 	 *	 writable except for performing relocations
248068fb4f1Sjruoho 	 *	 on an ET_DYN ELF file (non-PIC shared library)
249068fb4f1Sjruoho 	 *
250068fb4f1Sjruoho 	 *  The following will test only the case (3).
251068fb4f1Sjruoho 	 *
252068fb4f1Sjruoho 	 * [1] http://pax.grsecurity.net/docs/mprotect.txt
253068fb4f1Sjruoho 	 *
254068fb4f1Sjruoho 	 *     (Sun Apr 3 11:06:53 EEST 2011.)
255068fb4f1Sjruoho 	 */
256068fb4f1Sjruoho 	for (i = 0; i < __arraycount(prot); i++) {
257068fb4f1Sjruoho 
258068fb4f1Sjruoho 		map = mmap(NULL, page, prot[i], MAP_ANON, -1, 0);
259068fb4f1Sjruoho 
260068fb4f1Sjruoho 		if (map == MAP_FAILED)
261068fb4f1Sjruoho 			continue;
262068fb4f1Sjruoho 
263068fb4f1Sjruoho 		rv = mprotect(map, 1, prot[i] | PROT_EXEC);
264068fb4f1Sjruoho 
265068fb4f1Sjruoho 		(void)munmap(map, page);
266068fb4f1Sjruoho 
267068fb4f1Sjruoho 		if (rv == 0) {
268068fb4f1Sjruoho 			str = "non-executable mapping made executable";
269068fb4f1Sjruoho 			goto out;
270068fb4f1Sjruoho 		}
271068fb4f1Sjruoho 	}
272068fb4f1Sjruoho 
273068fb4f1Sjruoho out:
274068fb4f1Sjruoho 	if (str != NULL)
275068fb4f1Sjruoho 		atf_tc_fail("%s", str);
276068fb4f1Sjruoho }
277068fb4f1Sjruoho 
278068fb4f1Sjruoho ATF_TC(mprotect_write);
ATF_TC_HEAD(mprotect_write,tc)279068fb4f1Sjruoho ATF_TC_HEAD(mprotect_write, tc)
280068fb4f1Sjruoho {
281ca453df6Sjym 	atf_tc_set_md_var(tc, "descr", "Test mprotect(2) write protections");
282068fb4f1Sjruoho }
283068fb4f1Sjruoho 
ATF_TC_BODY(mprotect_write,tc)284068fb4f1Sjruoho ATF_TC_BODY(mprotect_write, tc)
285068fb4f1Sjruoho {
286068fb4f1Sjruoho 	pid_t pid;
287068fb4f1Sjruoho 	void *map;
288068fb4f1Sjruoho 	int sta;
289068fb4f1Sjruoho 
290068fb4f1Sjruoho 	/*
291068fb4f1Sjruoho 	 * Map a page read/write, change the protection
292068fb4f1Sjruoho 	 * to read-only with mprotect(2), and try to write
293068fb4f1Sjruoho 	 * to the page. This should generate a SIGSEGV.
294068fb4f1Sjruoho 	 */
295068fb4f1Sjruoho 	map = mmap(NULL, page, PROT_WRITE|PROT_READ, MAP_ANON, -1, 0);
296068fb4f1Sjruoho 	ATF_REQUIRE(map != MAP_FAILED);
297068fb4f1Sjruoho 
298068fb4f1Sjruoho 	ATF_REQUIRE(strlcpy(map, "XXX", 3) == 3);
299068fb4f1Sjruoho 	ATF_REQUIRE(mprotect(map, page, PROT_READ) == 0);
300068fb4f1Sjruoho 
301068fb4f1Sjruoho 	pid = fork();
302068fb4f1Sjruoho 	ATF_REQUIRE(pid >= 0);
303068fb4f1Sjruoho 
304068fb4f1Sjruoho 	if (pid == 0) {
305068fb4f1Sjruoho 		ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
306068fb4f1Sjruoho 		ATF_REQUIRE(strlcpy(map, "XXX", 3) == 0);
307068fb4f1Sjruoho 	}
308068fb4f1Sjruoho 
309068fb4f1Sjruoho 	(void)wait(&sta);
310068fb4f1Sjruoho 
311068fb4f1Sjruoho 	ATF_REQUIRE(WIFEXITED(sta) != 0);
312068fb4f1Sjruoho 	ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
313068fb4f1Sjruoho 	ATF_REQUIRE(munmap(map, page) == 0);
314068fb4f1Sjruoho }
315068fb4f1Sjruoho 
3164f77b889Sjoerg ATF_TC(mprotect_mremap_exec);
ATF_TC_HEAD(mprotect_mremap_exec,tc)3174f77b889Sjoerg ATF_TC_HEAD(mprotect_mremap_exec, tc)
3184f77b889Sjoerg {
3194f77b889Sjoerg 	atf_tc_set_md_var(tc, "descr",
3204f77b889Sjoerg 	    "Test mremap(2)+mprotect(2) executable space protections");
3214f77b889Sjoerg }
3224f77b889Sjoerg 
3234f77b889Sjoerg /*
3244f77b889Sjoerg  * Trivial function -- should fit into a page
3254f77b889Sjoerg  */
ATF_TC_BODY(mprotect_mremap_exec,tc)3264f77b889Sjoerg ATF_TC_BODY(mprotect_mremap_exec, tc)
3274f77b889Sjoerg {
3284f77b889Sjoerg 	void *map, *map2;
3294f77b889Sjoerg 	pid_t pid;
3304f77b889Sjoerg 	int sta;
3314f77b889Sjoerg 
3324f77b889Sjoerg 	/*
3334f77b889Sjoerg 	 * Map a page read/write/exec and duplicate it.
3344f77b889Sjoerg 	 * Map the copy executable.
3354f77b889Sjoerg 	 * Copy a trivial assembly function to the writeable mapping.
3364f77b889Sjoerg 	 * Try to execute it. This should never create a SIGSEGV.
3374f77b889Sjoerg 	 */
3384f77b889Sjoerg 
3394f77b889Sjoerg 	map = mmap(NULL, page, PROT_MPROTECT(PROT_EXEC|PROT_WRITE|PROT_READ),
3404f77b889Sjoerg 	    MAP_ANON, -1, 0);
3414f77b889Sjoerg 	ATF_REQUIRE(map != MAP_FAILED);
3424f77b889Sjoerg 	map2 = mremap(map, page, NULL, page, MAP_REMAPDUP);
3434f77b889Sjoerg 	ATF_REQUIRE(map2 != MAP_FAILED);
3444f77b889Sjoerg 	ATF_REQUIRE(mprotect(map, page, PROT_WRITE|PROT_READ) == 0);
3454f77b889Sjoerg 	ATF_REQUIRE(mprotect(map2, page, PROT_EXEC|PROT_READ) == 0);
3464f77b889Sjoerg 
3474f77b889Sjoerg 	memcpy(map, (void *)return_one,
3484f77b889Sjoerg 	    (uintptr_t)return_one_end - (uintptr_t)return_one);
3494f77b889Sjoerg 	__builtin___clear_cache(map, (void *)((uintptr_t)map + page));
3504f77b889Sjoerg 
3514f77b889Sjoerg 	ATF_REQUIRE(((int (*)(void))map2)() == 1);
3524f77b889Sjoerg 
3534f77b889Sjoerg 	/* Double check that the executable mapping is not writeable. */
3544f77b889Sjoerg 	pid = fork();
3554f77b889Sjoerg 	ATF_REQUIRE(pid >= 0);
3564f77b889Sjoerg 
3574f77b889Sjoerg 	if (pid == 0) {
3584f77b889Sjoerg 		ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
3594f77b889Sjoerg 		ATF_REQUIRE(strlcpy(map2, "XXX", 3) == 0);
3604f77b889Sjoerg 	}
3614f77b889Sjoerg 
3624f77b889Sjoerg 	(void)wait(&sta);
3634f77b889Sjoerg 
3644f77b889Sjoerg 	ATF_REQUIRE(WIFEXITED(sta) != 0);
3654f77b889Sjoerg 	ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
3664f77b889Sjoerg 
3674f77b889Sjoerg 	if (exec_prot_support() == PERPAGE_XP) {
3684f77b889Sjoerg 		/* Double check that the writeable mapping is not executable. */
3694f77b889Sjoerg 		pid = fork();
3704f77b889Sjoerg 		ATF_REQUIRE(pid >= 0);
3714f77b889Sjoerg 
3724f77b889Sjoerg 		if (pid == 0) {
3734f77b889Sjoerg 			ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
3744f77b889Sjoerg 			ATF_REQUIRE(((int (*)(void))map)() == 1);
3754f77b889Sjoerg 		}
3764f77b889Sjoerg 
3774f77b889Sjoerg 		(void)wait(&sta);
3784f77b889Sjoerg 
3794f77b889Sjoerg 		ATF_REQUIRE(WIFEXITED(sta) != 0);
3804f77b889Sjoerg 		ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
3814f77b889Sjoerg 	}
3824f77b889Sjoerg 
3834f77b889Sjoerg 	ATF_REQUIRE(munmap(map, page) == 0);
3844f77b889Sjoerg 	ATF_REQUIRE(munmap(map2, page) == 0);
3854f77b889Sjoerg }
3864f77b889Sjoerg 
387*37baba21Schristos ATF_TC(mprotect_mremap_fork_exec);
ATF_TC_HEAD(mprotect_mremap_fork_exec,tc)388*37baba21Schristos ATF_TC_HEAD(mprotect_mremap_fork_exec, tc)
389*37baba21Schristos {
390*37baba21Schristos        atf_tc_set_md_var(tc, "descr",
391*37baba21Schristos 	   "Test mremap(2)+fork(2)+mprotect(2) executable space protections");
392*37baba21Schristos }
393*37baba21Schristos 
ATF_TC_BODY(mprotect_mremap_fork_exec,tc)394*37baba21Schristos ATF_TC_BODY(mprotect_mremap_fork_exec, tc)
395*37baba21Schristos {
396*37baba21Schristos 	void *map, *map2;
397*37baba21Schristos 	pid_t pid;
398*37baba21Schristos 
399*37baba21Schristos 	atf_tc_expect_fail("PR lib/55177");
400*37baba21Schristos 
401*37baba21Schristos 	/*
402*37baba21Schristos 	 * Map a page read/write/exec and duplicate it.
403*37baba21Schristos 	 * Map the copy executable.
404*37baba21Schristos 	 * Copy a function to the writeable mapping and execute it
405*37baba21Schristos 	 * Fork a child and wait for it
406*37baba21Schristos 	 * Copy a different function to the writeable mapping and execute it
407*37baba21Schristos 	 * The original function shouldn't be called
408*37baba21Schristos 	 */
409*37baba21Schristos 
410*37baba21Schristos 	map = mmap(NULL, page, PROT_READ|PROT_WRITE|PROT_MPROTECT(PROT_EXEC),
411*37baba21Schristos 	    MAP_ANON, -1, 0);
412*37baba21Schristos 	ATF_REQUIRE(map != MAP_FAILED);
413*37baba21Schristos 	map2 = mremap(map, page, NULL, page, MAP_REMAPDUP);
414*37baba21Schristos 	ATF_REQUIRE(map2 != MAP_FAILED);
415*37baba21Schristos 	ATF_REQUIRE(mprotect(map2, page, PROT_EXEC|PROT_READ) == 0);
416*37baba21Schristos 
417*37baba21Schristos 	memcpy(map, (void *)return_1,
418*37baba21Schristos 	    (uintptr_t)return_2 - (uintptr_t)return_1);
419*37baba21Schristos 	__builtin___clear_cache(map, (void *)((uintptr_t)map + page));
420*37baba21Schristos 
421*37baba21Schristos 	ATF_REQUIRE(((int (*)(void))map2)() == 1);
422*37baba21Schristos 
423*37baba21Schristos 	pid = fork();
424*37baba21Schristos 	ATF_REQUIRE(pid >= 0);
425*37baba21Schristos 
426*37baba21Schristos 	if (pid == 0)
427*37baba21Schristos 		_exit(0);
428*37baba21Schristos 
429*37baba21Schristos 	(void)wait(NULL);
430*37baba21Schristos 
431*37baba21Schristos 	memcpy(map, (void *)return_2,
432*37baba21Schristos 	    (uintptr_t)return_3 - (uintptr_t)return_2);
433*37baba21Schristos 	__builtin___clear_cache(map, (void *)((uintptr_t)map + page));
434*37baba21Schristos 
435*37baba21Schristos 	ATF_REQUIRE(((int (*)(void))map2)() == 2);
436*37baba21Schristos 
437*37baba21Schristos 	ATF_REQUIRE(munmap(map, page) == 0);
438*37baba21Schristos 	ATF_REQUIRE(munmap(map2, page) == 0);
439*37baba21Schristos }
440*37baba21Schristos 
ATF_TP_ADD_TCS(tp)441068fb4f1Sjruoho ATF_TP_ADD_TCS(tp)
442068fb4f1Sjruoho {
443068fb4f1Sjruoho 	page = sysconf(_SC_PAGESIZE);
444068fb4f1Sjruoho 	ATF_REQUIRE(page >= 0);
445068fb4f1Sjruoho 
446068fb4f1Sjruoho 	ATF_TP_ADD_TC(tp, mprotect_access);
447068fb4f1Sjruoho 	ATF_TP_ADD_TC(tp, mprotect_err);
448ca453df6Sjym 	ATF_TP_ADD_TC(tp, mprotect_exec);
449068fb4f1Sjruoho 	ATF_TP_ADD_TC(tp, mprotect_pax);
450068fb4f1Sjruoho 	ATF_TP_ADD_TC(tp, mprotect_write);
4514f77b889Sjoerg 	ATF_TP_ADD_TC(tp, mprotect_mremap_exec);
452*37baba21Schristos 	ATF_TP_ADD_TC(tp, mprotect_mremap_fork_exec);
453068fb4f1Sjruoho 
454068fb4f1Sjruoho 	return atf_no_error();
455068fb4f1Sjruoho }
456