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>
This commit is contained in:
Arjun Shankar 2024-01-15 17:44:43 +01:00
parent 8aeec0eb5a
commit 6bd0e4efcc
4 changed files with 84 additions and 17 deletions

View File

@ -289,7 +289,10 @@ tests-special += $(objpfx)tst-error1-mem.out \
$(objpfx)tst-allocate_once-mem.out $(objpfx)tst-allocate_once-mem.out
endif endif
tests-container := tst-syslog tests-container := \
tst-syslog \
tst-syslog-long-progname \
# tests-container
CFLAGS-select.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-select.c += -fexceptions -fasynchronous-unwind-tables
CFLAGS-tsearch.c += $(uses-callbacks) 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 > $@; \ $(common-objpfx)malloc/mtrace $(objpfx)tst-allocate_once.mtrace > $@; \
$(evaluate-test) $(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: $(librt)
$(objpfx)tst-select-time64: $(librt) $(objpfx)tst-select-time64: $(librt)
$(objpfx)tst-pselect: $(librt) $(objpfx)tst-pselect: $(librt)

View File

@ -124,8 +124,9 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
{ {
/* Try to use a static buffer as an optimization. */ /* Try to use a static buffer as an optimization. */
char bufs[1024]; char bufs[1024];
char *buf = NULL; char *buf = bufs;
size_t bufsize = 0; size_t bufsize;
int msgoff; int msgoff;
int saved_errno = errno; 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) \ #define SYSLOG_HEADER_WITHOUT_TS(__pri, __msgoff) \
"<%d>: %n", __pri, __msgoff "<%d>: %n", __pri, __msgoff
int l; int l, vl;
if (has_ts) if (has_ts)
l = __snprintf (bufs, sizeof bufs, l = __snprintf (bufs, sizeof bufs,
SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); SYSLOG_HEADER (pri, timestamp, &msgoff, pid));
else else
l = __snprintf (bufs, sizeof bufs, l = __snprintf (bufs, sizeof bufs,
SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff));
char *pos;
size_t len;
if (0 <= l && l < sizeof bufs) if (0 <= l && l < sizeof bufs)
{ {
va_list apc; /* At this point, there is still a chance that we can print the
va_copy (apc, ap); remaining part of the log into bufs and use that. */
pos = bufs + l;
/* Restore errno for %m format. */ len = sizeof (bufs) - l;
__set_errno (saved_errno);
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;
va_end (apc);
} }
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);
}
{
va_list apc;
va_copy (apc, ap);
/* Restore errno for %m format. */
__set_errno (saved_errno);
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) if (buf == NULL)
{ {

View File

@ -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>