xref: /llvm-project/libc/test/src/sys/mman/linux/mlock_test.cpp (revision 04aaa35d40d8c5ff030014866691f9a56e59c142)
124923214SSchrodinger ZHU Yifan //===-- Unittests for mlock -----------------------------------------------===//
224923214SSchrodinger ZHU Yifan //
324923214SSchrodinger ZHU Yifan // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
424923214SSchrodinger ZHU Yifan // See https://llvm.org/LICENSE.txt for license information.
524923214SSchrodinger ZHU Yifan // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
624923214SSchrodinger ZHU Yifan //
724923214SSchrodinger ZHU Yifan //===----------------------------------------------------------------------===//
824923214SSchrodinger ZHU Yifan 
924923214SSchrodinger ZHU Yifan #include "src/__support/OSUtil/syscall.h" // For internal syscall function.
1024923214SSchrodinger ZHU Yifan #include "src/errno/libc_errno.h"
1124923214SSchrodinger ZHU Yifan #include "src/sys/mman/madvise.h"
1224923214SSchrodinger ZHU Yifan #include "src/sys/mman/mincore.h"
1324923214SSchrodinger ZHU Yifan #include "src/sys/mman/mlock.h"
1424923214SSchrodinger ZHU Yifan #include "src/sys/mman/mlock2.h"
1524923214SSchrodinger ZHU Yifan #include "src/sys/mman/mlockall.h"
1624923214SSchrodinger ZHU Yifan #include "src/sys/mman/mmap.h"
1724923214SSchrodinger ZHU Yifan #include "src/sys/mman/munlock.h"
1824923214SSchrodinger ZHU Yifan #include "src/sys/mman/munlockall.h"
1924923214SSchrodinger ZHU Yifan #include "src/sys/mman/munmap.h"
2024923214SSchrodinger ZHU Yifan #include "src/sys/resource/getrlimit.h"
2124923214SSchrodinger ZHU Yifan #include "src/unistd/sysconf.h"
2224923214SSchrodinger ZHU Yifan #include "test/UnitTest/ErrnoSetterMatcher.h"
2324923214SSchrodinger ZHU Yifan #include "test/UnitTest/Test.h"
2424923214SSchrodinger ZHU Yifan 
2524923214SSchrodinger ZHU Yifan #include <linux/capability.h>
2624923214SSchrodinger ZHU Yifan #include <sys/mman.h>
2724923214SSchrodinger ZHU Yifan #include <sys/resource.h>
2824923214SSchrodinger ZHU Yifan #include <sys/syscall.h>
2924923214SSchrodinger ZHU Yifan #include <unistd.h>
3024923214SSchrodinger ZHU Yifan 
3124923214SSchrodinger ZHU Yifan using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
3224923214SSchrodinger ZHU Yifan 
3324923214SSchrodinger ZHU Yifan struct PageHolder {
3424923214SSchrodinger ZHU Yifan   size_t size;
3524923214SSchrodinger ZHU Yifan   void *addr;
3624923214SSchrodinger ZHU Yifan 
3724923214SSchrodinger ZHU Yifan   PageHolder()
3824923214SSchrodinger ZHU Yifan       : size(LIBC_NAMESPACE::sysconf(_SC_PAGESIZE)),
3924923214SSchrodinger ZHU Yifan         addr(LIBC_NAMESPACE::mmap(nullptr, size, PROT_READ | PROT_WRITE,
4024923214SSchrodinger ZHU Yifan                                   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)) {}
4124923214SSchrodinger ZHU Yifan   ~PageHolder() {
4224923214SSchrodinger ZHU Yifan     if (addr != MAP_FAILED)
4324923214SSchrodinger ZHU Yifan       LIBC_NAMESPACE::munmap(addr, size);
4424923214SSchrodinger ZHU Yifan   }
4524923214SSchrodinger ZHU Yifan 
4624923214SSchrodinger ZHU Yifan   char &operator[](size_t i) { return reinterpret_cast<char *>(addr)[i]; }
4724923214SSchrodinger ZHU Yifan 
4824923214SSchrodinger ZHU Yifan   bool is_valid() { return addr != MAP_FAILED; }
4924923214SSchrodinger ZHU Yifan };
5024923214SSchrodinger ZHU Yifan 
5124923214SSchrodinger ZHU Yifan static bool get_capacity(unsigned int cap) {
5224923214SSchrodinger ZHU Yifan   __user_cap_header_struct header;
5324923214SSchrodinger ZHU Yifan   header.pid = 0;
5424923214SSchrodinger ZHU Yifan   header.version = _LINUX_CAPABILITY_VERSION_3;
5524923214SSchrodinger ZHU Yifan   __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
5624923214SSchrodinger ZHU Yifan   // TODO: use capget wrapper once implemented.
5724923214SSchrodinger ZHU Yifan   // https://github.com/llvm/llvm-project/issues/80037
5824923214SSchrodinger ZHU Yifan   long res = LIBC_NAMESPACE::syscall_impl(
5924923214SSchrodinger ZHU Yifan       SYS_capget, LIBC_NAMESPACE::cpp::bit_cast<long>(&header),
6024923214SSchrodinger ZHU Yifan       LIBC_NAMESPACE::cpp::bit_cast<long>(&data));
6124923214SSchrodinger ZHU Yifan   if (res < 0)
6224923214SSchrodinger ZHU Yifan     return false;
6324923214SSchrodinger ZHU Yifan   unsigned idx = CAP_TO_INDEX(cap);
6424923214SSchrodinger ZHU Yifan   unsigned shift = CAP_TO_MASK(cap);
6524923214SSchrodinger ZHU Yifan   return (data[idx].effective & shift) != 0;
6624923214SSchrodinger ZHU Yifan }
6724923214SSchrodinger ZHU Yifan 
6824923214SSchrodinger ZHU Yifan static bool is_permitted_size(size_t size) {
6924923214SSchrodinger ZHU Yifan   rlimit rlimits;
7024923214SSchrodinger ZHU Yifan   LIBC_NAMESPACE::getrlimit(RLIMIT_MEMLOCK, &rlimits);
7124923214SSchrodinger ZHU Yifan   return size <= static_cast<size_t>(rlimits.rlim_cur) ||
7224923214SSchrodinger ZHU Yifan          get_capacity(CAP_IPC_LOCK);
7324923214SSchrodinger ZHU Yifan }
7424923214SSchrodinger ZHU Yifan 
7524923214SSchrodinger ZHU Yifan TEST(LlvmLibcMlockTest, UnMappedMemory) {
7624923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mlock(nullptr, 1024), Fails(ENOMEM));
7724923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::munlock(nullptr, 1024), Fails(ENOMEM));
7824923214SSchrodinger ZHU Yifan }
7924923214SSchrodinger ZHU Yifan 
8024923214SSchrodinger ZHU Yifan TEST(LlvmLibcMlockTest, Overflow) {
8124923214SSchrodinger ZHU Yifan   PageHolder holder;
8224923214SSchrodinger ZHU Yifan   EXPECT_TRUE(holder.is_valid());
8324923214SSchrodinger ZHU Yifan   size_t negative_size = -holder.size;
8424923214SSchrodinger ZHU Yifan   int expected_errno = is_permitted_size(negative_size) ? EINVAL : ENOMEM;
8524923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mlock(holder.addr, negative_size),
8624923214SSchrodinger ZHU Yifan               Fails(expected_errno));
8724923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::munlock(holder.addr, negative_size),
8824923214SSchrodinger ZHU Yifan               Fails(EINVAL));
8924923214SSchrodinger ZHU Yifan }
9024923214SSchrodinger ZHU Yifan 
9124923214SSchrodinger ZHU Yifan #ifdef SYS_mlock2
9224923214SSchrodinger ZHU Yifan TEST(LlvmLibcMlockTest, MLock2) {
9324923214SSchrodinger ZHU Yifan   PageHolder holder;
9424923214SSchrodinger ZHU Yifan   EXPECT_TRUE(holder.is_valid());
9524923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::madvise(holder.addr, holder.size, MADV_DONTNEED),
9624923214SSchrodinger ZHU Yifan               Succeeds());
9724923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mlock2(holder.addr, holder.size, 0), Succeeds());
9824923214SSchrodinger ZHU Yifan   unsigned char vec;
9924923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
10024923214SSchrodinger ZHU Yifan               Succeeds());
10124923214SSchrodinger ZHU Yifan   EXPECT_EQ(vec & 1, 1);
10224923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::munlock(holder.addr, holder.size), Succeeds());
10324923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::madvise(holder.addr, holder.size, MADV_DONTNEED),
10424923214SSchrodinger ZHU Yifan               Succeeds());
10524923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mlock2(holder.addr, holder.size, MLOCK_ONFAULT),
10624923214SSchrodinger ZHU Yifan               Succeeds());
10724923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
10824923214SSchrodinger ZHU Yifan               Succeeds());
10924923214SSchrodinger ZHU Yifan   EXPECT_EQ(vec & 1, 0);
11024923214SSchrodinger ZHU Yifan   holder[0] = 1;
11124923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
11224923214SSchrodinger ZHU Yifan               Succeeds());
11324923214SSchrodinger ZHU Yifan   EXPECT_EQ(vec & 1, 1);
11424923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::munlock(holder.addr, holder.size), Succeeds());
11524923214SSchrodinger ZHU Yifan }
11624923214SSchrodinger ZHU Yifan #endif
11724923214SSchrodinger ZHU Yifan 
11824923214SSchrodinger ZHU Yifan TEST(LlvmLibcMlockTest, InvalidFlag) {
11924923214SSchrodinger ZHU Yifan   size_t alloc_size = 128; // page size
120*3eb1e6d8Smichaelrj-google   LIBC_NAMESPACE::libc_errno = 0;
12124923214SSchrodinger ZHU Yifan   void *addr = LIBC_NAMESPACE::mmap(nullptr, alloc_size, PROT_READ,
12224923214SSchrodinger ZHU Yifan                                     MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
12328699e38Slntue   ASSERT_ERRNO_SUCCESS();
12424923214SSchrodinger ZHU Yifan   EXPECT_NE(addr, MAP_FAILED);
12524923214SSchrodinger ZHU Yifan 
12624923214SSchrodinger ZHU Yifan   // Invalid mlock2 flags.
12724923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mlock2(addr, alloc_size, 1234), Fails(EINVAL));
12824923214SSchrodinger ZHU Yifan 
12924923214SSchrodinger ZHU Yifan   // Invalid mlockall flags.
13024923214SSchrodinger ZHU Yifan   EXPECT_THAT(LIBC_NAMESPACE::mlockall(1234), Fails(EINVAL));
13116c15b5fSNick Desaulniers 
13216c15b5fSNick Desaulniers   // man 2 mlockall says EINVAL is a valid return code when MCL_ONFAULT was
13316c15b5fSNick Desaulniers   // specified without MCL_FUTURE or MCL_CURRENT, but this seems to fail on
13416c15b5fSNick Desaulniers   // Linux 4.19.y (EOL).
13516c15b5fSNick Desaulniers   // TODO(ndesaulniers) re-enable after
13616c15b5fSNick Desaulniers   // https://github.com/llvm/llvm-project/issues/80073 is fixed.
13716c15b5fSNick Desaulniers   // EXPECT_THAT(LIBC_NAMESPACE::mlockall(MCL_ONFAULT), Fails(EINVAL));
13824923214SSchrodinger ZHU Yifan 
13924923214SSchrodinger ZHU Yifan   LIBC_NAMESPACE::munmap(addr, alloc_size);
14024923214SSchrodinger ZHU Yifan }
14124923214SSchrodinger ZHU Yifan 
14224923214SSchrodinger ZHU Yifan TEST(LlvmLibcMlockTest, MLockAll) {
14324923214SSchrodinger ZHU Yifan   {
14424923214SSchrodinger ZHU Yifan     PageHolder holder;
14524923214SSchrodinger ZHU Yifan     EXPECT_TRUE(holder.is_valid());
14624923214SSchrodinger ZHU Yifan     EXPECT_THAT(
14724923214SSchrodinger ZHU Yifan         LIBC_NAMESPACE::madvise(holder.addr, holder.size, MADV_DONTNEED),
14824923214SSchrodinger ZHU Yifan         Succeeds());
14924923214SSchrodinger ZHU Yifan     auto retval = LIBC_NAMESPACE::mlockall(MCL_CURRENT);
15024923214SSchrodinger ZHU Yifan     if (retval == -1) {
151*3eb1e6d8Smichaelrj-google       EXPECT_TRUE(LIBC_NAMESPACE::libc_errno == ENOMEM ||
152*3eb1e6d8Smichaelrj-google                   LIBC_NAMESPACE::libc_errno == EPERM);
153*3eb1e6d8Smichaelrj-google       LIBC_NAMESPACE::libc_errno = 0;
15424923214SSchrodinger ZHU Yifan       return;
15524923214SSchrodinger ZHU Yifan     }
15624923214SSchrodinger ZHU Yifan     unsigned char vec;
15724923214SSchrodinger ZHU Yifan     EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
15824923214SSchrodinger ZHU Yifan                 Succeeds());
15924923214SSchrodinger ZHU Yifan     EXPECT_EQ(vec & 1, 1);
16024923214SSchrodinger ZHU Yifan     EXPECT_THAT(LIBC_NAMESPACE::munlockall(), Succeeds());
16124923214SSchrodinger ZHU Yifan   }
16224923214SSchrodinger ZHU Yifan   {
16324923214SSchrodinger ZHU Yifan     auto retval = LIBC_NAMESPACE::mlockall(MCL_FUTURE);
16424923214SSchrodinger ZHU Yifan     if (retval == -1) {
165*3eb1e6d8Smichaelrj-google       EXPECT_TRUE(LIBC_NAMESPACE::libc_errno == ENOMEM ||
166*3eb1e6d8Smichaelrj-google                   LIBC_NAMESPACE::libc_errno == EPERM);
167*3eb1e6d8Smichaelrj-google       LIBC_NAMESPACE::libc_errno = 0;
16824923214SSchrodinger ZHU Yifan       return;
16924923214SSchrodinger ZHU Yifan     }
17024923214SSchrodinger ZHU Yifan     PageHolder holder;
17124923214SSchrodinger ZHU Yifan     EXPECT_TRUE(holder.is_valid());
17224923214SSchrodinger ZHU Yifan     unsigned char vec;
17324923214SSchrodinger ZHU Yifan     EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
17424923214SSchrodinger ZHU Yifan                 Succeeds());
17524923214SSchrodinger ZHU Yifan     EXPECT_EQ(vec & 1, 1);
17624923214SSchrodinger ZHU Yifan     EXPECT_THAT(LIBC_NAMESPACE::munlockall(), Succeeds());
17724923214SSchrodinger ZHU Yifan   }
17824923214SSchrodinger ZHU Yifan #ifdef MCL_ONFAULT
17924923214SSchrodinger ZHU Yifan   {
18024923214SSchrodinger ZHU Yifan     auto retval = LIBC_NAMESPACE::mlockall(MCL_FUTURE | MCL_ONFAULT);
18124923214SSchrodinger ZHU Yifan     if (retval == -1) {
182*3eb1e6d8Smichaelrj-google       EXPECT_TRUE(LIBC_NAMESPACE::libc_errno == ENOMEM ||
183*3eb1e6d8Smichaelrj-google                   LIBC_NAMESPACE::libc_errno == EPERM);
184*3eb1e6d8Smichaelrj-google       LIBC_NAMESPACE::libc_errno = 0;
18524923214SSchrodinger ZHU Yifan       return;
18624923214SSchrodinger ZHU Yifan     }
18724923214SSchrodinger ZHU Yifan     PageHolder holder;
18824923214SSchrodinger ZHU Yifan     EXPECT_TRUE(holder.is_valid());
18924923214SSchrodinger ZHU Yifan     unsigned char vec;
19024923214SSchrodinger ZHU Yifan     EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
19124923214SSchrodinger ZHU Yifan                 Succeeds());
19224923214SSchrodinger ZHU Yifan     EXPECT_EQ(vec & 1, 0);
19324923214SSchrodinger ZHU Yifan     holder[0] = 1;
19424923214SSchrodinger ZHU Yifan     EXPECT_THAT(LIBC_NAMESPACE::mincore(holder.addr, holder.size, &vec),
19524923214SSchrodinger ZHU Yifan                 Succeeds());
19624923214SSchrodinger ZHU Yifan     EXPECT_EQ(vec & 1, 1);
19724923214SSchrodinger ZHU Yifan     EXPECT_THAT(LIBC_NAMESPACE::munlockall(), Succeeds());
19824923214SSchrodinger ZHU Yifan   }
19924923214SSchrodinger ZHU Yifan #endif
20024923214SSchrodinger ZHU Yifan }
201