From e229be8f328c57d966a51d2521b44ed9968b235b Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Wed, 20 Aug 2025 00:00:27 +0200 Subject: [PATCH] gmessages: Fix stack overflow with many fields If g_log_structured is called recursively with more than 16 fields, then n_fields is larger than the stack array, leading to an eventual read overflow into the message buffer. Either the message can be properly prepared by an attacker or many bytes are left uninitialized (and potentially prepared through previous function calls by the attacker), which could lead to information leaks through logs. Since such a recursive overflow can only be triggered with test code (or users explicitly supplying G_LOG_FLAG_RECURSION), this is no security threat. Closes #3760 --- glib/gmessages.c | 4 ++- glib/tests/logging.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/glib/gmessages.c b/glib/gmessages.c index b2f88f1a0..4b71cc40d 100644 --- a/glib/gmessages.c +++ b/glib/gmessages.c @@ -1618,7 +1618,7 @@ g_log_structured (const gchar *log_domain, for (p = va_arg (args, gchar *), i = n_fields; strcmp (p, "MESSAGE") != 0; - p = va_arg (args, gchar *), i++) + p = va_arg (args, gchar *)) { GLogField field; const gchar *key = p; @@ -1647,6 +1647,8 @@ g_log_structured (const gchar *log_domain, g_array_append_val (array, field); } + + i++; } n_fields = i; diff --git a/glib/tests/logging.c b/glib/tests/logging.c index c2808b9c8..d8b4d271a 100644 --- a/glib/tests/logging.c +++ b/glib/tests/logging.c @@ -1,5 +1,10 @@ +#include "config.h" + #include #include +#ifdef HAVE_UNISTD_H +#include +#endif #define G_LOG_USE_STRUCTURED 1 #include @@ -1146,6 +1151,58 @@ test_structured_logging_set_writer_func_twice (void) } } +static GLogWriterOutput +n_fields_log_writer (GLogLevelFlags log_level, const GLogField *fields, gsize n_fields, gpointer user_data) +{ + size_t expected_n_fields = *(size_t *) user_data; + + /* Let process exit successfully for g_test_trap_assert_passed(). */ + if (n_fields == expected_n_fields) + _exit (0); + + return G_LOG_WRITER_HANDLED; +} + +static void +test_structured_logging_recursion_overflow (void) +{ + g_test_summary ("Test that g_log_structured always sets n_fields correctly."); + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3760"); + + if (g_test_subprocess ()) + { + size_t expected_n_fields = 16; + const int log_level = G_LOG_LEVEL_MESSAGE | G_LOG_FLAG_RECURSION; + + g_log_set_writer_func (n_fields_log_writer, &expected_n_fields, NULL); + g_log_structured (G_LOG_DOMAIN, log_level, + "key01", "value", + "key02", "value", + "key03", "value", + "key04", "value", + "key05", "value", + "key06", "value", + "key07", "value", + "key08", "value", + "key09", "value", + "key10", "value", + "key11", "value", + "key12", "value", + "key13", "value", + "key14", "value", + "key15", "value", + "key16", "value", + "key17", "value", + "key18", "value", + "MESSAGE", "Triggered segmentation fault in the past"); + } + else + { + g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT); + g_test_trap_assert_passed (); + } +} + int main (int argc, char *argv[]) { @@ -1187,6 +1244,7 @@ main (int argc, char *argv[]) g_test_add_func ("/logging/gibberish", test_gibberish); g_test_add_func ("/structured-logging/no-state", test_structured_logging_no_state); g_test_add_func ("/structured-logging/some-state", test_structured_logging_some_state); + g_test_add_func ("/structured-logging/recursion-overflow", test_structured_logging_recursion_overflow); g_test_add_func ("/structured-logging/robustness", test_structured_logging_robustness); g_test_add_func ("/structured-logging/roundtrip1", test_structured_logging_roundtrip1); g_test_add_func ("/structured-logging/roundtrip2", test_structured_logging_roundtrip2);