Merge "Introduce a new heap tagging level, M_HEAP_TAGGING_LEVEL_SYNC." am: 144a6ab341
Change-Id: Ie039b08a8edc06163508423a2d2771d9f8300e96
This commit is contained in:
commit
150d47edfb
9 changed files with 248 additions and 170 deletions
|
@ -34,13 +34,12 @@
|
|||
#include <platform/bionic/mte_kernel.h>
|
||||
|
||||
extern "C" void scudo_malloc_disable_memory_tagging();
|
||||
extern "C" void scudo_malloc_set_track_allocation_stacks(int);
|
||||
|
||||
static HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
|
||||
|
||||
void SetDefaultHeapTaggingLevel() {
|
||||
#if defined(__aarch64__)
|
||||
#define PR_SET_TAGGED_ADDR_CTRL 55
|
||||
#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
|
||||
#ifdef ANDROID_EXPERIMENTAL_MTE
|
||||
// First, try enabling MTE in asynchronous mode, with tag 0 excluded. This will fail if the kernel
|
||||
// or hardware doesn't support MTE, and we will fall back to just enabling tagged pointers in
|
||||
|
@ -81,34 +80,48 @@ bool SetHeapTaggingLevel(void* arg, size_t arg_size) {
|
|||
|
||||
switch (tag_level) {
|
||||
case M_HEAP_TAGGING_LEVEL_NONE:
|
||||
#if defined(USE_SCUDO)
|
||||
scudo_malloc_disable_memory_tagging();
|
||||
#endif
|
||||
if (heap_tagging_level == M_HEAP_TAGGING_LEVEL_TBI) {
|
||||
__libc_globals.mutate([](libc_globals* globals) {
|
||||
// Preserve the untag mask (we still want to untag pointers when passing them to the
|
||||
// allocator), but clear the fixed tag and the check mask, so that pointers are no longer
|
||||
// tagged and checks no longer happen.
|
||||
globals->heap_pointer_tag = static_cast<uintptr_t>(0xffull << UNTAG_SHIFT);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case M_HEAP_TAGGING_LEVEL_TBI:
|
||||
case M_HEAP_TAGGING_LEVEL_ASYNC:
|
||||
case M_HEAP_TAGGING_LEVEL_SYNC:
|
||||
if (heap_tagging_level == M_HEAP_TAGGING_LEVEL_NONE) {
|
||||
error_log(
|
||||
"SetHeapTaggingLevel: re-enabling tagging after it was disabled is not supported");
|
||||
} else {
|
||||
error_log("SetHeapTaggingLevel: switching between TBI and ASYNC is not supported");
|
||||
return false;
|
||||
} else if (tag_level == M_HEAP_TAGGING_LEVEL_TBI ||
|
||||
heap_tagging_level == M_HEAP_TAGGING_LEVEL_TBI) {
|
||||
error_log("SetHeapTaggingLevel: switching between TBI and ASYNC/SYNC is not supported");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (tag_level == M_HEAP_TAGGING_LEVEL_ASYNC) {
|
||||
#if defined(USE_SCUDO)
|
||||
scudo_malloc_set_track_allocation_stacks(0);
|
||||
#endif
|
||||
} else if (tag_level == M_HEAP_TAGGING_LEVEL_ASYNC) {
|
||||
#if defined(USE_SCUDO)
|
||||
scudo_malloc_set_track_allocation_stacks(1);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error_log("SetHeapTaggingLevel: unknown tagging level");
|
||||
return false;
|
||||
}
|
||||
|
||||
heap_tagging_level = tag_level;
|
||||
info_log("SetHeapTaggingLevel: tag level set to %d", tag_level);
|
||||
|
||||
if (heap_tagging_level == M_HEAP_TAGGING_LEVEL_NONE) {
|
||||
#if defined(USE_SCUDO)
|
||||
scudo_malloc_disable_memory_tagging();
|
||||
#endif
|
||||
__libc_globals.mutate([](libc_globals* globals) {
|
||||
// Preserve the untag mask (we still want to untag pointers when passing them to the
|
||||
// allocator if we were doing so before), but clear the fixed tag and the check mask,
|
||||
// so that pointers are no longer tagged and checks no longer happen.
|
||||
globals->heap_pointer_tag &= 0xffull << UNTAG_SHIFT;
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -366,6 +366,9 @@ static bool InstallHooks(libc_globals* globals, const char* options, const char*
|
|||
return true;
|
||||
}
|
||||
|
||||
extern "C" const char* __scudo_get_stack_depot_addr();
|
||||
extern "C" const char* __scudo_get_region_info_addr();
|
||||
|
||||
// Initializes memory allocation framework once per process.
|
||||
static void MallocInitImpl(libc_globals* globals) {
|
||||
char prop[PROP_VALUE_MAX];
|
||||
|
@ -373,6 +376,11 @@ static void MallocInitImpl(libc_globals* globals) {
|
|||
|
||||
MaybeInitGwpAsanFromLibc(globals);
|
||||
|
||||
#if defined(USE_SCUDO)
|
||||
__libc_shared_globals()->scudo_stack_depot = __scudo_get_stack_depot_addr();
|
||||
__libc_shared_globals()->scudo_region_info = __scudo_get_region_info_addr();
|
||||
#endif
|
||||
|
||||
// Prefer malloc debug since it existed first and is a more complete
|
||||
// malloc interceptor than the hooks.
|
||||
bool hook_installed = false;
|
||||
|
|
|
@ -116,6 +116,8 @@ enum HeapTaggingLevel {
|
|||
M_HEAP_TAGGING_LEVEL_TBI = 1,
|
||||
// Enable heap tagging if supported, at a level appropriate for asynchronous memory tag checks.
|
||||
M_HEAP_TAGGING_LEVEL_ASYNC = 2,
|
||||
// Enable heap tagging if supported, at a level appropriate for synchronous memory tag checks.
|
||||
M_HEAP_TAGGING_LEVEL_SYNC = 3,
|
||||
};
|
||||
|
||||
// Manipulates bionic-specific handling of memory allocation APIs such as
|
||||
|
|
|
@ -103,6 +103,9 @@ struct libc_shared_globals {
|
|||
|
||||
const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
|
||||
const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
|
||||
|
||||
const char* scudo_stack_depot = nullptr;
|
||||
const char* scudo_region_info = nullptr;
|
||||
};
|
||||
|
||||
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals();
|
||||
|
|
|
@ -40,6 +40,8 @@ static debugger_process_info get_process_info() {
|
|||
.fdsan_table = &__libc_shared_globals()->fd_table,
|
||||
.gwp_asan_state = __libc_shared_globals()->gwp_asan_state,
|
||||
.gwp_asan_metadata = __libc_shared_globals()->gwp_asan_metadata,
|
||||
.scudo_stack_depot = __libc_shared_globals()->scudo_stack_depot,
|
||||
.scudo_region_info = __libc_shared_globals()->scudo_region_info,
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -122,6 +122,7 @@ cc_test_library {
|
|||
"glob_test.cpp",
|
||||
"grp_pwd_test.cpp",
|
||||
"grp_pwd_file_test.cpp",
|
||||
"heap_tagging_level_test.cpp",
|
||||
"iconv_test.cpp",
|
||||
"ifaddrs_test.cpp",
|
||||
"ifunc_test.cpp",
|
||||
|
@ -236,9 +237,6 @@ cc_test_library {
|
|||
"libprocinfo",
|
||||
"libsystemproperties",
|
||||
],
|
||||
srcs: [
|
||||
"tagged_pointers_test.cpp",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
|
|
202
tests/heap_tagging_level_test.cpp
Normal file
202
tests/heap_tagging_level_test.cpp
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
#include "platform/bionic/malloc.h"
|
||||
#include "platform/bionic/mte.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "SignalUtils.h"
|
||||
|
||||
#include <bionic/malloc_tagged_pointers.h>
|
||||
|
||||
static bool KernelSupportsTaggedPointers() {
|
||||
#ifdef __aarch64__
|
||||
int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
|
||||
return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool SetHeapTaggingLevel(HeapTaggingLevel level) {
|
||||
return android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &level, sizeof(level));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(heap_tagging_level, tagged_pointer_dies) {
|
||||
#if defined(__BIONIC__)
|
||||
if (!KernelSupportsTaggedPointers()) {
|
||||
GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
|
||||
}
|
||||
|
||||
#ifdef __aarch64__
|
||||
if (mte_supported()) {
|
||||
GTEST_SKIP() << "Tagged pointers are not used on MTE hardware.";
|
||||
}
|
||||
|
||||
void *x = malloc(1);
|
||||
|
||||
// Ensure that `x` has a pointer tag.
|
||||
EXPECT_NE(reinterpret_cast<uintptr_t>(x) >> 56, 0u);
|
||||
|
||||
x = untag_address(x);
|
||||
EXPECT_DEATH(free(x), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
|
||||
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
|
||||
EXPECT_DEATH(free(untag_address(malloc(1))), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
|
||||
|
||||
x = malloc(1);
|
||||
void *y = malloc(1);
|
||||
// Disable heap tagging.
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
|
||||
// Ensure an older tagged pointer can still be freed.
|
||||
free(x);
|
||||
// Tag mismatch is not detected on old pointers.
|
||||
free(untag_address(y));
|
||||
#endif // defined(__aarch64__)
|
||||
#else
|
||||
GTEST_SKIP() << "bionic-only test";
|
||||
#endif // defined(__BIONIC__)
|
||||
}
|
||||
|
||||
#if defined(__BIONIC__) && defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
template <int SiCode> void CheckSiCode(int, siginfo_t* info, void*) {
|
||||
if (info->si_code != SiCode) {
|
||||
_exit(2);
|
||||
}
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static bool SetTagCheckingLevel(int level) {
|
||||
int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
|
||||
if (tagged_addr_ctrl < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level;
|
||||
return prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(heap_tagging_level, sync_async_bad_accesses_die) {
|
||||
#if defined(__BIONIC__) && defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) {
|
||||
GTEST_SKIP() << "requires MTE support";
|
||||
}
|
||||
|
||||
std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
|
||||
|
||||
// First, check that memory tagging is enabled and the default tag checking level is async.
|
||||
// We assume that scudo is used on all MTE enabled hardware; scudo inserts a header with a
|
||||
// mismatching tag before each allocation.
|
||||
EXPECT_EXIT(
|
||||
{
|
||||
ScopedSignalHandler ssh(SIGSEGV, CheckSiCode<SEGV_MTEAERR>, SA_SIGINFO);
|
||||
p[-1] = 42;
|
||||
},
|
||||
testing::ExitedWithCode(1), "");
|
||||
|
||||
EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_SYNC));
|
||||
EXPECT_EXIT(
|
||||
{
|
||||
ScopedSignalHandler ssh(SIGSEGV, CheckSiCode<SEGV_MTESERR>, SA_SIGINFO);
|
||||
p[-1] = 42;
|
||||
},
|
||||
testing::ExitedWithCode(1), "");
|
||||
|
||||
EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
|
||||
volatile int oob ATTRIBUTE_UNUSED = p[-1];
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(heap_tagging_level, none_pointers_untagged) {
|
||||
#if defined(__BIONIC__)
|
||||
#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
|
||||
#endif
|
||||
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
|
||||
std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
|
||||
EXPECT_EQ(untag_address(p.get()), p.get());
|
||||
#else
|
||||
GTEST_SKIP() << "bionic-only test";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(heap_tagging_level, tagging_level_transitions) {
|
||||
#if defined(__BIONIC__) && defined(__aarch64__)
|
||||
if (!KernelSupportsTaggedPointers()) {
|
||||
GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
|
||||
}
|
||||
|
||||
#if defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
|
||||
#endif
|
||||
|
||||
EXPECT_FALSE(SetHeapTaggingLevel(static_cast<HeapTaggingLevel>(12345)));
|
||||
|
||||
if (mte_supported()) {
|
||||
// ASYNC -> ...
|
||||
EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
|
||||
|
||||
// SYNC -> ...
|
||||
EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
|
||||
} else {
|
||||
// TBI -> ...
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
|
||||
EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
|
||||
EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
|
||||
}
|
||||
|
||||
// TBI -> NONE on non-MTE, ASYNC -> NONE on MTE.
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
|
||||
|
||||
// NONE -> ...
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
|
||||
EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
|
||||
EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
|
||||
EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
|
||||
#else
|
||||
GTEST_SKIP() << "bionic/arm64 only";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(heap_tagging_level, tagging_level_transition_sync_none) {
|
||||
#if defined(__BIONIC__) && defined(__aarch64__)
|
||||
// We can't test SYNC -> NONE in tagging_level_transitions because we can only make one transition
|
||||
// to NONE (which we use to test ASYNC -> NONE), so we test it here separately.
|
||||
if (!mte_supported()) {
|
||||
GTEST_SKIP() << "requires MTE support";
|
||||
}
|
||||
|
||||
#if defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
|
||||
#endif
|
||||
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
|
||||
EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
|
||||
#else
|
||||
GTEST_SKIP() << "bionic/arm64 only";
|
||||
#endif
|
||||
}
|
|
@ -1201,69 +1201,3 @@ TEST(android_mallopt, set_allocation_limit_multiple_threads) {
|
|||
GTEST_SKIP() << "bionic extension";
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__BIONIC__) && defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
template <int SiCode> void CheckSiCode(int, siginfo_t* info, void*) {
|
||||
if (info->si_code != SiCode) {
|
||||
_exit(2);
|
||||
}
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static bool SetTagCheckingLevel(int level) {
|
||||
int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
|
||||
if (tagged_addr_ctrl < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level;
|
||||
return prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(android_mallopt, tag_level) {
|
||||
#if defined(__BIONIC__) && defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
|
||||
if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) {
|
||||
GTEST_SKIP() << "requires MTE support";
|
||||
}
|
||||
|
||||
std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
|
||||
|
||||
// First, check that memory tagging is enabled and the default tag checking level is async.
|
||||
// We assume that scudo is used on all MTE enabled hardware; scudo inserts a header with a
|
||||
// mismatching tag before each allocation.
|
||||
EXPECT_EXIT(
|
||||
{
|
||||
ScopedSignalHandler ssh(SIGSEGV, CheckSiCode<SEGV_MTEAERR>, SA_SIGINFO);
|
||||
p[-1] = 42;
|
||||
},
|
||||
testing::ExitedWithCode(1), "");
|
||||
|
||||
EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_SYNC));
|
||||
EXPECT_EXIT(
|
||||
{
|
||||
ScopedSignalHandler ssh(SIGSEGV, CheckSiCode<SEGV_MTESERR>, SA_SIGINFO);
|
||||
p[-1] = 42;
|
||||
},
|
||||
testing::ExitedWithCode(1), "");
|
||||
|
||||
EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
|
||||
volatile int oob ATTRIBUTE_UNUSED = p[-1];
|
||||
|
||||
HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_TBI;
|
||||
EXPECT_FALSE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
|
||||
|
||||
tag_level = M_HEAP_TAGGING_LEVEL_NONE;
|
||||
EXPECT_TRUE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
|
||||
std::unique_ptr<int[]> p2 = std::make_unique<int[]>(4);
|
||||
EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(p2.get()) >> 56);
|
||||
|
||||
tag_level = M_HEAP_TAGGING_LEVEL_ASYNC;
|
||||
EXPECT_FALSE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
|
||||
|
||||
tag_level = M_HEAP_TAGGING_LEVEL_NONE;
|
||||
EXPECT_TRUE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
|
||||
#else
|
||||
GTEST_SKIP() << "arm64 only";
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "platform/bionic/malloc.h"
|
||||
#include "platform/bionic/mte.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <bionic/malloc_tagged_pointers.h>
|
||||
|
||||
static bool KernelSupportsTaggedPointers() {
|
||||
#ifdef __aarch64__
|
||||
#define PR_SET_TAGGED_ADDR_CTRL 55
|
||||
#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
|
||||
int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
|
||||
return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(tagged_pointers, check_tagged_pointer_dies) {
|
||||
if (!KernelSupportsTaggedPointers()) {
|
||||
GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
|
||||
}
|
||||
|
||||
#ifdef __aarch64__
|
||||
if (mte_supported()) {
|
||||
GTEST_SKIP() << "Tagged pointers are not used on MTE hardware.";
|
||||
}
|
||||
|
||||
void *x = malloc(1);
|
||||
|
||||
// Ensure that `x` has a pointer tag.
|
||||
EXPECT_NE(reinterpret_cast<uintptr_t>(x) >> 56, 0u);
|
||||
|
||||
x = untag_address(x);
|
||||
EXPECT_DEATH(free(x), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
|
||||
|
||||
HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_TBI;
|
||||
EXPECT_TRUE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
|
||||
EXPECT_DEATH(free(untag_address(malloc(1))), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
|
||||
|
||||
tag_level = M_HEAP_TAGGING_LEVEL_ASYNC;
|
||||
EXPECT_FALSE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
|
||||
|
||||
x = malloc(1);
|
||||
void *y = malloc(1);
|
||||
// Disable heap tagging.
|
||||
tag_level = M_HEAP_TAGGING_LEVEL_NONE;
|
||||
EXPECT_TRUE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
|
||||
// Ensure an older tagged pointer can still be freed.
|
||||
free(x);
|
||||
// Tag mismatch is not detected on old pointers.
|
||||
free(untag_address(y));
|
||||
// New pointers are not tagged.
|
||||
x = malloc(1);
|
||||
EXPECT_EQ(untag_address(x), x);
|
||||
free(x);
|
||||
|
||||
// Switching back to checked mode is not possible.
|
||||
tag_level = M_HEAP_TAGGING_LEVEL_TBI;
|
||||
EXPECT_FALSE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
|
||||
// We remain in the unchecked mode.
|
||||
x = malloc(1);
|
||||
EXPECT_EQ(untag_address(x), x);
|
||||
free(x);
|
||||
#endif // defined(__aarch64__)
|
||||
}
|
Loading…
Reference in a new issue