From cae21a9b53a10f0cba79bf6783c4a5af16228fed Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 5 Feb 2018 18:14:55 -0800 Subject: [PATCH] Add aligned_alloc to libc. Bug: 72969374 Test: Bionic unit tests pass. Test: Malloc debug unit tests pass. Change-Id: I235985bbc638855d94249c97c98f14ab2924bda0 (cherry picked from commit d69ee59594088c0d92ba9273188ef53ea5e6cd6a) --- libc/bionic/malloc_common.cpp | 13 +++++ libc/include/stdlib.h | 2 + libc/libc.arm.map | 1 + libc/libc.arm64.map | 1 + libc/libc.map.txt | 1 + libc/libc.mips.map | 1 + libc/libc.mips64.map | 1 + libc/libc.x86.map | 1 + libc/libc.x86_64.map | 1 + libc/malloc_debug/README.md | 5 ++ libc/malloc_debug/RecordData.cpp | 2 +- libc/malloc_debug/RecordData.h | 2 +- libc/malloc_debug/exported32.map | 1 + libc/malloc_debug/exported64.map | 1 + libc/malloc_debug/malloc_debug.cpp | 12 +++++ .../tests/malloc_debug_unit_tests.cpp | 13 +++++ libc/private/bionic_malloc_dispatch.h | 2 + tests/stdlib_test.cpp | 52 +++++++++++++++++++ 18 files changed, 110 insertions(+), 2 deletions(-) diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp index 1f201d1ca..940c418cd 100644 --- a/libc/bionic/malloc_common.cpp +++ b/libc/bionic/malloc_common.cpp @@ -69,6 +69,7 @@ static constexpr MallocDispatch __libc_malloc_default_dispatch Malloc(malloc_disable), Malloc(malloc_enable), Malloc(mallopt), + Malloc(aligned_alloc), }; // In a VM process, this is set to 1 after fork()ing out of zygote. @@ -142,6 +143,14 @@ extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) { return Malloc(posix_memalign)(memptr, alignment, size); } +extern "C" void* aligned_alloc(size_t alignment, size_t size) { + auto _aligned_alloc = __libc_globals->malloc_dispatch.aligned_alloc; + if (__predict_false(_aligned_alloc != nullptr)) { + return _aligned_alloc(alignment, size); + } + return Malloc(aligned_alloc)(alignment, size); +} + extern "C" void* realloc(void* old_mem, size_t bytes) { auto _realloc = __libc_globals->malloc_dispatch.realloc; if (__predict_false(_realloc != nullptr)) { @@ -276,6 +285,10 @@ static bool InitMalloc(void* malloc_impl_handler, MallocDispatch* table, const c prefix, "posix_memalign")) { return false; } + if (!InitMallocFunction(malloc_impl_handler, &table->aligned_alloc, + prefix, "aligned_alloc")) { + return false; + } if (!InitMallocFunction(malloc_impl_handler, &table->realloc, prefix, "realloc")) { return false; diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h index 944d72ba9..ef9753819 100644 --- a/libc/include/stdlib.h +++ b/libc/include/stdlib.h @@ -79,6 +79,8 @@ unsigned long long strtoull(const char* __s, char** __end_ptr, int __base); int posix_memalign(void** __memptr, size_t __alignment, size_t __size) __INTRODUCED_IN(16); +void* aligned_alloc(size_t __alignment, size_t __size) __INTRODUCED_IN(28); + double strtod(const char* __s, char** __end_ptr); long double strtold(const char* __s, char** __end_ptr) __RENAME_LDBL(strtod, 3, 21); diff --git a/libc/libc.arm.map b/libc/libc.arm.map index 839c40651..a7977ebf7 100644 --- a/libc/libc.arm.map +++ b/libc/libc.arm.map @@ -1322,6 +1322,7 @@ LIBC_P { # introduced=P global: __freading; __fwriting; + aligned_alloc; endhostent; endnetent; endprotoent; diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map index a39a2334a..1cae22b56 100644 --- a/libc/libc.arm64.map +++ b/libc/libc.arm64.map @@ -1242,6 +1242,7 @@ LIBC_P { # introduced=P global: __freading; __fwriting; + aligned_alloc; endhostent; endnetent; endprotoent; diff --git a/libc/libc.map.txt b/libc/libc.map.txt index b3bfa8a8f..1188a853a 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -1347,6 +1347,7 @@ LIBC_P { # introduced=P global: __freading; __fwriting; + aligned_alloc; endhostent; endnetent; endprotoent; diff --git a/libc/libc.mips.map b/libc/libc.mips.map index 2a9313228..3d9507994 100644 --- a/libc/libc.mips.map +++ b/libc/libc.mips.map @@ -1306,6 +1306,7 @@ LIBC_P { # introduced=P global: __freading; __fwriting; + aligned_alloc; endhostent; endnetent; endprotoent; diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map index a39a2334a..1cae22b56 100644 --- a/libc/libc.mips64.map +++ b/libc/libc.mips64.map @@ -1242,6 +1242,7 @@ LIBC_P { # introduced=P global: __freading; __fwriting; + aligned_alloc; endhostent; endnetent; endprotoent; diff --git a/libc/libc.x86.map b/libc/libc.x86.map index d2e73995e..bcb5feb0f 100644 --- a/libc/libc.x86.map +++ b/libc/libc.x86.map @@ -1304,6 +1304,7 @@ LIBC_P { # introduced=P global: __freading; __fwriting; + aligned_alloc; endhostent; endnetent; endprotoent; diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map index a39a2334a..1cae22b56 100644 --- a/libc/libc.x86_64.map +++ b/libc/libc.x86_64.map @@ -1242,6 +1242,7 @@ LIBC_P { # introduced=P global: __freading; __fwriting; + aligned_alloc; endhostent; endnetent; endprotoent; diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md index b7a12a5c6..69d064824 100644 --- a/libc/malloc_debug/README.md +++ b/libc/malloc_debug/README.md @@ -23,6 +23,7 @@ the normal allocation calls. The replaced calls are: * `realloc` * `posix_memalign` * `memalign` +* `aligned_alloc` * `malloc_usable_size` On 32 bit systems, these two deprecated functions are also replaced: @@ -324,6 +325,10 @@ pointer = memalign(alignment, size) **THREAD\_ID**: memalign pointer alignment size +pointer = aligned\_alloc(alignment, size) + +**THREAD\_ID**: memalign pointer alignment size + posix\_memalign(&pointer, alignment, size) **THREAD\_ID**: memalign pointer alignment size diff --git a/libc/malloc_debug/RecordData.cpp b/libc/malloc_debug/RecordData.cpp index 55d99438d..8e9c6716f 100644 --- a/libc/malloc_debug/RecordData.cpp +++ b/libc/malloc_debug/RecordData.cpp @@ -86,7 +86,7 @@ std::string ReallocEntry::GetString() const { old_pointer_, size_); } -// posix_memalign, memalgin, pvalloc, valloc all recorded with this class. +// aligned_alloc, posix_memalign, memalign, pvalloc, valloc all recorded with this class. MemalignEntry::MemalignEntry(void* pointer, size_t size, size_t alignment) : MallocEntry(pointer, size), alignment_(alignment) { } diff --git a/libc/malloc_debug/RecordData.h b/libc/malloc_debug/RecordData.h index ccabac22f..97ad81328 100644 --- a/libc/malloc_debug/RecordData.h +++ b/libc/malloc_debug/RecordData.h @@ -129,7 +129,7 @@ class ReallocEntry : public MallocEntry { DISALLOW_COPY_AND_ASSIGN(ReallocEntry); }; -// posix_memalign, memalign, pvalloc, valloc all recorded with this class. +// aligned_alloc, posix_memalign, memalign, pvalloc, valloc all recorded with this class. class MemalignEntry : public MallocEntry { public: MemalignEntry(void* pointer, size_t size, size_t alignment); diff --git a/libc/malloc_debug/exported32.map b/libc/malloc_debug/exported32.map index e92a7cfc8..78a6990aa 100644 --- a/libc/malloc_debug/exported32.map +++ b/libc/malloc_debug/exported32.map @@ -1,5 +1,6 @@ LIBC_MALLOC_DEBUG { global: + debug_aligned_alloc; debug_calloc; debug_dump_heap; debug_finalize; diff --git a/libc/malloc_debug/exported64.map b/libc/malloc_debug/exported64.map index 94104b0ca..2bfc38b82 100644 --- a/libc/malloc_debug/exported64.map +++ b/libc/malloc_debug/exported64.map @@ -1,5 +1,6 @@ LIBC_MALLOC_DEBUG { global: + debug_aligned_alloc; debug_calloc; debug_dump_heap; debug_finalize; diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp index a2ada2f2e..ecfbd7112 100644 --- a/libc/malloc_debug/malloc_debug.cpp +++ b/libc/malloc_debug/malloc_debug.cpp @@ -77,6 +77,7 @@ void debug_free_malloc_leak_info(uint8_t* info); size_t debug_malloc_usable_size(void* pointer); void* debug_malloc(size_t size); void debug_free(void* pointer); +void* debug_aligned_alloc(size_t alignment, size_t size); void* debug_memalign(size_t alignment, size_t bytes); void* debug_realloc(void* pointer, size_t bytes); void* debug_calloc(size_t nmemb, size_t bytes); @@ -669,6 +670,17 @@ int debug_mallopt(int param, int value) { return g_dispatch->mallopt(param, value); } +void* debug_aligned_alloc(size_t alignment, size_t size) { + if (DebugCallsDisabled()) { + return g_dispatch->aligned_alloc(alignment, size); + } + if (!powerof2(alignment)) { + errno = EINVAL; + return nullptr; + } + return debug_memalign(alignment, size); +} + int debug_posix_memalign(void** memptr, size_t alignment, size_t size) { if (DebugCallsDisabled()) { return g_dispatch->posix_memalign(memptr, alignment, size); diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp index 4e9066812..0e4a7d85f 100644 --- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp +++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp @@ -54,6 +54,7 @@ void* debug_calloc(size_t, size_t); void* debug_realloc(void*, size_t); int debug_posix_memalign(void**, size_t, size_t); void* debug_memalign(size_t, size_t); +void* debug_aligned_alloc(size_t, size_t); size_t debug_malloc_usable_size(void*); void debug_get_malloc_leak_info(uint8_t**, size_t*, size_t*, size_t*, size_t*); void debug_free_malloc_leak_info(uint8_t*); @@ -136,6 +137,7 @@ MallocDispatch MallocDebugTest::dispatch = { nullptr, nullptr, mallopt, + aligned_alloc, }; void VerifyAllocCalls(bool backtrace_enabled) { @@ -308,6 +310,11 @@ TEST_F(MallocDebugTest, expand_alloc) { ASSERT_LE(1039U, debug_malloc_usable_size(pointer)); debug_free(pointer); + pointer = debug_aligned_alloc(128, 15); + ASSERT_TRUE(pointer != nullptr); + ASSERT_LE(1039U, debug_malloc_usable_size(pointer)); + debug_free(pointer); + pointer = debug_realloc(nullptr, 30); ASSERT_TRUE(pointer != nullptr); ASSERT_LE(1054U, debug_malloc_usable_size(pointer)); @@ -1772,6 +1779,12 @@ void VerifyRecordAllocs() { debug_free(pointer); expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer); + pointer = debug_aligned_alloc(32, 50); + ASSERT_TRUE(pointer != nullptr); + expected += android::base::StringPrintf("%d: memalign %p 32 50\n", getpid(), pointer); + debug_free(pointer); + expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer); + ASSERT_EQ(0, debug_posix_memalign(&pointer, 32, 50)); ASSERT_TRUE(pointer != nullptr); expected += android::base::StringPrintf("%d: memalign %p 32 50\n", getpid(), pointer); diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h index cdae466c9..0dce03d94 100644 --- a/libc/private/bionic_malloc_dispatch.h +++ b/libc/private/bionic_malloc_dispatch.h @@ -46,6 +46,7 @@ typedef int (*MallocIterate)(uintptr_t, size_t, void (*)(uintptr_t, size_t, void typedef void (*MallocMallocDisable)(); typedef void (*MallocMallocEnable)(); typedef int (*MallocMallopt)(int, int); +typedef void* (*MallocAlignedAlloc)(size_t, size_t); #if defined(HAVE_DEPRECATED_MALLOC_FUNCS) typedef void* (*MallocPvalloc)(size_t); @@ -71,6 +72,7 @@ struct MallocDispatch { MallocMallocDisable malloc_disable; MallocMallocEnable malloc_enable; MallocMallopt mallopt; + MallocAlignedAlloc aligned_alloc; } __attribute__((aligned(32))); #endif diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp index caa7a851a..2fbd937da 100644 --- a/tests/stdlib_test.cpp +++ b/tests/stdlib_test.cpp @@ -35,6 +35,14 @@ #include #include +#if defined(__BIONIC__) + #define ALIGNED_ALLOC_AVAILABLE 1 +#elif defined(__GLIBC_PREREQ) + #if __GLIBC_PREREQ(2, 16) + #define ALIGNED_ALLOC_AVAILABLE 1 + #endif +#endif + // The random number generator tests all set the seed, get four values, reset the seed and check // that they get the first two values repeated, and then reset the seed and check two more values // to rule out the possibility that we're just going round a cycle of four values. @@ -226,6 +234,50 @@ TEST(stdlib, posix_memalign_overflow) { ASSERT_NE(0, posix_memalign(&ptr, 16, SIZE_MAX)); } +TEST(stdlib, aligned_alloc_sweep) { +#if defined(ALIGNED_ALLOC_AVAILABLE) + // Verify powers of 2 up to 2048 allocate, and verify that all other + // alignment values between the powers of 2 fail. + size_t last_align = 1; + for (size_t align = 1; align <= 2048; align <<= 1) { + // Try all of the non power of 2 values from the last until this value. + for (size_t fail_align = last_align + 1; fail_align < align; fail_align++) { + ASSERT_TRUE(aligned_alloc(fail_align, 256) == nullptr) + << "Unexpected success at align " << fail_align; + ASSERT_EQ(EINVAL, errno) << "Unexpected errno at align " << fail_align; + } + void* ptr = aligned_alloc(align, 256); + ASSERT_TRUE(ptr != nullptr) << "Unexpected failure at align " << align; + ASSERT_EQ(0U, reinterpret_cast(ptr) & (align - 1)) + << "Did not return a valid aligned ptr " << ptr << " expected alignment " << align; + free(ptr); + last_align = align; + } +#else + GTEST_LOG_(INFO) << "This test requires a C library that has aligned_alloc.\n"; +#endif +} + +TEST(stdlib, aligned_alloc_overflow) { +#if defined(ALIGNED_ALLOC_AVAILABLE) + ASSERT_TRUE(aligned_alloc(16, SIZE_MAX) == nullptr); +#else + GTEST_LOG_(INFO) << "This test requires a C library that has aligned_alloc.\n"; +#endif +} + +TEST(stdlib, aligned_alloc_size_not_multiple_of_alignment) { +#if defined(ALIGNED_ALLOC_AVAILABLE) + for (size_t size = 1; size <= 2048; size++) { + void* ptr = aligned_alloc(2048, size); + ASSERT_TRUE(ptr != nullptr) << "Failed at size " << std::to_string(size); + free(ptr); + } +#else + GTEST_LOG_(INFO) << "This test requires a C library that has aligned_alloc.\n"; +#endif +} + TEST(stdlib, realpath__NULL_filename) { errno = 0; // Work around the compile-time error generated by FORTIFY here.