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