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