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
31struct 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
50template <class WithCookie, class NoCookie>
51void 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
133template <std::size_t Size>
134struct NoCookie {
135 char padding[Size];
136};
137
138template <std::size_t Size>
139struct WithCookie {
140 WithCookie() = default;
141 WithCookie(WithCookie const&) {}
142 WithCookie& operator=(WithCookie const&) { return *this; }
143 ~WithCookie() {}
144 char padding[Size];
145};
146
147int 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

source code of libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/unique.ptr.observers/assert.subscript.pass.cpp