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