From 8ff7ff1a0691b7b409aa31c8f6dfcefec3e4720b Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 11 Mar 2025 17:29:01 +0000 Subject: [PATCH] libstdc++: Prevent dangling references in std::unique_ptr::operator* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LWG 4148 (approved in Wrocław, November 2024) makes it ill-formed to dereference a std::unique_ptr if that would return a dangling reference. That can happen with a custom pointer type and a const-qualified element_type, such that std::add_lvalue_reference_t is a reference-to-const that could bind to a short-lived temporary. In C++26 the compiler diagnoses this as an error anyway: bits/unique_ptr.h:457:16: error: returning reference to temporary [-Wreturn-local-addr] But that can be disabled with -Wno-return-local-addr so the static_assert ensures it is enforced consistently. libstdc++-v3/ChangeLog: * include/bits/unique_ptr.h (unique_ptr::operator*): Add static_assert to check for dangling reference, as per LWG 4148. * testsuite/20_util/unique_ptr/lwg4148.cc: New test. Reviewed-by: Tomasz Kamiński --- libstdc++-v3/include/bits/unique_ptr.h | 8 +++++ .../testsuite/20_util/unique_ptr/lwg4148.cc | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h index 746989dfe47..6ae46a93800 100644 --- a/libstdc++-v3/include/bits/unique_ptr.h +++ b/libstdc++-v3/include/bits/unique_ptr.h @@ -445,6 +445,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename add_lvalue_reference::type operator*() const noexcept(noexcept(*std::declval())) { +#if _GLIBCXX_USE_BUILTIN_TRAIT(__reference_converts_from_temporary) + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4148. unique_ptr::operator* should not allow dangling references + using _ResT = typename add_lvalue_reference::type; + using _DerefT = decltype(*get()); + static_assert(!__reference_converts_from_temporary(_ResT, _DerefT), + "operator* must not return a dangling reference"); +#endif __glibcxx_assert(get() != pointer()); return *get(); } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc new file mode 100644 index 00000000000..c70d7a60631 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc @@ -0,0 +1,31 @@ +// { dg-do compile { target c++11 } } + +// LWG 4148. unique_ptr::operator* should not allow dangling references + +#include + +struct pointer +{ + pointer() { } + pointer(std::nullptr_t) { } + int operator*() const { return 0; } + bool operator==(pointer) const { return true; } + bool operator==(std::nullptr_t) const { return false; } +#ifndef __cpp_lib_three_way_comparison + bool operator!=(pointer) const { return false; } + bool operator!=(std::nullptr_t) const { return true; } +#endif +}; + +struct Deleter +{ + using pointer = ::pointer; + void operator()(pointer) const { } +}; + +std::unique_ptr up; +int i = *up; // { dg-error "here" } +// { dg-error "dangling reference" "" { target *-*-* } 0 } + +// { dg-warning "returning reference to temporary" "" { target c++23_down } 0 } +// { dg-error "returning reference to temporary" "" { target c++26 } 0 }