TEST(malloc, calloc_mem_init_disabled) { #ifdefined(__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
}
auto bin = arena->FirstChildElement("bin"); for (; bin != nullptr; bin = bin ->NextSiblingElement()) { if (strcmp(bin->Name(), "bin") == 0) {
ASSERT_EQ(tinyxml2::XML_SUCCESS, bin->QueryIntAttribute("nr", &val));
ASSERT_EQ(tinyxml2::XML_SUCCESS,
bin->FirstChildElement("allocated")->QueryIntText(&val));
ASSERT_EQ(tinyxml2::XML_SUCCESS,
bin->FirstChildElement("nmalloc")->QueryIntText(&val));
ASSERT_EQ(tinyxml2::XML_SUCCESS,
bin->FirstChildElement("ndalloc")->QueryIntText(&val));
}
}
}
} elseif (version == "scudo-1") { auto element = root->FirstChildElement(); for (; element != nullptr; element = element->NextSiblingElement()) { int val;
size_t total_allocated_bytes = 0; auto root = doc.FirstChildElement();
ASSERT_NE(nullptr, root);
ASSERT_STREQ("malloc", root->Name());
std::string version(root->Attribute("version")); if (version == "jemalloc-1") { auto arena = root->FirstChildElement(); for (; arena != nullptr; arena = arena->NextSiblingElement()) { int val;
ASSERT_STREQ("heap", arena->Name());
ASSERT_EQ(tinyxml2::XML_SUCCESS, arena->QueryIntAttribute("nr", &val));
ASSERT_EQ(tinyxml2::XML_SUCCESS,
arena->FirstChildElement("allocated-large")->QueryIntText(&val));
total_allocated_bytes += val;
ASSERT_EQ(tinyxml2::XML_SUCCESS,
arena->FirstChildElement("allocated-huge")->QueryIntText(&val));
total_allocated_bytes += val;
ASSERT_EQ(tinyxml2::XML_SUCCESS,
arena->FirstChildElement("allocated-bins")->QueryIntText(&val));
total_allocated_bytes += val;
ASSERT_EQ(tinyxml2::XML_SUCCESS,
arena->FirstChildElement("bins-total")->QueryIntText(&val));
} // The total needs to be between the mallinfo call before and after // since malloc_info allocates some memory.
EXPECT_LE(mallinfo_before_allocated_bytes, total_allocated_bytes);
EXPECT_GE(mallinfo_after_allocated_bytes, total_allocated_bytes);
} elseif (version == "scudo-1") { auto element = root->FirstChildElement(); for (; element != nullptr; element = element->NextSiblingElement()) {
ASSERT_STREQ("alloc", element->Name()); int size;
ASSERT_EQ(tinyxml2::XML_SUCCESS, element->QueryIntAttribute("size", &size)); int count;
ASSERT_EQ(tinyxml2::XML_SUCCESS, element->QueryIntAttribute("count", &count));
total_allocated_bytes += size * count;
} // Scudo only gives the information on the primary, so simply make // sure that the value is non-zero.
EXPECT_NE(0U, total_allocated_bytes);
} else { // Do not verify output for debug malloc.
ASSERT_TRUE(version == "debug-malloc-1") << "Unknown version: " << version;
} #endif
}
// Make sure that memory returned by malloc is aligned to allow these data types.
TEST(malloc, verify_alignment) {
uint32_t** values_32 = new uint32_t*[MAX_LOOPS];
uint64_t** values_64 = new uint64_t*[MAX_LOOPS]; longdouble** values_ldouble = newlongdouble*[MAX_LOOPS]; // Use filler to attempt to force the allocator to get potentially bad alignments. void** filler = newvoid*[MAX_LOOPS];
for (size_t i = 0; i < MAX_LOOPS; i++) { // Check uint32_t pointers.
filler[i] = malloc(1);
ASSERT_TRUE(filler[i] != nullptr);
// Values that cause overflow to a result small enough (8 on LP64) that malloc would "succeed".
size_t a = static_cast<size_t>(INTPTR_MIN + 4);
size_t b = 2;
ASSERT_ERRNO_FAILURE(ENOMEM, nullptr, reallocarray(nullptr, a, b));
template <typename Type> void __attribute__((optnone)) TestAllocateType() { // The number of allocations to do in a row. This is to attempt to // expose the worst case alignment for native allocators that use // bins. static constexpr size_t kMaxConsecutiveAllocs = 100;
// Verify using new directly.
Type* types[kMaxConsecutiveAllocs]; for (size_t i = 0; i < kMaxConsecutiveAllocs; i++) {
types[i] = new Type;
VerifyAlignment(types[i]); if (::testing::Test::HasFatalFailure()) { return;
}
} for (size_t i = 0; i < kMaxConsecutiveAllocs; i++) { delete types[i];
}
// Verify using malloc. for (size_t i = 0; i < kMaxConsecutiveAllocs; i++) {
types[i] = reinterpret_cast<Type*>(malloc(sizeof(Type)));
ASSERT_TRUE(types[i] != nullptr);
VerifyAlignment(types[i]); if (::testing::Test::HasFatalFailure()) { return;
}
} for (size_t i = 0; i < kMaxConsecutiveAllocs; i++) {
free(types[i]);
}
// Verify using a vector.
std::vector<Type> type_vector(kMaxConsecutiveAllocs); for (size_t i = 0; i < type_vector.size(); i++) {
VerifyAlignment(&type_vector[i]); if (::testing::Test::HasFatalFailure()) { return;
}
}
}
void AlignCheck() { // See http://www.open-std.org/jtc1/sc22/wg14/www/docs/summary.htm#dr_445 // for a discussion of type alignment.
ASSERT_NO_FATAL_FAILURE(TestAllocateType<float>());
ASSERT_NO_FATAL_FAILURE(TestAllocateType<double>());
ASSERT_NO_FATAL_FAILURE(TestAllocateType<longdouble>());
#ifdefined(__ANDROID__) // On Android, there is a lot of code that expects certain alignments: // 1. Allocations of a size that rounds up to a multiple of 16 bytes // must have at least 16 byte alignment. // 2. Allocations of a size that rounds up to a multiple of 8 bytes and // not 16 bytes, are only required to have at least 8 byte alignment. // In addition, on Android clang has been configured for 64 bit such that: // 3. Allocations <= 8 bytes must be aligned to at least 8 bytes. // 4. Allocations > 8 bytes must be aligned to at least 16 bytes. // For 32 bit environments, only the first two requirements must be met.
// See http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm for // a discussion of this alignment mess. The code below is enforcing // strong-alignment, since who knows what code depends on this behavior now. // As mentioned before, for 64 bit this will enforce the higher // requirement since clang expects this behavior on Android now. for (size_t i = 1; i <= 128; i++) { #ifdefined(__LP64__) if (i <= 8) {
AndroidVerifyAlignment(i, 8);
} else {
AndroidVerifyAlignment(i, 16);
} #else
size_t rounded = (i + 7) & ~7; if ((rounded % 16) == 0) {
AndroidVerifyAlignment(i, 16);
} else {
AndroidVerifyAlignment(i, 8);
} #endif if (::testing::Test::HasFatalFailure()) { return;
}
} #endif
}
static size_t GetMaxAllocations() {
size_t max_pointers = 0; void* ptrs[20]; for (size_t i = 0; i < sizeof(ptrs) / sizeof(void*); i++) {
ptrs[i] = malloc(kAllocationSize); if (ptrs[i] == nullptr) {
max_pointers = i; break;
}
} for (size_t i = 0; i < max_pointers; i++) {
free(ptrs[i]);
} return max_pointers;
}
staticvoid VerifyMaxPointers(size_t max_pointers) { // Now verify that we can allocate the same number as before. void* ptrs[20]; for (size_t i = 0; i < max_pointers; i++) {
ptrs[i] = malloc(kAllocationSize);
ASSERT_TRUE(ptrs[i] != nullptr) << "Failed to allocate on iteration " << i;
}
// Make sure the next allocation still fails.
ASSERT_TRUE(malloc(kAllocationSize) == nullptr); for (size_t i = 0; i < max_pointers; i++) {
free(ptrs[i]);
}
} #endif
// Wait until all of the threads have started. while (num_running != kNumThreads)
;
// Now start all of the threads setting the mallopt at once.
start_running = true;
// Send hardcoded signal (BIONIC_SIGNAL_PROFILER with value 0) to trigger // heapprofd handler. This will verify that changing the limit while // the allocation handlers are being changed at the same time works, // or that the limit handler is changed first and this also works properly. union sigval signal_value {};
ASSERT_EQ(0, sigqueue(getpid(), BIONIC_SIGNAL_PROFILER, signal_value));
// Wait for all of the threads to finish. for (size_t i = 0; i < kNumThreads; i++) {
threads[i]->join();
}
ASSERT_EQ(1U, num_successful) << "Only one thread should be able to set the limit.";
_exit(0);
} #endif
// Run this a number of times as a stress test. for (size_t i = 0; i < 100; i++) { // Not using ASSERT_EXIT because errors messages are not displayed.
pid_t pid; if ((pid = fork()) == 0) {
ASSERT_NO_FATAL_FAILURE(SetAllocationLimitMultipleThreads());
}
ASSERT_NE(-1, pid); int status;
ASSERT_EQ(pid, wait(&status));
ASSERT_EQ(0, WEXITSTATUS(status));
} #else
GTEST_SKIP() << "bionic extension"; #endif
}
#ifdefined(__BIONIC__) using Mode = android_mallopt_gwp_asan_options_t::Mode;
TEST(android_mallopt, DISABLED_multiple_enable_gwp_asan) {
android_mallopt_gwp_asan_options_t options;
options.program_name = ""; // Don't infer GWP-ASan options from sysprops.
options.mode = Mode::APP_MANIFEST_NEVER; // GWP-ASan should already be enabled. Trying to enable or disable it should // always pass.
ASSERT_TRUE(android_mallopt(M_INITIALIZE_GWP_ASAN, &options, sizeof(options)));
options.mode = Mode::APP_MANIFEST_DEFAULT;
ASSERT_TRUE(android_mallopt(M_INITIALIZE_GWP_ASAN, &options, sizeof(options)));
} #endif// defined(__BIONIC__)
void TestHeapZeroing(int num_iterations, int (*get_alloc_size)(int iteration)) {
std::vector<void*> allocs;
constexpr int kMaxBytesToCheckZero = 64; constchar 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);
}
}
TEST(malloc, zero_init) { #ifdefined(__BIONIC__)
SKIP_WITH_HWASAN << "hwasan does not implement mallopt";
SKIP_WITHOUT_SCUDO << "scudo allocator only test";
mallopt(M_BIONIC_ZERO_INIT, 1);
// Test using a block of 4K small (1-32 byte) allocations.
TestHeapZeroing(/* num_iterations */ 0x1000, [](int iteration) -> int { return1 + 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, 1);
TestHeapZeroing(/* num_iterations */ 32, [](int iteration) -> int { return1 << (19 + iteration % 4);
});
// Note that MTE is enabled on cc_tests on devices that support MTE.
TEST(malloc, disable_mte) { #ifdefined(__BIONIC__) if (!mte_supported()) {
GTEST_SKIP() << "This function can only be tested with MTE";
}
SKIP_WITHOUT_SCUDO << "scudo allocator only test";
// 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); volatilechar *vp = p.get(); volatilechar oob ATTRIBUTE_UNUSED = vp[131072]; #else
GTEST_SKIP() << "bionic extension"; #endif
}
// Regression test for b/206701345 -- scudo bug, MTE only. // Fix: https://reviews.llvm.org/D105261 // Fix: https://android-review.googlesource.com/c/platform/external/scudo/+/1763655
TEST(malloc, realloc_mte_crash_b206701345) { // We want to hit in-place realloc at the very end of an mmap-ed region. Not // all size classes allow such placement - mmap size has to be divisible by // the block size. At the time of writing this could only be reproduced with // 64 byte size class (i.e. 48 byte allocations), but that may change in the // future. Try several different classes at the lower end.
std::vector<void*> ptrs(10000); for (int i = 1; i < 32; ++i) {
size_t sz = 16 * i - 1; for (void*& p : ptrs) {
p = realloc(malloc(sz), sz + 1);
}
TEST(android_mallopt, DISABLED_verify_decay_time_on) { #ifdefined(__BIONIC__) bool value;
EXPECT_TRUE(android_mallopt(M_GET_DECAY_TIME_ENABLED, &value, sizeof(value)));
EXPECT_TRUE(value) << "decay time did not get enabled properly."; #endif
}
TEST(android_mallopt, decay_time_set_using_env_variable) { #ifdefined(__BIONIC__)
SKIP_WITH_HWASAN << "hwasan does not implement mallopt";
bool value;
ASSERT_TRUE(android_mallopt(M_GET_DECAY_TIME_ENABLED, &value, sizeof(value)));
ASSERT_FALSE(value) << "decay time did not get disabled properly.";
// Verify that setting the environment variable here will be carried into // fork'd and exec'd processes.
ASSERT_EQ(0, setenv("MALLOC_USE_APP_DEFAULTS", "1", 1));
ExecTestHelper eth;
std::string executable(testing::internal::GetArgvs()[0]);
eth.SetArgs({executable.c_str(), "--gtest_also_run_disabled_tests", "--gtest_filter=android_mallopt.DISABLED_verify_decay_time_on", nullptr});
eth.Run([&]() { execv(executable.c_str(), eth.GetArgs()); }, 0, R"(\[ PASSED \] 1 test)"); #else
GTEST_SKIP() << "bionic-only test"; #endif
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.30 Sekunden
(vorverarbeitet am 2026-06-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.