1 //===-- memtag_test.cpp -----------------------------------------*- C++ -*-===// 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 "common.h" 10 #include "mem_map.h" 11 #include "memtag.h" 12 #include "platform.h" 13 #include "tests/scudo_unit_test.h" 14 15 extern "C" void __hwasan_init() __attribute__((weak)); 16 17 #if SCUDO_LINUX 18 namespace scudo { 19 20 TEST(MemtagBasicDeathTest, Unsupported) { 21 if (archSupportsMemoryTagging()) 22 TEST_SKIP("Memory tagging is not unsupported"); 23 // Skip when running with HWASan. 24 if (&__hwasan_init != 0) 25 TEST_SKIP("Incompatible with HWASan"); 26 27 EXPECT_DEATH(archMemoryTagGranuleSize(), "not supported"); 28 EXPECT_DEATH(untagPointer((uptr)0), "not supported"); 29 EXPECT_DEATH(extractTag((uptr)0), "not supported"); 30 31 EXPECT_DEATH(systemSupportsMemoryTagging(), "not supported"); 32 EXPECT_DEATH(systemDetectsMemoryTagFaultsTestOnly(), "not supported"); 33 EXPECT_DEATH(enableSystemMemoryTaggingTestOnly(), "not supported"); 34 35 EXPECT_DEATH(selectRandomTag((uptr)0, 0), "not supported"); 36 EXPECT_DEATH(addFixedTag((uptr)0, 1), "not supported"); 37 EXPECT_DEATH(storeTags((uptr)0, (uptr)0 + sizeof(0)), "not supported"); 38 EXPECT_DEATH(storeTag((uptr)0), "not supported"); 39 EXPECT_DEATH(loadTag((uptr)0), "not supported"); 40 41 EXPECT_DEATH(setRandomTag(nullptr, 64, 0, nullptr, nullptr), "not supported"); 42 EXPECT_DEATH(untagPointer(nullptr), "not supported"); 43 EXPECT_DEATH(loadTag(nullptr), "not supported"); 44 EXPECT_DEATH(addFixedTag(nullptr, 0), "not supported"); 45 } 46 47 class MemtagTest : public Test { 48 protected: 49 void SetUp() override { 50 if (!archSupportsMemoryTagging() || !systemDetectsMemoryTagFaultsTestOnly()) 51 TEST_SKIP("Memory tagging is not supported"); 52 53 BufferSize = getPageSizeCached(); 54 ASSERT_FALSE(MemMap.isAllocated()); 55 ASSERT_TRUE(MemMap.map(/*Addr=*/0U, BufferSize, "MemtagTest", MAP_MEMTAG)); 56 ASSERT_NE(MemMap.getBase(), 0U); 57 Addr = MemMap.getBase(); 58 Buffer = reinterpret_cast<u8 *>(Addr); 59 EXPECT_TRUE(isAligned(Addr, archMemoryTagGranuleSize())); 60 EXPECT_EQ(Addr, untagPointer(Addr)); 61 } 62 63 void TearDown() override { 64 if (Buffer) { 65 ASSERT_TRUE(MemMap.isAllocated()); 66 MemMap.unmap(); 67 } 68 } 69 70 uptr BufferSize = 0; 71 scudo::MemMapT MemMap = {}; 72 u8 *Buffer = nullptr; 73 uptr Addr = 0; 74 }; 75 76 using MemtagDeathTest = MemtagTest; 77 78 TEST_F(MemtagTest, ArchMemoryTagGranuleSize) { 79 EXPECT_GT(archMemoryTagGranuleSize(), 1u); 80 EXPECT_TRUE(isPowerOfTwo(archMemoryTagGranuleSize())); 81 } 82 83 TEST_F(MemtagTest, ExtractTag) { 84 // The test is already skipped on anything other than 64 bit. But 85 // compiling on 32 bit leads to warnings/errors, so skip compiling the test. 86 #if defined(__LP64__) 87 uptr Tags = 0; 88 // Try all value for the top byte and check the tags values are in the 89 // expected range. 90 for (u64 Top = 0; Top < 0x100; ++Top) 91 Tags = Tags | (1u << extractTag(Addr | (Top << 56))); 92 EXPECT_EQ(0xffffull, Tags); 93 #endif 94 } 95 96 TEST_F(MemtagDeathTest, AddFixedTag) { 97 for (uptr Tag = 0; Tag < 0x10; ++Tag) 98 EXPECT_EQ(Tag, extractTag(addFixedTag(Addr, Tag))); 99 if (SCUDO_DEBUG) { 100 EXPECT_DEATH(addFixedTag(Addr, 16), ""); 101 EXPECT_DEATH(addFixedTag(~Addr, 0), ""); 102 } 103 } 104 105 TEST_F(MemtagTest, UntagPointer) { 106 uptr UnTagMask = untagPointer(~uptr(0)); 107 for (u64 Top = 0; Top < 0x100; ++Top) { 108 uptr Ptr = (Addr | (Top << 56)) & UnTagMask; 109 EXPECT_EQ(addFixedTag(Ptr, 0), untagPointer(Ptr)); 110 } 111 } 112 113 TEST_F(MemtagDeathTest, ScopedDisableMemoryTagChecks) { 114 u8 *P = reinterpret_cast<u8 *>(addFixedTag(Addr, 1)); 115 EXPECT_NE(P, Buffer); 116 117 EXPECT_DEATH(*P = 20, ""); 118 ScopedDisableMemoryTagChecks Disable; 119 *P = 10; 120 } 121 122 TEST_F(MemtagTest, SelectRandomTag) { 123 for (uptr SrcTag = 0; SrcTag < 0x10; ++SrcTag) { 124 uptr Ptr = addFixedTag(Addr, SrcTag); 125 uptr Tags = 0; 126 for (uptr I = 0; I < 100000; ++I) 127 Tags = Tags | (1u << extractTag(selectRandomTag(Ptr, 0))); 128 // std::popcnt is C++20 129 int PopCnt = 0; 130 while (Tags) { 131 PopCnt += Tags & 1; 132 Tags >>= 1; 133 } 134 // Random tags are not always very random, and this test is not about PRNG 135 // quality. Anything above half would be satisfactory. 136 EXPECT_GE(PopCnt, 8); 137 } 138 } 139 140 TEST_F(MemtagTest, SelectRandomTagWithMask) { 141 // The test is already skipped on anything other than 64 bit. But 142 // compiling on 32 bit leads to warnings/errors, so skip compiling the test. 143 #if defined(__LP64__) 144 for (uptr j = 0; j < 32; ++j) { 145 for (uptr i = 0; i < 1000; ++i) 146 EXPECT_NE(j, extractTag(selectRandomTag(Addr, 1ull << j))); 147 } 148 #endif 149 } 150 151 TEST_F(MemtagDeathTest, SKIP_NO_DEBUG(LoadStoreTagUnaligned)) { 152 for (uptr P = Addr; P < Addr + 4 * archMemoryTagGranuleSize(); ++P) { 153 if (P % archMemoryTagGranuleSize() == 0) 154 continue; 155 EXPECT_DEATH(loadTag(P), ""); 156 EXPECT_DEATH(storeTag(P), ""); 157 } 158 } 159 160 TEST_F(MemtagTest, LoadStoreTag) { 161 uptr Base = Addr + 0x100; 162 uptr Tagged = addFixedTag(Base, 7); 163 storeTag(Tagged); 164 165 EXPECT_EQ(Base - archMemoryTagGranuleSize(), 166 loadTag(Base - archMemoryTagGranuleSize())); 167 EXPECT_EQ(Tagged, loadTag(Base)); 168 EXPECT_EQ(Base + archMemoryTagGranuleSize(), 169 loadTag(Base + archMemoryTagGranuleSize())); 170 } 171 172 TEST_F(MemtagDeathTest, SKIP_NO_DEBUG(StoreTagsUnaligned)) { 173 for (uptr P = Addr; P < Addr + 4 * archMemoryTagGranuleSize(); ++P) { 174 uptr Tagged = addFixedTag(P, 5); 175 if (Tagged % archMemoryTagGranuleSize() == 0) 176 continue; 177 EXPECT_DEATH(storeTags(Tagged, Tagged), ""); 178 } 179 } 180 181 TEST_F(MemtagTest, StoreTags) { 182 // The test is already skipped on anything other than 64 bit. But 183 // compiling on 32 bit leads to warnings/errors, so skip compiling the test. 184 #if defined(__LP64__) 185 const uptr MaxTaggedSize = 4 * archMemoryTagGranuleSize(); 186 for (uptr Size = 0; Size <= MaxTaggedSize; ++Size) { 187 uptr NoTagBegin = Addr + archMemoryTagGranuleSize(); 188 uptr NoTagEnd = NoTagBegin + Size; 189 190 u8 Tag = 5; 191 192 uptr TaggedBegin = addFixedTag(NoTagBegin, Tag); 193 uptr TaggedEnd = addFixedTag(NoTagEnd, Tag); 194 195 EXPECT_EQ(roundUp(TaggedEnd, archMemoryTagGranuleSize()), 196 storeTags(TaggedBegin, TaggedEnd)); 197 198 uptr LoadPtr = Addr; 199 // Untagged left granule. 200 EXPECT_EQ(LoadPtr, loadTag(LoadPtr)); 201 202 for (LoadPtr += archMemoryTagGranuleSize(); LoadPtr < NoTagEnd; 203 LoadPtr += archMemoryTagGranuleSize()) { 204 EXPECT_EQ(addFixedTag(LoadPtr, 5), loadTag(LoadPtr)); 205 } 206 207 // Untagged right granule. 208 EXPECT_EQ(LoadPtr, loadTag(LoadPtr)); 209 210 // Reset tags without using StoreTags. 211 MemMap.releasePagesToOS(Addr, BufferSize); 212 } 213 #endif 214 } 215 216 } // namespace scudo 217 218 #endif 219