| 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, c++14, c++17 |
| 11 | // UNSUPPORTED: libcpp-hardening-mode=none |
| 12 | // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing |
| 13 | |
| 14 | // <memory> |
| 15 | // |
| 16 | // unique_ptr<T[]> |
| 17 | // |
| 18 | // T& operator[](std::size_t); |
| 19 | |
| 20 | // This test ensures that we catch an out-of-bounds access in std::unique_ptr<T[]>::operator[] |
| 21 | // when unique_ptr has the appropriate ABI configuration. |
| 22 | |
| 23 | #include <memory> |
| 24 | #include <cstddef> |
| 25 | #include <string> |
| 26 | |
| 27 | #include "check_assertion.h" |
| 28 | #include "type_algorithms.h" |
| 29 | #include "test_macros.h" |
| 30 | |
| 31 | struct MyDeleter { |
| 32 | MyDeleter() = default; |
| 33 | |
| 34 | // required to exercise converting move-constructor |
| 35 | template <class T> |
| 36 | MyDeleter(std::default_delete<T> const&) {} |
| 37 | |
| 38 | // required to exercise converting move-assignment |
| 39 | template <class T> |
| 40 | MyDeleter& operator=(std::default_delete<T> const&) { |
| 41 | return *this; |
| 42 | } |
| 43 | |
| 44 | template <class T> |
| 45 | void operator()(T* ptr) const { |
| 46 | delete[] ptr; |
| 47 | } |
| 48 | }; |
| 49 | |
| 50 | template <class WithCookie, class NoCookie> |
| 51 | void test() { |
| 52 | LIBCPP_STATIC_ASSERT(std::__has_array_cookie<WithCookie>::value); |
| 53 | LIBCPP_STATIC_ASSERT(!std::__has_array_cookie<NoCookie>::value); |
| 54 | |
| 55 | // For types with an array cookie, we can always detect OOB accesses. Note that reliance on an array |
| 56 | // cookie is limited to the default deleter, since a unique_ptr with a custom deleter may not have |
| 57 | // been allocated with `new T[n]`. |
| 58 | { |
| 59 | { |
| 60 | std::unique_ptr<WithCookie[]> ptr(new WithCookie[5]); |
| 61 | TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 62 | } |
| 63 | { |
| 64 | std::unique_ptr<WithCookie[]> ptr = std::make_unique<WithCookie[]>(5); |
| 65 | TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 66 | } |
| 67 | #if TEST_STD_VER >= 20 |
| 68 | { |
| 69 | std::unique_ptr<WithCookie[]> ptr = std::make_unique_for_overwrite<WithCookie[]>(5); |
| 70 | TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = WithCookie(), "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 71 | } |
| 72 | #endif |
| 73 | } |
| 74 | |
| 75 | // For types that don't have an array cookie, things are a bit more complicated. We can detect OOB accesses |
| 76 | // only when the unique_ptr is created via an API where the size is passed down to the library so that we |
| 77 | // can store it inside the unique_ptr. That requires the appropriate ABI configuration to be enabled. |
| 78 | // |
| 79 | // Note that APIs that allow the size to be passed down to the library only support the default deleter |
| 80 | // as of writing this test. |
| 81 | #if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR) |
| 82 | { |
| 83 | { |
| 84 | std::unique_ptr<NoCookie[]> ptr = std::make_unique<NoCookie[]>(5); |
| 85 | TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 86 | } |
| 87 | # if TEST_STD_VER >= 20 |
| 88 | { |
| 89 | std::unique_ptr<NoCookie[]> ptr = std::make_unique_for_overwrite<NoCookie[]>(5); |
| 90 | TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = NoCookie(), "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 91 | } |
| 92 | # endif |
| 93 | } |
| 94 | #endif |
| 95 | |
| 96 | // Make sure that we carry the bounds information properly through conversions, assignments, etc. |
| 97 | // These tests are only relevant when the ABI setting is enabled (with a stateful bounds-checker). |
| 98 | #if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR) |
| 99 | types::for_each(types::type_list<NoCookie, WithCookie>(), []<class T> { |
| 100 | // Bounds carried through move construction |
| 101 | { |
| 102 | std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5); |
| 103 | std::unique_ptr<T[]> other(std::move(ptr)); |
| 104 | TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 105 | } |
| 106 | |
| 107 | // Bounds carried through move assignment |
| 108 | { |
| 109 | std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5); |
| 110 | std::unique_ptr<T[]> other; |
| 111 | other = std::move(ptr); |
| 112 | TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 113 | } |
| 114 | |
| 115 | // Bounds carried through converting move-constructor |
| 116 | { |
| 117 | std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5); |
| 118 | std::unique_ptr<T[], MyDeleter> other(std::move(ptr)); |
| 119 | TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 120 | } |
| 121 | |
| 122 | // Bounds carried through converting move-assignment |
| 123 | { |
| 124 | std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5); |
| 125 | std::unique_ptr<T[], MyDeleter> other; |
| 126 | other = std::move(ptr); |
| 127 | TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range" ); |
| 128 | } |
| 129 | }); |
| 130 | #endif |
| 131 | } |
| 132 | |
| 133 | template <std::size_t Size> |
| 134 | struct NoCookie { |
| 135 | char padding[Size]; |
| 136 | }; |
| 137 | |
| 138 | template <std::size_t Size> |
| 139 | struct WithCookie { |
| 140 | WithCookie() = default; |
| 141 | WithCookie(WithCookie const&) {} |
| 142 | WithCookie& operator=(WithCookie const&) { return *this; } |
| 143 | ~WithCookie() {} |
| 144 | char padding[Size]; |
| 145 | }; |
| 146 | |
| 147 | int main(int, char**) { |
| 148 | test<WithCookie<1>, NoCookie<1>>(); |
| 149 | test<WithCookie<2>, NoCookie<2>>(); |
| 150 | test<WithCookie<3>, NoCookie<3>>(); |
| 151 | test<WithCookie<4>, NoCookie<4>>(); |
| 152 | test<WithCookie<8>, NoCookie<8>>(); |
| 153 | test<WithCookie<16>, NoCookie<16>>(); |
| 154 | test<WithCookie<32>, NoCookie<32>>(); |
| 155 | test<WithCookie<256>, NoCookie<256>>(); |
| 156 | test<std::string, int>(); |
| 157 | |
| 158 | return 0; |
| 159 | } |
| 160 | |