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 <android-base/properties.h>
30 #include <android-base/test_utils.h>
31 #include <bionic/malloc_tagged_pointers.h>
32 
KernelSupportsTaggedPointers()33 static bool KernelSupportsTaggedPointers() {
34 #ifdef __aarch64__
35   int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
36   return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
37 #else
38   return false;
39 #endif
40 }
41 
SetHeapTaggingLevel(HeapTaggingLevel level)42 static bool SetHeapTaggingLevel(HeapTaggingLevel level) {
43   return mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, level);
44 }
45 #endif
46 
TEST(heap_tagging_level,tagged_pointer_dies)47 TEST(heap_tagging_level, tagged_pointer_dies) {
48 #if defined(__BIONIC__)
49   if (!KernelSupportsTaggedPointers()) {
50     GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
51   }
52 
53 #ifdef __aarch64__
54   if (mte_supported()) {
55     GTEST_SKIP() << "Tagged pointers are not used on MTE hardware.";
56   }
57   if (running_with_hwasan()) {
58     GTEST_SKIP() << "Tagged heap pointers feature is disabled under HWASan.";
59   }
60 
61   void *x = malloc(1);
62 
63   // Ensure that `x` has a pointer tag.
64   EXPECT_NE(reinterpret_cast<uintptr_t>(x) >> 56, 0u);
65 
66   x = untag_address(x);
67   EXPECT_DEATH(free(x), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
68 
69   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
70   EXPECT_DEATH(free(untag_address(malloc(1))), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
71 
72   x = malloc(1);
73   void *y = malloc(1);
74   // Disable heap tagging.
75   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
76   // Ensure an older tagged pointer can still be freed.
77   free(x);
78   // Tag mismatch is not detected on old pointers.
79   free(untag_address(y));
80 #endif // defined(__aarch64__)
81 #else
82   GTEST_SKIP() << "bionic-only test";
83 #endif // defined(__BIONIC__)
84 }
85 
86 namespace {
87 #if defined(__BIONIC__) && defined(__aarch64__)
ExitWithSiCode(int,siginfo_t * info,void *)88 void ExitWithSiCode(int, siginfo_t* info, void*) {
89   _exit(info->si_code);
90 }
91 
92 template <typename Pred>
93 class Or {
94   Pred A, B;
95 
96  public:
Or(Pred A,Pred B)97   Or(Pred A, Pred B) : A(A), B(B) {}
operator ()(int exit_status)98   bool operator()(int exit_status) { return A(exit_status) || B(exit_status); }
99 };
100 #endif
101 
TEST(heap_tagging_level,sync_async_bad_accesses_die)102 TEST(heap_tagging_level, sync_async_bad_accesses_die) {
103 #if defined(__BIONIC__) && defined(__aarch64__)
104   if (!mte_supported() || !running_with_mte()) {
105     GTEST_SKIP() << "requires MTE to be enabled";
106   }
107 
108   std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
109   volatile int sink ATTRIBUTE_UNUSED;
110 
111   // We assume that scudo is used on all MTE enabled hardware; scudo inserts a header with a
112   // mismatching tag before each allocation.
113   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
114   EXPECT_EXIT(
115       {
116         ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
117         p[-1] = 42;
118       },
119       testing::ExitedWithCode(SEGV_MTESERR), "");
120   EXPECT_EXIT(
121       {
122         ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
123         sink = p[-1];
124       },
125       testing::ExitedWithCode(SEGV_MTESERR), "");
126 
127   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
128   EXPECT_EXIT(
129       {
130         ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
131         p[-1] = 42;
132       },
133       Or(testing::ExitedWithCode(SEGV_MTESERR), testing::ExitedWithCode(SEGV_MTEAERR)), "");
134   EXPECT_EXIT(
135       {
136         ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
137         sink = p[-1];
138       },
139       Or(testing::ExitedWithCode(SEGV_MTESERR), testing::ExitedWithCode(SEGV_MTEAERR)), "");
140 
141   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
142   sink = p[-1];
143 #else
144   GTEST_SKIP() << "bionic/arm64 only";
145 #endif
146 }
147 }  // namespace
148 
TEST(heap_tagging_level,none_pointers_untagged)149 TEST(heap_tagging_level, none_pointers_untagged) {
150 #if defined(__BIONIC__)
151   if (running_with_hwasan()) {
152     GTEST_SKIP() << "HWASan is unaffected by heap tagging level.";
153   }
154   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
155   std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
156   EXPECT_EQ(untag_address(p.get()), p.get());
157 #else
158   GTEST_SKIP() << "bionic-only test";
159 #endif
160 }
161 
TEST(heap_tagging_level,tagging_level_transitions)162 TEST(heap_tagging_level, tagging_level_transitions) {
163 #if defined(__BIONIC__) && defined(__aarch64__)
164   if (!KernelSupportsTaggedPointers()) {
165     GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
166   }
167 
168   EXPECT_FALSE(SetHeapTaggingLevel(static_cast<HeapTaggingLevel>(12345)));
169 
170   if (running_with_hwasan()) {
171     // NONE -> ...
172     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
173     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
174     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
175     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
176   } else if (mte_supported() && running_with_mte()) {
177     // ASYNC -> ...
178     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
179     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
180     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
181 
182     // SYNC -> ...
183     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
184     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
185     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
186   } else if (!mte_supported()) {
187     // TBI -> ...
188     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
189     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
190     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
191   }
192 
193   // TBI -> NONE on non-MTE, ASYNC|SYNC|NONE -> NONE on MTE.
194   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
195 
196   // NONE -> ...
197   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
198   EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
199   EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
200   EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
201 #else
202   GTEST_SKIP() << "bionic/arm64 only";
203 #endif
204 }
205 
TEST(heap_tagging_level,tagging_level_transition_sync_none)206 TEST(heap_tagging_level, tagging_level_transition_sync_none) {
207 #if defined(__BIONIC__) && defined(__aarch64__)
208   // We can't test SYNC -> NONE in tagging_level_transitions because we can only make one transition
209   // to NONE (which we use to test ASYNC -> NONE), so we test it here separately.
210   if (!mte_supported() || !running_with_mte()) {
211     GTEST_SKIP() << "requires MTE to be enabled";
212   }
213 
214   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
215   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
216 #else
217   GTEST_SKIP() << "bionic/arm64 only";
218 #endif
219 }
220 
221 enum class MemtagNote { NONE, ASYNC, SYNC };
222 class MemtagNoteTest : public testing::TestWithParam<std::tuple<MemtagNote, bool>> {};
223 
TEST_P(MemtagNoteTest,SEGV)224 TEST_P(MemtagNoteTest, SEGV) {
225 #if defined(__BIONIC__) && defined(__aarch64__)
226   SKIP_WITH_NATIVE_BRIDGE;  // http://b/242170715
227   if (android::base::GetProperty("persist.arm64.memtag.default", "") != "") {
228     GTEST_SKIP() << "not supported when overriding memtag mode with property";
229   }
230   // Note that we do not check running_with_hwasan() - what matters here is whether the test binary
231   // itself is built with HWASan.
232   bool withHWASAN = __has_feature(hwaddress_sanitizer);
233   bool withMTE = getauxval(AT_HWCAP2) & HWCAP2_MTE;
234 
235   const char* kNoteSuffix[] = {"disabled", "async", "sync"};
236   const char* kExpectedOutputHWASAN[] = {".*tag-mismatch.*", ".*tag-mismatch.*",
237                                          ".*tag-mismatch.*"};
238   // Note that we do not check the exact si_code of the "async" variant, as it may be auto-upgraded
239   // to asymm or even sync.
240   const char* kExpectedOutputMTE[] = {"normal exit\n", "SEGV_MTE[AS]ERR\n", "SEGV_MTESERR\n"};
241   const char* kExpectedOutputNonMTE[] = {"normal exit\n", "normal exit\n", "normal exit\n"};
242   const char** kExpectedOutput =
243       withHWASAN ? kExpectedOutputHWASAN : (withMTE ? kExpectedOutputMTE : kExpectedOutputNonMTE);
244   const int kExpectedExitStatus = withHWASAN ? -SIGABRT : 0;
245 
246   MemtagNote note = std::get<0>(GetParam());
247   bool isStatic = std::get<1>(GetParam());
248   std::string helper_base = std::string("heap_tagging_") + (isStatic ? "static_" : "") +
249                             kNoteSuffix[static_cast<int>(note)] + "_helper";
250   std::string helper = GetTestlibRoot() + "/" + helper_base;
251   chmod(helper.c_str(), 0755);
252   ExecTestHelper eth;
253   eth.SetArgs({helper.c_str(), nullptr});
254   eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, kExpectedExitStatus,
255           kExpectedOutput[static_cast<int>(note)]);
256 #else
257   GTEST_SKIP() << "bionic/arm64 only";
258 #endif
259 }
260 
261 INSTANTIATE_TEST_SUITE_P(, MemtagNoteTest,
262                          testing::Combine(testing::Values(MemtagNote::NONE, MemtagNote::ASYNC,
263                                                           MemtagNote::SYNC),
264                                           testing::Bool()));
265