CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
244 lines
7.9 KiB
C++
244 lines
7.9 KiB
C++
//===-- map_test.cpp --------------------------------------------*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "tests/scudo_unit_test.h"
|
|
|
|
#include "common.h"
|
|
#include "mem_map.h"
|
|
|
|
#include <algorithm>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#if SCUDO_LINUX
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
static const char *MappingName = "scudo:test";
|
|
|
|
TEST(ScudoMapTest, PageSize) {
|
|
EXPECT_EQ(scudo::getPageSizeCached(),
|
|
static_cast<scudo::uptr>(sysconf(_SC_PAGESIZE)));
|
|
}
|
|
|
|
TEST(ScudoMapTest, VerifyGetResidentPages) {
|
|
if (!SCUDO_LINUX)
|
|
TEST_SKIP("Only valid on linux systems.");
|
|
|
|
constexpr scudo::uptr NumPages = 512;
|
|
const scudo::uptr SizeBytes = NumPages * scudo::getPageSizeCached();
|
|
|
|
scudo::MemMapT MemMap;
|
|
ASSERT_TRUE(MemMap.map(/*Addr=*/0U, SizeBytes, "ResidentMemorySize"));
|
|
ASSERT_NE(MemMap.getBase(), 0U);
|
|
|
|
// Only android seems to properly detect when single pages are touched.
|
|
#if SCUDO_ANDROID
|
|
// Verify nothing should be mapped in right after the map is created.
|
|
EXPECT_EQ(0U, MemMap.getResidentPages(MemMap.getBase(), SizeBytes));
|
|
|
|
// Touch a page.
|
|
scudo::u8 *Data = reinterpret_cast<scudo::u8 *>(MemMap.getBase());
|
|
Data[0] = 1;
|
|
EXPECT_EQ(1U, MemMap.getResidentPages(MemMap.getBase(), SizeBytes));
|
|
|
|
// Touch a non-consective page.
|
|
Data[scudo::getPageSizeCached() * 2] = 1;
|
|
EXPECT_EQ(2U, MemMap.getResidentPages(MemMap.getBase(), SizeBytes));
|
|
|
|
// Touch a page far enough that the function has to make multiple calls
|
|
// to mincore.
|
|
Data[scudo::getPageSizeCached() * 300] = 1;
|
|
EXPECT_EQ(3U, MemMap.getResidentPages(MemMap.getBase(), SizeBytes));
|
|
|
|
// Touch another page in the same range to make sure the second
|
|
// read is working.
|
|
Data[scudo::getPageSizeCached() * 400] = 1;
|
|
EXPECT_EQ(4U, MemMap.getResidentPages(MemMap.getBase(), SizeBytes));
|
|
#endif
|
|
|
|
// Now write the whole thing.
|
|
memset(reinterpret_cast<void *>(MemMap.getBase()), 1, SizeBytes);
|
|
scudo::s64 ResidentPages =
|
|
MemMap.getResidentPages(MemMap.getBase(), SizeBytes);
|
|
EXPECT_EQ(NumPages, static_cast<uintptr_t>(ResidentPages));
|
|
|
|
MemMap.unmap();
|
|
}
|
|
|
|
TEST(ScudoMapTest, VerifyReleasePagesToOS) {
|
|
if (!SCUDO_LINUX)
|
|
TEST_SKIP("Only valid on linux systems.");
|
|
|
|
constexpr scudo::uptr NumPages = 1000;
|
|
const scudo::uptr SizeBytes = NumPages * scudo::getPageSizeCached();
|
|
|
|
scudo::MemMapT MemMap;
|
|
ASSERT_TRUE(MemMap.map(/*Addr=*/0U, SizeBytes, "ResidentMemorySize"));
|
|
ASSERT_NE(MemMap.getBase(), 0U);
|
|
|
|
void *P = reinterpret_cast<void *>(MemMap.getBase());
|
|
EXPECT_EQ(0U, MemMap.getResidentPages(MemMap.getBase(), SizeBytes));
|
|
|
|
// Make the entire map resident.
|
|
memset(P, 1, SizeBytes);
|
|
scudo::s64 ResidentPages =
|
|
MemMap.getResidentPages(MemMap.getBase(), SizeBytes);
|
|
if (ResidentPages >= 0)
|
|
EXPECT_EQ(NumPages, static_cast<uintptr_t>(ResidentPages));
|
|
|
|
// Should release the memory to the kernel immediately.
|
|
MemMap.releasePagesToOS(MemMap.getBase(), SizeBytes);
|
|
EXPECT_EQ(0U, MemMap.getResidentPages(MemMap.getBase(), SizeBytes));
|
|
|
|
// Make the entire map resident again.
|
|
memset(P, 1, SizeBytes);
|
|
ResidentPages = MemMap.getResidentPages(MemMap.getBase(), SizeBytes);
|
|
EXPECT_EQ(NumPages, static_cast<uintptr_t>(ResidentPages));
|
|
|
|
MemMap.unmap();
|
|
}
|
|
|
|
TEST(ScudoMapTest, Zeros) {
|
|
const scudo::uptr Size = 1ull << 20;
|
|
|
|
scudo::MemMapT MemMap;
|
|
ASSERT_TRUE(MemMap.map(/*Addr=*/0U, Size, "Zeros"));
|
|
ASSERT_NE(MemMap.getBase(), 0U);
|
|
scudo::uptr *P = reinterpret_cast<scudo::uptr *>(MemMap.getBase());
|
|
const ptrdiff_t N = Size / sizeof(scudo::uptr);
|
|
EXPECT_EQ(std::count(P, P + N, 0), N);
|
|
|
|
memset(P, 1, Size);
|
|
EXPECT_EQ(std::count(P, P + N, 0), 0);
|
|
|
|
MemMap.releasePagesToOS(MemMap.getBase(), Size);
|
|
EXPECT_EQ(std::count(P, P + N, 0), N);
|
|
|
|
MemMap.unmap();
|
|
}
|
|
|
|
TEST(ScudoMapDeathTest, MapNoAccessUnmap) {
|
|
const scudo::uptr Size = 4 * scudo::getPageSizeCached();
|
|
scudo::ReservedMemoryT ReservedMemory;
|
|
|
|
ASSERT_TRUE(ReservedMemory.create(/*Addr=*/0U, Size, MappingName));
|
|
EXPECT_NE(ReservedMemory.getBase(), 0U);
|
|
EXPECT_DEATH(
|
|
memset(reinterpret_cast<void *>(ReservedMemory.getBase()), 0xaa, Size),
|
|
"");
|
|
|
|
ReservedMemory.release();
|
|
}
|
|
|
|
TEST(ScudoMapDeathTest, MapUnmap) {
|
|
const scudo::uptr Size = 4 * scudo::getPageSizeCached();
|
|
EXPECT_DEATH(
|
|
{
|
|
// Repeat few time to avoid missing crash if it's mmaped by unrelated
|
|
// code.
|
|
for (int i = 0; i < 10; ++i) {
|
|
scudo::MemMapT MemMap;
|
|
MemMap.map(/*Addr=*/0U, Size, MappingName);
|
|
scudo::uptr P = MemMap.getBase();
|
|
if (P == 0U)
|
|
continue;
|
|
MemMap.unmap();
|
|
memset(reinterpret_cast<void *>(P), 0xbb, Size);
|
|
}
|
|
},
|
|
"");
|
|
}
|
|
|
|
TEST(ScudoMapDeathTest, MapWithGuardUnmap) {
|
|
const scudo::uptr PageSize = scudo::getPageSizeCached();
|
|
const scudo::uptr Size = 4 * PageSize;
|
|
scudo::ReservedMemoryT ReservedMemory;
|
|
ASSERT_TRUE(
|
|
ReservedMemory.create(/*Addr=*/0U, Size + 2 * PageSize, MappingName));
|
|
ASSERT_NE(ReservedMemory.getBase(), 0U);
|
|
|
|
scudo::MemMapT MemMap =
|
|
ReservedMemory.dispatch(ReservedMemory.getBase(), Size + 2 * PageSize);
|
|
ASSERT_TRUE(MemMap.isAllocated());
|
|
scudo::uptr Q = MemMap.getBase() + PageSize;
|
|
ASSERT_TRUE(MemMap.remap(Q, Size, MappingName));
|
|
memset(reinterpret_cast<void *>(Q), 0xaa, Size);
|
|
EXPECT_DEATH(memset(reinterpret_cast<void *>(Q), 0xaa, Size + 1), "");
|
|
MemMap.unmap();
|
|
}
|
|
|
|
// These death tests only fail when debugging is enabled.
|
|
#if SCUDO_LINUX
|
|
TEST(ScudoMapDeathTest, ResidentPagesNotMapped) {
|
|
scudo::MemMapT MemMap;
|
|
ASSERT_EQ(MemMap.getResidentPages(), 0);
|
|
}
|
|
#endif
|
|
|
|
TEST(ScudoMapTest, MapGrowUnmap) {
|
|
const scudo::uptr PageSize = scudo::getPageSizeCached();
|
|
const scudo::uptr Size = 4 * PageSize;
|
|
scudo::ReservedMemoryT ReservedMemory;
|
|
ReservedMemory.create(/*Addr=*/0U, Size, MappingName);
|
|
ASSERT_TRUE(ReservedMemory.isCreated());
|
|
|
|
scudo::MemMapT MemMap =
|
|
ReservedMemory.dispatch(ReservedMemory.getBase(), Size);
|
|
ASSERT_TRUE(MemMap.isAllocated());
|
|
scudo::uptr Q = MemMap.getBase() + PageSize;
|
|
ASSERT_TRUE(MemMap.remap(Q, PageSize, MappingName));
|
|
memset(reinterpret_cast<void *>(Q), 0xaa, PageSize);
|
|
Q += PageSize;
|
|
ASSERT_TRUE(MemMap.remap(Q, PageSize, MappingName));
|
|
memset(reinterpret_cast<void *>(Q), 0xbb, PageSize);
|
|
MemMap.unmap();
|
|
}
|
|
|
|
// Verify that zeroing works properly.
|
|
TEST(ScudoMapTest, Zeroing) {
|
|
scudo::ReservedMemoryT ReservedMemory;
|
|
const scudo::uptr PageSize = scudo::getPageSizeCached();
|
|
const scudo::uptr Size = 3 * PageSize;
|
|
ReservedMemory.create(/*Addr=*/0U, Size, MappingName);
|
|
ASSERT_TRUE(ReservedMemory.isCreated());
|
|
|
|
scudo::MemMapT MemMap = ReservedMemory.dispatch(ReservedMemory.getBase(),
|
|
ReservedMemory.getCapacity());
|
|
EXPECT_TRUE(
|
|
MemMap.remap(MemMap.getBase(), MemMap.getCapacity(), MappingName));
|
|
unsigned char *Data = reinterpret_cast<unsigned char *>(MemMap.getBase());
|
|
memset(Data, 1U, MemMap.getCapacity());
|
|
// Spot check some values.
|
|
EXPECT_EQ(1U, Data[0]);
|
|
EXPECT_EQ(1U, Data[PageSize]);
|
|
EXPECT_EQ(1U, Data[PageSize * 2]);
|
|
MemMap.releaseAndZeroPagesToOS(MemMap.getBase(), MemMap.getCapacity());
|
|
EXPECT_EQ(0U, Data[0]);
|
|
EXPECT_EQ(0U, Data[PageSize]);
|
|
EXPECT_EQ(0U, Data[PageSize * 2]);
|
|
|
|
#if SCUDO_LINUX
|
|
// Now verify that if madvise fails, the data is still zeroed.
|
|
memset(Data, 1U, MemMap.getCapacity());
|
|
if (mlock(Data, MemMap.getCapacity()) != -1) {
|
|
EXPECT_EQ(1U, Data[0]);
|
|
EXPECT_EQ(1U, Data[PageSize]);
|
|
EXPECT_EQ(1U, Data[PageSize * 2]);
|
|
MemMap.releaseAndZeroPagesToOS(MemMap.getBase(), MemMap.getCapacity());
|
|
EXPECT_EQ(0U, Data[0]);
|
|
EXPECT_EQ(0U, Data[PageSize]);
|
|
EXPECT_EQ(0U, Data[PageSize * 2]);
|
|
|
|
EXPECT_NE(-1, munlock(Data, MemMap.getCapacity()));
|
|
}
|
|
#endif
|
|
|
|
MemMap.unmap();
|
|
}
|