//===-- sanitizer_posix_test.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Tests for POSIX-specific code. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_POSIX # include # include # include # include # include "gtest/gtest.h" # include "sanitizer_common/sanitizer_common.h" namespace __sanitizer { static pthread_key_t key; static bool destructor_executed; extern "C" void destructor(void *arg) { uptr iter = reinterpret_cast(arg); if (iter > 1) { ASSERT_EQ(0, pthread_setspecific(key, reinterpret_cast(iter - 1))); return; } destructor_executed = true; } extern "C" void *thread_func(void *arg) { return reinterpret_cast(pthread_setspecific(key, arg)); } static void SpawnThread(uptr iteration) { destructor_executed = false; pthread_t tid; ASSERT_EQ(0, pthread_create(&tid, 0, &thread_func, reinterpret_cast(iteration))); void *retval; ASSERT_EQ(0, pthread_join(tid, &retval)); ASSERT_EQ(0, retval); } TEST(SanitizerCommon, PthreadDestructorIterations) { ASSERT_EQ(0, pthread_key_create(&key, &destructor)); SpawnThread(GetPthreadDestructorIterations()); EXPECT_TRUE(destructor_executed); SpawnThread(GetPthreadDestructorIterations() + 1); #if SANITIZER_SOLARIS // Solaris continues calling destructors beyond PTHREAD_DESTRUCTOR_ITERATIONS. EXPECT_TRUE(destructor_executed); #else EXPECT_FALSE(destructor_executed); #endif ASSERT_EQ(0, pthread_key_delete(key)); } TEST(SanitizerCommon, IsAccessibleMemoryRange) { const int page_size = GetPageSize(); InternalMmapVector buffer(3 * page_size); uptr mem = reinterpret_cast(buffer.data()); // Protect the middle page. mprotect((void *)(mem + page_size), page_size, PROT_NONE); EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size - 1)); EXPECT_TRUE(IsAccessibleMemoryRange(mem, page_size)); EXPECT_FALSE(IsAccessibleMemoryRange(mem, page_size + 1)); EXPECT_TRUE(IsAccessibleMemoryRange(mem + page_size - 1, 1)); EXPECT_FALSE(IsAccessibleMemoryRange(mem + page_size - 1, 2)); EXPECT_FALSE(IsAccessibleMemoryRange(mem + 2 * page_size - 1, 1)); EXPECT_TRUE(IsAccessibleMemoryRange(mem + 2 * page_size, page_size)); EXPECT_FALSE(IsAccessibleMemoryRange(mem, 3 * page_size)); EXPECT_FALSE(IsAccessibleMemoryRange(0x0, 2)); } TEST(SanitizerCommon, IsAccessibleMemoryRangeLarge) { InternalMmapVector buffer(10000 * GetPageSize()); EXPECT_TRUE(IsAccessibleMemoryRange(reinterpret_cast(buffer.data()), buffer.size())); } TEST(SanitizerCommon, TryMemCpy) { std::vector src(10000000); std::iota(src.begin(), src.end(), 123); std::vector dst; // Don't use ::testing::ElementsAreArray or similar, as the huge output on an // error is not helpful. dst.assign(1, 0); EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size())); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin())); dst.assign(100, 0); EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size())); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin())); dst.assign(534, 0); EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size())); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin())); dst.assign(GetPageSize(), 0); EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size())); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin())); dst.assign(src.size(), 0); EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size())); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin())); dst.assign(src.size() - 1, 0); EXPECT_TRUE(TryMemCpy(dst.data(), src.data(), dst.size())); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), src.begin())); } TEST(SanitizerCommon, TryMemCpyNull) { std::vector dst(100); EXPECT_FALSE(TryMemCpy(dst.data(), nullptr, dst.size())); } TEST(SanitizerCommon, MemCpyAccessible) { const int page_num = 1000; const int page_size = GetPageSize(); InternalMmapVector src(page_num * page_size); std::iota(src.begin(), src.end(), 123); std::vector dst; std::vector exp = {src.begin(), src.end()}; // Protect some pages. for (int i = 7; i < page_num; i *= 2) { mprotect(src.data() + i * page_size, page_size, PROT_NONE); std::fill(exp.data() + i * page_size, exp.data() + (i + 1) * page_size, 0); } dst.assign(src.size(), 0); EXPECT_FALSE(TryMemCpy(dst.data(), src.data(), dst.size())); // Full page aligned range with mprotect pages. dst.assign(src.size(), 0); MemCpyAccessible(dst.data(), src.data(), dst.size()); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), exp.begin())); // Misaligned range with mprotect pages. size_t offb = 3; size_t offe = 7; dst.assign(src.size() - offb - offe, 0); MemCpyAccessible(dst.data(), src.data() + offb, dst.size()); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), exp.begin() + offb)); // Misaligned range with ends in mprotect pages. offb = 3 + 7 * page_size; offe = 7 + 14 * page_size; dst.assign(src.size() - offb - offe, 0); MemCpyAccessible(dst.data(), src.data() + offb, dst.size()); EXPECT_TRUE(std::equal(dst.begin(), dst.end(), exp.begin() + offb)); } } // namespace __sanitizer #endif // SANITIZER_POSIX