1 //===-- Unittests for mlock -----------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "src/__support/OSUtil/syscall.h" // For internal syscall function.
10 #include "src/errno/libc_errno.h"
11 #include "src/sys/mman/madvise.h"
12 #include "src/sys/mman/mincore.h"
13 #include "src/sys/mman/mlock.h"
14 #include "src/sys/mman/mlock2.h"
15 #include "src/sys/mman/mlockall.h"
16 #include "src/sys/mman/mmap.h"
17 #include "src/sys/mman/munlock.h"
18 #include "src/sys/mman/munlockall.h"
19 #include "src/sys/mman/munmap.h"
20 #include "src/sys/resource/getrlimit.h"
21 #include "src/unistd/sysconf.h"
22 #include "test/UnitTest/ErrnoSetterMatcher.h"
23 #include "test/UnitTest/LibcTest.h"
24 #include "test/UnitTest/Test.h"
25
26 #include <asm-generic/errno-base.h>
27 #include <asm-generic/mman.h>
28 #include <linux/capability.h>
29 #include <sys/mman.h>
30 #include <sys/resource.h>
31 #include <sys/syscall.h>
32 #include <unistd.h>
33
34 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
35
36 struct PageHolder {
37 size_t size;
38 void *addr;
39
PageHolderPageHolder40 PageHolder()
41 : size(LIBC_NAMESPACE::sysconf(_SC_PAGESIZE)),
42 addr(LIBC_NAMESPACE::mmap(nullptr, size, PROT_READ | PROT_WRITE,
43 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) {}
~PageHolderPageHolder44 ~PageHolder() {
45 if (addr != MAP_FAILED)
46 LIBC_NAMESPACE::munmap(addr, size);
47 }
48
operator []PageHolder49 char &operator[](size_t i) { return reinterpret_cast<char *>(addr)[i]; }
50
is_validPageHolder51 bool is_valid() { return addr != MAP_FAILED; }
52 };
53
get_capacity(unsigned int cap)54 static bool get_capacity(unsigned int cap) {
55 __user_cap_header_struct header;
56 header.pid = 0;
57 header.version = _LINUX_CAPABILITY_VERSION_3;
58 __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
59 // TODO: use capget wrapper once implemented.
60 // https://github.com/llvm/llvm-project/issues/80037
61 long res = LIBC_NAMESPACE::syscall_impl(
62 SYS_capget, LIBC_NAMESPACE::cpp::bit_cast<long>(&header),
63 LIBC_NAMESPACE::cpp::bit_cast<long>(&data));
64 if (res < 0)
65 return false;
66 unsigned idx = CAP_TO_INDEX(cap);
67 unsigned shift = CAP_TO_MASK(cap);
68 return (data[idx].effective & shift) != 0;
69 }
70
is_permitted_size(size_t size)71 static bool is_permitted_size(size_t size) {
72 rlimit rlimits;
73 LIBC_NAMESPACE::getrlimit(RLIMIT_MEMLOCK, &rlimits);
74 return size <= static_cast<size_t>(rlimits.rlim_cur) ||
75 get_capacity(CAP_IPC_LOCK);
76 }
77
TEST(LlvmLibcMlockTest,UnMappedMemory)78 TEST(LlvmLibcMlockTest, UnMappedMemory) {
79 EXPECT_THAT(LIBC_NAMESPACE::mlock(nullptr, 1024), Fails(ENOMEM));
80 EXPECT_THAT(LIBC_NAMESPACE::munlock(nullptr, 1024), Fails(ENOMEM));
81 }
82
TEST(LlvmLibcMlockTest,Overflow)83 TEST(LlvmLibcMlockTest, Overflow) {
84 PageHolder holder;
85 EXPECT_TRUE(holder.is_valid());
86 size_t negative_size = -holder.size;
87 int expected_errno = is_permitted_size(negative_size) ? EINVAL : ENOMEM;
88 EXPECT_THAT(LIBC_NAMESPACE::mlock(holder.addr, negative_size),
89 Fails(expected_errno));
90 EXPECT_THAT(LIBC_NAMESPACE::munlock(holder.addr, negative_size),
91 Fails(EINVAL));
92 }
93
94 #ifdef SYS_mlock2
TEST(LlvmLibcMlockTest,MLock2)95 TEST(LlvmLibcMlockTest, MLock2) {
96 PageHolder holder;
97 EXPECT_TRUE(holder.is_valid());
98 EXPECT_THAT(LIBC_NAMESPACE::madvise(holder.addr, holder.size, MADV_DONTNEED),
99 Succeeds());
100 EXPECT_THAT(LIBC_NAMESPACE::mlock2(holder.addr, holder.size, 0), Succeeds());
101 unsigned char vec;
102 EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
103 Succeeds());
104 EXPECT_EQ(vec & 1, 1);
105 EXPECT_THAT(LIBC_NAMESPACE::munlock(holder.addr, holder.size), Succeeds());
106 EXPECT_THAT(LIBC_NAMESPACE::madvise(holder.addr, holder.size, MADV_DONTNEED),
107 Succeeds());
108 EXPECT_THAT(LIBC_NAMESPACE::mlock2(holder.addr, holder.size, MLOCK_ONFAULT),
109 Succeeds());
110 EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
111 Succeeds());
112 EXPECT_EQ(vec & 1, 0);
113 holder[0] = 1;
114 EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
115 Succeeds());
116 EXPECT_EQ(vec & 1, 1);
117 EXPECT_THAT(LIBC_NAMESPACE::munlock(holder.addr, holder.size), Succeeds());
118 }
119 #endif
120
TEST(LlvmLibcMlockTest,InvalidFlag)121 TEST(LlvmLibcMlockTest, InvalidFlag) {
122 size_t alloc_size = 128; // page size
123 LIBC_NAMESPACE::libc_errno = 0;
124 void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ,
125 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
126 ASSERT_ERRNO_SUCCESS();
127 EXPECT_NE(addr, MAP_FAILED);
128
129 // Invalid mlock2 flags.
130 EXPECT_THAT(LIBC_NAMESPACE::mlock2(addr, alloc_size, 1234), Fails(EINVAL));
131
132 // Invalid mlockall flags.
133 EXPECT_THAT(LIBC_NAMESPACE::mlockall(1234), Fails(EINVAL));
134
135 // man 2 mlockall says EINVAL is a valid return code when MCL_ONFAULT was
136 // specified without MCL_FUTURE or MCL_CURRENT, but this seems to fail on
137 // Linux 4.19.y (EOL).
138 // TODO(ndesaulniers) re-enable after
139 // https://github.com/llvm/llvm-project/issues/80073 is fixed.
140 // EXPECT_THAT(LIBC_NAMESPACE::mlockall(MCL_ONFAULT), Fails(EINVAL));
141
142 LIBC_NAMESPACE::munmap(addr, alloc_size);
143 }
144
TEST(LlvmLibcMlockTest,MLockAll)145 TEST(LlvmLibcMlockTest, MLockAll) {
146 {
147 PageHolder holder;
148 EXPECT_TRUE(holder.is_valid());
149 EXPECT_THAT(
150 LIBC_NAMESPACE::madvise(holder.addr, holder.size, MADV_DONTNEED),
151 Succeeds());
152 auto retval = LIBC_NAMESPACE::mlockall(MCL_CURRENT);
153 if (retval == -1) {
154 EXPECT_TRUE(LIBC_NAMESPACE::libc_errno == ENOMEM ||
155 LIBC_NAMESPACE::libc_errno == EPERM);
156 LIBC_NAMESPACE::libc_errno = 0;
157 return;
158 }
159 unsigned char vec;
160 EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
161 Succeeds());
162 EXPECT_EQ(vec & 1, 1);
163 EXPECT_THAT(LIBC_NAMESPACE::munlockall(), Succeeds());
164 }
165 {
166 auto retval = LIBC_NAMESPACE::mlockall(MCL_FUTURE);
167 if (retval == -1) {
168 EXPECT_TRUE(LIBC_NAMESPACE::libc_errno == ENOMEM ||
169 LIBC_NAMESPACE::libc_errno == EPERM);
170 LIBC_NAMESPACE::libc_errno = 0;
171 return;
172 }
173 PageHolder holder;
174 EXPECT_TRUE(holder.is_valid());
175 unsigned char vec;
176 EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
177 Succeeds());
178 EXPECT_EQ(vec & 1, 1);
179 EXPECT_THAT(LIBC_NAMESPACE::munlockall(), Succeeds());
180 }
181 #ifdef MCL_ONFAULT
182 {
183 auto retval = LIBC_NAMESPACE::mlockall(MCL_FUTURE | MCL_ONFAULT);
184 if (retval == -1) {
185 EXPECT_TRUE(LIBC_NAMESPACE::libc_errno == ENOMEM ||
186 LIBC_NAMESPACE::libc_errno == EPERM);
187 LIBC_NAMESPACE::libc_errno = 0;
188 return;
189 }
190 PageHolder holder;
191 EXPECT_TRUE(holder.is_valid());
192 unsigned char vec;
193 EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
194 Succeeds());
195 EXPECT_EQ(vec & 1, 0);
196 holder[0] = 1;
197 EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
198 Succeeds());
199 EXPECT_EQ(vec & 1, 1);
200 EXPECT_THAT(LIBC_NAMESPACE::munlockall(), Succeeds());
201 }
202 #endif
203 }
204