//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // REQUIRES: has-unix-headers // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-hardening-mode=none // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing // // // unique_ptr // // T& operator[](std::size_t); // This test ensures that we catch an out-of-bounds access in std::unique_ptr::operator[] // when unique_ptr has the appropriate ABI configuration. #include #include #include #include "check_assertion.h" #include "type_algorithms.h" #include "test_macros.h" struct MyDeleter { MyDeleter() = default; // required to exercise converting move-constructor template MyDeleter(std::default_delete const&) {} // required to exercise converting move-assignment template MyDeleter& operator=(std::default_delete const&) { return *this; } template void operator()(T* ptr) const { delete[] ptr; } }; template void test() { LIBCPP_STATIC_ASSERT(std::__has_array_cookie::value); LIBCPP_STATIC_ASSERT(!std::__has_array_cookie::value); // For types with an array cookie, we can always detect OOB accesses. Note that reliance on an array // cookie is limited to the default deleter, since a unique_ptr with a custom deleter may not have // been allocated with `new T[n]`. { { std::unique_ptr ptr(new WithCookie[5]); TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr::operator[](index): index out of range"); } { std::unique_ptr ptr = std::make_unique(5); TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr::operator[](index): index out of range"); } #if TEST_STD_VER >= 20 { std::unique_ptr ptr = std::make_unique_for_overwrite(5); TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = WithCookie(), "unique_ptr::operator[](index): index out of range"); } #endif } // For types that don't have an array cookie, things are a bit more complicated. We can detect OOB accesses // only when the unique_ptr is created via an API where the size is passed down to the library so that we // can store it inside the unique_ptr. That requires the appropriate ABI configuration to be enabled. // // Note that APIs that allow the size to be passed down to the library only support the default deleter // as of writing this test. #if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR) { { std::unique_ptr ptr = std::make_unique(5); TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr::operator[](index): index out of range"); } # if TEST_STD_VER >= 20 { std::unique_ptr ptr = std::make_unique_for_overwrite(5); TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = NoCookie(), "unique_ptr::operator[](index): index out of range"); } # endif } #endif // Make sure that we carry the bounds information properly through conversions, assignments, etc. // These tests are only relevant when the ABI setting is enabled (with a stateful bounds-checker). #if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR) types::for_each(types::type_list(), [] { // Bounds carried through move construction { std::unique_ptr ptr = std::make_unique(5); std::unique_ptr other(std::move(ptr)); TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr::operator[](index): index out of range"); } // Bounds carried through move assignment { std::unique_ptr ptr = std::make_unique(5); std::unique_ptr other; other = std::move(ptr); TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr::operator[](index): index out of range"); } // Bounds carried through converting move-constructor { std::unique_ptr ptr = std::make_unique(5); std::unique_ptr other(std::move(ptr)); TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr::operator[](index): index out of range"); } // Bounds carried through converting move-assignment { std::unique_ptr ptr = std::make_unique(5); std::unique_ptr other; other = std::move(ptr); TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr::operator[](index): index out of range"); } }); #endif } template struct NoCookie { char padding[Size]; }; template struct WithCookie { WithCookie() = default; WithCookie(WithCookie const&) {} WithCookie& operator=(WithCookie const&) { return *this; } ~WithCookie() {} char padding[Size]; }; int main(int, char**) { test, NoCookie<1>>(); test, NoCookie<2>>(); test, NoCookie<3>>(); test, NoCookie<4>>(); test, NoCookie<8>>(); test, NoCookie<16>>(); test, NoCookie<32>>(); test, NoCookie<256>>(); test(); return 0; }