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