What changed, and why it matters
This commit changes a Bitcoin Core utility class so that a specific programming mistake now throws a catchable exception instead of crashing the program with an assertion failure. It also marks two operators as 'noexcept', meaning misuse of those operators will terminate the program rather than throw. The change is defensive and aligns the custom class with the C++ standard library's std::expected behavior. There is no direct evidence this fixes an exploitable security bug.
No immediate action required. Treat as a code-quality/hardening change. If reviewing for security, verify that no existing callers rely on value() being assertion-safe or that no new exception paths are reachable in consensus or networking code.
Security signals we found
Defensive hardening: converting a non-recoverable assertion failure into a catchable exception for API conformance
Behavioral change in error-handling path of a low-level utility class
No evidence of reachable misuse path in the current codebase
Commit message downplays security relevance
Evidence from the diff
The patch modifies util::Expected
Changed components
src/util/expected.hsrc/test/util_expected_tests.cppInspect captured patch +24 / −6
diff --git a/src/test/util_expected_tests.cpp b/src/test/util_expected_tests.cpp
index f02ea3fd..64f543bf 100644
--- a/src/test/util_expected_tests.cpp
+++ b/src/test/util_expected_tests.cpp
@@ -53,6 +53,15 @@ BOOST_AUTO_TEST_CASE(expected_value_or)
BOOST_CHECK_EQUAL(const_val.value_or("fallback"), "fallback");
}
+BOOST_AUTO_TEST_CASE(expected_value_throws)
+{
+ const Expected<int, std::string> e{Unexpected{"fail"}};
+ BOOST_CHECK_THROW(e.value(), BadExpectedAccess);
+
+ const Expected<void, std::string> void_e{Unexpected{"fail"}};
+ BOOST_CHECK_THROW(void_e.value(), BadExpectedAccess);
+}
+
BOOST_AUTO_TEST_CASE(expected_error)
{
Expected<void, std::string> e{};
diff --git a/src/util/expected.h b/src/util/expected.h
index 71cd0f94..d4611698 100644
--- a/src/util/expected.h
+++ b/src/util/expected.h
@@ -8,6 +8,7 @@
#include <attributes.h>
#include <cassert>
+#include <exception>
#include <type_traits>
#include <utility>
#include <variant>
@@ -30,6 +31,10 @@ private:
E m_error;
};
+struct BadExpectedAccess : std::exception {
+ const char* what() const noexcept override { return "Bad util::Expected access"; }
+};
+
/// The util::Expected class provides a standard way for low-level functions to
/// return either error values or result values.
///
@@ -55,12 +60,16 @@ public:
constexpr const ValueType& value() const LIFETIMEBOUND
{
- assert(has_value());
+ if (!has_value()) {
+ throw BadExpectedAccess{};
+ }
return std::get<0>(m_data);
}
constexpr ValueType& value() LIFETIMEBOUND
{
- assert(has_value());
+ if (!has_value()) {
+ throw BadExpectedAccess{};
+ }
return std::get<0>(m_data);
}
@@ -86,11 +95,11 @@ public:
return std::get<1>(m_data);
}
- constexpr ValueType& operator*() LIFETIMEBOUND { return value(); }
- constexpr const ValueType& operator*() const LIFETIMEBOUND { return value(); }
+ constexpr ValueType& operator*() noexcept LIFETIMEBOUND { return value(); }
+ constexpr const ValueType& operator*() const noexcept LIFETIMEBOUND { return value(); }
- constexpr ValueType* operator->() LIFETIMEBOUND { return &value(); }
- constexpr const ValueType* operator->() const LIFETIMEBOUND { return &value(); }
+ constexpr ValueType* operator->() noexcept LIFETIMEBOUND { return &value(); }
+ constexpr const ValueType* operator->() const noexcept LIFETIMEBOUND { return &value(); }
};
} // namespace util
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.