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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of libc/test/src/sys/mman/linux/mlock_test.cpp