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