Skip to content

Commit dc0fe7a

Browse files
laanwjfanquake
authored andcommitted
util: Filter control characters out of log messages
Belts and suspenders: make sure outgoing log messages don't contain potentially suspicious characters, such as terminal control codes. This escapes control characters except newline ('\n') in C syntax. It escapes instead of removes them to still allow for troubleshooting issues where they accidentally end up in strings. Github-Pull: bitcoin#17095 Rebased-From: d7820a1
1 parent ba46f39 commit dc0fe7a

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

src/logging.cpp

+23-1
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,32 @@ std::string BCLog::Logger::LogTimestampStr(const std::string& str)
224224
return strStamped;
225225
}
226226

227+
namespace BCLog {
228+
/** Belts and suspenders: make sure outgoing log messages don't contain
229+
* potentially suspicious characters, such as terminal control codes.
230+
*
231+
* This escapes control characters except newline ('\n') in C syntax.
232+
* It escapes instead of removes them to still allow for troubleshooting
233+
* issues where they accidentally end up in strings.
234+
*/
235+
std::string LogEscapeMessage(const std::string& str) {
236+
std::string ret;
237+
for (char ch_in : str) {
238+
uint8_t ch = (uint8_t)ch_in;
239+
if ((ch >= 32 || ch == '\n') && ch != '\x7f') {
240+
ret += ch_in;
241+
} else {
242+
ret += strprintf("\\x%02x", ch);
243+
}
244+
}
245+
return ret;
246+
}
247+
}
248+
227249
void BCLog::Logger::LogPrintStr(const std::string& str)
228250
{
229251
std::lock_guard<std::mutex> scoped_lock(m_cs);
230-
std::string str_prefixed = str;
252+
std::string str_prefixed = LogEscapeMessage(str);
231253

232254
if (m_log_threadnames && m_started_new_line) {
233255
str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");

src/test/util_tests.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525

2626
#include <boost/test/unit_test.hpp>
2727

28+
/* defined in logging.cpp */
29+
namespace BCLog {
30+
std::string LogEscapeMessage(const std::string& str);
31+
}
32+
2833
BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup)
2934

3035
BOOST_AUTO_TEST_CASE(util_criticalsection)
@@ -1572,4 +1577,17 @@ BOOST_AUTO_TEST_CASE(test_Capitalize)
15721577
BOOST_CHECK_EQUAL(Capitalize("\x00\xfe\xff"), "\x00\xfe\xff");
15731578
}
15741579

1580+
BOOST_AUTO_TEST_CASE(test_LogEscapeMessage)
1581+
{
1582+
// ASCII and UTF-8 must pass through unaltered.
1583+
BOOST_CHECK_EQUAL(BCLog::LogEscapeMessage("Valid log message貓"), "Valid log message貓");
1584+
// Newlines must pass through unaltered.
1585+
BOOST_CHECK_EQUAL(BCLog::LogEscapeMessage("Message\n with newlines\n"), "Message\n with newlines\n");
1586+
// Other control characters are escaped in C syntax.
1587+
BOOST_CHECK_EQUAL(BCLog::LogEscapeMessage("\x01\x7f Corrupted log message\x0d"), R"(\x01\x7f Corrupted log message\x0d)");
1588+
// Embedded NULL characters are escaped too.
1589+
const std::string NUL("O\x00O", 3);
1590+
BOOST_CHECK_EQUAL(BCLog::LogEscapeMessage(NUL), R"(O\x00O)");
1591+
}
1592+
15751593
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)