summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2021-08-02 01:09:58 -0700
committerLinux Build Service Account <lnxbuild@localhost>2021-08-02 01:09:58 -0700
commit555d228b48c09d9372395a9f66f4788e65782d6d (patch)
treede8c191b12ad526f3d50641b42b3926aafbec985
parentcfa63efcf51b81feb41d4c97cce98ae1c337e4b3 (diff)
parent7290ff7cfa79abad5f0a3b2d77383eea2ec90077 (diff)
Merge 7290ff7cfa79abad5f0a3b2d77383eea2ec90077 on remote branch
Change-Id: Ic055a046e8aaed75b4cad2c1f7d3704b63478bf9
-rw-r--r--libc/bionic/heap_tagging.cpp7
-rw-r--r--libc/bionic/libc_init_static.cpp6
-rw-r--r--libc/include/malloc.h43
-rw-r--r--tests/heap_tagging_level_test.cpp17
-rw-r--r--tests/mte_test.cpp2
-rw-r--r--tests/utils.h11
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
+}