[libcxx-commits] [libcxx] [libc++] Add an ABI setting to harden unique_ptr<T[]>::operator[] (PR #91798)

Nico Weber via libcxx-commits libcxx-commits at lists.llvm.org
Mon Sep 30 15:03:12 PDT 2024


nico wrote:

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

[unique.cc.zip](https://github.com/user-attachments/files/17196807/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?

https://github.com/llvm/llvm-project/pull/91798


More information about the libcxx-commits mailing list