aws_error: fix nested exception handling

The loop that unwraps nested exception, rethrows nested exception and saves pointer to the temporary std::exception& inner on stack, then continues. This pointer is, thus, pointing to a released temporary

Closes scylladb/scylladb#28143
This commit is contained in:
Ernest Zaslavsky
2026-01-14 11:30:22 +02:00
committed by Pavel Emelyanov
parent b7bc48e7b7
commit 829bd9b598
3 changed files with 86 additions and 15 deletions

View File

@@ -108,3 +108,57 @@ BOOST_AUTO_TEST_CASE(TestErrorsWithoutPrefixParse) {
BOOST_REQUIRE_EQUAL("JunkMessage", error.get_error_message());
BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::no);
}
BOOST_AUTO_TEST_CASE(TestNestedException) {
// Test nested exceptions where the innermost is a system_error
try {
try {
try {
throw std::system_error(std::error_code(ECONNABORTED, std::system_category()));
} catch (...) {
std::throw_with_nested(std::runtime_error("Higher level runtime_error"));
}
} catch (...) {
std::throw_with_nested(std::logic_error("Higher level logic_error"));
}
} catch (...) {
auto error = aws::aws_error::from_maybe_nested_exception(std::current_exception());
BOOST_REQUIRE_EQUAL(aws::aws_error_type::NETWORK_CONNECTION, error.get_error_type());
BOOST_REQUIRE_EQUAL("Software caused connection abort", error.get_error_message());
BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::yes);
}
// Test nested exceptions where the innermost is NOT a system_error
try {
try {
throw std::logic_error("Something bad happened");
} catch (...) {
std::throw_with_nested(std::runtime_error("Higher level runtime_error"));
}
} catch (...) {
auto error = aws::aws_error::from_maybe_nested_exception(std::current_exception());
BOOST_REQUIRE_EQUAL(aws::aws_error_type::UNKNOWN, error.get_error_type());
BOOST_REQUIRE_EQUAL("Higher level runtime_error", error.get_error_message());
BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::no);
}
// Test single exception which is NOT a nested exception
try {
throw std::runtime_error("Something bad happened");
} catch (...) {
auto error = aws::aws_error::from_maybe_nested_exception(std::current_exception());
BOOST_REQUIRE_EQUAL(aws::aws_error_type::UNKNOWN, error.get_error_type());
BOOST_REQUIRE_EQUAL("Something bad happened", error.get_error_message());
BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::no);
}
// Test with non-std::exception
try {
throw "foo";
} catch (...) {
auto error = aws::aws_error::from_maybe_nested_exception(std::current_exception());
BOOST_REQUIRE_EQUAL(aws::aws_error_type::UNKNOWN, error.get_error_type());
BOOST_REQUIRE_EQUAL("No error message was provided, exception content: char const*", error.get_error_message());
BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::no);
}
}

View File

@@ -160,24 +160,41 @@ aws_error aws_error::from_system_error(const std::system_error& system_error) {
}
}
aws_error aws_error::from_maybe_nested_exception(const std::exception& maybe_nested_error) {
const std::exception* current_exception = &maybe_nested_error;
while (current_exception) {
if (auto sys_error = dynamic_cast<const std::system_error*>(current_exception)) {
return from_system_error(*sys_error);
}
aws_error aws_error::from_maybe_nested_exception(std::exception_ptr eptr) {
std::string original_message;
while (eptr) {
try {
std::rethrow_if_nested(*current_exception);
} catch (const std::exception& inner) {
current_exception = &inner;
continue;
std::rethrow_exception(eptr);
} catch (const std::exception& e) {
if (original_message.empty()) {
original_message = e.what();
}
if (auto* sys = dynamic_cast<const std::system_error*>(&e)) {
return from_system_error(*sys);
}
try {
std::rethrow_if_nested(e);
} catch (...) {
eptr = std::current_exception();
continue;
}
break;
} catch (...) {
// Non-std::exception, should not happen in general
break;
}
current_exception = nullptr;
}
return {aws_error_type::UNKNOWN, maybe_nested_error.what(), retryable::no};
if (original_message.empty()) {
original_message = fmt::format("No error message was provided, exception content: {}", eptr);
}
return {aws_error_type::UNKNOWN, std::move(original_message), retryable::no};
}
aws_error aws_error::from_exception_ptr(std::exception_ptr exception) {
if (exception) {
try {
@@ -188,8 +205,8 @@ aws_error aws_error::from_exception_ptr(std::exception_ptr exception) {
return from_http_code(ex.status());
} catch (const std::system_error& ex) {
return from_system_error(ex);
} catch (const std::exception& ex) {
return from_maybe_nested_exception(ex);
} catch (const std::exception&) {
return from_maybe_nested_exception(std::current_exception());
} catch (...) {
return aws_error{aws_error_type::UNKNOWN, seastar::format("{}", std::current_exception()), retryable::no};
}

View File

@@ -106,7 +106,7 @@ public:
static std::optional<aws_error> parse(seastar::sstring&& body);
static aws_error from_http_code(seastar::http::reply::status_type http_code);
static aws_error from_system_error(const std::system_error& system_error);
static aws_error from_maybe_nested_exception(const std::exception& maybe_nested_error);
static aws_error from_maybe_nested_exception(std::exception_ptr maybe_nested_error);
static aws_error from_exception_ptr(std::exception_ptr exception);
static const aws_errors& get_errors();
};