#include "partition_alloc/shim/allocator_shim.h"
#include <atomic>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <memory>
#include <new>
#include <sstream>
#include <vector>
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "partition_alloc/build_config.h"
#include "partition_alloc/buildflags.h"
#include "partition_alloc/partition_alloc.h"
#include "partition_alloc/partition_alloc_base/memory/page_size.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if PA_BUILDFLAG(IS_WIN)
#include <windows.h>
#include <malloc.h>
#elif PA_BUILDFLAG(IS_APPLE)
#include <malloc/malloc.h>
#include "partition_alloc/shim/allocator_interception_apple.h"
#include "partition_alloc/third_party/apple_apsl/malloc.h"
#else
#include <malloc.h>
#endif
#if !PA_BUILDFLAG(IS_WIN)
#include <unistd.h>
#endif
#if PA_BUILDFLAG(PA_LIBC_GLIBC)
extern "C" void* __libc_memalign(size_t align, size_t s);
#endif
#if PA_BUILDFLAG( \
ENABLE_ALLOCATOR_SHIM_PARTITION_ALLOC_DISPATCH_WITH_ADVANCED_CHECKS_SUPPORT)
#include "partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc_with_advanced_checks.h"
#endif
namespace allocator_shim {
namespace {
_;
MockFunction;
extern AllocatorDispatch g_mock_dispatch;
const char kTestSizeEstimateData[] = …;
constexpr void* kTestSizeEstimateAddress = …;
constexpr size_t kTestSizeEstimate = …;
class AllocatorShimTest : public testing::Test { … };
struct TestStruct1 { … };
struct TestStruct2 { … };
class ThreadDelegateForNewHandlerTest : public base::PlatformThread::Delegate { … };
AllocatorShimTest* AllocatorShimTest::instance_ = …;
AllocatorDispatch g_mock_dispatch = …;
TEST_F(AllocatorShimTest, InterceptLibcSymbols) { … }
#if PA_BUILDFLAG(IS_APPLE) && !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
TEST_F(AllocatorShimTest, InterceptLibcSymbolsBatchMallocFree) {
InsertAllocatorDispatch(&g_mock_dispatch);
unsigned count = 13;
std::vector<void*> results;
results.resize(count);
unsigned result_count = malloc_zone_batch_malloc(malloc_default_zone(), 99,
results.data(), count);
ASSERT_EQ(count, result_count);
std::vector<void*> results_copy(results);
malloc_zone_batch_free(malloc_default_zone(), results.data(), count);
for (void* result : results_copy) {
ASSERT_GE(batch_frees_intercepted_by_addr[Hash(result)], 1u);
}
RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
}
TEST_F(AllocatorShimTest, InterceptLibcSymbolsFreeDefiniteSize) {
InsertAllocatorDispatch(&g_mock_dispatch);
void* alloc_ptr = malloc(19);
ASSERT_NE(nullptr, alloc_ptr);
ASSERT_GE(allocs_intercepted_by_size[19], 1u);
ChromeMallocZone* default_zone =
reinterpret_cast<ChromeMallocZone*>(malloc_default_zone());
default_zone->free_definite_size(malloc_default_zone(), alloc_ptr, 19);
ASSERT_GE(free_definite_sizes_intercepted_by_size[19], 1u);
RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
}
#endif
#if PA_BUILDFLAG(IS_WIN)
TEST_F(AllocatorShimTest, InterceptUcrtAlignedAllocationSymbols) {
InsertAllocatorDispatch(&g_mock_dispatch);
constexpr size_t kAlignment = 32;
void* alloc_ptr = _aligned_malloc(123, kAlignment);
EXPECT_GE(aligned_mallocs_intercepted_by_size[123], 1u);
void* new_alloc_ptr = _aligned_realloc(alloc_ptr, 1234, kAlignment);
EXPECT_GE(aligned_reallocs_intercepted_by_size[1234], 1u);
EXPECT_GE(aligned_reallocs_intercepted_by_addr[Hash(alloc_ptr)], 1u);
_aligned_free(new_alloc_ptr);
EXPECT_GE(aligned_frees_intercepted_by_addr[Hash(new_alloc_ptr)], 1u);
RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
}
TEST_F(AllocatorShimTest, AlignedReallocSizeZeroFrees) {
void* alloc_ptr = _aligned_malloc(123, 16);
ASSERT_TRUE(alloc_ptr);
alloc_ptr = _aligned_realloc(alloc_ptr, 0, 16);
ASSERT_TRUE(!alloc_ptr);
}
#endif
TEST_F(AllocatorShimTest, InterceptCppSymbols) { … }
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
struct TooLarge { … };
TEST_F(AllocatorShimTest, NewNoThrowTooLarge) { … }
#endif
TEST_F(AllocatorShimTest, NewHandlerConcurrency) { … }
#if PA_BUILDFLAG(IS_WIN)
TEST_F(AllocatorShimTest, ShimReplacesCRTHeapWhenEnabled) {
ASSERT_EQ(::GetProcessHeap(), reinterpret_cast<HANDLE>(_get_heap_handle()));
}
#endif
#if PA_BUILDFLAG(IS_WIN)
static size_t GetUsableSize(void* ptr) {
return _msize(ptr);
}
#elif PA_BUILDFLAG(IS_APPLE)
static size_t GetUsableSize(void* ptr) {
return malloc_size(ptr);
}
#elif PA_BUILDFLAG(IS_LINUX) || PA_BUILDFLAG(IS_CHROMEOS) || \
PA_BUILDFLAG(IS_ANDROID)
static size_t GetUsableSize(void* ptr) { … }
#else
#define NO_MALLOC_SIZE
#endif
#if !defined(NO_MALLOC_SIZE)
TEST_F(AllocatorShimTest, ShimReplacesMallocSizeWhenEnabled) { … }
TEST_F(AllocatorShimTest, ShimDoesntChangeMallocSizeWhenEnabled) { … }
#endif
#if PA_BUILDFLAG(IS_ANDROID)
TEST_F(AllocatorShimTest, InterceptCLibraryFunctions) {
auto total_counts = [](const std::vector<size_t>& counts) {
size_t total = 0;
for (const auto count : counts) {
total += count;
}
return total;
};
size_t counts_before;
size_t counts_after = total_counts(allocs_intercepted_by_size);
void* ptr;
InsertAllocatorDispatch(&g_mock_dispatch);
counts_before = counts_after;
ptr = realpath(".", nullptr);
EXPECT_NE(nullptr, ptr);
free(ptr);
counts_after = total_counts(allocs_intercepted_by_size);
EXPECT_GT(counts_after, counts_before);
counts_before = counts_after;
ptr = strdup("hello, world");
EXPECT_NE(nullptr, ptr);
free(ptr);
counts_after = total_counts(allocs_intercepted_by_size);
EXPECT_GT(counts_after, counts_before);
counts_before = counts_after;
ptr = strndup("hello, world", 5);
EXPECT_NE(nullptr, ptr);
free(ptr);
counts_after = total_counts(allocs_intercepted_by_size);
EXPECT_GT(counts_after, counts_before);
counts_before = counts_after;
ptr = getcwd(nullptr, 0);
EXPECT_NE(nullptr, ptr);
free(ptr);
counts_after = total_counts(allocs_intercepted_by_size);
EXPECT_GT(counts_after, counts_before);
#ifndef COMPONENT_BUILD
counts_before = counts_after;
std::stringstream stream;
stream << std::setprecision(1) << std::showpoint << std::fixed << 1.e38;
EXPECT_GT(stream.str().size(), 30u);
counts_after = total_counts(allocs_intercepted_by_size);
EXPECT_GT(counts_after, counts_before);
#endif
RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
}
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
TEST_F(AllocatorShimTest, InterceptVasprintf) {
std::stringstream stream;
stream << std::setprecision(1) << std::showpoint << std::fixed << 1.e38;
EXPECT_GT(stream.str().size(), 30u);
}
TEST_F(AllocatorShimTest, InterceptLongVasprintf) {
char* str = nullptr;
const char* lorem_ipsum =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. "
"Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, "
"ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula "
"massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci "
"nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit "
"amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat "
"in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero "
"pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo "
"in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue "
"blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus "
"et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed "
"pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales "
"hendrerit.";
int err = asprintf(&str, "%s", lorem_ipsum);
EXPECT_EQ(err, static_cast<int>(strlen(lorem_ipsum)));
EXPECT_TRUE(str);
free(str);
}
#endif
#endif
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && PA_BUILDFLAG(IS_APPLE)
TEST_F(AllocatorShimTest, BatchMalloc) {
constexpr unsigned kNumToAllocate = 20;
void* pointers[kNumToAllocate];
EXPECT_EQ(kNumToAllocate, malloc_zone_batch_malloc(malloc_default_zone(), 10,
pointers, kNumToAllocate));
malloc_zone_batch_free(malloc_default_zone(), pointers, kNumToAllocate);
}
TEST_F(AllocatorShimTest, MallocGoodSize) {
constexpr size_t kTestSize = 100;
size_t good_size = malloc_good_size(kTestSize);
EXPECT_GE(good_size, kTestSize);
}
#endif
TEST_F(AllocatorShimTest, OptimizeAllocatorDispatchTable) { … }
#if PA_BUILDFLAG( \
ENABLE_ALLOCATOR_SHIM_PARTITION_ALLOC_DISPATCH_WITH_ADVANCED_CHECKS_SUPPORT)
void MockFreeWithAdvancedChecks(void* address, void* context);
void* MockReallocWithAdvancedChecks(void* address, size_t size, void* context);
std::atomic_size_t g_mock_free_with_advanced_checks_count;
AllocatorDispatch g_mock_dispatch_for_advanced_checks = {
.realloc_function = &MockReallocWithAdvancedChecks,
.free_function = &MockFreeWithAdvancedChecks,
.next = nullptr,
};
void MockFreeWithAdvancedChecks(void* address, void* context) {
g_mock_free_with_advanced_checks_count++;
g_mock_dispatch_for_advanced_checks.next->free_function(address, context);
}
void* MockReallocWithAdvancedChecks(void* address, size_t size, void* context) {
return g_mock_dispatch_for_advanced_checks.next->realloc_function(
address, size, context);
}
TEST_F(AllocatorShimTest, InstallDispatchToPartitionAllocWithAdvancedChecks) {
AutoResetAllocatorDispatchChainForTesting chain_reset;
g_mock_free_with_advanced_checks_count = 0u;
InsertAllocatorDispatch(&g_mock_dispatch);
int* alloc_ptr = new int;
delete alloc_ptr;
EXPECT_GE(frees_intercepted_by_addr[Hash(alloc_ptr)], 1u);
EXPECT_EQ(g_mock_free_with_advanced_checks_count, 0u);
InstallDispatchToPartitionAllocWithAdvancedChecks(
&g_mock_dispatch_for_advanced_checks);
alloc_ptr = new int;
delete alloc_ptr;
EXPECT_GE(frees_intercepted_by_addr[Hash(alloc_ptr)], 1u);
EXPECT_GE(g_mock_free_with_advanced_checks_count, 1u);
UninstallDispatchToPartitionAllocWithAdvancedChecks();
g_mock_free_with_advanced_checks_count = 0u;
alloc_ptr = new int;
delete alloc_ptr;
EXPECT_GE(frees_intercepted_by_addr[Hash(alloc_ptr)], 1u);
EXPECT_EQ(g_mock_free_with_advanced_checks_count, 0u);
RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
}
#endif
}
}