|  | /* | 
|  | * 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 <stdint.h> | 
|  | #include <stdlib.h> | 
|  | #include <time.h> | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #if defined(__BIONIC__) | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include <procinfo/process_map.h> | 
|  |  | 
|  | #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<AllocDataType> 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<TestDataType*>(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<uintptr_t>(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, 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<size_t>& 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<size_t> 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_LOG_(INFO) << "Skipping, this is a 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<size_t> sizes{131072, 262144, 524288, 1048576, 2097152}; | 
|  | AllocateSizes(&test_data, sizes); | 
|  |  | 
|  | SCOPED_TRACE(""); | 
|  | VerifyPtrs(&test_data); | 
|  |  | 
|  | FreePtrs(&test_data); | 
|  | #else | 
|  | GTEST_LOG_(INFO) << "Skipping, this is a 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, 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_LOG_(INFO) << "Skipping, this is a 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_LOG_(INFO) << "Skipping, this is a bionic only test."; | 
|  | #endif | 
|  | } |