From 23514c72b780f3da097ecf33a793b7ba9c2070d2 Mon Sep 17 00:00:00 2001
From: Arjun Shankar <arjun@redhat.com>
Date: Mon, 15 Jan 2024 17:44:43 +0100
Subject: [PATCH 1/3] syslog: Fix heap buffer overflow in __vsyslog_internal
 (CVE-2023-6246)

__vsyslog_internal did not handle a case where printing a SYSLOG_HEADER
containing a long program name failed to update the required buffer
size, leading to the allocation and overflow of a too-small buffer on
the heap.  This commit fixes that.  It also adds a new regression test
that uses glibc.malloc.check.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 6bd0e4efcc78f3c0115e5ea9739a1642807450da)
---
 misc/Makefile                                 |  8 ++-
 misc/syslog.c                                 | 50 +++++++++++++------
 misc/tst-syslog-long-progname.c               | 39 +++++++++++++++
 .../postclean.req                             |  0
 4 files changed, 82 insertions(+), 15 deletions(-)
 create mode 100644 misc/tst-syslog-long-progname.c
 create mode 100644 misc/tst-syslog-long-progname.root/postclean.req

diff --git a/misc/Makefile b/misc/Makefile
index fe0d49c1de..90b31952c5 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -289,7 +289,10 @@ tests-special += $(objpfx)tst-error1-mem.out \
   $(objpfx)tst-allocate_once-mem.out
 endif
 
-tests-container := tst-syslog
+tests-container := \
+  tst-syslog \
+  tst-syslog-long-progname \
+  # tests-container
 
 CFLAGS-select.c += -fexceptions -fasynchronous-unwind-tables
 CFLAGS-tsearch.c += $(uses-callbacks)
@@ -351,6 +354,9 @@ $(objpfx)tst-allocate_once-mem.out: $(objpfx)tst-allocate_once.out
 	$(common-objpfx)malloc/mtrace $(objpfx)tst-allocate_once.mtrace > $@; \
 	$(evaluate-test)
 
+tst-syslog-long-progname-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 \
+			       LD_PRELOAD=libc_malloc_debug.so.0
+
 $(objpfx)tst-select: $(librt)
 $(objpfx)tst-select-time64: $(librt)
 $(objpfx)tst-pselect: $(librt)
diff --git a/misc/syslog.c b/misc/syslog.c
index 1b8cb722c5..814d224a1e 100644
--- a/misc/syslog.c
+++ b/misc/syslog.c
@@ -124,8 +124,9 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
 {
   /* Try to use a static buffer as an optimization.  */
   char bufs[1024];
-  char *buf = NULL;
-  size_t bufsize = 0;
+  char *buf = bufs;
+  size_t bufsize;
+
   int msgoff;
   int saved_errno = errno;
 
@@ -177,29 +178,50 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
 #define SYSLOG_HEADER_WITHOUT_TS(__pri, __msgoff)        \
   "<%d>: %n", __pri, __msgoff
 
-  int l;
+  int l, vl;
   if (has_ts)
     l = __snprintf (bufs, sizeof bufs,
 		    SYSLOG_HEADER (pri, timestamp, &msgoff, pid));
   else
     l = __snprintf (bufs, sizeof bufs,
 		    SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff));
+
+  char *pos;
+  size_t len;
+
   if (0 <= l && l < sizeof bufs)
     {
-      va_list apc;
-      va_copy (apc, ap);
+      /* At this point, there is still a chance that we can print the
+         remaining part of the log into bufs and use that.  */
+      pos = bufs + l;
+      len = sizeof (bufs) - l;
+    }
+  else
+    {
+      buf = NULL;
+      /* We already know that bufs is too small to use for this log message.
+         The next vsnprintf into bufs is used only to calculate the total
+         required buffer length.  We will discard bufs contents and allocate
+         an appropriately sized buffer later instead.  */
+      pos = bufs;
+      len = sizeof (bufs);
+    }
 
-      /* Restore errno for %m format.  */
-      __set_errno (saved_errno);
+  {
+    va_list apc;
+    va_copy (apc, ap);
 
-      int vl = __vsnprintf_internal (bufs + l, sizeof bufs - l, fmt, apc,
-                                     mode_flags);
-      if (0 <= vl && vl < sizeof bufs - l)
-        buf = bufs;
-      bufsize = l + vl;
+    /* Restore errno for %m format.  */
+    __set_errno (saved_errno);
 
-      va_end (apc);
-    }
+    vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags);
+
+    if (!(0 <= vl && vl < len))
+      buf = NULL;
+
+    bufsize = l + vl;
+    va_end (apc);
+  }
 
   if (buf == NULL)
     {
diff --git a/misc/tst-syslog-long-progname.c b/misc/tst-syslog-long-progname.c
new file mode 100644
index 0000000000..88f37a8a00
--- /dev/null
+++ b/misc/tst-syslog-long-progname.c
@@ -0,0 +1,39 @@
+/* Test heap buffer overflow in syslog with long __progname (CVE-2023-6246)
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <syslog.h>
+#include <string.h>
+
+extern char * __progname;
+
+static int
+do_test (void)
+{
+  char long_progname[2048];
+
+  memset (long_progname, 'X', sizeof (long_progname) - 1);
+  long_progname[sizeof (long_progname) - 1] = '\0';
+
+  __progname = long_progname;
+
+  syslog (LOG_INFO, "Hello, World!");
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/misc/tst-syslog-long-progname.root/postclean.req b/misc/tst-syslog-long-progname.root/postclean.req
new file mode 100644
index 0000000000..e69de29bb2
-- 
2.33.0

