/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #if defined(__BIONIC__) #include #include #include "utils.h" extern "C" void malloc_disable(); extern "C" void malloc_enable(); extern "C" int malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t base, size_t size, void* arg), void* arg); struct AllocDataType { void* ptr; size_t size; size_t size_reported; size_t count; }; struct TestDataType { size_t total_allocated_bytes; std::vector allocs; }; static void AllocPtr(TestDataType* test_data, size_t size) { test_data->allocs.resize(test_data->allocs.size() + 1); AllocDataType* alloc = &test_data->allocs.back(); void* ptr = malloc(size); ASSERT_TRUE(ptr != nullptr); alloc->ptr = ptr; alloc->size = malloc_usable_size(ptr); alloc->size_reported = 0; alloc->count = 0; } static void FreePtrs(TestDataType* test_data) { for (size_t i = 0; i < test_data->allocs.size(); i++) { free(test_data->allocs[i].ptr); } } static void SavePointers(uintptr_t base, size_t size, void* data) { TestDataType* test_data = reinterpret_cast(data); test_data->total_allocated_bytes += size; uintptr_t end; if (__builtin_add_overflow(base, size, &end)) { // Skip this entry. return; } for (size_t i = 0; i < test_data->allocs.size(); i++) { uintptr_t ptr = reinterpret_cast(test_data->allocs[i].ptr); if (ptr >= base && ptr < end) { test_data->allocs[i].count++; uintptr_t max_size = end - ptr; if (max_size > test_data->allocs[i].size) { test_data->allocs[i].size_reported = test_data->allocs[i].size; } else { test_data->allocs[i].size_reported = max_size; } } } } static void VerifyPtrs(TestDataType* test_data) { test_data->total_allocated_bytes = 0; // Find all of the maps that are [anon:libc_malloc]. ASSERT_TRUE(android::procinfo::ReadMapFile( "/proc/self/maps", [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) { if (std::string(name) == "[anon:libc_malloc]") { malloc_disable(); malloc_iterate(start, end - start, SavePointers, test_data); malloc_enable(); } })); for (size_t i = 0; i < test_data->allocs.size(); i++) { EXPECT_EQ(1UL, test_data->allocs[i].count) << "Failed on size " << test_data->allocs[i].size; if (test_data->allocs[i].count == 1) { EXPECT_EQ(test_data->allocs[i].size, test_data->allocs[i].size_reported); } } } static void AllocateSizes(TestDataType* test_data, const std::vector& sizes) { static constexpr size_t kInitialAllocations = 40; static constexpr size_t kNumAllocs = 50; for (size_t size : sizes) { // Verify that if the tcache is enabled, that tcache pointers // are found by allocating and freeing 20 pointers (should be larger // than the total number of cache entries). for (size_t i = 0; i < kInitialAllocations; i++) { void* ptr = malloc(size); ASSERT_TRUE(ptr != nullptr); memset(ptr, 0, size); free(ptr); } for (size_t i = 0; i < kNumAllocs; i++) { AllocPtr(test_data, size); } } } #endif // Verify that small allocs can be found properly. TEST(malloc_iterate, small_allocs) { #if defined(__BIONIC__) SKIP_WITH_HWASAN; TestDataType test_data; // Try to cycle through all of the different small bins. // This is specific to the implementation of jemalloc and should be // adjusted if a different native memory allocator is used. std::vector sizes{8, 16, 32, 48, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792, 2048, 2560, 3072, 3584, 4096, 5120, 6144, 7168, 8192, 10240, 12288, 14336, 16384, 32768, 65536}; AllocateSizes(&test_data, sizes); SCOPED_TRACE(""); VerifyPtrs(&test_data); FreePtrs(&test_data); #else GTEST_SKIP() << "bionic-only test"; #endif } // Verify that large allocs can be found properly. TEST(malloc_iterate, large_allocs) { #if defined(__BIONIC__) SKIP_WITH_HWASAN; TestDataType test_data; // Try some larger sizes. std::vector sizes{131072, 262144, 524288, 1048576, 2097152}; AllocateSizes(&test_data, sizes); SCOPED_TRACE(""); VerifyPtrs(&test_data); FreePtrs(&test_data); #else GTEST_SKIP() << "bionic-only test"; #endif } // Verify that there are no crashes attempting to get pointers from // non-allocated pointers. TEST(malloc_iterate, invalid_pointers) { #if defined(__BIONIC__) SKIP_WITH_HWASAN; TestDataType test_data = {}; // Find all of the maps that are not [anon:libc_malloc]. ASSERT_TRUE(android::procinfo::ReadMapFile( "/proc/self/maps", [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) { if (std::string(name) != "[anon:libc_malloc]") { malloc_disable(); malloc_iterate(start, end - start, SavePointers, &test_data); malloc_enable(); } })); ASSERT_EQ(0UL, test_data.total_allocated_bytes); #else GTEST_SKIP() << "bionic-only test"; #endif } TEST(malloc_iterate, malloc_disable_prevents_allocs) { #if defined(__BIONIC__) SKIP_WITH_HWASAN; pid_t pid; if ((pid = fork()) == 0) { malloc_disable(); void* ptr = malloc(1024); if (ptr == nullptr) { exit(1); } memset(ptr, 0, 1024); exit(0); } ASSERT_NE(-1, pid); // Expect that the malloc will hang forever, and that if the process // does not return for two seconds, it is hung. sleep(2); pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); if (wait_pid <= 0) { kill(pid, SIGKILL); } ASSERT_NE(-1, wait_pid) << "Unknown failure in waitpid."; ASSERT_EQ(0, wait_pid) << "malloc_disable did not prevent allocation calls."; #else GTEST_SKIP() << "bionic-only test"; #endif }