xref: /aosp_15_r20/external/llvm-libc/test/src/sys/mman/linux/mlock_test.cpp (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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