Skip to content

Commit

Permalink
Improve tests and fix a few bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
ldionne committed Sep 26, 2024
1 parent 6fbdea9 commit b23d82d
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 47 deletions.
5 changes: 3 additions & 2 deletions libcxx/include/__memory/unique_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr& operator=(unique_ptr&& __u) _NOEXCEPT {
reset(__u.release());
__deleter_ = std::forward<deleter_type>(__u.get_deleter());
__checker_ = std::move(__u.__checker_);
__checker_ = std::move(std::move(__u.__checker_));
return *this;
}

Expand Down Expand Up @@ -580,7 +580,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer release() _NOEXCEPT {
pointer __t = __ptr_;
__ptr_ = pointer();
__checker_ = _BoundsChecker();
// The deleter and the optional bounds-checker are left unchanged. The bounds-checker
// will be reinitialized appropriately when/if the unique_ptr gets assigned-to or reset.
return __t;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

// UNSUPPORTED: libcpp-has-abi-fix-unordered-container-size-type, libcpp-abi-no-compressed-pair-padding

// std::unique_ptr is used as an implementation detail of the unordered containers, so the layout of
// unordered containers changes when bounded unique_ptr is enabled.
// UNSUPPORTED: libcpp-has-abi-bounded-unique_ptr

#include <cstdint>
#include <unordered_map>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

// UNSUPPORTED: libcpp-has-abi-fix-unordered-container-size-type, libcpp-abi-no-compressed-pair-padding

// std::unique_ptr is used as an implementation detail of the unordered containers, so the layout of
// unordered containers changes when bounded unique_ptr is enabled.
// UNSUPPORTED: libcpp-has-abi-bounded-unique_ptr

#include <cstdint>
#include <unordered_set>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//

// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-hardening-mode=none
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing

Expand All @@ -25,16 +25,32 @@
#include <string>

#include "check_assertion.h"
#include "type_algorithms.h"

struct MyDeleter {
void operator()(int* ptr) const { delete[] ptr; }
MyDeleter() = default;

// required to exercise converting move-constructor
template <class T>
MyDeleter(std::default_delete<T> const&) {}

// required to exercise converting move-assignment
template <class T>
MyDeleter& operator=(std::default_delete<T> const&) {
return *this;
}

template <class T>
void operator()(T* ptr) const {
delete[] ptr;
}
};

template <class WithCookie, class NoCookie>
void test() {
// Check with a default deleter
// For types with an array cookie, we can always detect OOB accesses.
{
// Types that have an array cookie
// Check with the default deleter
{
{
std::unique_ptr<WithCookie[]> ptr(new WithCookie[5]);
Expand All @@ -47,64 +63,78 @@ void test() {
#if TEST_STD_VER >= 20
{
std::unique_ptr<WithCookie[]> ptr = std::make_unique_for_overwrite<WithCookie[]>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = WithCookie(), "unique_ptr<T[]>::operator[](index): index out of range");
}
#endif
}

// Types that don't have an array cookie (only available under the right ABI configuration)
#if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
// Check with a custom deleter
{
{
std::unique_ptr<NoCookie[]> ptr(new NoCookie[5]);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
{
std::unique_ptr<NoCookie[]> ptr = std::make_unique<NoCookie[]>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
# if TEST_STD_VER >= 20
{
std::unique_ptr<NoCookie[]> ptr = std::make_unique_for_overwrite<NoCookie[]>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
# endif
std::unique_ptr<WithCookie[], MyDeleter> ptr(new WithCookie[5]);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
#endif // defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
}

// Check with a custom deleter (only available under the right ABI configuration)
// 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)
{
// Types that have an array cookie
{
{
std::unique_ptr<WithCookie[], MyDeleter> ptr = std::make_unique<WithCookie[]>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
std::unique_ptr<NoCookie[]> ptr = std::make_unique<NoCookie[]>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
# if TEST_STD_VER >= 20
{
std::unique_ptr<WithCookie[], MyDeleter> ptr = std::make_unique_for_overwrite<WithCookie[], MyDeleter>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
{
std::unique_ptr<NoCookie[]> ptr = std::make_unique_for_overwrite<NoCookie[]>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = NoCookie(), "unique_ptr<T[]>::operator[](index): index out of range");
}
# endif
}
#endif

// Make sure that we carry the bounds information properly through conversions, assignments, etc.
// These tests are mostly relevant when the ABI setting is enabled (with a stateful bounds-checker),
// but we still run them for types with an array cookie either way.
#if defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)
using Types = types::type_list<NoCookie, WithCookie>;
#else
using Types = types::type_list<WithCookie>;
#endif
types::for_each(Types(), []<class T> {
// Bounds carried through move construction
{
std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5);
std::unique_ptr<T[]> other(std::move(ptr));
TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range");
}

// Types that don't have an array cookie
// Bounds carried through move assignment
{
{
std::unique_ptr<NoCookie[], MyDeleter> ptr = std::make_unique<NoCookie[]>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = 42, "unique_ptr<T[]>::operator[](index): index out of range");
}
# if TEST_STD_VER >= 20
{
std::unique_ptr<NoCookie[], MyDeleter> ptr = std::make_unique_for_overwrite<NoCookie[], MyDeleter>(5);
TEST_LIBCPP_ASSERT_FAILURE(ptr[6] = 42, "unique_ptr<T[]>::operator[](index): index out of range");
}
# endif
std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5);
std::unique_ptr<T[]> other;
other = std::move(ptr);
TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
}
#endif // defined(_LIBCPP_ABI_BOUNDED_UNIQUE_PTR)

// Bounds carried through converting move-constructor
{
std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5);
std::unique_ptr<T[], MyDeleter> other(std::move(ptr));
TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range");
}

// Bounds carried through converting move-assignment
{
std::unique_ptr<T[]> ptr = std::make_unique<T[]>(5);
std::unique_ptr<T[], MyDeleter> other;
other = std::move(ptr);
TEST_LIBCPP_ASSERT_FAILURE(other[6], "unique_ptr<T[]>::operator[](index): index out of range");
}
});
}

template <std::size_t Size>
Expand All @@ -114,6 +144,9 @@ struct NoCookie {

template <std::size_t Size>
struct WithCookie {
WithCookie() = default;
WithCookie(WithCookie const&) {}
WithCookie& operator=(WithCookie const&) { return *this; }
~WithCookie() {}
char padding[Size];
};
Expand Down
1 change: 1 addition & 0 deletions libcxx/utils/libcxx/test/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ def _mingwSupportsModules(cfg):
"_LIBCPP_ABI_BOUNDED_ITERATORS": "libcpp-has-abi-bounded-iterators",
"_LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING": "libcpp-has-abi-bounded-iterators-in-string",
"_LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR": "libcpp-has-abi-bounded-iterators-in-vector",
"_LIBCPP_ABI_BOUNDED_UNIQUE_PTR": "libcpp-has-abi-bounded-unique_ptr",
"_LIBCPP_ABI_FIX_UNORDERED_CONTAINER_SIZE_TYPE": "libcpp-has-abi-fix-unordered-container-size-type",
"_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR": "libcpp-deprecated-abi-disable-pair-trivial-copy-ctor",
"_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
Expand Down

0 comments on commit b23d82d

Please sign in to comment.