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 #if SCUDO_LINUX
16 namespace scudo {
17
TEST(MemtagBasicDeathTest,Unsupported)18 TEST(MemtagBasicDeathTest, Unsupported) {
19 if (archSupportsMemoryTagging())
20 GTEST_SKIP();
21
22 EXPECT_DEATH(archMemoryTagGranuleSize(), "not supported");
23 EXPECT_DEATH(untagPointer((uptr)0), "not supported");
24 EXPECT_DEATH(extractTag((uptr)0), "not supported");
25
26 EXPECT_DEATH(systemSupportsMemoryTagging(), "not supported");
27 EXPECT_DEATH(systemDetectsMemoryTagFaultsTestOnly(), "not supported");
28 EXPECT_DEATH(enableSystemMemoryTaggingTestOnly(), "not supported");
29
30 EXPECT_DEATH(selectRandomTag((uptr)0, 0), "not supported");
31 EXPECT_DEATH(addFixedTag((uptr)0, 1), "not supported");
32 EXPECT_DEATH(storeTags((uptr)0, (uptr)0 + sizeof(0)), "not supported");
33 EXPECT_DEATH(storeTag((uptr)0), "not supported");
34 EXPECT_DEATH(loadTag((uptr)0), "not supported");
35
36 EXPECT_DEATH(setRandomTag(nullptr, 64, 0, nullptr, nullptr), "not supported");
37 EXPECT_DEATH(untagPointer(nullptr), "not supported");
38 EXPECT_DEATH(loadTag(nullptr), "not supported");
39 EXPECT_DEATH(addFixedTag(nullptr, 0), "not supported");
40 }
41
42 class MemtagTest : public Test {
43 protected:
SetUp()44 void SetUp() override {
45 if (!archSupportsMemoryTagging() || !systemDetectsMemoryTagFaultsTestOnly())
46 GTEST_SKIP() << "Memory tagging is not supported";
47
48 BufferSize = getPageSizeCached();
49 ASSERT_FALSE(MemMap.isAllocated());
50 ASSERT_TRUE(MemMap.map(/*Addr=*/0U, BufferSize, "MemtagTest", MAP_MEMTAG));
51 ASSERT_NE(MemMap.getBase(), 0U);
52 Addr = MemMap.getBase();
53 Buffer = reinterpret_cast<u8 *>(Addr);
54 EXPECT_TRUE(isAligned(Addr, archMemoryTagGranuleSize()));
55 EXPECT_EQ(Addr, untagPointer(Addr));
56 }
57
TearDown()58 void TearDown() override {
59 if (Buffer) {
60 ASSERT_TRUE(MemMap.isAllocated());
61 MemMap.unmap(MemMap.getBase(), MemMap.getCapacity());
62 }
63 }
64
65 uptr BufferSize = 0;
66 scudo::MemMapT MemMap = {};
67 u8 *Buffer = nullptr;
68 uptr Addr = 0;
69 };
70
71 using MemtagDeathTest = MemtagTest;
72
TEST_F(MemtagTest,ArchMemoryTagGranuleSize)73 TEST_F(MemtagTest, ArchMemoryTagGranuleSize) {
74 EXPECT_GT(archMemoryTagGranuleSize(), 1u);
75 EXPECT_TRUE(isPowerOfTwo(archMemoryTagGranuleSize()));
76 }
77
TEST_F(MemtagTest,ExtractTag)78 TEST_F(MemtagTest, ExtractTag) {
79 uptr Tags = 0;
80 // Try all value for the top byte and check the tags values are in the
81 // expected range.
82 for (u64 Top = 0; Top < 0x100; ++Top)
83 Tags = Tags | (1u << extractTag(Addr | (Top << 56)));
84 EXPECT_EQ(0xffffull, Tags);
85 }
86
TEST_F(MemtagDeathTest,AddFixedTag)87 TEST_F(MemtagDeathTest, AddFixedTag) {
88 for (uptr Tag = 0; Tag < 0x10; ++Tag)
89 EXPECT_EQ(Tag, extractTag(addFixedTag(Addr, Tag)));
90 if (SCUDO_DEBUG) {
91 EXPECT_DEBUG_DEATH(addFixedTag(Addr, 16), "");
92 EXPECT_DEBUG_DEATH(addFixedTag(~Addr, 0), "");
93 }
94 }
95
TEST_F(MemtagTest,UntagPointer)96 TEST_F(MemtagTest, UntagPointer) {
97 uptr UnTagMask = untagPointer(~uptr(0));
98 for (u64 Top = 0; Top < 0x100; ++Top) {
99 uptr Ptr = (Addr | (Top << 56)) & UnTagMask;
100 EXPECT_EQ(addFixedTag(Ptr, 0), untagPointer(Ptr));
101 }
102 }
103
TEST_F(MemtagDeathTest,ScopedDisableMemoryTagChecks)104 TEST_F(MemtagDeathTest, ScopedDisableMemoryTagChecks) {
105 u8 *P = reinterpret_cast<u8 *>(addFixedTag(Addr, 1));
106 EXPECT_NE(P, Buffer);
107
108 EXPECT_DEATH(*P = 20, "");
109 ScopedDisableMemoryTagChecks Disable;
110 *P = 10;
111 }
112
TEST_F(MemtagTest,SelectRandomTag)113 TEST_F(MemtagTest, SelectRandomTag) {
114 for (uptr SrcTag = 0; SrcTag < 0x10; ++SrcTag) {
115 uptr Ptr = addFixedTag(Addr, SrcTag);
116 uptr Tags = 0;
117 for (uptr I = 0; I < 100000; ++I)
118 Tags = Tags | (1u << extractTag(selectRandomTag(Ptr, 0)));
119 EXPECT_EQ(0xfffeull, Tags);
120 }
121 }
122
TEST_F(MemtagTest,SelectRandomTagWithMask)123 TEST_F(MemtagTest, SelectRandomTagWithMask) {
124 for (uptr j = 0; j < 32; ++j) {
125 for (uptr i = 0; i < 1000; ++i)
126 EXPECT_NE(j, extractTag(selectRandomTag(Addr, 1ull << j)));
127 }
128 }
129
TEST_F(MemtagDeathTest,SKIP_NO_DEBUG (LoadStoreTagUnaligned))130 TEST_F(MemtagDeathTest, SKIP_NO_DEBUG(LoadStoreTagUnaligned)) {
131 for (uptr P = Addr; P < Addr + 4 * archMemoryTagGranuleSize(); ++P) {
132 if (P % archMemoryTagGranuleSize() == 0)
133 continue;
134 EXPECT_DEBUG_DEATH(loadTag(P), "");
135 EXPECT_DEBUG_DEATH(storeTag(P), "");
136 }
137 }
138
TEST_F(MemtagTest,LoadStoreTag)139 TEST_F(MemtagTest, LoadStoreTag) {
140 uptr Base = Addr + 0x100;
141 uptr Tagged = addFixedTag(Base, 7);
142 storeTag(Tagged);
143
144 EXPECT_EQ(Base - archMemoryTagGranuleSize(),
145 loadTag(Base - archMemoryTagGranuleSize()));
146 EXPECT_EQ(Tagged, loadTag(Base));
147 EXPECT_EQ(Base + archMemoryTagGranuleSize(),
148 loadTag(Base + archMemoryTagGranuleSize()));
149 }
150
TEST_F(MemtagDeathTest,SKIP_NO_DEBUG (StoreTagsUnaligned))151 TEST_F(MemtagDeathTest, SKIP_NO_DEBUG(StoreTagsUnaligned)) {
152 for (uptr P = Addr; P < Addr + 4 * archMemoryTagGranuleSize(); ++P) {
153 uptr Tagged = addFixedTag(P, 5);
154 if (Tagged % archMemoryTagGranuleSize() == 0)
155 continue;
156 EXPECT_DEBUG_DEATH(storeTags(Tagged, Tagged), "");
157 }
158 }
159
TEST_F(MemtagTest,StoreTags)160 TEST_F(MemtagTest, StoreTags) {
161 const uptr MaxTaggedSize = 4 * archMemoryTagGranuleSize();
162 for (uptr Size = 0; Size <= MaxTaggedSize; ++Size) {
163 uptr NoTagBegin = Addr + archMemoryTagGranuleSize();
164 uptr NoTagEnd = NoTagBegin + Size;
165
166 u8 Tag = 5;
167
168 uptr TaggedBegin = addFixedTag(NoTagBegin, Tag);
169 uptr TaggedEnd = addFixedTag(NoTagEnd, Tag);
170
171 EXPECT_EQ(roundUp(TaggedEnd, archMemoryTagGranuleSize()),
172 storeTags(TaggedBegin, TaggedEnd));
173
174 uptr LoadPtr = Addr;
175 // Untagged left granule.
176 EXPECT_EQ(LoadPtr, loadTag(LoadPtr));
177
178 for (LoadPtr += archMemoryTagGranuleSize(); LoadPtr < NoTagEnd;
179 LoadPtr += archMemoryTagGranuleSize()) {
180 EXPECT_EQ(addFixedTag(LoadPtr, 5), loadTag(LoadPtr));
181 }
182
183 // Untagged right granule.
184 EXPECT_EQ(LoadPtr, loadTag(LoadPtr));
185
186 // Reset tags without using StoreTags.
187 MemMap.releasePagesToOS(Addr, BufferSize);
188 }
189 }
190
191 } // namespace scudo
192
193 #endif
194