434 lines · cpp
1//===- llvm/unittest/Support/AllocatorTest.cpp - BumpPtrAllocator tests ---===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Support/Memory.h"10#include "llvm/Support/Process.h"11#include "gtest/gtest.h"12#include <cstdlib>13 14#if defined(__NetBSD__)15// clang-format off16#include <sys/param.h>17#include <sys/types.h>18#include <sys/sysctl.h>19#include <err.h>20#include <unistd.h>21// clang-format on22#endif23 24using namespace llvm;25using namespace sys;26 27namespace {28 29bool IsMPROTECT() {30#if defined(__NetBSD__)31 int mib[3];32 int paxflags;33 size_t len = sizeof(paxflags);34 35 mib[0] = CTL_PROC;36 mib[1] = getpid();37 mib[2] = PROC_PID_PAXFLAGS;38 39 if (sysctl(mib, 3, &paxflags, &len, NULL, 0) != 0)40 err(EXIT_FAILURE, "sysctl");41 42 return !!(paxflags & CTL_PROC_PAXFLAGS_MPROTECT);43#elif (defined(__APPLE__) && defined(__aarch64__)) || defined(__OpenBSD__)44 return true;45#else46 return false;47#endif48}49 50class MappedMemoryTest : public ::testing::TestWithParam<unsigned> {51public:52 MappedMemoryTest() {53 Flags = GetParam();54 PageSize = sys::Process::getPageSizeEstimate();55 }56 57protected:58 // Adds RW flags to permit testing of the resulting memory59 unsigned getTestableEquivalent(unsigned RequestedFlags) {60 switch (RequestedFlags) {61 case Memory::MF_READ:62 case Memory::MF_WRITE:63 case Memory::MF_READ|Memory::MF_WRITE:64 return Memory::MF_READ|Memory::MF_WRITE;65 case Memory::MF_READ|Memory::MF_EXEC:66 case Memory::MF_READ|Memory::MF_WRITE|Memory::MF_EXEC:67 case Memory::MF_EXEC:68 return Memory::MF_READ|Memory::MF_WRITE|Memory::MF_EXEC;69 }70 // Default in case values are added to the enum, as required by some compilers71 return Memory::MF_READ|Memory::MF_WRITE;72 }73 74 // Returns true if the memory blocks overlap75 bool doesOverlap(MemoryBlock M1, MemoryBlock M2) {76 if (M1.base() == M2.base())77 return true;78 79 if (M1.base() > M2.base())80 return (unsigned char *)M2.base() + M2.allocatedSize() > M1.base();81 82 return (unsigned char *)M1.base() + M1.allocatedSize() > M2.base();83 }84 85 unsigned Flags;86 size_t PageSize;87};88 89// MPROTECT prevents W+X mmaps90#define CHECK_UNSUPPORTED() \91 do { \92 if ((Flags & Memory::MF_WRITE) && (Flags & Memory::MF_EXEC) && \93 IsMPROTECT()) \94 GTEST_SKIP(); \95 } while (0)96 97TEST_P(MappedMemoryTest, AllocAndRelease) {98 CHECK_UNSUPPORTED();99 std::error_code EC;100 MemoryBlock M1 = Memory::allocateMappedMemory(sizeof(int), nullptr, Flags,EC);101 EXPECT_EQ(std::error_code(), EC);102 103 EXPECT_NE((void*)nullptr, M1.base());104 EXPECT_LE(sizeof(int), M1.allocatedSize());105 106 EXPECT_FALSE(Memory::releaseMappedMemory(M1));107}108 109TEST_P(MappedMemoryTest, AllocAndReleaseHuge) {110 CHECK_UNSUPPORTED();111 std::error_code EC;112 MemoryBlock M1 = Memory::allocateMappedMemory(113 sizeof(int), nullptr, Flags | Memory::MF_HUGE_HINT, EC);114 EXPECT_EQ(std::error_code(), EC);115 116 // Test large/huge memory pages. In the worst case, 4kb pages should be117 // returned, if large pages aren't available.118 119 EXPECT_NE((void *)nullptr, M1.base());120 EXPECT_LE(sizeof(int), M1.allocatedSize());121 122 EXPECT_FALSE(Memory::releaseMappedMemory(M1));123}124 125TEST_P(MappedMemoryTest, MultipleAllocAndRelease) {126 CHECK_UNSUPPORTED();127 std::error_code EC;128 MemoryBlock M1 = Memory::allocateMappedMemory(16, nullptr, Flags, EC);129 EXPECT_EQ(std::error_code(), EC);130 MemoryBlock M2 = Memory::allocateMappedMemory(64, nullptr, Flags, EC);131 EXPECT_EQ(std::error_code(), EC);132 MemoryBlock M3 = Memory::allocateMappedMemory(32, nullptr, Flags, EC);133 EXPECT_EQ(std::error_code(), EC);134 135 EXPECT_NE((void*)nullptr, M1.base());136 EXPECT_LE(16U, M1.allocatedSize());137 EXPECT_NE((void*)nullptr, M2.base());138 EXPECT_LE(64U, M2.allocatedSize());139 EXPECT_NE((void*)nullptr, M3.base());140 EXPECT_LE(32U, M3.allocatedSize());141 142 EXPECT_FALSE(doesOverlap(M1, M2));143 EXPECT_FALSE(doesOverlap(M2, M3));144 EXPECT_FALSE(doesOverlap(M1, M3));145 146 EXPECT_FALSE(Memory::releaseMappedMemory(M1));147 EXPECT_FALSE(Memory::releaseMappedMemory(M3));148 MemoryBlock M4 = Memory::allocateMappedMemory(16, nullptr, Flags, EC);149 EXPECT_EQ(std::error_code(), EC);150 EXPECT_NE((void*)nullptr, M4.base());151 EXPECT_LE(16U, M4.allocatedSize());152 EXPECT_FALSE(Memory::releaseMappedMemory(M4));153 EXPECT_FALSE(Memory::releaseMappedMemory(M2));154}155 156TEST_P(MappedMemoryTest, BasicWrite) {157 // This test applies only to readable and writeable combinations158 if (Flags &&159 !((Flags & Memory::MF_READ) && (Flags & Memory::MF_WRITE)))160 GTEST_SKIP();161 CHECK_UNSUPPORTED();162 163 std::error_code EC;164 MemoryBlock M1 = Memory::allocateMappedMemory(sizeof(int), nullptr, Flags,EC);165 EXPECT_EQ(std::error_code(), EC);166 167 EXPECT_NE((void*)nullptr, M1.base());168 EXPECT_LE(sizeof(int), M1.allocatedSize());169 170 int *a = (int*)M1.base();171 *a = 1;172 EXPECT_EQ(1, *a);173 174 EXPECT_FALSE(Memory::releaseMappedMemory(M1));175}176 177TEST_P(MappedMemoryTest, MultipleWrite) {178 // This test applies only to readable and writeable combinations179 if (Flags &&180 !((Flags & Memory::MF_READ) && (Flags & Memory::MF_WRITE)))181 GTEST_SKIP();182 CHECK_UNSUPPORTED();183 184 std::error_code EC;185 MemoryBlock M1 = Memory::allocateMappedMemory(sizeof(int), nullptr, Flags,186 EC);187 EXPECT_EQ(std::error_code(), EC);188 MemoryBlock M2 = Memory::allocateMappedMemory(8 * sizeof(int), nullptr, Flags,189 EC);190 EXPECT_EQ(std::error_code(), EC);191 MemoryBlock M3 = Memory::allocateMappedMemory(4 * sizeof(int), nullptr, Flags,192 EC);193 EXPECT_EQ(std::error_code(), EC);194 195 EXPECT_FALSE(doesOverlap(M1, M2));196 EXPECT_FALSE(doesOverlap(M2, M3));197 EXPECT_FALSE(doesOverlap(M1, M3));198 199 EXPECT_NE((void*)nullptr, M1.base());200 EXPECT_LE(1U * sizeof(int), M1.allocatedSize());201 EXPECT_NE((void*)nullptr, M2.base());202 EXPECT_LE(8U * sizeof(int), M2.allocatedSize());203 EXPECT_NE((void*)nullptr, M3.base());204 EXPECT_LE(4U * sizeof(int), M3.allocatedSize());205 206 int *x = (int*)M1.base();207 *x = 1;208 209 int *y = (int*)M2.base();210 for (int i = 0; i < 8; i++) {211 y[i] = i;212 }213 214 int *z = (int*)M3.base();215 *z = 42;216 217 EXPECT_EQ(1, *x);218 EXPECT_EQ(7, y[7]);219 EXPECT_EQ(42, *z);220 221 EXPECT_FALSE(Memory::releaseMappedMemory(M1));222 EXPECT_FALSE(Memory::releaseMappedMemory(M3));223 224 MemoryBlock M4 = Memory::allocateMappedMemory(64 * sizeof(int), nullptr,225 Flags, EC);226 EXPECT_EQ(std::error_code(), EC);227 EXPECT_NE((void*)nullptr, M4.base());228 EXPECT_LE(64U * sizeof(int), M4.allocatedSize());229 x = (int*)M4.base();230 *x = 4;231 EXPECT_EQ(4, *x);232 EXPECT_FALSE(Memory::releaseMappedMemory(M4));233 234 // Verify that M2 remains unaffected by other activity235 for (int i = 0; i < 8; i++) {236 EXPECT_EQ(i, y[i]);237 }238 EXPECT_FALSE(Memory::releaseMappedMemory(M2));239}240 241TEST_P(MappedMemoryTest, EnabledWrite) {242 // MPROTECT prevents W+X, and since this test always adds W we need243 // to block any variant with X.244 if ((Flags & Memory::MF_EXEC) && IsMPROTECT())245 GTEST_SKIP();246 247 std::error_code EC;248 MemoryBlock M1 = Memory::allocateMappedMemory(2 * sizeof(int), nullptr, Flags,249 EC);250 EXPECT_EQ(std::error_code(), EC);251 MemoryBlock M2 = Memory::allocateMappedMemory(8 * sizeof(int), nullptr, Flags,252 EC);253 EXPECT_EQ(std::error_code(), EC);254 MemoryBlock M3 = Memory::allocateMappedMemory(4 * sizeof(int), nullptr, Flags,255 EC);256 EXPECT_EQ(std::error_code(), EC);257 258 EXPECT_NE((void*)nullptr, M1.base());259 EXPECT_LE(2U * sizeof(int), M1.allocatedSize());260 EXPECT_NE((void*)nullptr, M2.base());261 EXPECT_LE(8U * sizeof(int), M2.allocatedSize());262 EXPECT_NE((void*)nullptr, M3.base());263 EXPECT_LE(4U * sizeof(int), M3.allocatedSize());264 265 EXPECT_FALSE(Memory::protectMappedMemory(M1, getTestableEquivalent(Flags)));266 EXPECT_FALSE(Memory::protectMappedMemory(M2, getTestableEquivalent(Flags)));267 EXPECT_FALSE(Memory::protectMappedMemory(M3, getTestableEquivalent(Flags)));268 269 EXPECT_FALSE(doesOverlap(M1, M2));270 EXPECT_FALSE(doesOverlap(M2, M3));271 EXPECT_FALSE(doesOverlap(M1, M3));272 273 int *x = (int*)M1.base();274 *x = 1;275 int *y = (int*)M2.base();276 for (unsigned int i = 0; i < 8; i++) {277 y[i] = i;278 }279 int *z = (int*)M3.base();280 *z = 42;281 282 EXPECT_EQ(1, *x);283 EXPECT_EQ(7, y[7]);284 EXPECT_EQ(42, *z);285 286 EXPECT_FALSE(Memory::releaseMappedMemory(M1));287 EXPECT_FALSE(Memory::releaseMappedMemory(M3));288 EXPECT_EQ(6, y[6]);289 290 MemoryBlock M4 = Memory::allocateMappedMemory(16, nullptr, Flags, EC);291 EXPECT_EQ(std::error_code(), EC);292 EXPECT_NE((void*)nullptr, M4.base());293 EXPECT_LE(16U, M4.allocatedSize());294 EXPECT_EQ(std::error_code(),295 Memory::protectMappedMemory(M4, getTestableEquivalent(Flags)));296 x = (int*)M4.base();297 *x = 4;298 EXPECT_EQ(4, *x);299 EXPECT_FALSE(Memory::releaseMappedMemory(M4));300 EXPECT_FALSE(Memory::releaseMappedMemory(M2));301}302 303TEST_P(MappedMemoryTest, SuccessiveNear) {304 CHECK_UNSUPPORTED();305 std::error_code EC;306 MemoryBlock M1 = Memory::allocateMappedMemory(16, nullptr, Flags, EC);307 EXPECT_EQ(std::error_code(), EC);308 MemoryBlock M2 = Memory::allocateMappedMemory(64, &M1, Flags, EC);309 EXPECT_EQ(std::error_code(), EC);310 MemoryBlock M3 = Memory::allocateMappedMemory(32, &M2, Flags, EC);311 EXPECT_EQ(std::error_code(), EC);312 313 EXPECT_NE((void*)nullptr, M1.base());314 EXPECT_LE(16U, M1.allocatedSize());315 EXPECT_NE((void*)nullptr, M2.base());316 EXPECT_LE(64U, M2.allocatedSize());317 EXPECT_NE((void*)nullptr, M3.base());318 EXPECT_LE(32U, M3.allocatedSize());319 320 EXPECT_FALSE(doesOverlap(M1, M2));321 EXPECT_FALSE(doesOverlap(M2, M3));322 EXPECT_FALSE(doesOverlap(M1, M3));323 324 EXPECT_FALSE(Memory::releaseMappedMemory(M1));325 EXPECT_FALSE(Memory::releaseMappedMemory(M3));326 EXPECT_FALSE(Memory::releaseMappedMemory(M2));327}328 329TEST_P(MappedMemoryTest, DuplicateNear) {330 CHECK_UNSUPPORTED();331 std::error_code EC;332 MemoryBlock Near((void*)(3*PageSize), 16);333 MemoryBlock M1 = Memory::allocateMappedMemory(16, &Near, Flags, EC);334 EXPECT_EQ(std::error_code(), EC);335 MemoryBlock M2 = Memory::allocateMappedMemory(64, &Near, Flags, EC);336 EXPECT_EQ(std::error_code(), EC);337 MemoryBlock M3 = Memory::allocateMappedMemory(32, &Near, Flags, EC);338 EXPECT_EQ(std::error_code(), EC);339 340 EXPECT_NE((void*)nullptr, M1.base());341 EXPECT_LE(16U, M1.allocatedSize());342 EXPECT_NE((void*)nullptr, M2.base());343 EXPECT_LE(64U, M2.allocatedSize());344 EXPECT_NE((void*)nullptr, M3.base());345 EXPECT_LE(32U, M3.allocatedSize());346 347 EXPECT_FALSE(Memory::releaseMappedMemory(M1));348 EXPECT_FALSE(Memory::releaseMappedMemory(M3));349 EXPECT_FALSE(Memory::releaseMappedMemory(M2));350}351 352TEST_P(MappedMemoryTest, ZeroNear) {353 CHECK_UNSUPPORTED();354 std::error_code EC;355 MemoryBlock Near(nullptr, 0);356 MemoryBlock M1 = Memory::allocateMappedMemory(16, &Near, Flags, EC);357 EXPECT_EQ(std::error_code(), EC);358 MemoryBlock M2 = Memory::allocateMappedMemory(64, &Near, Flags, EC);359 EXPECT_EQ(std::error_code(), EC);360 MemoryBlock M3 = Memory::allocateMappedMemory(32, &Near, Flags, EC);361 EXPECT_EQ(std::error_code(), EC);362 363 EXPECT_NE((void*)nullptr, M1.base());364 EXPECT_LE(16U, M1.allocatedSize());365 EXPECT_NE((void*)nullptr, M2.base());366 EXPECT_LE(64U, M2.allocatedSize());367 EXPECT_NE((void*)nullptr, M3.base());368 EXPECT_LE(32U, M3.allocatedSize());369 370 EXPECT_FALSE(doesOverlap(M1, M2));371 EXPECT_FALSE(doesOverlap(M2, M3));372 EXPECT_FALSE(doesOverlap(M1, M3));373 374 EXPECT_FALSE(Memory::releaseMappedMemory(M1));375 EXPECT_FALSE(Memory::releaseMappedMemory(M3));376 EXPECT_FALSE(Memory::releaseMappedMemory(M2));377}378 379TEST_P(MappedMemoryTest, ZeroSizeNear) {380 CHECK_UNSUPPORTED();381 std::error_code EC;382 MemoryBlock Near((void*)(4*PageSize), 0);383 MemoryBlock M1 = Memory::allocateMappedMemory(16, &Near, Flags, EC);384 EXPECT_EQ(std::error_code(), EC);385 MemoryBlock M2 = Memory::allocateMappedMemory(64, &Near, Flags, EC);386 EXPECT_EQ(std::error_code(), EC);387 MemoryBlock M3 = Memory::allocateMappedMemory(32, &Near, Flags, EC);388 EXPECT_EQ(std::error_code(), EC);389 390 EXPECT_NE((void*)nullptr, M1.base());391 EXPECT_LE(16U, M1.allocatedSize());392 EXPECT_NE((void*)nullptr, M2.base());393 EXPECT_LE(64U, M2.allocatedSize());394 EXPECT_NE((void*)nullptr, M3.base());395 EXPECT_LE(32U, M3.allocatedSize());396 397 EXPECT_FALSE(doesOverlap(M1, M2));398 EXPECT_FALSE(doesOverlap(M2, M3));399 EXPECT_FALSE(doesOverlap(M1, M3));400 401 EXPECT_FALSE(Memory::releaseMappedMemory(M1));402 EXPECT_FALSE(Memory::releaseMappedMemory(M3));403 EXPECT_FALSE(Memory::releaseMappedMemory(M2));404}405 406TEST_P(MappedMemoryTest, UnalignedNear) {407 CHECK_UNSUPPORTED();408 std::error_code EC;409 MemoryBlock Near((void*)(2*PageSize+5), 0);410 MemoryBlock M1 = Memory::allocateMappedMemory(15, &Near, Flags, EC);411 EXPECT_EQ(std::error_code(), EC);412 413 EXPECT_NE((void*)nullptr, M1.base());414 EXPECT_LE(sizeof(int), M1.allocatedSize());415 416 EXPECT_FALSE(Memory::releaseMappedMemory(M1));417}418 419// Note that Memory::MF_WRITE is not supported exclusively across420// operating systems and architectures and can imply MF_READ|MF_WRITE421unsigned MemoryFlags[] = {422 Memory::MF_READ,423 Memory::MF_WRITE,424 Memory::MF_READ|Memory::MF_WRITE,425 Memory::MF_EXEC,426 Memory::MF_READ|Memory::MF_EXEC,427 Memory::MF_READ|Memory::MF_WRITE|Memory::MF_EXEC428 };429 430INSTANTIATE_TEST_SUITE_P(AllocationTests, MappedMemoryTest,431 ::testing::ValuesIn(MemoryFlags));432 433} // anonymous namespace434