Index: lib/scudo/scudo_allocator.h =================================================================== --- lib/scudo/scudo_allocator.h +++ lib/scudo/scudo_allocator.h @@ -115,12 +115,10 @@ void initScudo(); -void *scudoMalloc(uptr Size, AllocType Type); -void scudoFree(void *Ptr, AllocType Type); -void scudoSizedFree(void *Ptr, uptr Size, AllocType Type); +void *scudoAllocate(uptr Size, uptr Alignment, AllocType Type); +void scudoDeallocate(void *Ptr, uptr Size, uptr Alignment, AllocType Type); void *scudoRealloc(void *Ptr, uptr Size); void *scudoCalloc(uptr NMemB, uptr Size); -void *scudoMemalign(uptr Alignment, uptr Size); void *scudoValloc(uptr Size); void *scudoPvalloc(uptr Size); int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size); Index: lib/scudo/scudo_allocator.cpp =================================================================== --- lib/scudo/scudo_allocator.cpp +++ lib/scudo/scudo_allocator.cpp @@ -480,7 +480,8 @@ // Deallocates a Chunk, which means either adding it to the quarantine or // directly returning it to the backend if criteria are met. - void deallocate(void *Ptr, uptr DeleteSize, AllocType Type) { + void deallocate(void *Ptr, uptr DeleteSize, uptr DeleteAlignment, + AllocType Type) { // For a deallocation, we only ensure minimal initialization, meaning thread // local data will be left uninitialized for now (when using ELF TLS). The // fallback cache will be used instead. This is a workaround for a situation @@ -513,6 +514,7 @@ dieWithMessage("invalid sized delete when deallocating address %p\n", Ptr); } + (void)DeleteAlignment; // TODO(kostyak): verify that the alignment matches. quarantineOrDeallocateChunk(Ptr, &Header, Size); } @@ -627,23 +629,23 @@ Instance.commitBack(this); } -void *scudoMalloc(uptr Size, AllocType Type) { - return SetErrnoOnNull(Instance.allocate(Size, MinAlignment, Type)); -} - -void scudoFree(void *Ptr, AllocType Type) { - Instance.deallocate(Ptr, 0, Type); +void *scudoAllocate(uptr Size, uptr Alignment, AllocType Type) { + if (Alignment && UNLIKELY(!IsPowerOfTwo(Alignment))) { + errno = EINVAL; + return Instance.handleBadRequest(); + } + return SetErrnoOnNull(Instance.allocate(Size, Alignment, Type)); } -void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) { - Instance.deallocate(Ptr, Size, Type); +void scudoDeallocate(void *Ptr, uptr Size, uptr Alignment, AllocType Type) { + Instance.deallocate(Ptr, Size, Alignment, Type); } void *scudoRealloc(void *Ptr, uptr Size) { if (!Ptr) return SetErrnoOnNull(Instance.allocate(Size, MinAlignment, FromMalloc)); if (Size == 0) { - Instance.deallocate(Ptr, 0, FromMalloc); + Instance.deallocate(Ptr, 0, 0, FromMalloc); return nullptr; } return SetErrnoOnNull(Instance.reallocate(Ptr, Size)); @@ -669,14 +671,6 @@ return SetErrnoOnNull(Instance.allocate(Size, PageSize, FromMemalign)); } -void *scudoMemalign(uptr Alignment, uptr Size) { - if (UNLIKELY(!IsPowerOfTwo(Alignment))) { - errno = EINVAL; - return Instance.handleBadRequest(); - } - return SetErrnoOnNull(Instance.allocate(Size, Alignment, FromMemalign)); -} - int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) { if (UNLIKELY(!CheckPosixMemalignAlignment(Alignment))) { Instance.handleBadRequest(); Index: lib/scudo/scudo_malloc.cpp =================================================================== --- lib/scudo/scudo_malloc.cpp +++ lib/scudo/scudo_malloc.cpp @@ -20,11 +20,11 @@ extern "C" { INTERCEPTOR_ATTRIBUTE void free(void *ptr) { - scudoFree(ptr, FromMalloc); + scudoDeallocate(ptr, 0, 0, FromMalloc); } INTERCEPTOR_ATTRIBUTE void *malloc(SIZE_T size) { - return scudoMalloc(size, FromMalloc); + return scudoAllocate(size, 0, FromMalloc); } INTERCEPTOR_ATTRIBUTE void *realloc(void *ptr, SIZE_T size) { @@ -50,7 +50,7 @@ #if SANITIZER_INTERCEPT_MEMALIGN INTERCEPTOR_ATTRIBUTE void *memalign(SIZE_T alignment, SIZE_T size) { - return scudoMemalign(alignment, size); + return scudoAllocate(size, alignment, FromMemalign); } INTERCEPTOR_ATTRIBUTE Index: lib/scudo/scudo_new_delete.cpp =================================================================== --- lib/scudo/scudo_new_delete.cpp +++ lib/scudo/scudo_new_delete.cpp @@ -24,51 +24,84 @@ // Fake std::nothrow_t to avoid including . namespace std { struct nothrow_t {}; +enum class align_val_t: size_t {}; } // namespace std // TODO(alekseys): throw std::bad_alloc instead of dying on OOM. +#define OPERATOR_NEW_BODY_ALIGN(Type, Align, NoThrow) \ + void *Ptr = scudoAllocate(size, static_cast(Align), Type); \ + if (!NoThrow && UNLIKELY(!Ptr)) DieOnFailure::OnOOM(); \ + return Ptr; +#define OPERATOR_NEW_BODY(Type, NoThrow) \ + OPERATOR_NEW_BODY_ALIGN(Type, 0, NoThrow) + +CXX_OPERATOR_ATTRIBUTE +void *operator new(size_t size) +{ OPERATOR_NEW_BODY(FromNew, /*NoThrow=*/false); } +CXX_OPERATOR_ATTRIBUTE +void *operator new[](size_t size) +{ OPERATOR_NEW_BODY(FromNewArray, /*NoThrow=*/false); } +CXX_OPERATOR_ATTRIBUTE +void *operator new(size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FromNew, /*NoThrow=*/true); } +CXX_OPERATOR_ATTRIBUTE +void *operator new[](size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FromNewArray, /*NoThrow=*/true); } +CXX_OPERATOR_ATTRIBUTE +void *operator new(size_t size, std::align_val_t align) +{ OPERATOR_NEW_BODY_ALIGN(FromNew, align, /*NoThrow=*/false); } +CXX_OPERATOR_ATTRIBUTE +void *operator new[](size_t size, std::align_val_t align) +{ OPERATOR_NEW_BODY_ALIGN(FromNewArray, align, /*NoThrow=*/false); } +CXX_OPERATOR_ATTRIBUTE +void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_NEW_BODY_ALIGN(FromNew, align, /*NoThrow=*/true); } +CXX_OPERATOR_ATTRIBUTE +void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_NEW_BODY_ALIGN(FromNewArray, align, /*NoThrow=*/true); } + +#define OPERATOR_DELETE_BODY(Type) \ + scudoDeallocate(ptr, 0, 0, Type); +#define OPERATOR_DELETE_BODY_SIZE(Type) \ + scudoDeallocate(ptr, size, 0, Type); +#define OPERATOR_DELETE_BODY_ALIGN(Type) \ + scudoDeallocate(ptr, 0, static_cast(align), Type); +#define OPERATOR_DELETE_BODY_SIZE_ALIGN(Type) \ + scudoDeallocate(ptr, size, static_cast(align), Type); + +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr) NOEXCEPT +{ OPERATOR_DELETE_BODY(FromNew); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr) NOEXCEPT +{ OPERATOR_DELETE_BODY(FromNewArray); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FromNew); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FromNewArray); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, size_t size) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE(FromNew); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, size_t size) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE(FromNewArray); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_ALIGN(FromNew); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_ALIGN(FromNewArray); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY_ALIGN(FromNew); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY_ALIGN(FromNewArray); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, size_t size, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FromNew); } CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size) { - void *res = scudoMalloc(size, FromNew); - if (UNLIKELY(!res)) DieOnFailure::OnOOM(); - return res; -} -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size) { - void *res = scudoMalloc(size, FromNewArray); - if (UNLIKELY(!res)) DieOnFailure::OnOOM(); - return res; -} -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size, std::nothrow_t const&) { - return scudoMalloc(size, FromNew); -} -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size, std::nothrow_t const&) { - return scudoMalloc(size, FromNewArray); -} - -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr) NOEXCEPT { - return scudoFree(ptr, FromNew); -} -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr) NOEXCEPT { - return scudoFree(ptr, FromNewArray); -} -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::nothrow_t const&) NOEXCEPT { - return scudoFree(ptr, FromNew); -} -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::nothrow_t const&) NOEXCEPT { - return scudoFree(ptr, FromNewArray); -} -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, size_t size) NOEXCEPT { - scudoSizedFree(ptr, size, FromNew); -} -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, size_t size) NOEXCEPT { - scudoSizedFree(ptr, size, FromNewArray); -} +void operator delete[](void *ptr, size_t size, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FromNewArray); } Index: test/scudo/aligned-new.cpp =================================================================== --- test/scudo/aligned-new.cpp +++ test/scudo/aligned-new.cpp @@ -0,0 +1,84 @@ +// RUN: %clangxx_scudo -std=c++1z -faligned-allocation %s -o %t +// RUN: %run %t valid 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 + +// Tests that the C++17 aligned new/delete operators are working as expected. +// Currently we do not check the consistency of the alignment on deallocation, +// so this just tests that the APIs work. + +#include +#include +#include +#include + +// Define all new/delete to not depend on the version provided by the platform. + +namespace std { +struct nothrow_t {}; +static const nothrow_t nothrow; +enum class align_val_t : size_t {}; +} // namespace std + +void *operator new(size_t); +void *operator new[](size_t); +void *operator new(size_t, std::nothrow_t const&); +void *operator new[](size_t, std::nothrow_t const&); +void *operator new(size_t, std::align_val_t); +void *operator new[](size_t, std::align_val_t); +void *operator new(size_t, std::align_val_t, std::nothrow_t const&); +void *operator new[](size_t, std::align_val_t, std::nothrow_t const&); + +void operator delete(void*) throw(); +void operator delete[](void*) throw(); +void operator delete(void*, std::nothrow_t const&); +void operator delete[](void*, std::nothrow_t const&); +void operator delete(void*, size_t) throw(); +void operator delete[](void*, size_t) throw(); +void operator delete(void*, std::align_val_t) throw(); +void operator delete[](void*, std::align_val_t) throw(); +void operator delete(void*, std::align_val_t, std::nothrow_t const&); +void operator delete[](void*, std::align_val_t, std::nothrow_t const&); +void operator delete(void*, size_t, std::align_val_t) throw(); +void operator delete[](void*, size_t, std::align_val_t) throw(); + +template +inline T* break_optimization(T *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); + return arg; +} + +struct S12 { int a, b, c; }; +struct alignas(128) S12_128 { int a, b, c; }; +struct alignas(256) S12_256 { int a, b, c; }; +struct alignas(512) S1024_512 { char a[1024]; }; +struct alignas(1024) S1024_1024 { char a[1024]; }; + +int main(int argc, char **argv) { + assert(argc == 2); + + if (!strcmp(argv[1], "valid")) { + // Standard use case. + delete break_optimization(new S12); + delete break_optimization(new S12_128); + delete[] break_optimization(new S12_128[4]); + delete break_optimization(new S12_256); + delete break_optimization(new S1024_512); + delete[] break_optimization(new S1024_512[4]); + delete break_optimization(new S1024_1024); + + // Call directly the aligned versions of the operators. + const size_t alignment = 1U << 8; + void *p = operator new(1, static_cast(alignment)); + assert((reinterpret_cast(p) & (alignment - 1)) == 0); + operator delete(p, static_cast(alignment)); + } + if (!strcmp(argv[1], "invalid")) { + // Alignment must be a power of 2. + const size_t alignment = (1U << 8) - 1; + void *p = operator new(1, static_cast(alignment), + std::nothrow); + assert(!p); + } + + return 0; +}