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