gui: Add ExceptionSafeConnect that takes a lambda
What changed, and why it matters
This commit adds a helper function in Bitcoin Core's graphical user interface code that safely connects a Qt signal to an anonymous function (lambda). It wraps the function so that if it throws an exception, the error is caught and reported instead of crashing the program. It also updates an existing error-printing helper to handle cases where there is no named receiver object. This is a defensive code-quality improvement, not a fix for an active security vulnerability.
No immediate action required. Treat as routine code-quality/maintenance change. Reviewers may verify that the new overload is used consistently and that assert(ok) behavior is acceptable in release builds.
Security signals we found
Defensive exception handling added to prevent unhandled exceptions in lambda slots
Null-pointer guard added in PrintSlotException to avoid dereferencing a null receiver
No change to consensus, networking, wallet cryptography, or validation logic
Evidence from the diff
The patch introduces a new three-argument ExceptionSafeConnect overload in src/qt/guiutil.h that accepts a lambda slot. The wrapper catches NonFatalCheckError and std::exception, routes them to the existing GUI exception handlers (handleNonFatalException / handleRunawayException) via QMetaObject::invokeMethod, and asserts that the invocation succeeded. It also modifies PrintSlotException in src/qt/guiutil.cpp to tolerate a null receiver by printing “anonymous function” instead of dereferencing it. The change mirrors the existing four-argument ExceptionSafeConnect variant and reduces the risk of unhandled exceptions propagating out of Qt signal handlers.
Changed components
src/qt/guiutil.hsrc/qt/guiutil.cppInspect captured patch +37 / −1
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index e0966b9c..7fe022b0 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -970,7 +970,11 @@ void PrintSlotException(
{
std::string description = sender->metaObject()->className();
description += "->";
- description += receiver->metaObject()->className();
+ if (receiver) {
+ description += receiver->metaObject()->className();
+ } else {
+ description += "anonymous function";
+ }
PrintExceptionContinue(exception, description);
}
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 57d042f6..2b1522ee 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -399,6 +399,38 @@ namespace GUIUtil
},
type);
}
+ template <typename Sender, typename Signal, typename Slot>
+ auto ExceptionSafeConnect(
+ Sender sender, Signal signal, Slot method)
+ {
+ return QObject::connect(
+ sender, signal,
+ [sender, method](auto&&... args) {
+ bool ok{true};
+ try {
+ method(std::forward<decltype(args)>(args)...);
+ } catch (const NonFatalCheckError& e) {
+ PrintSlotException(&e, sender, nullptr);
+ ok = QMetaObject::invokeMethod(
+ qApp, "handleNonFatalException",
+ blockingGUIThreadConnection(),
+ Q_ARG(QString, QString::fromStdString(e.what())));
+ } catch (const std::exception& e) {
+ PrintSlotException(&e, sender, nullptr);
+ ok = QMetaObject::invokeMethod(
+ qApp, "handleRunawayException",
+ blockingGUIThreadConnection(),
+ Q_ARG(QString, QString::fromStdString(e.what())));
+ } catch (...) {
+ PrintSlotException(nullptr, sender, nullptr);
+ ok = QMetaObject::invokeMethod(
+ qApp, "handleRunawayException",
+ blockingGUIThreadConnection(),
+ Q_ARG(QString, "Unknown failure occurred."));
+ }
+ assert(ok);
+ });
+ }
/**
* Shows a QDialog instance asynchronously, and deletes it on close.
Why this scored 17/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.