[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