From 6e20911e9cd00fd408e534ab9b96133ef4d98b1c Mon Sep 17 00:00:00 2001 From: Xusheng Date: Fri, 24 Jul 2026 14:15:15 -0400 Subject: [PATCH 1/2] Reject a TTD launch with no trace specified (Fixes #910) The TTD adapter replays a recorded trace, but nothing checked that one was actually configured, so launching without a trace path failed deep inside the engine with an opaque "OpenDumpFile failed: 0x..." HRESULT. Validate the trace path in AdapterSettingsDialog::apply() and keep the dialog open with an explanation instead of accepting it. The settings view writes each value as it is edited, so the current value can simply be read back; no changes to SettingsView are needed. The check is limited to the launch group with the DBGENG_TTD adapter selected so the other flows are unaffected. Also check the trace path in DbgEngTTDAdapter::ExecuteWithArgsInternal before starting the engine, which covers the headless and scripted paths as well as the launches where the settings dialog is skipped (#1133). Co-Authored-By: Claude Opus 5 (1M context) --- core/adapters/dbgengttdadapter.cpp | 24 +++++++++++++++++ ui/adaptersettings.cpp | 42 +++++++++++++++++++++++++++++- ui/adaptersettings.h | 2 ++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/core/adapters/dbgengttdadapter.cpp b/core/adapters/dbgengttdadapter.cpp index f68e1690..ef87bcdf 100644 --- a/core/adapters/dbgengttdadapter.cpp +++ b/core/adapters/dbgengttdadapter.cpp @@ -27,6 +27,30 @@ bool DbgEngTTDAdapter::ExecuteWithArgsInternal(const std::string& path, const st scope = SettingsResourceScope; auto inputFile = adapterSettings->Get("common.inputFile", data, &scope); + // A trace is not optional for replay, but a missing one only surfaces later as an opaque OpenDumpFile + // HRESULT, so check it up front. The UI blocks this in the adapter settings dialog; this covers the + // headless and scripted paths, plus the launches where that dialog is skipped. + std::error_code ec; + if (tracePath.empty() || !std::filesystem::exists(tracePath, ec)) + { + DebuggerEvent event; + event.type = LaunchFailureEventType; + if (tracePath.empty()) + { + event.data.errorData.error = "No TTD trace specified. Set the trace path in the debug adapter " + "settings, or record a new trace first."; + event.data.errorData.shortError = "No TTD trace specified"; + } + else + { + event.data.errorData.error = fmt::format("The TTD trace \"{}\" does not exist. Set the trace path in " + "the debug adapter settings.", tracePath); + event.data.errorData.shortError = "TTD trace not found"; + } + PostDebuggerEvent(event); + return false; + } + if (this->m_dbgengInitialized) { this->Reset(); } diff --git a/ui/adaptersettings.cpp b/ui/adaptersettings.cpp index fc79d6a6..38b1dab0 100644 --- a/ui/adaptersettings.cpp +++ b/ui/adaptersettings.cpp @@ -18,13 +18,15 @@ limitations under the License. #include "uicontext.h" #include "qfiledialog.h" #include "settingsview.h" +#include +#include using namespace BinaryNinjaDebuggerAPI; using namespace BinaryNinja; using namespace std; AdapterSettingsDialog::AdapterSettingsDialog(QWidget* parent, DbgRef controller, const std::string& highlightGroup) : - QDialog(), m_controller(controller) + QDialog(), m_controller(controller), m_highlightGroup(highlightGroup) { setWindowTitle("Debug Adapter Settings"); setAttribute(Qt::WA_DeleteOnClose); @@ -158,8 +160,46 @@ QWidget* AdapterSettingsDialog::getWidgetForAdapter(const QString& adapter) } +// The settings view writes each value as it is edited, so the current values can be read straight back from the +// adapter settings here. Only the settings whose absence is guaranteed to fail the operation are checked, so that +// the dialog does not get in the way of anything the adapter itself is willing to accept. +bool AdapterSettingsDialog::validateSettings() +{ + if ((m_highlightGroup != "launch") || (m_adapterEntry->currentText() != "DBGENG_TTD")) + return true; + + auto adapterSettings = m_controller->GetAdapterSettings(); + if (!adapterSettings) + return true; + + BNSettingsScope scope = SettingsResourceScope; + auto tracePath = adapterSettings->Get("launch.trace_path", m_controller->GetData(), &scope); + if (tracePath.empty()) + { + QMessageBox::warning(this, "No Trace Specified", + "The TTD adapter replays a previously recorded trace, so a trace path is required.\n\n" + "Set \"Trace Path\" in the launch settings, or record a new trace first."); + return false; + } + + std::error_code ec; + if (!std::filesystem::exists(tracePath, ec)) + { + QMessageBox::warning(this, "Trace Not Found", + QString("The trace file\n\n%1\n\ndoes not exist. Set \"Trace Path\" in the launch settings to an " + "existing trace.").arg(QString::fromStdString(tracePath))); + return false; + } + + return true; +} + + void AdapterSettingsDialog::apply() { + if (!validateSettings()) + return; + if (m_useSameSettingsCheckbox) m_controller->SetShowAdapterSettingsNextTime(!m_useSameSettingsCheckbox->isChecked()); accept(); diff --git a/ui/adaptersettings.h b/ui/adaptersettings.h index 0aa74d9f..ff5085c6 100644 --- a/ui/adaptersettings.h +++ b/ui/adaptersettings.h @@ -43,8 +43,10 @@ class AdapterSettingsDialog : public QDialog QMap m_viewMap; QLabel* m_noSettingsLabel; QCheckBox* m_useSameSettingsCheckbox = nullptr; + std::string m_highlightGroup; QWidget* getWidgetForAdapter(const QString& adapter); + bool validateSettings(); public: AdapterSettingsDialog(QWidget* parent, DbgRef controller, const std::string& highlightGroup = ""); From d4530113c81d5fa292973da1aa313a11f25c369a Mon Sep 17 00:00:00 2001 From: Xusheng Date: Thu, 13 Aug 2026 13:14:45 -0400 Subject: [PATCH 2/2] Require the TTD trace path to be a regular file std::filesystem::exists accepts directories, so point the check at is_regular_file instead and report "is not a file" separately from "does not exist" so the message still fits what the user set. Co-Authored-By: Claude Opus 5 (1M context) --- core/adapters/dbgengttdadapter.cpp | 10 ++++++++-- ui/adaptersettings.cpp | 17 +++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/core/adapters/dbgengttdadapter.cpp b/core/adapters/dbgengttdadapter.cpp index ef87bcdf..58dd6ad7 100644 --- a/core/adapters/dbgengttdadapter.cpp +++ b/core/adapters/dbgengttdadapter.cpp @@ -31,7 +31,7 @@ bool DbgEngTTDAdapter::ExecuteWithArgsInternal(const std::string& path, const st // HRESULT, so check it up front. The UI blocks this in the adapter settings dialog; this covers the // headless and scripted paths, plus the launches where that dialog is skipped. std::error_code ec; - if (tracePath.empty() || !std::filesystem::exists(tracePath, ec)) + if (tracePath.empty() || !std::filesystem::is_regular_file(tracePath, ec)) { DebuggerEvent event; event.type = LaunchFailureEventType; @@ -41,12 +41,18 @@ bool DbgEngTTDAdapter::ExecuteWithArgsInternal(const std::string& path, const st "settings, or record a new trace first."; event.data.errorData.shortError = "No TTD trace specified"; } - else + else if (!std::filesystem::exists(tracePath, ec)) { event.data.errorData.error = fmt::format("The TTD trace \"{}\" does not exist. Set the trace path in " "the debug adapter settings.", tracePath); event.data.errorData.shortError = "TTD trace not found"; } + else + { + event.data.errorData.error = fmt::format("The TTD trace path \"{}\" is not a file. Set the trace path in " + "the debug adapter settings to a recorded trace file.", tracePath); + event.data.errorData.shortError = "TTD trace is not a file"; + } PostDebuggerEvent(event); return false; } diff --git a/ui/adaptersettings.cpp b/ui/adaptersettings.cpp index 38b1dab0..95f41260 100644 --- a/ui/adaptersettings.cpp +++ b/ui/adaptersettings.cpp @@ -183,11 +183,20 @@ bool AdapterSettingsDialog::validateSettings() } std::error_code ec; - if (!std::filesystem::exists(tracePath, ec)) + if (!std::filesystem::is_regular_file(tracePath, ec)) { - QMessageBox::warning(this, "Trace Not Found", - QString("The trace file\n\n%1\n\ndoes not exist. Set \"Trace Path\" in the launch settings to an " - "existing trace.").arg(QString::fromStdString(tracePath))); + if (!std::filesystem::exists(tracePath, ec)) + { + QMessageBox::warning(this, "Trace Not Found", + QString("The trace file\n\n%1\n\ndoes not exist. Set \"Trace Path\" in the launch settings to an " + "existing trace.").arg(QString::fromStdString(tracePath))); + } + else + { + QMessageBox::warning(this, "Invalid Trace", + QString("The trace path\n\n%1\n\nis not a file. Set \"Trace Path\" in the launch settings to a " + "recorded trace file.").arg(QString::fromStdString(tracePath))); + } return false; }