Skip to content

[libc++] Hardened unique_ptr<T[]> incorrectly handles custom deleters #110683

Closed
@ldionne

Description

@ldionne

This makes the attached program crash (…in most runs, not 100% of the time):

unique.cc.zip

 % out/gn/bin/clang++ unique.cc -isysroot $(xcrun -show-sdk-path) -std=c++20 -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE     
% ./a.out
zsh: trace trap  ./a.out

lldb shows that this indeed this check:

    frame #0: 0x00000001000029e4 a.out`std::__1::unique_ptr<std::__1::vector<int, std::__1::allocator<int>> [], AlignedDeleter<std::__1::vector<int, std::__1::allocator<int>>>>::operator[][abi:se200000](unsigned long) const [inlined] __clang_trap_msg$libc++$/Users/thakis/src/llvm-project/out/gn/bin/../include/c++/v1/__memory/unique_ptr.h:566: assertion __checker_.__in_bounds(std::__to_address(__ptr_), __i) failed: unique_ptr<T[]>::operator[](index): index out of range

The program uses a unique_ptr with a custom deleter to refer to raw malloc-ed memory, "to allow having uninitialized entries inside it".

It has this typedef:

template <typename T>
using AlignedUniquePtr =
    std::unique_ptr<T, AlignedDeleter<typename std::remove_extent<T>::type>>;

It uses this typedef with an array type:

  // Underlying storage. It's raw malloc-ed rather than being a unique_ptr<T[]>
  // to allow having uninitialized entries inside it.
  AlignedUniquePtr<T[]> entries_;

It creates objects of this unique_ptr like so:

template <typename T>
AlignedUniquePtr<T> AlignedAllocTyped(size_t n_membs) {
  using TU = typename std::remove_extent<T>::type;
  return AlignedUniquePtr<T>(
      static_cast<TU*>(AlignedAlloc(alignof(TU), sizeof(TU) * n_membs)));
}

AlignedAlloc() is basically malloc – in particular, nothing calls new[], so it doesn't write an array cookie.

The custom deleter looks like so (AlignedFree() is just free()):

template <typename T>
struct AlignedDeleter {
  inline void operator()(T* ptr) const { AlignedFree(ptr); }
};

The class containing entries_ calls dtors as needed, and the deleter really only needs to free memory. delete [] is never called.

Is this a valid use of unique_ptr<T[]>? If so, should this hardening only be used for unique_ptrs that don't have a custom deleter?

Originally posted by @nico in #91798 (comment)

Metadata

Metadata

Assignees

Labels

hardeningIssues related to the hardening effortlibc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions