[libcxx-commits] [libcxx] [libc++] Fix UB in <expected> related to "has value" flag (#68552) (PR #68733)
Jan Kokemüller via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Oct 10 11:46:51 PDT 2023
https://github.com/jiixyj updated https://github.com/llvm/llvm-project/pull/68733
>From 13b037a38f0ed193195393d2de88b076954fd4ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 10 Oct 2023 20:24:06 +0200
Subject: [PATCH] [libc++] Fix UB in <expected> related to "has value" flag
(#68552)
The calls to `std::construct_at` might overwrite the previously set
`__has_value_` flag, so reverse the order everywhere.
---
libcxx/include/__expected/expected.h | 98 ++++++++++---------
.../observers/has_value_clobber.pass.cpp | 34 +++++++
2 files changed, 84 insertions(+), 48 deletions(-)
create mode 100644 libcxx/test/std/utilities/expected/expected.expected/observers/has_value_clobber.pass.cpp
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 045370a486fae6b..cd31694d8b53c41 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -118,9 +118,9 @@ class expected {
// [expected.object.ctor], constructors
_LIBCPP_HIDE_FROM_ABI constexpr expected()
noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
- requires is_default_constructible_v<_Tp>
- : __has_val_(true) {
+ requires is_default_constructible_v<_Tp> {
std::construct_at(std::addressof(__union_.__val_));
+ __has_val_ = true;
}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -135,13 +135,13 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __other)
noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
- !(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
- : __has_val_(__other.__has_val_) {
- if (__has_val_) {
+ !(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>)) {
+ if (__other.__has_val_) {
std::construct_at(std::addressof(__union_.__val_), __other.__union_.__val_);
} else {
std::construct_at(std::addressof(__union_.__unex_), __other.__union_.__unex_);
}
+ __has_val_ = __other.__has_val_;
}
@@ -153,13 +153,13 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __other)
noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
- !(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
- : __has_val_(__other.__has_val_) {
- if (__has_val_) {
+ !(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>)) {
+ if (__other.__has_val_) {
std::construct_at(std::addressof(__union_.__val_), std::move(__other.__union_.__val_));
} else {
std::construct_at(std::addressof(__union_.__unex_), std::move(__other.__union_.__unex_));
}
+ __has_val_ = __other.__has_val_;
}
private:
@@ -199,26 +199,26 @@ class expected {
!is_convertible_v<const _OtherErr&, _Err>)
expected(const expected<_Up, _OtherErr>& __other)
noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
- is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __has_val_(__other.__has_val_) {
- if (__has_val_) {
+ is_nothrow_constructible_v<_Err, const _OtherErr&>) { // strengthened
+ if (__other.__has_val_) {
std::construct_at(std::addressof(__union_.__val_), __other.__union_.__val_);
} else {
std::construct_at(std::addressof(__union_.__unex_), __other.__union_.__unex_);
}
+ __has_val_ = __other.__has_val_;
}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __other)
- noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __has_val_(__other.__has_val_) {
- if (__has_val_) {
+ noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) { // strengthened
+ if (__other.__has_val_) {
std::construct_at(std::addressof(__union_.__val_), std::move(__other.__union_.__val_));
} else {
std::construct_at(std::addressof(__union_.__unex_), std::move(__other.__union_.__unex_));
}
+ __has_val_ = __other.__has_val_;
}
template <class _Up = _Tp>
@@ -226,61 +226,61 @@ class expected {
is_constructible_v<_Tp, _Up> && !__is_std_unexpected<remove_cvref_t<_Up>>::value &&
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
- expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
- : __has_val_(true) {
+ expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) { // strengthened
std::construct_at(std::addressof(__union_.__val_), std::forward<_Up>(__u));
+ __has_val_ = true;
}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
- noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __has_val_(false) {
+ noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) { // strengthened
std::construct_at(std::addressof(__union_.__unex_), __unex.error());
+ __has_val_ = false;
}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
- noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __has_val_(false) {
+ noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) { // strengthened
std::construct_at(std::addressof(__union_.__unex_), std::move(__unex.error()));
+ __has_val_ = false;
}
template <class... _Args>
requires is_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
- : __has_val_(true) {
+ noexcept(is_nothrow_constructible_v<_Tp, _Args...>) { // strengthened
std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
+ __has_val_ = true;
}
template <class _Up, class... _Args>
requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
- : __has_val_(true) {
+ noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) { // strengthened
std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
+ __has_val_ = true;
}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __has_val_(false) {
+ noexcept(is_nothrow_constructible_v<_Err, _Args...>) { // strengthened
std::construct_at(std::addressof(__union_.__unex_), std::forward<_Args>(__args)...);
+ __has_val_ = false;
}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __has_val_(false) {
+ noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) { // strengthened
std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...);
+ __has_val_ = false;
}
// [expected.object.dtor], destructor
@@ -440,9 +440,10 @@ class expected {
std::destroy_at(std::addressof(__union_.__val_));
} else {
std::destroy_at(std::addressof(__union_.__unex_));
- __has_val_ = true;
}
- return *std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
+ std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
+ __has_val_ = true;
+ return *std::addressof(__union_.__val_);
}
template <class _Up, class... _Args>
@@ -452,9 +453,10 @@ class expected {
std::destroy_at(std::addressof(__union_.__val_));
} else {
std::destroy_at(std::addressof(__union_.__unex_));
- __has_val_ = true;
}
- return *std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
+ std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
+ __has_val_ = true;
+ return *std::addressof(__union_.__val_);
}
@@ -997,11 +999,11 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
- requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
- : __has_val_(__rhs.__has_val_) {
+ requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>) {
if (!__rhs.__has_val_) {
std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_);
}
+ __has_val_ = __rhs.__has_val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
@@ -1010,51 +1012,51 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
noexcept(is_nothrow_move_constructible_v<_Err>)
- requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
- : __has_val_(__rhs.__has_val_) {
+ requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>) {
if (!__rhs.__has_val_) {
std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_));
}
+ __has_val_ = __rhs.__has_val_;
}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const expected<_Up, _OtherErr>& __rhs)
- noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __has_val_(__rhs.__has_val_) {
+ noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) { // strengthened
if (!__rhs.__has_val_) {
std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_);
}
+ __has_val_ = __rhs.__has_val_;
}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __rhs)
- noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __has_val_(__rhs.__has_val_) {
+ noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) { // strengthened
if (!__rhs.__has_val_) {
std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_));
}
+ __has_val_ = __rhs.__has_val_;
}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
- noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __has_val_(false) {
+ noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) { // strengthened
std::construct_at(std::addressof(__union_.__unex_), __unex.error());
+ __has_val_ = false;
}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
- noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __has_val_(false) {
+ noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) { // strengthened
std::construct_at(std::addressof(__union_.__unex_), std::move(__unex.error()));
+ __has_val_ = false;
}
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __has_val_(true) {}
@@ -1062,17 +1064,17 @@ class expected<_Tp, _Err> {
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __has_val_(false) {
+ noexcept(is_nothrow_constructible_v<_Err, _Args...>) { // strengthened
std::construct_at(std::addressof(__union_.__unex_), std::forward<_Args>(__args)...);
+ __has_val_ = false;
}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __has_val_(false) {
+ noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) { // strengthened
std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...);
+ __has_val_ = false;
}
private:
diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value_clobber.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value_clobber.pass.cpp
new file mode 100644
index 000000000000000..fdfbb0041abd3f8
--- /dev/null
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value_clobber.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O2
+
+// constexpr bool has_value() const noexcept;
+
+#include <cassert>
+#include <expected>
+#include <optional>
+
+#include "test_macros.h"
+
+// See https://github.com/llvm/llvm-project/issues/68552
+void testClobberHasValue() {
+ static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
+
+ static constexpr auto f2 = [] -> std::expected<std::optional<int>, int> {
+ return f1().transform_error([](auto) { return 0; });
+ };
+
+ auto e = f2();
+ assert(e.has_value());
+}
+
+int main(int, char**) {
+ testClobberHasValue();
+ return 0;
+}
More information about the libcxx-commits
mailing list