syslog: Fix heap buffer overflow in __vsyslog_internal (CVE-2023-6779)

__vsyslog_internal used the return value of snprintf/vsnprintf to
calculate buffer sizes for memory allocation.  If these functions (for
any reason) failed and returned -1, the resulting buffer would be too
small to hold output.  This commit fixes that.

All snprintf/vsnprintf calls are checked for negative return values and
the function silently returns upon encountering them.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
This commit is contained in:
Arjun Shankar 2024-01-15 17:44:44 +01:00
parent 6bd0e4efcc
commit 7e5a0c286d

View File

@ -185,11 +185,13 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
else else
l = __snprintf (bufs, sizeof bufs, l = __snprintf (bufs, sizeof bufs,
SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff));
if (l < 0)
goto out;
char *pos; char *pos;
size_t len; size_t len;
if (0 <= l && l < sizeof bufs) if (l < sizeof bufs)
{ {
/* At this point, there is still a chance that we can print the /* At this point, there is still a chance that we can print the
remaining part of the log into bufs and use that. */ remaining part of the log into bufs and use that. */
@ -215,12 +217,15 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
__set_errno (saved_errno); __set_errno (saved_errno);
vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags); vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags);
va_end (apc);
if (!(0 <= vl && vl < len)) if (vl < 0)
goto out;
if (vl >= len)
buf = NULL; buf = NULL;
bufsize = l + vl; bufsize = l + vl;
va_end (apc);
} }
if (buf == NULL) if (buf == NULL)
@ -231,25 +236,37 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
/* Tell the cancellation handler to free this buffer. */ /* Tell the cancellation handler to free this buffer. */
clarg.buf = buf; clarg.buf = buf;
int cl;
if (has_ts) if (has_ts)
__snprintf (buf, l + 1, cl = __snprintf (buf, l + 1,
SYSLOG_HEADER (pri, timestamp, &msgoff, pid)); SYSLOG_HEADER (pri, timestamp, &msgoff, pid));
else else
__snprintf (buf, l + 1, cl = __snprintf (buf, l + 1,
SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff)); SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff));
if (cl != l)
goto out;
va_list apc; va_list apc;
va_copy (apc, ap); va_copy (apc, ap);
__vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc, cl = __vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc,
mode_flags); mode_flags);
va_end (apc); va_end (apc);
if (cl != vl)
goto out;
} }
else else
{ {
int bl;
/* Nothing much to do but emit an error message. */ /* Nothing much to do but emit an error message. */
bufsize = __snprintf (bufs, sizeof bufs, bl = __snprintf (bufs, sizeof bufs,
"out of memory[%d]", __getpid ()); "out of memory[%d]", __getpid ());
if (bl < 0 || bl >= sizeof bufs)
goto out;
bufsize = bl;
buf = bufs; buf = bufs;
msgoff = 0;
} }
} }