1*5d55fed3Sskrll /* $NetBSD: t_mlock.c,v 1.8 2020/01/24 08:45:16 skrll Exp $ */
271224831Sjruoho
371224831Sjruoho /*-
471224831Sjruoho * Copyright (c) 2012 The NetBSD Foundation, Inc.
571224831Sjruoho * All rights reserved.
671224831Sjruoho *
771224831Sjruoho * This code is derived from software contributed to The NetBSD Foundation
871224831Sjruoho * by Jukka Ruohonen.
971224831Sjruoho *
1071224831Sjruoho * Redistribution and use in source and binary forms, with or without
1171224831Sjruoho * modification, are permitted provided that the following conditions
1271224831Sjruoho * are met:
1371224831Sjruoho * 1. Redistributions of source code must retain the above copyright
1471224831Sjruoho * notice, this list of conditions and the following disclaimer.
1571224831Sjruoho * 2. Redistributions in binary form must reproduce the above copyright
1671224831Sjruoho * notice, this list of conditions and the following disclaimer in the
1771224831Sjruoho * documentation and/or other materials provided with the distribution.
1871224831Sjruoho *
1971224831Sjruoho * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2071224831Sjruoho * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2171224831Sjruoho * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2271224831Sjruoho * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2371224831Sjruoho * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2471224831Sjruoho * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2571224831Sjruoho * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2671224831Sjruoho * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2771224831Sjruoho * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2871224831Sjruoho * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2971224831Sjruoho * POSSIBILITY OF SUCH DAMAGE.
3071224831Sjruoho */
3171224831Sjruoho #include <sys/cdefs.h>
32*5d55fed3Sskrll __RCSID("$NetBSD: t_mlock.c,v 1.8 2020/01/24 08:45:16 skrll Exp $");
3371224831Sjruoho
3471224831Sjruoho #include <sys/mman.h>
3571224831Sjruoho #include <sys/resource.h>
361f6c8fa4Smartin #include <sys/sysctl.h>
3771224831Sjruoho #include <sys/wait.h>
3871224831Sjruoho
3971224831Sjruoho #include <errno.h>
4071224831Sjruoho #include <atf-c.h>
4169c011c5Smartin #include <stdint.h>
4271224831Sjruoho #include <stdio.h>
4371224831Sjruoho #include <stdlib.h>
44e5eb6710Skre #include <string.h>
4571224831Sjruoho #include <unistd.h>
4671224831Sjruoho
4771224831Sjruoho static long page = 0;
4871224831Sjruoho
4971224831Sjruoho ATF_TC(mlock_clip);
ATF_TC_HEAD(mlock_clip,tc)5071224831Sjruoho ATF_TC_HEAD(mlock_clip, tc)
5171224831Sjruoho {
5271224831Sjruoho atf_tc_set_md_var(tc, "descr", "Test with mlock(2) that UVM only "
5371224831Sjruoho "clips if the clip address is within the entry (PR kern/44788)");
5471224831Sjruoho }
5571224831Sjruoho
ATF_TC_BODY(mlock_clip,tc)5671224831Sjruoho ATF_TC_BODY(mlock_clip, tc)
5771224831Sjruoho {
5871224831Sjruoho void *buf;
59e5eb6710Skre int err1, err2;
6071224831Sjruoho
6171224831Sjruoho buf = malloc(page);
6271224831Sjruoho ATF_REQUIRE(buf != NULL);
63e5eb6710Skre fprintf(stderr, "mlock_clip: buf = %p (page=%ld)\n", buf, page);
6471224831Sjruoho
6571224831Sjruoho if (page < 1024)
6671224831Sjruoho atf_tc_skip("page size too small");
6771224831Sjruoho
6871224831Sjruoho for (size_t i = page; i >= 1; i = i - 1024) {
69e5eb6710Skre err1 = mlock(buf, page - i);
70e5eb6710Skre if (err1 != 0)
71e5eb6710Skre fprintf(stderr, "mlock_clip: page=%ld i=%zu,"
72e5eb6710Skre " mlock(%p, %ld): %s\n", page, i, buf, page - i,
73e5eb6710Skre strerror(errno));
74e5eb6710Skre err2 = munlock(buf, page - i);
75e5eb6710Skre if (err2 != 0)
76e5eb6710Skre fprintf(stderr, "mlock_clip: page=%ld i=%zu,"
77e5eb6710Skre " munlock(%p, %ld): %s (mlock %s)\n", page, i,
78e5eb6710Skre buf, page - i, strerror(errno), err1?"failed":"ok");
7971224831Sjruoho }
8071224831Sjruoho
8171224831Sjruoho free(buf);
8271224831Sjruoho }
8371224831Sjruoho
8471224831Sjruoho ATF_TC(mlock_err);
ATF_TC_HEAD(mlock_err,tc)8571224831Sjruoho ATF_TC_HEAD(mlock_err, tc)
8671224831Sjruoho {
8771224831Sjruoho atf_tc_set_md_var(tc, "descr",
8871224831Sjruoho "Test error conditions in mlock(2) and munlock(2)");
8971224831Sjruoho }
9071224831Sjruoho
ATF_TC_BODY(mlock_err,tc)9171224831Sjruoho ATF_TC_BODY(mlock_err, tc)
9271224831Sjruoho {
9369c011c5Smartin void *invalid_ptr;
9428a55750Skre void *buf;
95e5eb6710Skre int mlock_err, munlock_err;
9669c011c5Smartin
9728a55750Skre /*
9828a55750Skre * Any bad address must return ENOMEM (for lock & unlock)
9928a55750Skre */
10028a55750Skre errno = 0;
10128a55750Skre ATF_REQUIRE_ERRNO(ENOMEM, mlock(NULL, page) == -1);
10271224831Sjruoho
10371224831Sjruoho errno = 0;
10428a55750Skre ATF_REQUIRE_ERRNO(ENOMEM, mlock((char *)0, page) == -1);
10571224831Sjruoho
10671224831Sjruoho errno = 0;
10728a55750Skre ATF_REQUIRE_ERRNO(ENOMEM, mlock((char *)-1, page) == -1);
10871224831Sjruoho
10971224831Sjruoho errno = 0;
11028a55750Skre ATF_REQUIRE_ERRNO(ENOMEM, munlock(NULL, page) == -1);
11171224831Sjruoho
11271224831Sjruoho errno = 0;
11328a55750Skre ATF_REQUIRE_ERRNO(ENOMEM, munlock((char *)0, page) == -1);
11471224831Sjruoho
11571224831Sjruoho errno = 0;
11628a55750Skre ATF_REQUIRE_ERRNO(ENOMEM, munlock((char *)-1, page) == -1);
11728a55750Skre
11828a55750Skre buf = malloc(page);
11928a55750Skre ATF_REQUIRE(buf != NULL);
120e5eb6710Skre fprintf(stderr, "mlock_err: buf = %p (page=%ld)\n", buf, page);
12128a55750Skre
12228a55750Skre /*
12328a55750Skre * unlocking memory that is not locked is an error...
12428a55750Skre */
12571224831Sjruoho
12671224831Sjruoho errno = 0;
12728a55750Skre ATF_REQUIRE_ERRNO(ENOMEM, munlock(buf, page) == -1);
12828a55750Skre
12928a55750Skre /*
13028a55750Skre * These are permitted to fail (EINVAL) but do not on NetBSD
13128a55750Skre */
132e5eb6710Skre mlock_err = mlock((void *)(((uintptr_t)buf) + page/3), page/5);
133e5eb6710Skre if (mlock_err != 0)
134e5eb6710Skre fprintf(stderr, "mlock_err: mlock(%p, %ld): %d [%d] %s\n",
135e5eb6710Skre (void *)(((uintptr_t)buf) + page/3), page/5, mlock_err,
136e5eb6710Skre errno, strerror(errno));
137e5eb6710Skre ATF_REQUIRE(mlock_err == 0);
138e5eb6710Skre munlock_err= munlock((void *)(((uintptr_t)buf) + page/3), page/5);
139e5eb6710Skre if (munlock_err != 0)
140e5eb6710Skre fprintf(stderr, "mlock_err: munlock(%p, %ld): %d [%d] %s\n",
141e5eb6710Skre (void *)(((uintptr_t)buf) + page/3), page/5, munlock_err,
142e5eb6710Skre errno, strerror(errno));
143e5eb6710Skre ATF_REQUIRE(munlock_err == 0);
14428a55750Skre
14528a55750Skre (void)free(buf);
14669c011c5Smartin
14769c011c5Smartin /*
14869c011c5Smartin * Try to create a pointer to an unmapped page - first after current
14969c011c5Smartin * brk will likely do.
15069c011c5Smartin */
15169c011c5Smartin invalid_ptr = (void*)(((uintptr_t)sbrk(0)+page) & ~(page-1));
15269c011c5Smartin printf("testing with (hopefully) invalid pointer %p\n", invalid_ptr);
15369c011c5Smartin
15469c011c5Smartin errno = 0;
15569c011c5Smartin ATF_REQUIRE_ERRNO(ENOMEM, mlock(invalid_ptr, page) == -1);
15669c011c5Smartin
15769c011c5Smartin errno = 0;
15869c011c5Smartin ATF_REQUIRE_ERRNO(ENOMEM, munlock(invalid_ptr, page) == -1);
15971224831Sjruoho }
16071224831Sjruoho
16171224831Sjruoho ATF_TC(mlock_limits);
ATF_TC_HEAD(mlock_limits,tc)16271224831Sjruoho ATF_TC_HEAD(mlock_limits, tc)
16371224831Sjruoho {
16471224831Sjruoho atf_tc_set_md_var(tc, "descr", "Test system limits with mlock(2)");
16571224831Sjruoho }
16671224831Sjruoho
ATF_TC_BODY(mlock_limits,tc)16771224831Sjruoho ATF_TC_BODY(mlock_limits, tc)
16871224831Sjruoho {
16971224831Sjruoho struct rlimit res;
17071224831Sjruoho void *buf;
17171224831Sjruoho pid_t pid;
17271224831Sjruoho int sta;
17371224831Sjruoho
17471224831Sjruoho buf = malloc(page);
17571224831Sjruoho ATF_REQUIRE(buf != NULL);
176e5eb6710Skre fprintf(stderr, "mlock_limits: buf = %p (page=%ld)\n", buf, page);
17771224831Sjruoho
17871224831Sjruoho pid = fork();
17971224831Sjruoho ATF_REQUIRE(pid >= 0);
18071224831Sjruoho
18171224831Sjruoho if (pid == 0) {
18271224831Sjruoho
18371224831Sjruoho for (ssize_t i = page; i >= 2; i -= 100) {
18471224831Sjruoho
18571224831Sjruoho res.rlim_cur = i - 1;
18671224831Sjruoho res.rlim_max = i - 1;
18771224831Sjruoho
188e5eb6710Skre (void)fprintf(stderr, "trying to lock %zu bytes "
18971224831Sjruoho "with %zu byte limit\n", i, (size_t)res.rlim_cur);
19071224831Sjruoho
19171224831Sjruoho if (setrlimit(RLIMIT_MEMLOCK, &res) != 0)
19271224831Sjruoho _exit(EXIT_FAILURE);
19371224831Sjruoho
19471224831Sjruoho errno = 0;
19571224831Sjruoho
196e5eb6710Skre if ((sta = mlock(buf, i)) != -1 || errno != EAGAIN) {
197e5eb6710Skre fprintf(stderr, "mlock(%p, %zu): %d [%d] %s\n",
198e5eb6710Skre buf, i, sta, errno, strerror(errno));
19971224831Sjruoho (void)munlock(buf, i);
20071224831Sjruoho _exit(EXIT_FAILURE);
20171224831Sjruoho }
20271224831Sjruoho }
20371224831Sjruoho
20471224831Sjruoho _exit(EXIT_SUCCESS);
20571224831Sjruoho }
20671224831Sjruoho
20771224831Sjruoho (void)wait(&sta);
20871224831Sjruoho
20971224831Sjruoho if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS)
21071224831Sjruoho atf_tc_fail("mlock(2) locked beyond system limits");
21171224831Sjruoho
21271224831Sjruoho free(buf);
21371224831Sjruoho }
21471224831Sjruoho
21571224831Sjruoho ATF_TC(mlock_mmap);
ATF_TC_HEAD(mlock_mmap,tc)21671224831Sjruoho ATF_TC_HEAD(mlock_mmap, tc)
21771224831Sjruoho {
21871224831Sjruoho atf_tc_set_md_var(tc, "descr", "Test mlock(2)-mmap(2) interaction");
21971224831Sjruoho }
22071224831Sjruoho
ATF_TC_BODY(mlock_mmap,tc)22171224831Sjruoho ATF_TC_BODY(mlock_mmap, tc)
22271224831Sjruoho {
22371224831Sjruoho static const int flags = MAP_ANON | MAP_PRIVATE | MAP_WIRED;
22471224831Sjruoho void *buf;
22571224831Sjruoho
22671224831Sjruoho /*
22771224831Sjruoho * Make a wired RW mapping and check that mlock(2)
22871224831Sjruoho * does not fail for the (already locked) mapping.
22971224831Sjruoho */
23071224831Sjruoho buf = mmap(NULL, page, PROT_READ | PROT_WRITE, flags, -1, 0);
23171224831Sjruoho
232e5eb6710Skre if (buf == MAP_FAILED)
233e5eb6710Skre fprintf(stderr,
234e5eb6710Skre "mlock_mmap: mmap(NULL, %ld, %#x, %#x, -1, 0): MAP_FAILED"
235e5eb6710Skre " [%d] %s\n", page, PROT_READ | PROT_WRITE, flags, errno,
236e5eb6710Skre strerror(errno));
237e5eb6710Skre
23871224831Sjruoho ATF_REQUIRE(buf != MAP_FAILED);
239e5eb6710Skre
240e5eb6710Skre fprintf(stderr, "mlock_mmap: buf=%p, page=%ld\n", buf, page);
241e5eb6710Skre
24271224831Sjruoho ATF_REQUIRE(mlock(buf, page) == 0);
24371224831Sjruoho ATF_REQUIRE(munlock(buf, page) == 0);
24471224831Sjruoho ATF_REQUIRE(munmap(buf, page) == 0);
24571224831Sjruoho ATF_REQUIRE(munlock(buf, page) != 0);
24671224831Sjruoho
247e5eb6710Skre fprintf(stderr, "mlock_mmap: first test succeeded\n");
248e5eb6710Skre
24971224831Sjruoho /*
25071224831Sjruoho * But it should be impossible to mlock(2) a PROT_NONE mapping.
25171224831Sjruoho */
25271224831Sjruoho buf = mmap(NULL, page, PROT_NONE, flags, -1, 0);
25371224831Sjruoho
254e5eb6710Skre if (buf == MAP_FAILED)
255e5eb6710Skre fprintf(stderr,
256e5eb6710Skre "mlock_mmap: mmap(NULL, %ld, %#x, %#x, -1, 0): MAP_FAILED"
257e5eb6710Skre " [%d] %s\n", page, PROT_NONE, flags, errno,
258e5eb6710Skre strerror(errno));
259e5eb6710Skre
26071224831Sjruoho ATF_REQUIRE(buf != MAP_FAILED);
26171224831Sjruoho ATF_REQUIRE(mlock(buf, page) != 0);
26271224831Sjruoho ATF_REQUIRE(munmap(buf, page) == 0);
263e5eb6710Skre
264e5eb6710Skre fprintf(stderr, "mlock_mmap: second test succeeded\n");
26571224831Sjruoho }
26671224831Sjruoho
26771224831Sjruoho ATF_TC(mlock_nested);
ATF_TC_HEAD(mlock_nested,tc)26871224831Sjruoho ATF_TC_HEAD(mlock_nested, tc)
26971224831Sjruoho {
27071224831Sjruoho atf_tc_set_md_var(tc, "descr",
27171224831Sjruoho "Test that consecutive mlock(2) calls succeed");
27271224831Sjruoho }
27371224831Sjruoho
ATF_TC_BODY(mlock_nested,tc)27471224831Sjruoho ATF_TC_BODY(mlock_nested, tc)
27571224831Sjruoho {
27671224831Sjruoho const size_t maxiter = 100;
27771224831Sjruoho void *buf;
278e5eb6710Skre int err;
27971224831Sjruoho
28071224831Sjruoho buf = malloc(page);
28171224831Sjruoho ATF_REQUIRE(buf != NULL);
282e5eb6710Skre fprintf(stderr, "mlock_nested: buf = %p (page=%ld)\n", buf, page);
28371224831Sjruoho
284e5eb6710Skre for (size_t i = 0; i < maxiter; i++) {
285e5eb6710Skre err = mlock(buf, page);
286e5eb6710Skre if (err != 0)
287e5eb6710Skre fprintf(stderr,
288e5eb6710Skre "mlock_nested: i=%zu (of %zu) mlock(%p, %ld): %d [%d] %s\n",
289e5eb6710Skre i, maxiter, buf, page, err, errno, strerror(errno));
290e5eb6710Skre ATF_REQUIRE(err == 0);
291e5eb6710Skre }
29271224831Sjruoho
293e5eb6710Skre err = munlock(buf, page);
294e5eb6710Skre if (err != 0)
295e5eb6710Skre fprintf(stderr, "mlock_nested: munlock(%p, %ld): %d [%d] %s\n",
296e5eb6710Skre buf, page, err, errno, strerror(errno));
297e5eb6710Skre ATF_REQUIRE(err == 0);
29871224831Sjruoho free(buf);
29971224831Sjruoho }
30071224831Sjruoho
ATF_TP_ADD_TCS(tp)30171224831Sjruoho ATF_TP_ADD_TCS(tp)
30271224831Sjruoho {
30371224831Sjruoho
30471224831Sjruoho page = sysconf(_SC_PAGESIZE);
30571224831Sjruoho ATF_REQUIRE(page >= 0);
30671224831Sjruoho
30771224831Sjruoho ATF_TP_ADD_TC(tp, mlock_clip);
30871224831Sjruoho ATF_TP_ADD_TC(tp, mlock_err);
30971224831Sjruoho ATF_TP_ADD_TC(tp, mlock_limits);
31071224831Sjruoho ATF_TP_ADD_TC(tp, mlock_mmap);
31171224831Sjruoho ATF_TP_ADD_TC(tp, mlock_nested);
31271224831Sjruoho
31371224831Sjruoho return atf_no_error();
31471224831Sjruoho }
315