1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <gtest/gtest.h>
18 
19 #include <malloc.h>
20 #include <sys/prctl.h>
21 
22 #if defined(__BIONIC__)
23 #include "gtest_globals.h"
24 #include "platform/bionic/mte.h"
25 #include "utils.h"
26 
27 #include "SignalUtils.h"
28 
29 #include <bionic/malloc_tagged_pointers.h>
30 
KernelSupportsTaggedPointers()31 static bool KernelSupportsTaggedPointers() {
32 #ifdef __aarch64__
33   int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
34   return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
35 #else
36   return false;
37 #endif
38 }
39 
SetHeapTaggingLevel(HeapTaggingLevel level)40 static bool SetHeapTaggingLevel(HeapTaggingLevel level) {
41   return mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, level);
42 }
43 #endif
44 
TEST(heap_tagging_level,tagged_pointer_dies)45 TEST(heap_tagging_level, tagged_pointer_dies) {
46 #if defined(__BIONIC__)
47   if (!KernelSupportsTaggedPointers()) {
48     GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
49   }
50 
51 #ifdef __aarch64__
52   if (mte_supported()) {
53     GTEST_SKIP() << "Tagged pointers are not used on MTE hardware.";
54   }
55 
56   void *x = malloc(1);
57 
58   // Ensure that `x` has a pointer tag.
59   EXPECT_NE(reinterpret_cast<uintptr_t>(x) >> 56, 0u);
60 
61   x = untag_address(x);
62   EXPECT_DEATH(free(x), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
63 
64   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
65   EXPECT_DEATH(free(untag_address(malloc(1))), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
66 
67   x = malloc(1);
68   void *y = malloc(1);
69   // Disable heap tagging.
70   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
71   // Ensure an older tagged pointer can still be freed.
72   free(x);
73   // Tag mismatch is not detected on old pointers.
74   free(untag_address(y));
75 #endif // defined(__aarch64__)
76 #else
77   GTEST_SKIP() << "bionic-only test";
78 #endif // defined(__BIONIC__)
79 }
80 
81 #if defined(__BIONIC__) && defined(__aarch64__)
ExitWithSiCode(int,siginfo_t * info,void *)82 void ExitWithSiCode(int, siginfo_t* info, void*) {
83   _exit(info->si_code);
84 }
85 #endif
86 
TEST(heap_tagging_level,sync_async_bad_accesses_die)87 TEST(heap_tagging_level, sync_async_bad_accesses_die) {
88 #if defined(__BIONIC__) && defined(__aarch64__)
89   if (!mte_supported() || !running_with_mte()) {
90     GTEST_SKIP() << "requires MTE to be enabled";
91   }
92 
93   std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
94 
95   // We assume that scudo is used on all MTE enabled hardware; scudo inserts a header with a
96   // mismatching tag before each allocation.
97   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
98   EXPECT_EXIT(
99       {
100         ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
101         p[-1] = 42;
102       },
103       testing::ExitedWithCode(SEGV_MTESERR), "");
104 
105   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
106   EXPECT_EXIT(
107       {
108         ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
109         p[-1] = 42;
110       },
111       testing::ExitedWithCode(SEGV_MTEAERR), "");
112 
113   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
114   volatile int oob ATTRIBUTE_UNUSED = p[-1];
115 #else
116   GTEST_SKIP() << "bionic/arm64 only";
117 #endif
118 }
119 
TEST(heap_tagging_level,none_pointers_untagged)120 TEST(heap_tagging_level, none_pointers_untagged) {
121 #if defined(__BIONIC__)
122   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
123   std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
124   EXPECT_EQ(untag_address(p.get()), p.get());
125 #else
126   GTEST_SKIP() << "bionic-only test";
127 #endif
128 }
129 
TEST(heap_tagging_level,tagging_level_transitions)130 TEST(heap_tagging_level, tagging_level_transitions) {
131 #if defined(__BIONIC__) && defined(__aarch64__)
132   if (!KernelSupportsTaggedPointers()) {
133     GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
134   }
135 
136   EXPECT_FALSE(SetHeapTaggingLevel(static_cast<HeapTaggingLevel>(12345)));
137 
138   if (mte_supported() && running_with_mte()) {
139     // ASYNC -> ...
140     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
141     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
142     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
143 
144     // SYNC -> ...
145     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
146     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
147     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
148   } else if (!mte_supported()) {
149     // TBI -> ...
150     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
151     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
152     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
153   }
154 
155   // TBI -> NONE on non-MTE, ASYNC|SYNC|NONE -> NONE on MTE.
156   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
157 
158   // NONE -> ...
159   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
160   EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
161   EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
162   EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
163 #else
164   GTEST_SKIP() << "bionic/arm64 only";
165 #endif
166 }
167 
TEST(heap_tagging_level,tagging_level_transition_sync_none)168 TEST(heap_tagging_level, tagging_level_transition_sync_none) {
169 #if defined(__BIONIC__) && defined(__aarch64__)
170   // We can't test SYNC -> NONE in tagging_level_transitions because we can only make one transition
171   // to NONE (which we use to test ASYNC -> NONE), so we test it here separately.
172   if (!mte_supported() || !running_with_mte()) {
173     GTEST_SKIP() << "requires MTE to be enabled";
174   }
175 
176   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
177   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
178 #else
179   GTEST_SKIP() << "bionic/arm64 only";
180 #endif
181 }
182 
183 enum class MemtagNote { NONE, ASYNC, SYNC };
184 class MemtagNoteTest : public testing::TestWithParam<std::tuple<MemtagNote, bool>> {};
185 
TEST_P(MemtagNoteTest,SEGV)186 TEST_P(MemtagNoteTest, SEGV) {
187 #if defined(__BIONIC__) && defined(__aarch64__)
188   if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) {
189     GTEST_SKIP() << "requires MTE support";
190   }
191 
192   const char* kNoteSuffix[] = {"disabled", "async", "sync"};
193   const char* kExpectedOutput[] = {"normal exit\n", "SEGV_MTEAERR\n", "SEGV_MTESERR\n"};
194 
195   MemtagNote note = std::get<0>(GetParam());
196   bool isStatic = std::get<1>(GetParam());
197   std::string helper_base = std::string("heap_tagging_") + (isStatic ? "static_" : "") +
198                             kNoteSuffix[static_cast<int>(note)] + "_helper";
199   fprintf(stderr, "=== %s\n", helper_base.c_str());
200   std::string helper = GetTestlibRoot() + "/" + helper_base + "/" + helper_base;
201   chmod(helper.c_str(), 0755);
202   ExecTestHelper eth;
203   eth.SetArgs({helper.c_str(), nullptr});
204   eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0,
205           kExpectedOutput[static_cast<int>(note)]);
206 #endif
207 }
208 
209 INSTANTIATE_TEST_SUITE_P(, MemtagNoteTest,
210                          testing::Combine(testing::Values(MemtagNote::NONE, MemtagNote::ASYNC,
211                                                           MemtagNote::SYNC),
212                                           testing::Bool()));
213