Skip to content

Commit ddf5fe4

Browse files
committed
memory cleanse for ClearCommandHistory
1 parent 211ef5a commit ddf5fe4

File tree

1 file changed

+38
-7
lines changed

1 file changed

+38
-7
lines changed

src/qt/rpcconsole.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#endif // ENABLE_WALLET
2525
#include <rpc/client.h>
2626
#include <rpc/server.h>
27+
#include <support/cleanse.h>
2728
#include <util/strencodings.h>
2829
#include <util/string.h>
2930
#include <util/time.h>
@@ -693,12 +694,28 @@ void RPCConsole::WriteCommandHistory()
693694

694695
void RPCConsole::ClearCommandHistory()
695696
{
696-
// Overwrite each command in history with dummy data before clearing
697-
// This helps ensure the sensitive data is overwritten in memory
697+
// Best-effort attempt to securely overwrite command history in memory.
698+
// Note: Complete memory sanitization cannot be guaranteed with QString due to
699+
// Qt's implicit sharing (copy-on-write) and internal memory management.
698700
for (QString& cmd : history) {
699-
// Overwrite with characters of same length (don't store new length info)
700-
cmd.fill('x'); // First pass: fill with 'x'
701-
cmd.fill('\0'); // Second pass: fill with null chars
701+
if (cmd.isEmpty()) continue;
702+
703+
// Attempt to cleanse QString's internal buffer using Bitcoin Core's
704+
// secure memory_cleanse function (uses platform-specific secure zeroing)
705+
QChar* data = cmd.data(); // Get pointer to mutable internal buffer
706+
size_t byte_size = cmd.size() * sizeof(QChar);
707+
708+
// First pass: overwrite with 'x' and cleanse
709+
cmd.fill('x');
710+
if (data == cmd.data()) { // Verify we still have the same buffer
711+
memory_cleanse(cmd.data(), byte_size);
712+
}
713+
714+
// Second pass: overwrite with null and cleanse
715+
cmd.fill('\0');
716+
if (data == cmd.data()) { // Verify we still have the same buffer
717+
memory_cleanse(cmd.data(), byte_size);
718+
}
702719
}
703720

704721
// Overwrite saved history in settings with dummy data
@@ -729,6 +746,20 @@ void RPCConsole::ClearCommandHistory()
729746
// Clear the history list
730747
history.clear();
731748
historyPtr = 0;
749+
750+
// Also cleanse cmdBeforeBrowsing which may contain command data
751+
if (!cmdBeforeBrowsing.isEmpty()) {
752+
QChar* data = cmdBeforeBrowsing.data();
753+
size_t byte_size = cmdBeforeBrowsing.size() * sizeof(QChar);
754+
cmdBeforeBrowsing.fill('x');
755+
if (data == cmdBeforeBrowsing.data()) {
756+
memory_cleanse(cmdBeforeBrowsing.data(), byte_size);
757+
}
758+
cmdBeforeBrowsing.fill('\0');
759+
if (data == cmdBeforeBrowsing.data()) {
760+
memory_cleanse(cmdBeforeBrowsing.data(), byte_size);
761+
}
762+
}
732763
cmdBeforeBrowsing.clear();
733764

734765
// Second pass: write empty history to settings
@@ -1236,8 +1267,8 @@ void RPCConsole::on_lineEdit_returnPressed()
12361267
QMessageBox::StandardButton reply = QMessageBox::question(this,
12371268
tr("Clear Command History"),
12381269
tr("This will permanently clear your command history and console output.<br><br>"
1239-
"While this action is irreversible, it cannot be guaranteed to be "
1240-
"completely irrecoverable from disk.<br><br>"
1270+
"While this action is irreversible, complete removal from memory and disk "
1271+
"cannot be guaranteed due to system and Qt framework limitations.<br><br>"
12411272
"Are you sure you want to proceed?"),
12421273
QMessageBox::Yes | QMessageBox::No,
12431274
QMessageBox::No);

0 commit comments

Comments
 (0)