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