Author: Yao Qi Date: 2026-08-16T11:25:30+01:00 New Revision: ee7f9bcf43f0f1adc7d98f23fe56e4331b508454
URL: https://github.com/llvm/llvm-project/commit/ee7f9bcf43f0f1adc7d98f23fe56e4331b508454 DIFF: https://github.com/llvm/llvm-project/commit/ee7f9bcf43f0f1adc7d98f23fe56e4331b508454.diff LOG: [lldb] Fix invalid UTF-8 in JSON log message (#216185) Enabling the JSON packet log part way through a session aborts an assertions build: ``` (lldb) b f (lldb) run (lldb) log enable -j -f /tmp/pk.json gdb-remote packets (lldb) next Assertion failed: (false && "Invalid UTF-8 in value used as JSON"), function Value, file JSON.h, line 333. ``` `Log::EmitJSONMessage` passed the message straight to `llvm::json::Value`, which asserts on ill-formed UTF-8 and only then falls back to `fixUTF8`. So a release build repairs the message while an assertions build dies. The bytes come from the saved packets. Once logging is turned on, `GDBRemoteCommunicationHistory::Dump` replays them verbatim, and the reply to an `x` (binary memory read) is binary: ``` history[80] tid=0x5c0f < 18> send packet: $x16fdfea00,200#f3 history[81] tid=0x5c0f < 516> read packet: $<binary> ``` The normal live packet path escapes binary before logging it, so this is usually only seen when logging is enabled mid-session. Added: Modified: lldb/source/Utility/Log.cpp lldb/unittests/Utility/LogTest.cpp Removed: ################################################################################ diff --git a/lldb/source/Utility/Log.cpp b/lldb/source/Utility/Log.cpp index dd813efb90f90..62693dc314758 100644 --- a/lldb/source/Utility/Log.cpp +++ b/lldb/source/Utility/Log.cpp @@ -438,7 +438,11 @@ void Log::EmitJSONMessage(llvm::StringRef file, llvm::StringRef function, llvm::StringRef message) { llvm::json::Object obj; WriteJSONHeader(obj, file, function); - obj["message"] = message; + // Log messages can carry arbitrary bytes; JSON strings must be valid UTF-8. + if (llvm::json::isUTF8(message)) + obj["message"] = message; + else + obj["message"] = llvm::json::fixUTF8(message); std::string out; llvm::raw_string_ostream os(out); os << llvm::json::Value(std::move(obj)) << "\n"; diff --git a/lldb/unittests/Utility/LogTest.cpp b/lldb/unittests/Utility/LogTest.cpp index 6eaa318dfe34c..8cfcef6d504c0 100644 --- a/lldb/unittests/Utility/LogTest.cpp +++ b/lldb/unittests/Utility/LogTest.cpp @@ -387,6 +387,35 @@ TEST_F(LogChannelEnabledTest, JSONLOutput) { EXPECT_EQ(Obj->getString("function").value_or(""), "logAndTakeOutput"); } +TEST_F(LogChannelEnabledTest, JSONLOutputInvalidUTF8) { + // Arbitrary bytes must not abort the JSON writer. + EXPECT_THAT_ERROR(Log::EnableLogChannel(getLogHandler(), + /*log_options=*/LLDB_LOG_OPTION_JSON, + "chan", {}), + llvm::Succeeded()); + + auto CheckMessage = [](llvm::StringRef Msg) { + llvm::Expected<llvm::json::Value> Parsed = llvm::json::parse(Msg); + ASSERT_TRUE(static_cast<bool>(Parsed)) + << llvm::toString(Parsed.takeError()); + llvm::json::Object *Obj = Parsed->getAsObject(); + ASSERT_NE(Obj, nullptr); + std::optional<llvm::StringRef> Message = Obj->getString("message"); + ASSERT_TRUE(Message.has_value()); + EXPECT_TRUE(llvm::json::isUTF8(*Message)); + EXPECT_TRUE(Message->contains("before")); + EXPECT_TRUE(Message->contains("after")); + }; + + // Check both entry points: Format and PutString. + CheckMessage(logAndTakeOutput("before\xff\xfe" + "after")); + + getLog()->PutString("before\xff\xfe" + "after"); + CheckMessage(takeOutput()); +} + TEST_F(LogChannelEnabledTest, LLDB_LOG_ERROR) { LLDB_LOG_ERROR(getLog(), llvm::Error::success(), "Foo failed: {0}"); ASSERT_EQ("", takeOutput()); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
