Force particular malloc tests to use _FORTIFY_SOURCE=2
We plan to make _FORTIFY_SOURCE=3 global on device. Some malloc tests
use malloc_usable_size so we separated them out and forced them to use
_FORTIFY_SOURCE=2.
Bug: 291762537
Test: Presubmit plus I ran the below commands
- atest malloc
- atest malloc_iterate
- atest MallocHooksTest
Change-Id: Ie9e0cbc5f726c583c1bbdcae8105b8256b9c8887
diff --git a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp
index 39cd645..ff94755 100644
--- a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp
+++ b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp
@@ -26,6 +26,13 @@
* SUCH DAMAGE.
*/
+// (b/291762537): This code uses malloc_usable_size(), and thus can't be
+// built with _FORTIFY_SOURCE=3.
+#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE == 3
+#undef _FORTIFY_SOURCE
+#define _FORTIFY_SOURCE 2
+#endif
+
#include <fcntl.h>
#include <malloc.h>
#include <stdlib.h>
diff --git a/tests/Android.bp b/tests/Android.bp
index 34229ce..2f44553 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -432,6 +432,7 @@
"locale_test.cpp",
"malloc_iterate_test.cpp",
"malloc_test.cpp",
+ "malloc_test_with_usable_size.cpp",
"math_test.cpp",
"membarrier_test.cpp",
"memtag_globals_test.cpp",
diff --git a/tests/NOTICE b/tests/NOTICE
index de95698..accde44 100644
--- a/tests/NOTICE
+++ b/tests/NOTICE
@@ -482,3 +482,31 @@
-------------------------------------------------------------------
+Copyright (C) 2025 The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
diff --git a/tests/malloc_iterate_test.cpp b/tests/malloc_iterate_test.cpp
index 297f637..4146c57 100644
--- a/tests/malloc_iterate_test.cpp
+++ b/tests/malloc_iterate_test.cpp
@@ -14,6 +14,13 @@
* limitations under the License.
*/
+// (b/291762537): This code uses malloc_usable_size(), and thus can't be
+// built with _FORTIFY_SOURCE=3.
+#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE == 3
+#undef _FORTIFY_SOURCE
+#define _FORTIFY_SOURCE 2
+#endif
+
#include <gtest/gtest.h>
#if defined(__BIONIC__)
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index db814dc..5c6db4b 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -72,14 +72,6 @@
#endif
-TEST(malloc, malloc_std) {
- // Simple malloc test.
- void *ptr = malloc(100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(100U, malloc_usable_size(ptr));
- free(ptr);
-}
-
TEST(malloc, malloc_overflow) {
SKIP_WITH_HWASAN;
errno = 0;
@@ -87,18 +79,6 @@
ASSERT_ERRNO(ENOMEM);
}
-TEST(malloc, calloc_std) {
- // Simple calloc test.
- size_t alloc_len = 100;
- char *ptr = (char *)calloc(1, alloc_len);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(alloc_len, malloc_usable_size(ptr));
- for (size_t i = 0; i < alloc_len; i++) {
- ASSERT_EQ(0, ptr[i]);
- }
- free(ptr);
-}
-
TEST(malloc, calloc_mem_init_disabled) {
#if defined(__BIONIC__)
// calloc should still zero memory if mem-init is disabled.
@@ -140,21 +120,6 @@
ASSERT_ERRNO(ENOMEM);
}
-TEST(malloc, memalign_multiple) {
- SKIP_WITH_HWASAN << "hwasan requires power of 2 alignment";
- // Memalign test where the alignment is any value.
- for (size_t i = 0; i <= 12; i++) {
- for (size_t alignment = 1 << i; alignment < (1U << (i+1)); alignment++) {
- char *ptr = reinterpret_cast<char*>(memalign(alignment, 100));
- ASSERT_TRUE(ptr != nullptr) << "Failed at alignment " << alignment;
- ASSERT_LE(100U, malloc_usable_size(ptr)) << "Failed at alignment " << alignment;
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % ((1U << i)))
- << "Failed at alignment " << alignment;
- free(ptr);
- }
- }
-}
-
TEST(malloc, memalign_overflow) {
SKIP_WITH_HWASAN;
ASSERT_EQ(nullptr, memalign(4096, SIZE_MAX));
@@ -170,179 +135,6 @@
}
}
-TEST(malloc, memalign_realloc) {
- // Memalign and then realloc the pointer a couple of times.
- for (size_t alignment = 1; alignment <= 4096; alignment <<= 1) {
- char *ptr = (char*)memalign(alignment, 100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(100U, malloc_usable_size(ptr));
- ASSERT_EQ(0U, (intptr_t)ptr % alignment);
- memset(ptr, 0x23, 100);
-
- ptr = (char*)realloc(ptr, 200);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(200U, malloc_usable_size(ptr));
- ASSERT_TRUE(ptr != nullptr);
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(0x23, ptr[i]);
- }
- memset(ptr, 0x45, 200);
-
- ptr = (char*)realloc(ptr, 300);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(300U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 200; i++) {
- ASSERT_EQ(0x45, ptr[i]);
- }
- memset(ptr, 0x67, 300);
-
- ptr = (char*)realloc(ptr, 250);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(250U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 250; i++) {
- ASSERT_EQ(0x67, ptr[i]);
- }
- free(ptr);
- }
-}
-
-TEST(malloc, malloc_realloc_larger) {
- // Realloc to a larger size, malloc is used for the original allocation.
- char *ptr = (char *)malloc(100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(100U, malloc_usable_size(ptr));
- memset(ptr, 67, 100);
-
- ptr = (char *)realloc(ptr, 200);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(200U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(67, ptr[i]);
- }
- free(ptr);
-}
-
-TEST(malloc, malloc_realloc_smaller) {
- // Realloc to a smaller size, malloc is used for the original allocation.
- char *ptr = (char *)malloc(200);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(200U, malloc_usable_size(ptr));
- memset(ptr, 67, 200);
-
- ptr = (char *)realloc(ptr, 100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(100U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(67, ptr[i]);
- }
- free(ptr);
-}
-
-TEST(malloc, malloc_multiple_realloc) {
- // Multiple reallocs, malloc is used for the original allocation.
- char *ptr = (char *)malloc(200);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(200U, malloc_usable_size(ptr));
- memset(ptr, 0x23, 200);
-
- ptr = (char *)realloc(ptr, 100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(100U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(0x23, ptr[i]);
- }
-
- ptr = (char*)realloc(ptr, 50);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(50U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 50; i++) {
- ASSERT_EQ(0x23, ptr[i]);
- }
-
- ptr = (char*)realloc(ptr, 150);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(150U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 50; i++) {
- ASSERT_EQ(0x23, ptr[i]);
- }
- memset(ptr, 0x23, 150);
-
- ptr = (char*)realloc(ptr, 425);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(425U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 150; i++) {
- ASSERT_EQ(0x23, ptr[i]);
- }
- free(ptr);
-}
-
-TEST(malloc, calloc_realloc_larger) {
- // Realloc to a larger size, calloc is used for the original allocation.
- char *ptr = (char *)calloc(1, 100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(100U, malloc_usable_size(ptr));
-
- ptr = (char *)realloc(ptr, 200);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(200U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(0, ptr[i]);
- }
- free(ptr);
-}
-
-TEST(malloc, calloc_realloc_smaller) {
- // Realloc to a smaller size, calloc is used for the original allocation.
- char *ptr = (char *)calloc(1, 200);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(200U, malloc_usable_size(ptr));
-
- ptr = (char *)realloc(ptr, 100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(100U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(0, ptr[i]);
- }
- free(ptr);
-}
-
-TEST(malloc, calloc_multiple_realloc) {
- // Multiple reallocs, calloc is used for the original allocation.
- char *ptr = (char *)calloc(1, 200);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(200U, malloc_usable_size(ptr));
-
- ptr = (char *)realloc(ptr, 100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(100U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(0, ptr[i]);
- }
-
- ptr = (char*)realloc(ptr, 50);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(50U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 50; i++) {
- ASSERT_EQ(0, ptr[i]);
- }
-
- ptr = (char*)realloc(ptr, 150);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(150U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 50; i++) {
- ASSERT_EQ(0, ptr[i]);
- }
- memset(ptr, 0, 150);
-
- ptr = (char*)realloc(ptr, 425);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_LE(425U, malloc_usable_size(ptr));
- for (size_t i = 0; i < 150; i++) {
- ASSERT_EQ(0, ptr[i]);
- }
- free(ptr);
-}
-
TEST(malloc, realloc_overflow) {
SKIP_WITH_HWASAN;
errno = 0;
@@ -361,19 +153,6 @@
extern "C" void* valloc(size_t);
#endif
-TEST(malloc, pvalloc_std) {
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
- size_t pagesize = sysconf(_SC_PAGESIZE);
- void* ptr = pvalloc(100);
- ASSERT_TRUE(ptr != nullptr);
- ASSERT_TRUE((reinterpret_cast<uintptr_t>(ptr) & (pagesize-1)) == 0);
- ASSERT_LE(pagesize, malloc_usable_size(ptr));
- free(ptr);
-#else
- GTEST_SKIP() << "pvalloc not supported.";
-#endif
-}
-
TEST(malloc, pvalloc_overflow) {
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
ASSERT_EQ(nullptr, pvalloc(SIZE_MAX));
@@ -538,25 +317,6 @@
#endif
}
-TEST(malloc, calloc_usable_size) {
- for (size_t size = 1; size <= 2048; size++) {
- void* pointer = malloc(size);
- ASSERT_TRUE(pointer != nullptr);
- memset(pointer, 0xeb, malloc_usable_size(pointer));
- free(pointer);
-
- // We should get a previous pointer that has been set to non-zero.
- // If calloc does not zero out all of the data, this will fail.
- uint8_t* zero_mem = reinterpret_cast<uint8_t*>(calloc(1, size));
- ASSERT_TRUE(pointer != nullptr);
- size_t usable_size = malloc_usable_size(zero_mem);
- for (size_t i = 0; i < usable_size; i++) {
- ASSERT_EQ(0, zero_mem[i]) << "Failed at allocation size " << size << " at byte " << i;
- }
- free(zero_mem);
- }
-}
-
TEST(malloc, malloc_0) {
void* p = malloc(0);
ASSERT_TRUE(p != nullptr);
@@ -808,132 +568,6 @@
#endif
}
-TEST(malloc, reallocarray) {
-#if HAVE_REALLOCARRAY
- void* p = reallocarray(nullptr, 2, 32);
- ASSERT_TRUE(p != nullptr);
- ASSERT_GE(malloc_usable_size(p), 64U);
-#else
- GTEST_SKIP() << "reallocarray not available";
-#endif
-}
-
-TEST(malloc, mallinfo) {
-#if defined(__BIONIC__) || defined(ANDROID_HOST_MUSL)
- SKIP_WITH_HWASAN << "hwasan does not implement mallinfo";
- static size_t sizes[] = {
- 8, 32, 128, 4096, 32768, 131072, 1024000, 10240000, 20480000, 300000000
- };
-
- static constexpr size_t kMaxAllocs = 50;
-
- for (size_t size : sizes) {
- // If some of these allocations are stuck in a thread cache, then keep
- // looping until we make an allocation that changes the total size of the
- // memory allocated.
- // jemalloc implementations counts the thread cache allocations against
- // total memory allocated.
- void* ptrs[kMaxAllocs] = {};
- bool pass = false;
- for (size_t i = 0; i < kMaxAllocs; i++) {
- size_t allocated = mallinfo().uordblks;
- ptrs[i] = malloc(size);
- ASSERT_TRUE(ptrs[i] != nullptr);
- size_t new_allocated = mallinfo().uordblks;
- if (allocated != new_allocated) {
- size_t usable_size = malloc_usable_size(ptrs[i]);
- // Only check if the total got bigger by at least allocation size.
- // Sometimes the mallinfo numbers can go backwards due to compaction
- // and/or freeing of cached data.
- if (new_allocated >= allocated + usable_size) {
- pass = true;
- break;
- }
- }
- }
- for (void* ptr : ptrs) {
- free(ptr);
- }
- ASSERT_TRUE(pass)
- << "For size " << size << " allocated bytes did not increase after "
- << kMaxAllocs << " allocations.";
- }
-#else
- GTEST_SKIP() << "glibc is broken";
-#endif
-}
-
-TEST(malloc, mallinfo2) {
-#if defined(__BIONIC__) || defined(ANDROID_HOST_MUSL)
- SKIP_WITH_HWASAN << "hwasan does not implement mallinfo2";
- static size_t sizes[] = {8, 32, 128, 4096, 32768, 131072, 1024000, 10240000, 20480000, 300000000};
-
- static constexpr size_t kMaxAllocs = 50;
-
- for (size_t size : sizes) {
- // If some of these allocations are stuck in a thread cache, then keep
- // looping until we make an allocation that changes the total size of the
- // memory allocated.
- // jemalloc implementations counts the thread cache allocations against
- // total memory allocated.
- void* ptrs[kMaxAllocs] = {};
- bool pass = false;
- for (size_t i = 0; i < kMaxAllocs; i++) {
- struct mallinfo info = mallinfo();
- struct mallinfo2 info2 = mallinfo2();
- // Verify that mallinfo and mallinfo2 are exactly the same.
- ASSERT_EQ(static_cast<size_t>(info.arena), info2.arena);
- ASSERT_EQ(static_cast<size_t>(info.ordblks), info2.ordblks);
- ASSERT_EQ(static_cast<size_t>(info.smblks), info2.smblks);
- ASSERT_EQ(static_cast<size_t>(info.hblks), info2.hblks);
- ASSERT_EQ(static_cast<size_t>(info.hblkhd), info2.hblkhd);
- ASSERT_EQ(static_cast<size_t>(info.usmblks), info2.usmblks);
- ASSERT_EQ(static_cast<size_t>(info.fsmblks), info2.fsmblks);
- ASSERT_EQ(static_cast<size_t>(info.uordblks), info2.uordblks);
- ASSERT_EQ(static_cast<size_t>(info.fordblks), info2.fordblks);
- ASSERT_EQ(static_cast<size_t>(info.keepcost), info2.keepcost);
-
- size_t allocated = info2.uordblks;
- ptrs[i] = malloc(size);
- ASSERT_TRUE(ptrs[i] != nullptr);
-
- info = mallinfo();
- info2 = mallinfo2();
- // Verify that mallinfo and mallinfo2 are exactly the same.
- ASSERT_EQ(static_cast<size_t>(info.arena), info2.arena);
- ASSERT_EQ(static_cast<size_t>(info.ordblks), info2.ordblks);
- ASSERT_EQ(static_cast<size_t>(info.smblks), info2.smblks);
- ASSERT_EQ(static_cast<size_t>(info.hblks), info2.hblks);
- ASSERT_EQ(static_cast<size_t>(info.hblkhd), info2.hblkhd);
- ASSERT_EQ(static_cast<size_t>(info.usmblks), info2.usmblks);
- ASSERT_EQ(static_cast<size_t>(info.fsmblks), info2.fsmblks);
- ASSERT_EQ(static_cast<size_t>(info.uordblks), info2.uordblks);
- ASSERT_EQ(static_cast<size_t>(info.fordblks), info2.fordblks);
- ASSERT_EQ(static_cast<size_t>(info.keepcost), info2.keepcost);
-
- size_t new_allocated = info2.uordblks;
- if (allocated != new_allocated) {
- size_t usable_size = malloc_usable_size(ptrs[i]);
- // Only check if the total got bigger by at least allocation size.
- // Sometimes the mallinfo2 numbers can go backwards due to compaction
- // and/or freeing of cached data.
- if (new_allocated >= allocated + usable_size) {
- pass = true;
- break;
- }
- }
- }
- for (void* ptr : ptrs) {
- free(ptr);
- }
- ASSERT_TRUE(pass) << "For size " << size << " allocated bytes did not increase after "
- << kMaxAllocs << " allocations.";
- }
-#else
- GTEST_SKIP() << "glibc is broken";
-#endif
-}
-
template <typename Type>
void __attribute__((optnone)) VerifyAlignment(Type* floating) {
size_t expected_alignment = alignof(Type);
@@ -1065,69 +699,6 @@
AlignCheck();
}
-// Jemalloc doesn't pass this test right now, so leave it as disabled.
-TEST(malloc, DISABLED_alloc_after_fork) {
- // Both of these need to be a power of 2.
- static constexpr size_t kMinAllocationSize = 8;
- static constexpr size_t kMaxAllocationSize = 2097152;
-
- static constexpr size_t kNumAllocatingThreads = 5;
- static constexpr size_t kNumForkLoops = 100;
-
- std::atomic_bool stop;
-
- // Create threads that simply allocate and free different sizes.
- std::vector<std::thread*> threads;
- for (size_t i = 0; i < kNumAllocatingThreads; i++) {
- std::thread* t = new std::thread([&stop] {
- while (!stop) {
- for (size_t size = kMinAllocationSize; size <= kMaxAllocationSize; size <<= 1) {
- void* ptr;
- DoNotOptimize(ptr = malloc(size));
- free(ptr);
- }
- }
- });
- threads.push_back(t);
- }
-
- // Create a thread to fork and allocate.
- for (size_t i = 0; i < kNumForkLoops; i++) {
- pid_t pid;
- if ((pid = fork()) == 0) {
- for (size_t size = kMinAllocationSize; size <= kMaxAllocationSize; size <<= 1) {
- void* ptr;
- DoNotOptimize(ptr = malloc(size));
- ASSERT_TRUE(ptr != nullptr);
- // Make sure we can touch all of the allocation.
- memset(ptr, 0x1, size);
- ASSERT_LE(size, malloc_usable_size(ptr));
- free(ptr);
- }
- _exit(10);
- }
- ASSERT_NE(-1, pid);
- AssertChildExited(pid, 10);
- }
-
- stop = true;
- for (auto thread : threads) {
- thread->join();
- delete thread;
- }
-}
-
-TEST(android_mallopt, error_on_unexpected_option) {
-#if defined(__BIONIC__)
- const int unrecognized_option = -1;
- errno = 0;
- EXPECT_EQ(false, android_mallopt(unrecognized_option, nullptr, 0));
- EXPECT_ERRNO(ENOTSUP);
-#else
- GTEST_SKIP() << "bionic-only test";
-#endif
-}
-
bool IsDynamic() {
#if defined(__LP64__)
Elf64_Ehdr ehdr;
@@ -1582,167 +1153,6 @@
}
}
-void VerifyAllocationsAreZero(std::function<void*(size_t)> alloc_func, std::string function_name,
- std::vector<size_t>& test_sizes, size_t max_allocations) {
- // Vector of zero'd data used for comparisons. Make it twice the largest size.
- std::vector<char> zero(test_sizes.back() * 2, 0);
-
- SCOPED_TRACE(testing::Message() << function_name << " failed to zero memory");
-
- for (size_t test_size : test_sizes) {
- std::vector<void*> ptrs(max_allocations);
- for (size_t i = 0; i < ptrs.size(); i++) {
- SCOPED_TRACE(testing::Message() << "size " << test_size << " at iteration " << i);
- ptrs[i] = alloc_func(test_size);
- ASSERT_TRUE(ptrs[i] != nullptr);
- size_t alloc_size = malloc_usable_size(ptrs[i]);
- ASSERT_LE(alloc_size, zero.size());
- ASSERT_EQ(0, memcmp(ptrs[i], zero.data(), alloc_size));
-
- // Set the memory to non-zero to make sure if the pointer
- // is reused it's still zero.
- memset(ptrs[i], 0xab, alloc_size);
- }
- // Free the pointers.
- for (size_t i = 0; i < ptrs.size(); i++) {
- free(ptrs[i]);
- }
- for (size_t i = 0; i < ptrs.size(); i++) {
- SCOPED_TRACE(testing::Message() << "size " << test_size << " at iteration " << i);
- ptrs[i] = malloc(test_size);
- ASSERT_TRUE(ptrs[i] != nullptr);
- size_t alloc_size = malloc_usable_size(ptrs[i]);
- ASSERT_LE(alloc_size, zero.size());
- ASSERT_EQ(0, memcmp(ptrs[i], zero.data(), alloc_size));
- }
- // Free all of the pointers later to maximize the chance of reusing from
- // the first loop.
- for (size_t i = 0; i < ptrs.size(); i++) {
- free(ptrs[i]);
- }
- }
-}
-
-// Verify that small and medium allocations are always zero.
-// @CddTest = 9.7/C-4-1
-TEST(malloc, zeroed_allocations_small_medium_sizes) {
-#if !defined(__BIONIC__)
- GTEST_SKIP() << "Only valid on bionic";
-#endif
- SKIP_WITH_HWASAN << "Only test system allocator, not hwasan allocator.";
-
- if (IsLowRamDevice()) {
- GTEST_SKIP() << "Skipped on low memory devices.";
- }
-
- constexpr size_t kMaxAllocations = 1024;
- std::vector<size_t> test_sizes = {16, 48, 128, 1024, 4096, 65536};
- VerifyAllocationsAreZero([](size_t size) -> void* { return malloc(size); }, "malloc", test_sizes,
- kMaxAllocations);
-
- VerifyAllocationsAreZero([](size_t size) -> void* { return memalign(64, size); }, "memalign",
- test_sizes, kMaxAllocations);
-
- VerifyAllocationsAreZero(
- [](size_t size) -> void* {
- void* ptr;
- if (posix_memalign(&ptr, 64, size) == 0) {
- return ptr;
- }
- return nullptr;
- },
- "posix_memalign", test_sizes, kMaxAllocations);
-}
-
-// Verify that large allocations are always zero.
-// @CddTest = 9.7/C-4-1
-TEST(malloc, zeroed_allocations_large_sizes) {
-#if !defined(__BIONIC__)
- GTEST_SKIP() << "Only valid on bionic";
-#endif
- SKIP_WITH_HWASAN << "Only test system allocator, not hwasan allocator.";
-
- if (IsLowRamDevice()) {
- GTEST_SKIP() << "Skipped on low memory devices.";
- }
-
- constexpr size_t kMaxAllocations = 20;
- std::vector<size_t> test_sizes = {1000000, 2000000, 3000000, 4000000};
- VerifyAllocationsAreZero([](size_t size) -> void* { return malloc(size); }, "malloc", test_sizes,
- kMaxAllocations);
-
- VerifyAllocationsAreZero([](size_t size) -> void* { return memalign(64, size); }, "memalign",
- test_sizes, kMaxAllocations);
-
- VerifyAllocationsAreZero(
- [](size_t size) -> void* {
- void* ptr;
- if (posix_memalign(&ptr, 64, size) == 0) {
- return ptr;
- }
- return nullptr;
- },
- "posix_memalign", test_sizes, kMaxAllocations);
-}
-
-// Verify that reallocs are zeroed when expanded.
-// @CddTest = 9.7/C-4-1
-TEST(malloc, zeroed_allocations_realloc) {
-#if !defined(__BIONIC__)
- GTEST_SKIP() << "Only valid on bionic";
-#endif
- SKIP_WITH_HWASAN << "Only test system allocator, not hwasan allocator.";
-
- if (IsLowRamDevice()) {
- GTEST_SKIP() << "Skipped on low memory devices.";
- }
-
- // Vector of zero'd data used for comparisons.
- constexpr size_t kMaxMemorySize = 131072;
- std::vector<char> zero(kMaxMemorySize, 0);
-
- constexpr size_t kMaxAllocations = 1024;
- std::vector<size_t> test_sizes = {16, 48, 128, 1024, 4096, 65536};
- // Do a number of allocations and set them to non-zero.
- for (size_t test_size : test_sizes) {
- std::vector<void*> ptrs(kMaxAllocations);
- for (size_t i = 0; i < kMaxAllocations; i++) {
- ptrs[i] = malloc(test_size);
- ASSERT_TRUE(ptrs[i] != nullptr);
-
- // Set the memory to non-zero to make sure if the pointer
- // is reused it's still zero.
- memset(ptrs[i], 0xab, malloc_usable_size(ptrs[i]));
- }
- // Free the pointers.
- for (size_t i = 0; i < kMaxAllocations; i++) {
- free(ptrs[i]);
- }
- }
-
- // Do the reallocs to a larger size and verify the rest of the allocation
- // is zero.
- constexpr size_t kInitialSize = 8;
- for (size_t test_size : test_sizes) {
- std::vector<void*> ptrs(kMaxAllocations);
- for (size_t i = 0; i < kMaxAllocations; i++) {
- ptrs[i] = malloc(kInitialSize);
- ASSERT_TRUE(ptrs[i] != nullptr);
- size_t orig_alloc_size = malloc_usable_size(ptrs[i]);
-
- ptrs[i] = realloc(ptrs[i], test_size);
- ASSERT_TRUE(ptrs[i] != nullptr);
- size_t new_alloc_size = malloc_usable_size(ptrs[i]);
- char* ptr = reinterpret_cast<char*>(ptrs[i]);
- ASSERT_EQ(0, memcmp(&ptr[orig_alloc_size], zero.data(), new_alloc_size - orig_alloc_size))
- << "realloc from " << kInitialSize << " to size " << test_size << " at iteration " << i;
- }
- for (size_t i = 0; i < kMaxAllocations; i++) {
- free(ptrs[i]);
- }
- }
-}
-
TEST(android_mallopt, get_decay_time_enabled_errors) {
#if defined(__BIONIC__)
errno = 0;
diff --git a/tests/malloc_test_with_usable_size.cpp b/tests/malloc_test_with_usable_size.cpp
new file mode 100644
index 0000000..150e599
--- /dev/null
+++ b/tests/malloc_test_with_usable_size.cpp
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+// (b/291762537): This code uses malloc_usable_size(), and thus can't be
+// built with _FORTIFY_SOURCE=3.
+#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE == 3
+#undef _FORTIFY_SOURCE
+#define _FORTIFY_SOURCE 2
+#endif
+
+#include <gtest/gtest.h>
+
+#include <elf.h>
+#include <limits.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/auxv.h>
+#include <sys/cdefs.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <atomic>
+#include <functional>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <tinyxml2.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+
+#include "DoNotOptimize.h"
+#include "utils.h"
+
+#if defined(__BIONIC__)
+
+#include "SignalUtils.h"
+#include "dlext_private_tests.h"
+
+#include "platform/bionic/malloc.h"
+#include "platform/bionic/mte.h"
+#include "platform/bionic/reserved_signals.h"
+#include "private/bionic_config.h"
+
+#define HAVE_REALLOCARRAY 1
+
+#elif defined(__GLIBC__)
+
+#define HAVE_REALLOCARRAY __GLIBC_PREREQ(2, 26)
+
+#elif defined(ANDROID_HOST_MUSL)
+
+#define HAVE_REALLOCARRAY 1
+
+#endif
+
+TEST(malloc, malloc_std) {
+ // Simple malloc test.
+ void* ptr = malloc(100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(100U, malloc_usable_size(ptr));
+ free(ptr);
+}
+
+TEST(malloc, calloc_std) {
+ // Simple calloc test.
+ size_t alloc_len = 100;
+ char* ptr = (char*)calloc(1, alloc_len);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(alloc_len, malloc_usable_size(ptr));
+ for (size_t i = 0; i < alloc_len; i++) {
+ ASSERT_EQ(0, ptr[i]);
+ }
+ free(ptr);
+}
+
+TEST(malloc, memalign_multiple) {
+ SKIP_WITH_HWASAN << "hwasan requires power of 2 alignment";
+ // Memalign test where the alignment is any value.
+ for (size_t i = 0; i <= 12; i++) {
+ for (size_t alignment = 1 << i; alignment < (1U << (i + 1)); alignment++) {
+ char* ptr = reinterpret_cast<char*>(memalign(alignment, 100));
+ ASSERT_TRUE(ptr != nullptr) << "Failed at alignment " << alignment;
+ ASSERT_LE(100U, malloc_usable_size(ptr)) << "Failed at alignment " << alignment;
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % ((1U << i)))
+ << "Failed at alignment " << alignment;
+ free(ptr);
+ }
+ }
+}
+
+TEST(malloc, memalign_realloc) {
+ // Memalign and then realloc the pointer a couple of times.
+ for (size_t alignment = 1; alignment <= 4096; alignment <<= 1) {
+ char* ptr = (char*)memalign(alignment, 100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(100U, malloc_usable_size(ptr));
+ ASSERT_EQ(0U, (intptr_t)ptr % alignment);
+ memset(ptr, 0x23, 100);
+
+ ptr = (char*)realloc(ptr, 200);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(200U, malloc_usable_size(ptr));
+ ASSERT_TRUE(ptr != nullptr);
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(0x23, ptr[i]);
+ }
+ memset(ptr, 0x45, 200);
+
+ ptr = (char*)realloc(ptr, 300);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(300U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 200; i++) {
+ ASSERT_EQ(0x45, ptr[i]);
+ }
+ memset(ptr, 0x67, 300);
+
+ ptr = (char*)realloc(ptr, 250);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(250U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 250; i++) {
+ ASSERT_EQ(0x67, ptr[i]);
+ }
+ free(ptr);
+ }
+}
+
+TEST(malloc, malloc_realloc_larger) {
+ // Realloc to a larger size, malloc is used for the original allocation.
+ char* ptr = (char*)malloc(100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(100U, malloc_usable_size(ptr));
+ memset(ptr, 67, 100);
+
+ ptr = (char*)realloc(ptr, 200);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(200U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(67, ptr[i]);
+ }
+ free(ptr);
+}
+
+TEST(malloc, malloc_realloc_smaller) {
+ // Realloc to a smaller size, malloc is used for the original allocation.
+ char* ptr = (char*)malloc(200);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(200U, malloc_usable_size(ptr));
+ memset(ptr, 67, 200);
+
+ ptr = (char*)realloc(ptr, 100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(100U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(67, ptr[i]);
+ }
+ free(ptr);
+}
+
+TEST(malloc, malloc_multiple_realloc) {
+ // Multiple reallocs, malloc is used for the original allocation.
+ char* ptr = (char*)malloc(200);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(200U, malloc_usable_size(ptr));
+ memset(ptr, 0x23, 200);
+
+ ptr = (char*)realloc(ptr, 100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(100U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(0x23, ptr[i]);
+ }
+
+ ptr = (char*)realloc(ptr, 50);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(50U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 50; i++) {
+ ASSERT_EQ(0x23, ptr[i]);
+ }
+
+ ptr = (char*)realloc(ptr, 150);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(150U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 50; i++) {
+ ASSERT_EQ(0x23, ptr[i]);
+ }
+ memset(ptr, 0x23, 150);
+
+ ptr = (char*)realloc(ptr, 425);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(425U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 150; i++) {
+ ASSERT_EQ(0x23, ptr[i]);
+ }
+ free(ptr);
+}
+
+TEST(malloc, calloc_realloc_larger) {
+ // Realloc to a larger size, calloc is used for the original allocation.
+ char* ptr = (char*)calloc(1, 100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(100U, malloc_usable_size(ptr));
+
+ ptr = (char*)realloc(ptr, 200);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(200U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(0, ptr[i]);
+ }
+ free(ptr);
+}
+
+TEST(malloc, calloc_realloc_smaller) {
+ // Realloc to a smaller size, calloc is used for the original allocation.
+ char* ptr = (char*)calloc(1, 200);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(200U, malloc_usable_size(ptr));
+
+ ptr = (char*)realloc(ptr, 100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(100U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(0, ptr[i]);
+ }
+ free(ptr);
+}
+
+TEST(malloc, calloc_multiple_realloc) {
+ // Multiple reallocs, calloc is used for the original allocation.
+ char* ptr = (char*)calloc(1, 200);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(200U, malloc_usable_size(ptr));
+
+ ptr = (char*)realloc(ptr, 100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(100U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(0, ptr[i]);
+ }
+
+ ptr = (char*)realloc(ptr, 50);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(50U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 50; i++) {
+ ASSERT_EQ(0, ptr[i]);
+ }
+
+ ptr = (char*)realloc(ptr, 150);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(150U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 50; i++) {
+ ASSERT_EQ(0, ptr[i]);
+ }
+ memset(ptr, 0, 150);
+
+ ptr = (char*)realloc(ptr, 425);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_LE(425U, malloc_usable_size(ptr));
+ for (size_t i = 0; i < 150; i++) {
+ ASSERT_EQ(0, ptr[i]);
+ }
+ free(ptr);
+}
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+extern "C" void* pvalloc(size_t);
+#endif
+
+TEST(malloc, pvalloc_std) {
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+ size_t pagesize = sysconf(_SC_PAGESIZE);
+ void* ptr = pvalloc(100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_TRUE((reinterpret_cast<uintptr_t>(ptr) & (pagesize - 1)) == 0);
+ ASSERT_LE(pagesize, malloc_usable_size(ptr));
+ free(ptr);
+#else
+ GTEST_SKIP() << "pvalloc not supported.";
+#endif
+}
+
+TEST(malloc, calloc_usable_size) {
+ for (size_t size = 1; size <= 2048; size++) {
+ void* pointer = malloc(size);
+ ASSERT_TRUE(pointer != nullptr);
+ memset(pointer, 0xeb, malloc_usable_size(pointer));
+ free(pointer);
+
+ // We should get a previous pointer that has been set to non-zero.
+ // If calloc does not zero out all of the data, this will fail.
+ uint8_t* zero_mem = reinterpret_cast<uint8_t*>(calloc(1, size));
+ ASSERT_TRUE(pointer != nullptr);
+ size_t usable_size = malloc_usable_size(zero_mem);
+ for (size_t i = 0; i < usable_size; i++) {
+ ASSERT_EQ(0, zero_mem[i]) << "Failed at allocation size " << size << " at byte " << i;
+ }
+ free(zero_mem);
+ }
+}
+
+TEST(malloc, reallocarray) {
+#if HAVE_REALLOCARRAY
+ void* p = reallocarray(nullptr, 2, 32);
+ ASSERT_TRUE(p != nullptr);
+ ASSERT_GE(malloc_usable_size(p), 64U);
+#else
+ GTEST_SKIP() << "reallocarray not available";
+#endif
+}
+
+TEST(malloc, mallinfo) {
+#if defined(__BIONIC__) || defined(ANDROID_HOST_MUSL)
+ SKIP_WITH_HWASAN << "hwasan does not implement mallinfo";
+ static size_t sizes[] = {8, 32, 128, 4096, 32768, 131072, 1024000, 10240000, 20480000, 300000000};
+
+ static constexpr size_t kMaxAllocs = 50;
+
+ for (size_t size : sizes) {
+ // If some of these allocations are stuck in a thread cache, then keep
+ // looping until we make an allocation that changes the total size of the
+ // memory allocated.
+ // jemalloc implementations counts the thread cache allocations against
+ // total memory allocated.
+ void* ptrs[kMaxAllocs] = {};
+ bool pass = false;
+ for (size_t i = 0; i < kMaxAllocs; i++) {
+ size_t allocated = mallinfo().uordblks;
+ ptrs[i] = malloc(size);
+ ASSERT_TRUE(ptrs[i] != nullptr);
+ size_t new_allocated = mallinfo().uordblks;
+ if (allocated != new_allocated) {
+ size_t usable_size = malloc_usable_size(ptrs[i]);
+ // Only check if the total got bigger by at least allocation size.
+ // Sometimes the mallinfo numbers can go backwards due to compaction
+ // and/or freeing of cached data.
+ if (new_allocated >= allocated + usable_size) {
+ pass = true;
+ break;
+ }
+ }
+ }
+ for (void* ptr : ptrs) {
+ free(ptr);
+ }
+ ASSERT_TRUE(pass) << "For size " << size << " allocated bytes did not increase after "
+ << kMaxAllocs << " allocations.";
+ }
+#else
+ GTEST_SKIP() << "glibc is broken";
+#endif
+}
+
+TEST(malloc, mallinfo2) {
+#if defined(__BIONIC__) || defined(ANDROID_HOST_MUSL)
+ SKIP_WITH_HWASAN << "hwasan does not implement mallinfo2";
+ static size_t sizes[] = {8, 32, 128, 4096, 32768, 131072, 1024000, 10240000, 20480000, 300000000};
+
+ static constexpr size_t kMaxAllocs = 50;
+
+ for (size_t size : sizes) {
+ // If some of these allocations are stuck in a thread cache, then keep
+ // looping until we make an allocation that changes the total size of the
+ // memory allocated.
+ // jemalloc implementations counts the thread cache allocations against
+ // total memory allocated.
+ void* ptrs[kMaxAllocs] = {};
+ bool pass = false;
+ for (size_t i = 0; i < kMaxAllocs; i++) {
+ struct mallinfo info = mallinfo();
+ struct mallinfo2 info2 = mallinfo2();
+ // Verify that mallinfo and mallinfo2 are exactly the same.
+ ASSERT_EQ(static_cast<size_t>(info.arena), info2.arena);
+ ASSERT_EQ(static_cast<size_t>(info.ordblks), info2.ordblks);
+ ASSERT_EQ(static_cast<size_t>(info.smblks), info2.smblks);
+ ASSERT_EQ(static_cast<size_t>(info.hblks), info2.hblks);
+ ASSERT_EQ(static_cast<size_t>(info.hblkhd), info2.hblkhd);
+ ASSERT_EQ(static_cast<size_t>(info.usmblks), info2.usmblks);
+ ASSERT_EQ(static_cast<size_t>(info.fsmblks), info2.fsmblks);
+ ASSERT_EQ(static_cast<size_t>(info.uordblks), info2.uordblks);
+ ASSERT_EQ(static_cast<size_t>(info.fordblks), info2.fordblks);
+ ASSERT_EQ(static_cast<size_t>(info.keepcost), info2.keepcost);
+
+ size_t allocated = info2.uordblks;
+ ptrs[i] = malloc(size);
+ ASSERT_TRUE(ptrs[i] != nullptr);
+
+ info = mallinfo();
+ info2 = mallinfo2();
+ // Verify that mallinfo and mallinfo2 are exactly the same.
+ ASSERT_EQ(static_cast<size_t>(info.arena), info2.arena);
+ ASSERT_EQ(static_cast<size_t>(info.ordblks), info2.ordblks);
+ ASSERT_EQ(static_cast<size_t>(info.smblks), info2.smblks);
+ ASSERT_EQ(static_cast<size_t>(info.hblks), info2.hblks);
+ ASSERT_EQ(static_cast<size_t>(info.hblkhd), info2.hblkhd);
+ ASSERT_EQ(static_cast<size_t>(info.usmblks), info2.usmblks);
+ ASSERT_EQ(static_cast<size_t>(info.fsmblks), info2.fsmblks);
+ ASSERT_EQ(static_cast<size_t>(info.uordblks), info2.uordblks);
+ ASSERT_EQ(static_cast<size_t>(info.fordblks), info2.fordblks);
+ ASSERT_EQ(static_cast<size_t>(info.keepcost), info2.keepcost);
+
+ size_t new_allocated = info2.uordblks;
+ if (allocated != new_allocated) {
+ size_t usable_size = malloc_usable_size(ptrs[i]);
+ // Only check if the total got bigger by at least allocation size.
+ // Sometimes the mallinfo2 numbers can go backwards due to compaction
+ // and/or freeing of cached data.
+ if (new_allocated >= allocated + usable_size) {
+ pass = true;
+ break;
+ }
+ }
+ }
+ for (void* ptr : ptrs) {
+ free(ptr);
+ }
+ ASSERT_TRUE(pass) << "For size " << size << " allocated bytes did not increase after "
+ << kMaxAllocs << " allocations.";
+ }
+#else
+ GTEST_SKIP() << "glibc is broken";
+#endif
+}
+
+// Jemalloc doesn't pass this test right now, so leave it as disabled.
+TEST(malloc, DISABLED_alloc_after_fork) {
+ // Both of these need to be a power of 2.
+ static constexpr size_t kMinAllocationSize = 8;
+ static constexpr size_t kMaxAllocationSize = 2097152;
+
+ static constexpr size_t kNumAllocatingThreads = 5;
+ static constexpr size_t kNumForkLoops = 100;
+
+ std::atomic_bool stop;
+
+ // Create threads that simply allocate and free different sizes.
+ std::vector<std::thread*> threads;
+ for (size_t i = 0; i < kNumAllocatingThreads; i++) {
+ std::thread* t = new std::thread([&stop] {
+ while (!stop) {
+ for (size_t size = kMinAllocationSize; size <= kMaxAllocationSize; size <<= 1) {
+ void* ptr;
+ DoNotOptimize(ptr = malloc(size));
+ free(ptr);
+ }
+ }
+ });
+ threads.push_back(t);
+ }
+
+ // Create a thread to fork and allocate.
+ for (size_t i = 0; i < kNumForkLoops; i++) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ for (size_t size = kMinAllocationSize; size <= kMaxAllocationSize; size <<= 1) {
+ void* ptr;
+ DoNotOptimize(ptr = malloc(size));
+ ASSERT_TRUE(ptr != nullptr);
+ // Make sure we can touch all of the allocation.
+ memset(ptr, 0x1, size);
+ ASSERT_LE(size, malloc_usable_size(ptr));
+ free(ptr);
+ }
+ _exit(10);
+ }
+ ASSERT_NE(-1, pid);
+ AssertChildExited(pid, 10);
+ }
+
+ stop = true;
+ for (auto thread : threads) {
+ thread->join();
+ delete thread;
+ }
+}
+
+void VerifyAllocationsAreZero(std::function<void*(size_t)> alloc_func, std::string function_name,
+ std::vector<size_t>& test_sizes, size_t max_allocations) {
+ // Vector of zero'd data used for comparisons. Make it twice the largest size.
+ std::vector<char> zero(test_sizes.back() * 2, 0);
+
+ SCOPED_TRACE(testing::Message() << function_name << " failed to zero memory");
+
+ for (size_t test_size : test_sizes) {
+ std::vector<void*> ptrs(max_allocations);
+ for (size_t i = 0; i < ptrs.size(); i++) {
+ SCOPED_TRACE(testing::Message() << "size " << test_size << " at iteration " << i);
+ ptrs[i] = alloc_func(test_size);
+ ASSERT_TRUE(ptrs[i] != nullptr);
+ size_t alloc_size = malloc_usable_size(ptrs[i]);
+ ASSERT_LE(alloc_size, zero.size());
+ ASSERT_EQ(0, memcmp(ptrs[i], zero.data(), alloc_size));
+
+ // Set the memory to non-zero to make sure if the pointer
+ // is reused it's still zero.
+ memset(ptrs[i], 0xab, alloc_size);
+ }
+ // Free the pointers.
+ for (size_t i = 0; i < ptrs.size(); i++) {
+ free(ptrs[i]);
+ }
+ for (size_t i = 0; i < ptrs.size(); i++) {
+ SCOPED_TRACE(testing::Message() << "size " << test_size << " at iteration " << i);
+ ptrs[i] = malloc(test_size);
+ ASSERT_TRUE(ptrs[i] != nullptr);
+ size_t alloc_size = malloc_usable_size(ptrs[i]);
+ ASSERT_LE(alloc_size, zero.size());
+ ASSERT_EQ(0, memcmp(ptrs[i], zero.data(), alloc_size));
+ }
+ // Free all of the pointers later to maximize the chance of reusing from
+ // the first loop.
+ for (size_t i = 0; i < ptrs.size(); i++) {
+ free(ptrs[i]);
+ }
+ }
+}
+
+// Verify that small and medium allocations are always zero.
+// @CddTest = 9.7/C-4-1
+TEST(malloc, zeroed_allocations_small_medium_sizes) {
+#if !defined(__BIONIC__)
+ GTEST_SKIP() << "Only valid on bionic";
+#endif
+ SKIP_WITH_HWASAN << "Only test system allocator, not hwasan allocator.";
+
+ if (IsLowRamDevice()) {
+ GTEST_SKIP() << "Skipped on low memory devices.";
+ }
+
+ constexpr size_t kMaxAllocations = 1024;
+ std::vector<size_t> test_sizes = {16, 48, 128, 1024, 4096, 65536};
+ VerifyAllocationsAreZero([](size_t size) -> void* { return malloc(size); }, "malloc", test_sizes,
+ kMaxAllocations);
+
+ VerifyAllocationsAreZero([](size_t size) -> void* { return memalign(64, size); }, "memalign",
+ test_sizes, kMaxAllocations);
+
+ VerifyAllocationsAreZero(
+ [](size_t size) -> void* {
+ void* ptr;
+ if (posix_memalign(&ptr, 64, size) == 0) {
+ return ptr;
+ }
+ return nullptr;
+ },
+ "posix_memalign", test_sizes, kMaxAllocations);
+}
+
+// Verify that large allocations are always zero.
+// @CddTest = 9.7/C-4-1
+TEST(malloc, zeroed_allocations_large_sizes) {
+#if !defined(__BIONIC__)
+ GTEST_SKIP() << "Only valid on bionic";
+#endif
+ SKIP_WITH_HWASAN << "Only test system allocator, not hwasan allocator.";
+
+ if (IsLowRamDevice()) {
+ GTEST_SKIP() << "Skipped on low memory devices.";
+ }
+
+ constexpr size_t kMaxAllocations = 20;
+ std::vector<size_t> test_sizes = {1000000, 2000000, 3000000, 4000000};
+ VerifyAllocationsAreZero([](size_t size) -> void* { return malloc(size); }, "malloc", test_sizes,
+ kMaxAllocations);
+
+ VerifyAllocationsAreZero([](size_t size) -> void* { return memalign(64, size); }, "memalign",
+ test_sizes, kMaxAllocations);
+
+ VerifyAllocationsAreZero(
+ [](size_t size) -> void* {
+ void* ptr;
+ if (posix_memalign(&ptr, 64, size) == 0) {
+ return ptr;
+ }
+ return nullptr;
+ },
+ "posix_memalign", test_sizes, kMaxAllocations);
+}
+
+// Verify that reallocs are zeroed when expanded.
+// @CddTest = 9.7/C-4-1
+TEST(malloc, zeroed_allocations_realloc) {
+#if !defined(__BIONIC__)
+ GTEST_SKIP() << "Only valid on bionic";
+#endif
+ SKIP_WITH_HWASAN << "Only test system allocator, not hwasan allocator.";
+
+ if (IsLowRamDevice()) {
+ GTEST_SKIP() << "Skipped on low memory devices.";
+ }
+
+ // Vector of zero'd data used for comparisons.
+ constexpr size_t kMaxMemorySize = 131072;
+ std::vector<char> zero(kMaxMemorySize, 0);
+
+ constexpr size_t kMaxAllocations = 1024;
+ std::vector<size_t> test_sizes = {16, 48, 128, 1024, 4096, 65536};
+ // Do a number of allocations and set them to non-zero.
+ for (size_t test_size : test_sizes) {
+ std::vector<void*> ptrs(kMaxAllocations);
+ for (size_t i = 0; i < kMaxAllocations; i++) {
+ ptrs[i] = malloc(test_size);
+ ASSERT_TRUE(ptrs[i] != nullptr);
+
+ // Set the memory to non-zero to make sure if the pointer
+ // is reused it's still zero.
+ memset(ptrs[i], 0xab, malloc_usable_size(ptrs[i]));
+ }
+ // Free the pointers.
+ for (size_t i = 0; i < kMaxAllocations; i++) {
+ free(ptrs[i]);
+ }
+ }
+
+ // Do the reallocs to a larger size and verify the rest of the allocation
+ // is zero.
+ constexpr size_t kInitialSize = 8;
+ for (size_t test_size : test_sizes) {
+ std::vector<void*> ptrs(kMaxAllocations);
+ for (size_t i = 0; i < kMaxAllocations; i++) {
+ ptrs[i] = malloc(kInitialSize);
+ ASSERT_TRUE(ptrs[i] != nullptr);
+ size_t orig_alloc_size = malloc_usable_size(ptrs[i]);
+
+ ptrs[i] = realloc(ptrs[i], test_size);
+ ASSERT_TRUE(ptrs[i] != nullptr);
+ size_t new_alloc_size = malloc_usable_size(ptrs[i]);
+ char* ptr = reinterpret_cast<char*>(ptrs[i]);
+ ASSERT_EQ(0, memcmp(&ptr[orig_alloc_size], zero.data(), new_alloc_size - orig_alloc_size))
+ << "realloc from " << kInitialSize << " to size " << test_size << " at iteration " << i;
+ }
+ for (size_t i = 0; i < kMaxAllocations; i++) {
+ free(ptrs[i]);
+ }
+ }
+}