v30.3-roots.1 · Commit 50: [PATCH 50/68] qt: cleanse and release transient sweep cache
src/qt/sweepdialog.cpp
Before: src/qt/sweepdialog.cpp · After: src/qt/sweepdialog.cpp
File metadata
index c410a97daa2643469ab2a149605b2ae48d09a3fd..4cdfcb7e5d350a1e1327ec5ee9dd199494361ec6 100644
Download original patch · Release notes
Diff rows 1–97 of 97. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/qt/sweepdialog.cpp
CHANGED
|
@@ -36,7 +36,8 @@ public:
|
|
| 36 |
SecureString result{encoded.constData(), static_cast<size_t>(encoded.size())};
|
| 37 |
if (!encoded.isEmpty()) memory_cleanse(encoded.data(), encoded.size());
|
| 38 |
// QLineEdit does not expose its internal buffer for cleansing. Replace
|
| 39 |
-
// its contents before clearing so the widget no longer retains the WIF.
|
|
|
|
| 40 |
setText(QString(text().size(), QChar{' '}));
|
| 41 |
clear();
|
| 42 |
return result;
|
|
@@ -118,7 +119,7 @@ SweepDialog::SweepDialog(WalletModel* wallet_model, QWidget* parent)
|
|
| 118 |
m_request_generation = 0;
|
| 119 |
const QSignalBlocker block{m_private_key};
|
| 120 |
m_private_key->clear();
|
| 121 |
-
m_private_key_cache.clear();
|
| 122 |
m_preview_txid.clear();
|
| 123 |
setBusy(false);
|
| 124 |
m_preview_current = false;
|
|
@@ -130,6 +131,15 @@ SweepDialog::SweepDialog(WalletModel* wallet_model, QWidget* parent)
|
|
| 130 |
|
| 131 |
SweepDialog::~SweepDialog() { clearSensitive(); }
|
| 132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
void SweepDialog::invalidate()
|
| 134 |
{
|
| 135 |
if (m_request_generation != 0 && !m_wallet_model->invalidateSweepRequests()) {
|
|
@@ -137,7 +147,7 @@ void SweepDialog::invalidate()
|
|
| 137 |
return;
|
| 138 |
}
|
| 139 |
m_request_generation = 0;
|
| 140 |
-
m_private_key_cache.clear();
|
| 141 |
m_preview_txid.clear();
|
| 142 |
m_preview_current = false;
|
| 143 |
m_broadcast->setEnabled(false);
|
|
@@ -162,6 +172,7 @@ void SweepDialog::setResult(const QString& text, bool can_broadcast)
|
|
| 162 |
|
| 163 |
void SweepDialog::preview()
|
| 164 |
{
|
|
|
|
| 165 |
const CTxDestination destination{DecodeDestination(m_destination->text().toStdString())};
|
| 166 |
if (!IsValidDestination(destination)) { setResult(tr("Enter a valid destination address."), false); return; }
|
| 167 |
const QSignalBlocker block{m_private_key};
|
|
@@ -170,7 +181,11 @@ void SweepDialog::preview()
|
|
| 170 |
setBusy(true);
|
| 171 |
m_result->setText(tr("Scanning for eligible coins and preparing a preview…"));
|
| 172 |
m_request_generation = m_wallet_model->requestSweep(m_private_key_cache, destination, false);
|
| 173 |
-
if (m_request_generation == 0) setResult(tr("Another sweep broadcast is already being submitted."), false);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
}
|
| 175 |
|
| 176 |
void SweepDialog::broadcast()
|
|
@@ -182,12 +197,20 @@ void SweepDialog::broadcast()
|
|
| 182 |
if (answer != QMessageBox::Yes) return;
|
| 183 |
const CTxDestination destination{DecodeDestination(m_destination->text().toStdString())};
|
| 184 |
const auto expected_txid{Txid::FromHex(m_preview_txid.toStdString())};
|
| 185 |
-
if (!expected_txid) { setResult(tr("The preview is no longer valid. Create a fresh preview."), false); return; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
setBusy(true);
|
| 187 |
m_preview_current = false;
|
| 188 |
m_result->setText(tr("Rechecking the preview and broadcasting the sweep…"));
|
| 189 |
m_request_generation = m_wallet_model->requestSweep(m_private_key_cache, destination, true, expected_txid);
|
| 190 |
-
if (m_request_generation == 0) setResult(tr("Sweep broadcast submission has already started. Wait for its result."), false);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
}
|
| 192 |
|
| 193 |
void SweepDialog::reject()
|
|
@@ -211,7 +234,7 @@ void SweepDialog::handleSweepFinished(uint64_t generation, bool broadcast, bool
|
|
| 211 |
setResult(broadcast
|
| 212 |
? tr("Broadcast failed: %1. Create a fresh preview before trying again.").arg(error)
|
| 213 |
: tr("Preview failed: %1").arg(error), false);
|
| 214 |
-
m_private_key_cache.clear();
|
| 215 |
m_preview_txid.clear();
|
| 216 |
return;
|
| 217 |
}
|
|
@@ -237,7 +260,7 @@ void SweepDialog::clearSensitive()
|
|
| 237 |
m_request_generation = 0;
|
| 238 |
const QSignalBlocker block{m_private_key};
|
| 239 |
m_private_key->clear();
|
| 240 |
-
m_private_key_cache.clear();
|
| 241 |
m_preview_txid.clear();
|
| 242 |
m_preview_current = false;
|
| 243 |
setBusy(false);
|
|
|
|
| 36 |
SecureString result{encoded.constData(), static_cast<size_t>(encoded.size())};
|
| 37 |
if (!encoded.isEmpty()) memory_cleanse(encoded.data(), encoded.size());
|
| 38 |
// QLineEdit does not expose its internal buffer for cleansing. Replace
|
| 39 |
+
// its visible contents before clearing; Qt does not guarantee physical
|
| 40 |
+
// erasure of every internal QString or undo-buffer copy.
|
| 41 |
setText(QString(text().size(), QChar{' '}));
|
| 42 |
clear();
|
| 43 |
return result;
|
|
|
|
| 119 |
m_request_generation = 0;
|
| 120 |
const QSignalBlocker block{m_private_key};
|
| 121 |
m_private_key->clear();
|
| 122 |
+
clearPrivateKeyCache();
|
| 123 |
m_preview_txid.clear();
|
| 124 |
setBusy(false);
|
| 125 |
m_preview_current = false;
|
|
|
|
| 131 |
|
| 132 |
SweepDialog::~SweepDialog() { clearSensitive(); }
|
| 133 |
|
| 134 |
+
void SweepDialog::clearPrivateKeyCache()
|
| 135 |
+
{
|
| 136 |
+
// clear() alone retains storage, including the old secret beyond size().
|
| 137 |
+
// Wipe live bytes first, including short-string storage, then release any
|
| 138 |
+
// allocation through secure_allocator's cleansing deallocator.
|
| 139 |
+
if (!m_private_key_cache.empty()) memory_cleanse(m_private_key_cache.data(), m_private_key_cache.size());
|
| 140 |
+
SecureString{}.swap(m_private_key_cache);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
void SweepDialog::invalidate()
|
| 144 |
{
|
| 145 |
if (m_request_generation != 0 && !m_wallet_model->invalidateSweepRequests()) {
|
|
|
|
| 147 |
return;
|
| 148 |
}
|
| 149 |
m_request_generation = 0;
|
| 150 |
+
clearPrivateKeyCache();
|
| 151 |
m_preview_txid.clear();
|
| 152 |
m_preview_current = false;
|
| 153 |
m_broadcast->setEnabled(false);
|
|
|
|
| 172 |
|
| 173 |
void SweepDialog::preview()
|
| 174 |
{
|
| 175 |
+
clearPrivateKeyCache();
|
| 176 |
const CTxDestination destination{DecodeDestination(m_destination->text().toStdString())};
|
| 177 |
if (!IsValidDestination(destination)) { setResult(tr("Enter a valid destination address."), false); return; }
|
| 178 |
const QSignalBlocker block{m_private_key};
|
|
|
|
| 181 |
setBusy(true);
|
| 182 |
m_result->setText(tr("Scanning for eligible coins and preparing a preview…"));
|
| 183 |
m_request_generation = m_wallet_model->requestSweep(m_private_key_cache, destination, false);
|
| 184 |
+
if (m_request_generation == 0) {
|
| 185 |
+
clearPrivateKeyCache();
|
| 186 |
+
setBusy(false);
|
| 187 |
+
setResult(tr("Another sweep broadcast is already being submitted."), false);
|
| 188 |
+
}
|
| 189 |
}
|
| 190 |
|
| 191 |
void SweepDialog::broadcast()
|
|
|
|
| 197 |
if (answer != QMessageBox::Yes) return;
|
| 198 |
const CTxDestination destination{DecodeDestination(m_destination->text().toStdString())};
|
| 199 |
const auto expected_txid{Txid::FromHex(m_preview_txid.toStdString())};
|
| 200 |
+
if (!expected_txid) {
|
| 201 |
+
clearPrivateKeyCache();
|
| 202 |
+
setResult(tr("The preview is no longer valid. Create a fresh preview."), false);
|
| 203 |
+
return;
|
| 204 |
+
}
|
| 205 |
setBusy(true);
|
| 206 |
m_preview_current = false;
|
| 207 |
m_result->setText(tr("Rechecking the preview and broadcasting the sweep…"));
|
| 208 |
m_request_generation = m_wallet_model->requestSweep(m_private_key_cache, destination, true, expected_txid);
|
| 209 |
+
if (m_request_generation == 0) {
|
| 210 |
+
clearPrivateKeyCache();
|
| 211 |
+
setBusy(false);
|
| 212 |
+
setResult(tr("Sweep broadcast submission has already started. Wait for its result."), false);
|
| 213 |
+
}
|
| 214 |
}
|
| 215 |
|
| 216 |
void SweepDialog::reject()
|
|
|
|
| 234 |
setResult(broadcast
|
| 235 |
? tr("Broadcast failed: %1. Create a fresh preview before trying again.").arg(error)
|
| 236 |
: tr("Preview failed: %1").arg(error), false);
|
| 237 |
+
clearPrivateKeyCache();
|
| 238 |
m_preview_txid.clear();
|
| 239 |
return;
|
| 240 |
}
|
|
|
|
| 260 |
m_request_generation = 0;
|
| 261 |
const QSignalBlocker block{m_private_key};
|
| 262 |
m_private_key->clear();
|
| 263 |
+
clearPrivateKeyCache();
|
| 264 |
m_preview_txid.clear();
|
| 265 |
m_preview_current = false;
|
| 266 |
setBusy(false);
|