|
24 | 24 | #endif // ENABLE_WALLET |
25 | 25 | #include <rpc/client.h> |
26 | 26 | #include <rpc/server.h> |
| 27 | +#include <support/cleanse.h> |
27 | 28 | #include <util/strencodings.h> |
28 | 29 | #include <util/string.h> |
29 | 30 | #include <util/time.h> |
@@ -693,12 +694,28 @@ void RPCConsole::WriteCommandHistory() |
693 | 694 |
|
694 | 695 | void RPCConsole::ClearCommandHistory() |
695 | 696 | { |
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. |
698 | 700 | 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 | + } |
702 | 719 | } |
703 | 720 |
|
704 | 721 | // Overwrite saved history in settings with dummy data |
@@ -729,6 +746,20 @@ void RPCConsole::ClearCommandHistory() |
729 | 746 | // Clear the history list |
730 | 747 | history.clear(); |
731 | 748 | 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 | + } |
732 | 763 | cmdBeforeBrowsing.clear(); |
733 | 764 |
|
734 | 765 | // Second pass: write empty history to settings |
@@ -1236,8 +1267,8 @@ void RPCConsole::on_lineEdit_returnPressed() |
1236 | 1267 | QMessageBox::StandardButton reply = QMessageBox::question(this, |
1237 | 1268 | tr("Clear Command History"), |
1238 | 1269 | 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>" |
1241 | 1272 | "Are you sure you want to proceed?"), |
1242 | 1273 | QMessageBox::Yes | QMessageBox::No, |
1243 | 1274 | QMessageBox::No); |
|
0 commit comments