From 7b938b4d14aed5375745b8c72789afabfad4df25 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 18 Jun 2024 17:20:06 +0200 Subject: [PATCH] util/Exception: sanitize message strings This should prevent leaking unsanitized strings from libraries. --- src/util/Exception.cxx | 38 ++++++++++++++++++++++++++++++++++--- test/util/TestException.cxx | 18 ++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/util/Exception.cxx b/src/util/Exception.cxx index 351d5b7f5..17eff34df 100644 --- a/src/util/Exception.cxx +++ b/src/util/Exception.cxx @@ -2,9 +2,40 @@ // author: Max Kellermann #include "Exception.hxx" +#include "CharUtil.hxx" +#include "StringStrip.hxx" #include +/** + * Append the given C string to a std::string, with some + * sanitizations: strip whitespace at the beginning and the end and + * combine multiple whitespace to a single space. + * + * This shall compress strangely formatted multi-line messages (which + * may come from third-party libraries) to a single line. + */ +static void +AppendSanitize(std::string &dest, const char *src) noexcept +{ + src = StripLeft(src); + + bool space = false; + while (char ch = *src++) { + if (IsWhitespaceFast(ch)) { + space = true; + continue; + } + + if (space) { + space = false; + dest.push_back(' '); + } + + dest.push_back(ch); + } +} + template static void AppendNestedMessage(std::string &result, T &&e, @@ -14,13 +45,13 @@ AppendNestedMessage(std::string &result, T &&e, std::rethrow_if_nested(std::forward(e)); } catch (const std::exception &nested) { result += separator; - result += nested.what(); + AppendSanitize(result, nested.what()); AppendNestedMessage(result, nested, fallback, separator); } catch (const std::nested_exception &ne) { AppendNestedMessage(result, ne, fallback, separator); } catch (const char *s) { result += separator; - result += s; + AppendSanitize(result, s); } catch (...) { result += separator; result += fallback; @@ -31,7 +62,8 @@ std::string GetFullMessage(const std::exception &e, const char *fallback, const char *separator) noexcept { - std::string result = e.what(); + std::string result; + AppendSanitize(result, e.what()); AppendNestedMessage(result, e, fallback, separator); return result; } diff --git a/test/util/TestException.cxx b/test/util/TestException.cxx index c5dda7fd5..6e3e706e5 100644 --- a/test/util/TestException.cxx +++ b/test/util/TestException.cxx @@ -6,6 +6,8 @@ #include +using std::string_view_literals::operator""sv; + TEST(ExceptionTest, RuntimeError) { ASSERT_EQ(GetFullMessage(std::make_exception_ptr(std::runtime_error("Foo"))), "Foo"); @@ -22,6 +24,22 @@ TEST(ExceptionTest, DerivedError) ASSERT_EQ(GetFullMessage(std::make_exception_ptr(DerivedError("Foo"))), "Foo"); } +TEST(ExceptionTest, GetFullMessageSanitize) +{ + try { + try { + throw " ABC \n DEF "; + } catch (...) { + std::throw_with_nested(std::runtime_error{"foo\r\n\tbar"}); + } + + FAIL(); + } catch (...) { + ASSERT_EQ(GetFullMessage(std::current_exception()), + "foo bar; ABC DEF"sv); + } +} + TEST(ExceptionTest, FindNestedDirect) { struct Foo {};