diff options
-rw-r--r-- | libc/bionic/heap_tagging.cpp | 7 | ||||
-rw-r--r-- | libc/bionic/libc_init_static.cpp | 6 | ||||
-rw-r--r-- | libc/include/malloc.h | 43 | ||||
-rw-r--r-- | tests/heap_tagging_level_test.cpp | 17 | ||||
-rw-r--r-- | tests/mte_test.cpp | 2 | ||||
-rw-r--r-- | tests/utils.h | 11 |
6 files changed, 72 insertions, 14 deletions
diff --git a/libc/bionic/heap_tagging.cpp b/libc/bionic/heap_tagging.cpp index ffbabb9a0..41aa20507 100644 --- a/libc/bionic/heap_tagging.cpp +++ b/libc/bionic/heap_tagging.cpp @@ -139,7 +139,12 @@ bool SetHeapTaggingLevel(HeapTaggingLevel tag_level) { } if (tag_level == M_HEAP_TAGGING_LEVEL_ASYNC) { - set_tcf_on_all_threads(PR_MTE_TCF_ASYNC); + // When entering ASYNC mode, specify that we want to allow upgrading to SYNC by OR'ing in + // the SYNC flag. But if the kernel doesn't support specifying multiple TCF modes, fall back + // to specifying a single mode. + if (!set_tcf_on_all_threads(PR_MTE_TCF_ASYNC | PR_MTE_TCF_SYNC)) { + set_tcf_on_all_threads(PR_MTE_TCF_ASYNC); + } #if defined(USE_SCUDO) scudo_malloc_set_track_allocation_stacks(0); #endif diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp index 069ebb0ab..3a8513f98 100644 --- a/libc/bionic/libc_init_static.cpp +++ b/libc/bionic/libc_init_static.cpp @@ -311,7 +311,11 @@ __attribute__((no_sanitize("hwaddress", "memtag"))) void __libc_init_mte(const v unsigned long prctl_arg = PR_TAGGED_ADDR_ENABLE | PR_MTE_TAG_SET_NONZERO; prctl_arg |= (level == M_HEAP_TAGGING_LEVEL_SYNC) ? PR_MTE_TCF_SYNC : PR_MTE_TCF_ASYNC; - if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg, 0, 0, 0) == 0) { + // When entering ASYNC mode, specify that we want to allow upgrading to SYNC by OR'ing in the + // SYNC flag. But if the kernel doesn't support specifying multiple TCF modes, fall back to + // specifying a single mode. + if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg | PR_MTE_TCF_SYNC, 0, 0, 0) == 0 || + prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg, 0, 0, 0) == 0) { __libc_shared_globals()->initial_heap_tagging_level = level; return; } diff --git a/libc/include/malloc.h b/libc/include/malloc.h index bae1f6823..f7beb2c10 100644 --- a/libc/include/malloc.h +++ b/libc/include/malloc.h @@ -170,7 +170,45 @@ int malloc_info(int __must_be_zero, FILE* __fp) __INTRODUCED_IN(23); * Available since API level 28. */ #define M_PURGE (-101) -/* + + +/** + * mallopt() option to tune the allocator's choice of memory tags to + * make it more likely that a certain class of memory errors will be + * detected. This is only relevant if MTE is enabled in this process + * and ignored otherwise. The value argument should be one of the + * M_MEMTAG_TUNING_* flags. + * NOTE: This is only available in scudo. + * + * Available since API level 31. + */ +#define M_MEMTAG_TUNING (-102) + +/** + * When passed as a value of M_MEMTAG_TUNING mallopt() call, enables + * deterministic detection of linear buffer overflow and underflow + * bugs by assigning distinct tag values to adjacent allocations. This + * mode has a slightly reduced chance to detect use-after-free bugs + * because only half of the possible tag values are available for each + * memory location. + * + * Please keep in mind that MTE can not detect overflow within the + * same tag granule (16-byte aligned chunk), and can miss small + * overflows even in this mode. Such overflow can not be the cause of + * a memory corruption, because the memory within one granule is never + * used for multiple allocations. + */ +#define M_MEMTAG_TUNING_BUFFER_OVERFLOW 0 + +/** + * When passed as a value of M_MEMTAG_TUNING mallopt() call, enables + * independently randomized tags for uniform ~93% probability of + * detecting both spatial (buffer overflow) and temporal (use after + * free) bugs. + */ +#define M_MEMTAG_TUNING_UAF 1 + +/** * mallopt() option for per-thread memory initialization tuning. * The value argument should be one of: * 1: Disable automatic heap initialization and, where possible, memory tagging, @@ -210,7 +248,7 @@ int malloc_info(int __must_be_zero, FILE* __fp) __INTRODUCED_IN(23); * should not be zero-initialized, any other value indicates to initialize heap * memory to zero. * - * Note that this memory mitigations is only implemented in scudo and therefore + * Note that this memory mitigation is only implemented in scudo and therefore * this will have no effect when using another allocator (such as jemalloc on * Android Go devices). * @@ -222,6 +260,7 @@ int malloc_info(int __must_be_zero, FILE* __fp) __INTRODUCED_IN(23); * mallopt() option to change the heap tagging state. May be called at any * time, including when multiple threads are running. * The value must be one of the M_HEAP_TAGGING_LEVEL_ constants. + * NOTE: This is only available in scudo. * * Available since API level 31. */ diff --git a/tests/heap_tagging_level_test.cpp b/tests/heap_tagging_level_test.cpp index b3c8f22a9..5f5904f9a 100644 --- a/tests/heap_tagging_level_test.cpp +++ b/tests/heap_tagging_level_test.cpp @@ -86,16 +86,15 @@ void ExitWithSiCode(int, siginfo_t* info, void*) { TEST(heap_tagging_level, sync_async_bad_accesses_die) { #if defined(__BIONIC__) && defined(__aarch64__) - if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) { - GTEST_SKIP() << "requires MTE support"; + if (!mte_supported() || !running_with_mte()) { + GTEST_SKIP() << "requires MTE to be enabled"; } std::unique_ptr<int[]> p = std::make_unique<int[]>(4); - // First, check that memory tagging is enabled and the default tag checking level is sync. - // cc_test targets get sync MTE by default. // We assume that scudo is used on all MTE enabled hardware; scudo inserts a header with a // mismatching tag before each allocation. + EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC)); EXPECT_EXIT( { ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO); @@ -136,7 +135,7 @@ TEST(heap_tagging_level, tagging_level_transitions) { EXPECT_FALSE(SetHeapTaggingLevel(static_cast<HeapTaggingLevel>(12345))); - if (mte_supported()) { + if (mte_supported() && running_with_mte()) { // ASYNC -> ... EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI)); EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC)); @@ -146,14 +145,14 @@ TEST(heap_tagging_level, tagging_level_transitions) { 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 { + } else if (!mte_supported()) { // 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. + // TBI -> NONE on non-MTE, ASYNC|SYNC|NONE -> NONE on MTE. EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE)); // NONE -> ... @@ -170,8 +169,8 @@ 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 (!mte_supported() || !running_with_mte()) { + GTEST_SKIP() << "requires MTE to be enabled"; } EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC)); diff --git a/tests/mte_test.cpp b/tests/mte_test.cpp index f329d8d70..ade95326c 100644 --- a/tests/mte_test.cpp +++ b/tests/mte_test.cpp @@ -38,7 +38,7 @@ static void test_tag_mismatch() { #endif } #if defined(__aarch64__) - if (mte_supported()) { + if (mte_supported() && running_with_mte()) { EXPECT_DEATH( { volatile int load ATTRIBUTE_UNUSED = *mistagged_p; diff --git a/tests/utils.h b/tests/utils.h index 145ba1ac2..592ac0e56 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -21,6 +21,7 @@ #include <fcntl.h> #include <inttypes.h> #include <sys/mman.h> +#include <sys/prctl.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> @@ -304,3 +305,13 @@ template <class Tp> static inline void DoNotOptimize(Tp& value) { asm volatile("" : "+r,m"(value) : : "memory"); } + +static inline bool running_with_mte() { +#ifdef __aarch64__ + int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); + return level >= 0 && (level & PR_TAGGED_ADDR_ENABLE) && + (level & PR_MTE_TCF_MASK) != PR_MTE_TCF_NONE; +#else + return false; +#endif +} |