• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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