Skip to content

Commit 4841e1a

Browse files
committed
[libc++] Add an ABI setting to harden unique_ptr<T[]>::operator[]
1 parent 9232591 commit 4841e1a

File tree

5 files changed

+81
-3
lines changed

5 files changed

+81
-3
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
set(LIBCXX_HARDENING_MODE "fast" CACHE STRING "")
2-
set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS" CACHE STRING "")
2+
set(LIBCXX_ABI_DEFINES "_LIBCPP_ABI_BOUNDED_ITERATORS;_LIBCPP_ABI_BOUNDED_UNIQUE_PTR" CACHE STRING "")

libcxx/include/__config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@
206206
// - `array`.
207207
// #define _LIBCPP_ABI_BOUNDED_ITERATORS
208208

209+
// Tracks the bounds of the array owned by std::unique_ptr<T[]>, allowing it to trap when accessed out-of-bounds.
210+
// #define _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
211+
209212
// } ABI
210213

211214
// HARDENING {

libcxx/include/__memory/unique_ptr.h

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <__type_traits/type_identity.h>
4040
#include <__utility/forward.h>
4141
#include <__utility/move.h>
42+
#include <__utility/private_constructor_tag.h>
4243
#include <cstddef>
4344

4445
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -301,6 +302,21 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
301302

302303
private:
303304
__compressed_pair<pointer, deleter_type> __ptr_;
305+
#ifdef _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
306+
// Under the "bounded unique_ptr" ABI, we store the size of the allocation when it is known.
307+
// The size of the allocation can be known when unique_ptr is created via make_unique or a
308+
// similar API, however it can't be known when constructed with e.g.
309+
//
310+
// unique_ptr<T[]> ptr(new T[3]);
311+
//
312+
// In that case, we don't know the size of the allocation from within the unique_ptr.
313+
// Semantically, we'd need to store `optional<size_t>`. However, since that is really
314+
// heavy weight, we instead store a size_t and use 0 as a magic value meaning that we
315+
// don't know the size. This means that we can't catch OOB accesses inside a unique_ptr
316+
// with a 0-sized allocation, however since this is a degenerate case, it doesn't matter
317+
// in practice.
318+
size_t __size_ = 0;
319+
#endif
304320

305321
template <class _From>
306322
struct _CheckArrayPointerConversion : is_same<_From, pointer> {};
@@ -397,6 +413,18 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
397413
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr&& __u) _NOEXCEPT
398414
: __ptr_(__u.release(), std::forward<deleter_type>(__u.get_deleter())) {}
399415

416+
// Constructor used by make_unique & friends to pass the size that was allocated
417+
template <class _Ptr>
418+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit unique_ptr(
419+
__private_constructor_tag, _Ptr __ptr, size_t __size) _NOEXCEPT
420+
: __ptr_(__ptr, __value_init_tag())
421+
#ifdef _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
422+
,
423+
__size_(__size)
424+
#endif
425+
{
426+
}
427+
400428
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr&& __u) _NOEXCEPT {
401429
reset(__u.release());
402430
__ptr_.second() = std::forward<deleter_type>(__u.get_deleter());
@@ -434,6 +462,10 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
434462
}
435463

436464
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 __add_lvalue_reference_t<_Tp> operator[](size_t __i) const {
465+
#ifdef _LIBCPP_ABI_BOUNDED_UNIQUE_PTR
466+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
467+
__size_ == 0 || __i < __size_, "unique_ptr<T[]>::operator[](index): index out of range");
468+
#endif
437469
return __ptr_.first()[__i];
438470
}
439471
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer get() const _NOEXCEPT { return __ptr_.first(); }
@@ -624,7 +656,7 @@ template <class _Tp>
624656
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound
625657
make_unique(size_t __n) {
626658
typedef __remove_extent_t<_Tp> _Up;
627-
return unique_ptr<_Tp>(new _Up[__n]());
659+
return unique_ptr<_Tp>(__private_constructor_tag(), new _Up[__n](), __n);
628660
}
629661

630662
template <class _Tp, class... _Args>
@@ -643,7 +675,7 @@ make_unique_for_overwrite() {
643675
template <class _Tp>
644676
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound
645677
make_unique_for_overwrite(size_t __n) {
646-
return unique_ptr<_Tp>(new __remove_extent_t<_Tp>[__n]);
678+
return unique_ptr<_Tp>(__private_constructor_tag(), new __remove_extent_t<_Tp>[__n], __n);
647679
}
648680

649681
template <class _Tp, class... _Args>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: has-unix-headers
10+
// UNSUPPORTED: c++03, c++11
11+
// UNSUPPORTED: libcpp-hardening-mode=none
12+
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
13+
// REQUIRES: libcpp-has-abi-bounded-unique_ptr
14+
15+
// <memory>
16+
//
17+
// unique_ptr<T[]>
18+
//
19+
// T& operator[](std::size_t);
20+
21+
// This test ensures that we catch an out-of-bounds access in std::unique_ptr<T[]>::operator[]
22+
// when unique_ptr has the appropriate ABI configuration.
23+
24+
#include <memory>
25+
26+
#include "check_assertion.h"
27+
28+
int main(int, char**) {
29+
{
30+
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(5);
31+
TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = 42, "unique_ptr<T[]>::operator[](index): index out of range");
32+
}
33+
34+
#if TEST_STD_VER >= 20
35+
{
36+
std::unique_ptr<int[]> ptr = std::make_unique_for_overwrite<int[]>(5);
37+
TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = 42, "unique_ptr<T[]>::operator[](index): index out of range");
38+
}
39+
#endif
40+
41+
return 0;
42+
}

libcxx/utils/libcxx/test/features.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ def _getAndroidDeviceApi(cfg):
312312
"_LIBCPP_NO_VCRUNTIME": "libcpp-no-vcruntime",
313313
"_LIBCPP_ABI_VERSION": "libcpp-abi-version",
314314
"_LIBCPP_ABI_BOUNDED_ITERATORS": "libcpp-has-abi-bounded-iterators",
315+
"_LIBCPP_ABI_BOUNDED_UNIQUE_PTR": "libcpp-has-abi-bounded-unique_ptr",
315316
"_LIBCPP_HAS_NO_FILESYSTEM": "no-filesystem",
316317
"_LIBCPP_HAS_NO_RANDOM_DEVICE": "no-random-device",
317318
"_LIBCPP_HAS_NO_LOCALIZATION": "no-localization",

0 commit comments

Comments
 (0)