18beadca5SMark Johnston /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
38beadca5SMark Johnston *
48beadca5SMark Johnston * Copyright (c) 2019 Mark Johnston <markj@FreeBSD.org>
58beadca5SMark Johnston *
68beadca5SMark Johnston * Redistribution and use in source and binary forms, with or without
78beadca5SMark Johnston * modification, are permitted provided that the following conditions are
88beadca5SMark Johnston * met:
98beadca5SMark Johnston * 1. Redistributions of source code must retain the above copyright
108beadca5SMark Johnston * notice, this list of conditions and the following disclaimer.
118beadca5SMark Johnston * 2. Redistributions in binary form must reproduce the above copyright
128beadca5SMark Johnston * notice, this list of conditions and the following disclaimer in
138beadca5SMark Johnston * the documentation and/or other materials provided with the distribution.
148beadca5SMark Johnston *
158beadca5SMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
168beadca5SMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
178beadca5SMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
188beadca5SMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
198beadca5SMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
208beadca5SMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
218beadca5SMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
228beadca5SMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
238beadca5SMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
248beadca5SMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
258beadca5SMark Johnston * SUCH DAMAGE.
268beadca5SMark Johnston */
278beadca5SMark Johnston
288beadca5SMark Johnston #include <sys/cdefs.h>
298beadca5SMark Johnston #include <sys/mman.h>
308beadca5SMark Johnston #include <sys/ptrace.h>
31*8f26ed01SMark Johnston #include <sys/resource.h>
328beadca5SMark Johnston #include <sys/wait.h>
338beadca5SMark Johnston
348beadca5SMark Johnston #include <errno.h>
35*8f26ed01SMark Johnston #include <fcntl.h>
368beadca5SMark Johnston #include <signal.h>
37*8f26ed01SMark Johnston #include <stdatomic.h>
388beadca5SMark Johnston #include <stdio.h>
39*8f26ed01SMark Johnston #include <stdlib.h>
408beadca5SMark Johnston #include <string.h>
418beadca5SMark Johnston #include <unistd.h>
428beadca5SMark Johnston
438beadca5SMark Johnston #include <atf-c.h>
448beadca5SMark Johnston
458beadca5SMark Johnston static void
test_wired_copy_on_write(void * addr,size_t len)468beadca5SMark Johnston test_wired_copy_on_write(void *addr, size_t len)
478beadca5SMark Johnston {
488beadca5SMark Johnston int status, val;
498beadca5SMark Johnston pid_t pid;
508beadca5SMark Johnston
518beadca5SMark Johnston pid = fork();
528beadca5SMark Johnston if (pid == -1)
538beadca5SMark Johnston atf_tc_fail("fork() failed: %s", strerror(errno));
548beadca5SMark Johnston if (pid == 0) {
558beadca5SMark Johnston if (mlock(addr, len) != 0)
568beadca5SMark Johnston _exit(1);
578beadca5SMark Johnston if (ptrace(PT_TRACE_ME, 0, NULL, 0) != 0)
588beadca5SMark Johnston _exit(2);
598beadca5SMark Johnston if (raise(SIGSTOP) != 0)
608beadca5SMark Johnston _exit(3);
618beadca5SMark Johnston if (munlock(addr, len) != 0)
628beadca5SMark Johnston _exit(4);
638beadca5SMark Johnston _exit(0);
648beadca5SMark Johnston }
658beadca5SMark Johnston
668beadca5SMark Johnston ATF_REQUIRE(waitpid(pid, &status, 0) == pid);
678beadca5SMark Johnston ATF_REQUIRE_MSG(!WIFEXITED(status),
688beadca5SMark Johnston "child exited with status %d", WEXITSTATUS(status));
698beadca5SMark Johnston ATF_REQUIRE(WIFSTOPPED(status));
708beadca5SMark Johnston ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
718beadca5SMark Johnston
728beadca5SMark Johnston errno = 0;
738beadca5SMark Johnston val = ptrace(PT_READ_D, pid, addr, 0);
748beadca5SMark Johnston ATF_REQUIRE(errno == 0);
758beadca5SMark Johnston ATF_REQUIRE(ptrace(PT_WRITE_D, pid, addr, val) == 0);
768beadca5SMark Johnston ATF_REQUIRE(ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) == 0);
778beadca5SMark Johnston ATF_REQUIRE(waitpid(pid, &status, 0) == pid);
788beadca5SMark Johnston ATF_REQUIRE(WIFEXITED(status));
798beadca5SMark Johnston ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0,
808beadca5SMark Johnston "child exited with status %d", WSTOPSIG(status));
818beadca5SMark Johnston }
828beadca5SMark Johnston
838beadca5SMark Johnston /*
848beadca5SMark Johnston * Use ptrace(2) to trigger a copy-on-write fault of anonymous memory.
858beadca5SMark Johnston */
868beadca5SMark Johnston ATF_TC_WITHOUT_HEAD(mlock__copy_on_write_anon);
ATF_TC_BODY(mlock__copy_on_write_anon,tc)878beadca5SMark Johnston ATF_TC_BODY(mlock__copy_on_write_anon, tc)
888beadca5SMark Johnston {
898beadca5SMark Johnston char *addr;
908beadca5SMark Johnston int len;
918beadca5SMark Johnston
928beadca5SMark Johnston len = getpagesize();
938beadca5SMark Johnston addr = mmap(NULL, len, PROT_READ, MAP_ANON, -1, 0);
948beadca5SMark Johnston ATF_REQUIRE(addr != MAP_FAILED);
958beadca5SMark Johnston
968beadca5SMark Johnston test_wired_copy_on_write(addr, len);
978beadca5SMark Johnston }
988beadca5SMark Johnston
998beadca5SMark Johnston /*
1008beadca5SMark Johnston * Use ptrace(2) to trigger a copy-on-write fault of a read-only text page.
1018beadca5SMark Johnston */
1028beadca5SMark Johnston ATF_TC_WITHOUT_HEAD(mlock__copy_on_write_vnode);
ATF_TC_BODY(mlock__copy_on_write_vnode,tc)1038beadca5SMark Johnston ATF_TC_BODY(mlock__copy_on_write_vnode, tc)
1048beadca5SMark Johnston {
1058beadca5SMark Johnston void *addr;
1068beadca5SMark Johnston int len;
1078beadca5SMark Johnston
1088beadca5SMark Johnston len = getpagesize();
1098beadca5SMark Johnston addr = (void *)((uintptr_t)test_wired_copy_on_write & ~(len - 1));
1108beadca5SMark Johnston
1118beadca5SMark Johnston test_wired_copy_on_write(addr, len);
1128beadca5SMark Johnston }
1138beadca5SMark Johnston
1148beadca5SMark Johnston /*
1158beadca5SMark Johnston * Try truncating and then resizing an mlock()ed mapping.
1168beadca5SMark Johnston */
1178beadca5SMark Johnston ATF_TC_WITHOUT_HEAD(mlock__truncate_and_resize);
ATF_TC_BODY(mlock__truncate_and_resize,tc)1188beadca5SMark Johnston ATF_TC_BODY(mlock__truncate_and_resize, tc)
1198beadca5SMark Johnston {
1208beadca5SMark Johnston char filename[16];
1218beadca5SMark Johnston char *addr;
1228beadca5SMark Johnston int fd, i, len;
1238beadca5SMark Johnston
1248beadca5SMark Johnston snprintf(filename, sizeof(filename), "tmp.XXXXXX");
1258beadca5SMark Johnston fd = mkstemp(filename);
1268beadca5SMark Johnston ATF_REQUIRE(fd >= 0);
1278beadca5SMark Johnston ATF_REQUIRE(unlink(filename) == 0);
1288beadca5SMark Johnston
1298beadca5SMark Johnston len = getpagesize();
1308beadca5SMark Johnston ATF_REQUIRE(ftruncate(fd, len) == 0);
1318beadca5SMark Johnston
1328beadca5SMark Johnston addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1338beadca5SMark Johnston ATF_REQUIRE(addr != MAP_FAILED);
1348beadca5SMark Johnston ATF_REQUIRE(mlock(addr, len) == 0);
1358beadca5SMark Johnston memset(addr, 1, len);
1368beadca5SMark Johnston ATF_REQUIRE(ftruncate(fd, 0) == 0);
1378beadca5SMark Johnston ATF_REQUIRE(ftruncate(fd, len) == 0);
1388beadca5SMark Johnston for (i = 0; i < len; i++)
1398beadca5SMark Johnston ATF_REQUIRE(addr[i] == 0);
1408beadca5SMark Johnston ATF_REQUIRE(munlock(addr, len) == 0);
1418beadca5SMark Johnston }
1428beadca5SMark Johnston
1438beadca5SMark Johnston /*
1448beadca5SMark Johnston * Make sure that we can munlock() a truncated mapping.
1458beadca5SMark Johnston */
1468beadca5SMark Johnston ATF_TC_WITHOUT_HEAD(mlock__truncate_and_unlock);
ATF_TC_BODY(mlock__truncate_and_unlock,tc)1478beadca5SMark Johnston ATF_TC_BODY(mlock__truncate_and_unlock, tc)
1488beadca5SMark Johnston {
1498beadca5SMark Johnston char filename[16];
1508beadca5SMark Johnston void *addr;
1518beadca5SMark Johnston int fd, len;
1528beadca5SMark Johnston
1538beadca5SMark Johnston snprintf(filename, sizeof(filename), "tmp.XXXXXX");
1548beadca5SMark Johnston fd = mkstemp(filename);
1558beadca5SMark Johnston ATF_REQUIRE(fd >= 0);
1568beadca5SMark Johnston ATF_REQUIRE(unlink(filename) == 0);
1578beadca5SMark Johnston
1588beadca5SMark Johnston len = getpagesize();
1598beadca5SMark Johnston ATF_REQUIRE(ftruncate(fd, len) == 0);
1608beadca5SMark Johnston
1618beadca5SMark Johnston addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1628beadca5SMark Johnston ATF_REQUIRE(addr != MAP_FAILED);
1638beadca5SMark Johnston ATF_REQUIRE(mlock(addr, len) == 0);
1648beadca5SMark Johnston ATF_REQUIRE(ftruncate(fd, 0) == 0);
1658beadca5SMark Johnston ATF_REQUIRE(munlock(addr, len) == 0);
1668beadca5SMark Johnston }
1678beadca5SMark Johnston
168*8f26ed01SMark Johnston /*
169*8f26ed01SMark Johnston * Exercise a corner case involving an interaction between mlock() and superpage
170*8f26ed01SMark Johnston * creation: a truncation of the object backing a mapping results in the
171*8f26ed01SMark Johnston * truncated region being unmapped by the pmap, but does not affect the logical
172*8f26ed01SMark Johnston * mapping. In particular, the truncated region remains mlock()ed. If the
173*8f26ed01SMark Johnston * mapping is later extended, a page fault in the formerly truncated region can
174*8f26ed01SMark Johnston * result in superpage creation via a call to pmap_enter(psind = 1).
175*8f26ed01SMark Johnston */
176*8f26ed01SMark Johnston ATF_TC(mlock__superpage_fault);
ATF_TC_HEAD(mlock__superpage_fault,tc)177*8f26ed01SMark Johnston ATF_TC_HEAD(mlock__superpage_fault, tc)
178*8f26ed01SMark Johnston {
179*8f26ed01SMark Johnston atf_tc_set_md_var(tc, "require.user", "root");
180*8f26ed01SMark Johnston }
ATF_TC_BODY(mlock__superpage_fault,tc)181*8f26ed01SMark Johnston ATF_TC_BODY(mlock__superpage_fault, tc)
182*8f26ed01SMark Johnston {
183*8f26ed01SMark Johnston struct rlimit rlim;
184*8f26ed01SMark Johnston void *addr1, *addr2;
185*8f26ed01SMark Johnston size_t len, pagesizes[MAXPAGESIZES];
186*8f26ed01SMark Johnston int count, error, shmfd;
187*8f26ed01SMark Johnston char vec;
188*8f26ed01SMark Johnston
189*8f26ed01SMark Johnston count = getpagesizes(pagesizes, MAXPAGESIZES);
190*8f26ed01SMark Johnston ATF_REQUIRE_MSG(count >= 1,
191*8f26ed01SMark Johnston "failed to get page sizes: %s", strerror(errno));
192*8f26ed01SMark Johnston if (count == 1)
193*8f26ed01SMark Johnston atf_tc_skip("system does not support multiple page sizes");
194*8f26ed01SMark Johnston len = pagesizes[1];
195*8f26ed01SMark Johnston
196*8f26ed01SMark Johnston error = getrlimit(RLIMIT_MEMLOCK, &rlim);
197*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "getrlimit: %s", strerror(errno));
198*8f26ed01SMark Johnston rlim.rlim_cur += len;
199*8f26ed01SMark Johnston rlim.rlim_max += len;
200*8f26ed01SMark Johnston error = setrlimit(RLIMIT_MEMLOCK, &rlim);
201*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "setrlimit: %s", strerror(errno));
202*8f26ed01SMark Johnston
203*8f26ed01SMark Johnston shmfd = shm_open(SHM_ANON, O_RDWR | O_CREAT, 0600);
204*8f26ed01SMark Johnston ATF_REQUIRE_MSG(shmfd >= 0, "shm_open: %s", strerror(errno));
205*8f26ed01SMark Johnston error = ftruncate(shmfd, len);
206*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "ftruncate: %s", strerror(errno));
207*8f26ed01SMark Johnston
208*8f26ed01SMark Johnston addr1 = mmap(NULL, len, PROT_READ | PROT_WRITE,
209*8f26ed01SMark Johnston MAP_SHARED | MAP_ALIGNED_SUPER, shmfd, 0);
210*8f26ed01SMark Johnston ATF_REQUIRE_MSG(addr1 != MAP_FAILED, "mmap: %s", strerror(errno));
211*8f26ed01SMark Johnston ATF_REQUIRE_MSG(((uintptr_t)addr1 & (len - 1)) == 0,
212*8f26ed01SMark Johnston "addr %p is misaligned", addr1);
213*8f26ed01SMark Johnston addr2 = mmap(NULL, len, PROT_READ,
214*8f26ed01SMark Johnston MAP_SHARED | MAP_ALIGNED_SUPER, shmfd, 0);
215*8f26ed01SMark Johnston ATF_REQUIRE_MSG(addr2 != MAP_FAILED, "mmap: %s", strerror(errno));
216*8f26ed01SMark Johnston ATF_REQUIRE_MSG(((uintptr_t)addr2 & (len - 1)) == 0,
217*8f26ed01SMark Johnston "addr %p is misaligned", addr2);
218*8f26ed01SMark Johnston
219*8f26ed01SMark Johnston memset(addr1, 0x42, len);
220*8f26ed01SMark Johnston error = mincore(addr1, pagesizes[0], &vec);
221*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
222*8f26ed01SMark Johnston if ((vec & MINCORE_SUPER) == 0)
223*8f26ed01SMark Johnston atf_tc_skip("initial superpage promotion failed");
224*8f26ed01SMark Johnston
225*8f26ed01SMark Johnston error = mlock(addr2, len);
226*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "mlock: %s", strerror(errno));
227*8f26ed01SMark Johnston error = mincore(addr2, pagesizes[0], &vec);
228*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
229*8f26ed01SMark Johnston ATF_REQUIRE((vec & MINCORE_SUPER) != 0);
230*8f26ed01SMark Johnston
231*8f26ed01SMark Johnston /*
232*8f26ed01SMark Johnston * Free a page back to the superpage reservation, demoting both
233*8f26ed01SMark Johnston * mappings.
234*8f26ed01SMark Johnston */
235*8f26ed01SMark Johnston error = ftruncate(shmfd, len - pagesizes[0]);
236*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "ftruncate: %s", strerror(errno));
237*8f26ed01SMark Johnston
238*8f26ed01SMark Johnston /*
239*8f26ed01SMark Johnston * Extend the mapping back to its original size.
240*8f26ed01SMark Johnston */
241*8f26ed01SMark Johnston error = ftruncate(shmfd, len);
242*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "ftruncate: %s", strerror(errno));
243*8f26ed01SMark Johnston
244*8f26ed01SMark Johnston /*
245*8f26ed01SMark Johnston * Trigger re-promotion.
246*8f26ed01SMark Johnston */
247*8f26ed01SMark Johnston error = mincore(addr1, pagesizes[0], &vec);
248*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
249*8f26ed01SMark Johnston ATF_REQUIRE((vec & MINCORE_SUPER) == 0);
250*8f26ed01SMark Johnston memset((char *)addr1 + len - pagesizes[0], 0x43, pagesizes[0]);
251*8f26ed01SMark Johnston error = mincore(addr1, pagesizes[0], &vec);
252*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
253*8f26ed01SMark Johnston ATF_REQUIRE((vec & MINCORE_SUPER) != 0);
254*8f26ed01SMark Johnston
255*8f26ed01SMark Johnston /*
256*8f26ed01SMark Johnston * Trigger a read fault, which should install a superpage mapping
257*8f26ed01SMark Johnston * without promotion.
258*8f26ed01SMark Johnston */
259*8f26ed01SMark Johnston error = mincore(addr2, pagesizes[0], &vec);
260*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
261*8f26ed01SMark Johnston ATF_REQUIRE((vec & MINCORE_SUPER) == 0);
262*8f26ed01SMark Johnston (void)atomic_load(
263*8f26ed01SMark Johnston (_Atomic int *)(void *)((char *)addr2 + len - pagesizes[0]));
264*8f26ed01SMark Johnston error = mincore(addr2, pagesizes[0], &vec);
265*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
266*8f26ed01SMark Johnston ATF_REQUIRE((vec & MINCORE_SUPER) != 0);
267*8f26ed01SMark Johnston
268*8f26ed01SMark Johnston /*
269*8f26ed01SMark Johnston * Trigger demotion of the wired mapping.
270*8f26ed01SMark Johnston */
271*8f26ed01SMark Johnston error = munlock(addr2, pagesizes[0]);
272*8f26ed01SMark Johnston ATF_REQUIRE_MSG(error == 0, "munlock: %s", strerror(errno));
273*8f26ed01SMark Johnston
274*8f26ed01SMark Johnston ATF_REQUIRE(close(shmfd) == 0);
275*8f26ed01SMark Johnston }
276*8f26ed01SMark Johnston
ATF_TP_ADD_TCS(tp)2778beadca5SMark Johnston ATF_TP_ADD_TCS(tp)
2788beadca5SMark Johnston {
2798beadca5SMark Johnston ATF_TP_ADD_TC(tp, mlock__copy_on_write_anon);
2808beadca5SMark Johnston ATF_TP_ADD_TC(tp, mlock__copy_on_write_vnode);
2818beadca5SMark Johnston ATF_TP_ADD_TC(tp, mlock__truncate_and_resize);
2828beadca5SMark Johnston ATF_TP_ADD_TC(tp, mlock__truncate_and_unlock);
283*8f26ed01SMark Johnston ATF_TP_ADD_TC(tp, mlock__superpage_fault);
2848beadca5SMark Johnston
2858beadca5SMark Johnston return (atf_no_error());
2868beadca5SMark Johnston }
287