summaryrefslogtreecommitdiff
path: root/tests/malloc_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/malloc_test.cpp')
-rw-r--r--tests/malloc_test.cpp189
1 files changed, 126 insertions, 63 deletions
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index bd0daddbd..d73f2436d 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -20,6 +20,7 @@
#include <limits.h>
#include <malloc.h>
#include <pthread.h>
+#include <semaphore.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
@@ -31,8 +32,10 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <algorithm>
#include <atomic>
#include <thread>
+#include <vector>
#include <tinyxml2.h>
@@ -43,9 +46,10 @@
#if defined(__BIONIC__)
#include "SignalUtils.h"
+#include "dlext_private.h"
#include "platform/bionic/malloc.h"
-#include "platform/bionic/mte_kernel.h"
+#include "platform/bionic/mte.h"
#include "platform/bionic/reserved_signals.h"
#include "private/bionic_config.h"
@@ -84,6 +88,24 @@ TEST(malloc, calloc_std) {
free(ptr);
}
+TEST(malloc, calloc_mem_init_disabled) {
+#if defined(__BIONIC__)
+ // calloc should still zero memory if mem-init is disabled.
+ // With jemalloc the mallopts will fail but that shouldn't affect the
+ // execution of the test.
+ mallopt(M_THREAD_DISABLE_MEM_INIT, 1);
+ size_t alloc_len = 100;
+ char *ptr = reinterpret_cast<char*>(calloc(1, alloc_len));
+ for (size_t i = 0; i < alloc_len; i++) {
+ ASSERT_EQ(0, ptr[i]);
+ }
+ free(ptr);
+ mallopt(M_THREAD_DISABLE_MEM_INIT, 0);
+#else
+ GTEST_SKIP() << "bionic-only test";
+#endif
+}
+
TEST(malloc, calloc_illegal) {
SKIP_WITH_HWASAN;
errno = 0;
@@ -905,12 +927,8 @@ TEST(malloc, DISABLED_alloc_after_fork) {
std::thread* t = new std::thread([&stop] {
while (!stop) {
for (size_t size = kMinAllocationSize; size <= kMaxAllocationSize; size <<= 1) {
- void* ptr = malloc(size);
- if (ptr == nullptr) {
- return;
- }
- // Make sure this value is not optimized away.
- asm volatile("" : : "r,m"(ptr) : "memory");
+ void* ptr;
+ DoNotOptimize(ptr = malloc(size));
free(ptr);
}
}
@@ -923,10 +941,9 @@ TEST(malloc, DISABLED_alloc_after_fork) {
pid_t pid;
if ((pid = fork()) == 0) {
for (size_t size = kMinAllocationSize; size <= kMaxAllocationSize; size <<= 1) {
- void* ptr = malloc(size);
+ void* ptr;
+ DoNotOptimize(ptr = malloc(size));
ASSERT_TRUE(ptr != nullptr);
- // Make sure this value is not optimized away.
- asm volatile("" : : "r,m"(ptr) : "memory");
// Make sure we can touch all of the allocation.
memset(ptr, 0x1, size);
ASSERT_LE(size, malloc_usable_size(ptr));
@@ -1242,69 +1259,115 @@ TEST(android_mallopt, set_allocation_limit_multiple_threads) {
#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);
+void TestHeapZeroing(int num_iterations, int (*get_alloc_size)(int iteration)) {
+ std::vector<void*> allocs;
+ constexpr int kMaxBytesToCheckZero = 64;
+ const char kBlankMemory[kMaxBytesToCheckZero] = {};
+
+ for (int i = 0; i < num_iterations; ++i) {
+ int size = get_alloc_size(i);
+ allocs.push_back(malloc(size));
+ memset(allocs.back(), 'X', std::min(size, kMaxBytesToCheckZero));
+ }
+
+ for (void* alloc : allocs) {
+ free(alloc);
+ }
+ allocs.clear();
+
+ for (int i = 0; i < num_iterations; ++i) {
+ int size = get_alloc_size(i);
+ allocs.push_back(malloc(size));
+ ASSERT_EQ(0, memcmp(allocs.back(), kBlankMemory, std::min(size, kMaxBytesToCheckZero)));
+ }
+
+ for (void* alloc : allocs) {
+ free(alloc);
}
- _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;
+TEST(malloc, zero_init) {
+#if defined(__BIONIC__)
+ SKIP_WITH_HWASAN << "hwasan does not implement mallopt";
+ bool allocator_scudo;
+ GetAllocatorVersion(&allocator_scudo);
+ if (!allocator_scudo) {
+ GTEST_SKIP() << "scudo allocator only test";
}
- 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;
+ mallopt(M_BIONIC_ZERO_INIT, 1);
+
+ // Test using a block of 4K small (1-32 byte) allocations.
+ TestHeapZeroing(/* num_iterations */ 0x1000, [](int iteration) -> int {
+ return 1 + iteration % 32;
+ });
+
+ // Also test large allocations that land in the scudo secondary, as this is
+ // the only part of Scudo that's changed by enabling zero initialization with
+ // MTE. Uses 32 allocations, totalling 60MiB memory. Decay time (time to
+ // release secondary allocations back to the OS) was modified to 0ms/1ms by
+ // mallopt_decay. Ensure that we delay for at least a second before releasing
+ // pages to the OS in order to avoid implicit zeroing by the kernel.
+ mallopt(M_DECAY_TIME, 1000);
+ TestHeapZeroing(/* num_iterations */ 32, [](int iteration) -> int {
+ return 1 << (19 + iteration % 4);
+ });
+
+#else
+ GTEST_SKIP() << "bionic-only test";
+#endif
}
+
+// Note that MTE is enabled on cc_tests on devices that support MTE.
+TEST(malloc, disable_mte) {
+#if defined(__BIONIC__)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "This function can only be tested with MTE";
+ }
+
+ sem_t sem;
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(
+ &thread, nullptr,
+ [](void* ptr) -> void* {
+ auto* sem = reinterpret_cast<sem_t*>(ptr);
+ sem_wait(sem);
+ return reinterpret_cast<void*>(prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0));
+ },
+ &sem));
+
+ ASSERT_EQ(1, mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_NONE));
+ ASSERT_EQ(0, sem_post(&sem));
+
+ int my_tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ ASSERT_EQ(PR_MTE_TCF_NONE, my_tagged_addr_ctrl & PR_MTE_TCF_MASK);
+
+ void* retval;
+ ASSERT_EQ(0, pthread_join(thread, &retval));
+ int thread_tagged_addr_ctrl = reinterpret_cast<uintptr_t>(retval);
+ ASSERT_EQ(my_tagged_addr_ctrl, thread_tagged_addr_ctrl);
+#else
+ GTEST_SKIP() << "bionic extension";
#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";
- return;
+TEST(malloc, allocation_slack) {
+#if defined(__BIONIC__)
+ bool allocator_scudo;
+ GetAllocatorVersion(&allocator_scudo);
+ if (!allocator_scudo) {
+ GTEST_SKIP() << "scudo allocator only test";
}
- 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)));
+ // Test that older target SDK levels let you access a few bytes off the end of
+ // a large allocation.
+ android_set_application_target_sdk_version(29);
+ auto p = std::make_unique<char[]>(131072);
+ volatile char *vp = p.get();
+ volatile char oob ATTRIBUTE_UNUSED = vp[131072];
#else
- GTEST_SKIP() << "arm64 only";
+ GTEST_SKIP() << "bionic extension";
#endif
}