208 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// REQUIRES: has-unix-headers10// UNSUPPORTED: c++03, c++11, c++14, c++1711// UNSUPPORTED: libcpp-hardening-mode=none12// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing13 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-constructor35 template <class T>36 MyDeleter(std::default_delete<T> const&) {}37 38 // required to exercise converting move-assignment39 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 array56 // cookie is limited to the default deleter, since a unique_ptr with a custom deleter may not have57 // been allocated with `new T[n]`.58 {59 {60 std::unique_ptr<WithCookie[]> ptr(new WithCookie[5]);61 assert(&ptr[1] == ptr.get() + 1); // ensure no assertion62 TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");63 }64 {65 std::unique_ptr<WithCookie[]> ptr = std::make_unique<WithCookie[]>(5);66 assert(&ptr[1] == ptr.get() + 1); // ensure no assertion67 TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");68 }69#if TEST_STD_VER >= 2070 {71 std::unique_ptr<WithCookie[]> ptr = std::make_unique_for_overwrite<WithCookie[]>(5);72 assert(&ptr[1] == ptr.get() + 1); // ensure no assertion73 TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = WithCookie(), "unique_ptr<T[]>::operator[](index): index out of range");74 }75#endif76 }77 78 // For types that don't have an array cookie, things are a bit more complicated. We can detect OOB accesses79 // only when the unique_ptr is created via an API where the size is passed down to the library so that we80 // can store it inside the unique_ptr. That requires the appropriate ABI configuration to be enabled.81 //82 // Note that APIs that allow the size to be passed down to the library only support the default deleter83 // as of writing this test.84#if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)85 {86 {87 std::unique_ptr<NoCookie[]> ptr = std::make_unique<NoCookie[]>(5);88 assert(&ptr[1] == ptr.get() + 1); // ensure no assertion89 TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");90 }91# if TEST_STD_VER >= 2092 {93 std::unique_ptr<NoCookie[]> ptr = std::make_unique_for_overwrite<NoCookie[]>(5);94 assert(&ptr[1] == ptr.get() + 1); // ensure no assertion95 TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = NoCookie(), "unique_ptr<T[]>::operator[](index): index out of range");96 }97# endif98 }99#endif100 101 // Make sure that we carry the bounds information properly through conversions, assignments, etc.102 // These tests are only relevant when the ABI setting is enabled (with a stateful bounds-checker).103#if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)104 types::for_each(types::type_list<NoCookie, WithCookie>(), []<class T> {105 // Bounds carried through move construction106 {107 std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5);108 std::unique_ptr<T[]> other(std::move(ptr));109 assert(&other[1] == other.get() + 1); // ensure no assertion110 TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range");111 }112 113 // Bounds carried through move assignment114 {115 std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5);116 std::unique_ptr<T[]> other;117 other = std::move(ptr);118 assert(&other[1] == other.get() + 1); // ensure no assertion119 TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range");120 }121 122 // Bounds carried through converting move-constructor123 {124 std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5);125 std::unique_ptr<T[], MyDeleter> other(std::move(ptr));126 assert(&other[1] == other.get() + 1); // ensure no assertion127 TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range");128 }129 130 // Bounds carried through converting move-assignment131 {132 std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5);133 std::unique_ptr<T[], MyDeleter> other;134 other = std::move(ptr);135 assert(&other[1] == other.get() + 1); // ensure no assertion136 TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range");137 }138 });139#endif140}141 142template <std::size_t Size>143struct NoCookie {144 char padding[Size];145};146 147template <std::size_t Size>148struct WithCookie {149 WithCookie() = default;150 WithCookie(WithCookie const&) {}151 WithCookie& operator=(WithCookie const&) { return *this; }152 ~WithCookie() {}153 char padding[Size];154};155 156template <std::size_t Size>157struct alignas(128) OveralignedNoCookie {158 char padding[Size];159};160 161template <std::size_t Size>162struct alignas(128) OveralignedWithCookie {163 OveralignedWithCookie() = default;164 OveralignedWithCookie(OveralignedWithCookie const&) {}165 OveralignedWithCookie& operator=(OveralignedWithCookie const&) { return *this; }166 ~OveralignedWithCookie() {}167 char padding[Size];168};169 170// These types have a different ABI alignment (alignof) and preferred alignment (__alignof) on some platforms.171// Make sure things work with these types because array cookies can be sensitive to preferred alignment on some172// platforms.173struct WithCookiePreferredAlignment {174 WithCookiePreferredAlignment() = default;175 WithCookiePreferredAlignment(WithCookiePreferredAlignment const&) {}176 WithCookiePreferredAlignment& operator=(WithCookiePreferredAlignment const&) { return *this; }177 ~WithCookiePreferredAlignment() {}178 long double data;179};180struct NoCookiePreferredAlignment {181 long double data;182};183 184int main(int, char**) {185 test<WithCookie<1>, NoCookie<1>>();186 test<WithCookie<2>, NoCookie<2>>();187 test<WithCookie<3>, NoCookie<3>>();188 test<WithCookie<4>, NoCookie<4>>();189 test<WithCookie<8>, NoCookie<8>>();190 test<WithCookie<16>, NoCookie<16>>();191 test<WithCookie<32>, NoCookie<32>>();192 test<WithCookie<256>, NoCookie<256>>();193 194 test<OveralignedWithCookie<1>, OveralignedNoCookie<1>>();195 test<OveralignedWithCookie<2>, OveralignedNoCookie<2>>();196 test<OveralignedWithCookie<3>, OveralignedNoCookie<3>>();197 test<OveralignedWithCookie<4>, OveralignedNoCookie<4>>();198 test<OveralignedWithCookie<8>, OveralignedNoCookie<8>>();199 test<OveralignedWithCookie<16>, OveralignedNoCookie<16>>();200 test<OveralignedWithCookie<32>, OveralignedNoCookie<32>>();201 test<OveralignedWithCookie<256>, OveralignedNoCookie<256>>();202 203 test<std::string, int>();204 test<WithCookiePreferredAlignment, NoCookiePreferredAlignment>();205 206 return 0;207}208