xref: /llvm-project/compiler-rt/lib/scudo/standalone/tests/memtag_test.cpp (revision a20020192845c1b9461b2b34308f19aa444676b4)
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