mirror of git://sourceware.org/git/glibc.git
stdlib: Fix __libc_message_impl iovec size (BZ 32947)
The iovec size should account for all substrings between each conversion specification. For the format: "abc %s efg" The list of substrings are: ["abc ", arg, " efg] which is 2 times the number of maximum arguments *plus* one. This issue triggered 'out of bounds' errors by stdlib/tst-bz20544 when glibc is built with experimental UBSAN support [1]. Besides adjusting the iovec size, a new runtime and check is added to avoid wrong __libc_message_impl usage. Checked on x86_64-linux-gnu. [1] https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/azanella/ubsan-undef Co-authored-by: Carlos O'Donell <carlos@redhat.com> Tested-by: Carlos O'Donell <carlos@redhat.com> Reviewed-by: Carlos O'Donell <carlos@redhat.com>
This commit is contained in:
parent
681a24ae4d
commit
eeb7b079d5
|
|
@ -173,6 +173,10 @@ libc_hidden_proto (__fortify_fail)
|
|||
/* The maximum number of varargs allowed in a __libc_message format string */
|
||||
#define LIBC_MESSAGE_MAX_ARGS 4
|
||||
|
||||
#define IOVEC_MAX_ERR_MSG "Fatal glibc error: Internal " \
|
||||
"__libc_message error. Too many arguments.\n"
|
||||
#define IOVEC_MAX_ERR_MSG_LEN (sizeof (IOVEC_MAX_ERR_MSG) - 1)
|
||||
|
||||
_Noreturn void __libc_message_impl (const char *__fnt, ...) attribute_hidden
|
||||
__attribute__ ((__format__ (__printf__, 1, 2)));
|
||||
|
||||
|
|
|
|||
|
|
@ -348,6 +348,7 @@ tests-internal := \
|
|||
bug-regex5 \
|
||||
bug-regex20 \
|
||||
bug-regex33 \
|
||||
tst-libc-message \
|
||||
# tests-internal
|
||||
|
||||
tests-container := \
|
||||
|
|
@ -391,6 +392,7 @@ endif
|
|||
|
||||
tests-static = \
|
||||
tst-exec-static \
|
||||
tst-libc-message \
|
||||
tst-spawn-static \
|
||||
# tests-static
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
/* Internal test to verify __libc_fatal.
|
||||
Copyright (C) 2025 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 <signal.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
|
||||
static _Noreturn void
|
||||
run_libc_message (void *closure)
|
||||
{
|
||||
/* We only support 4 arguments. Call with 5 to trigger failure. */
|
||||
__libc_message_impl ("%s %s %s %s %s\n", "1", "2", "3", "4", "5");
|
||||
__builtin_unreachable ();
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct support_capture_subprocess result
|
||||
= support_capture_subprocess (run_libc_message, NULL);
|
||||
support_capture_subprocess_check (&result, "libc_message", -SIGABRT,
|
||||
sc_allow_stderr);
|
||||
|
||||
TEST_COMPARE_STRING (result.err.buffer, IOVEC_MAX_ERR_MSG);
|
||||
|
||||
support_capture_subprocess_free (&result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
|
|
@ -16,23 +16,13 @@
|
|||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <atomic.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <ldsodefs.h>
|
||||
#include <libc-pointer-arith.h>
|
||||
#include <paths.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysdep.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/uio.h>
|
||||
#include <not-cancel.h>
|
||||
#include <setvmaname.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef FATAL_PREPARE_INCLUDE
|
||||
#include FATAL_PREPARE_INCLUDE
|
||||
|
|
@ -47,6 +37,10 @@ writev_for_fatal (int fd, const struct iovec *iov, size_t niov, size_t total)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* At most a substring before each conversion specification and the
|
||||
trailing substring (the plus one). */
|
||||
#define IOVEC_MAX (LIBC_MESSAGE_MAX_ARGS * 2 + 1)
|
||||
|
||||
/* Abort with an error message. */
|
||||
void
|
||||
__libc_message_impl (const char *fmt, ...)
|
||||
|
|
@ -61,7 +55,7 @@ __libc_message_impl (const char *fmt, ...)
|
|||
if (fd == -1)
|
||||
fd = STDERR_FILENO;
|
||||
|
||||
struct iovec iov[LIBC_MESSAGE_MAX_ARGS * 2 - 1];
|
||||
struct iovec iov[IOVEC_MAX];
|
||||
int iovcnt = 0;
|
||||
ssize_t total = 0;
|
||||
|
||||
|
|
@ -99,6 +93,16 @@ __libc_message_impl (const char *fmt, ...)
|
|||
iov[iovcnt].iov_len = len;
|
||||
total += len;
|
||||
iovcnt++;
|
||||
|
||||
if (__glibc_unlikely (iovcnt > IOVEC_MAX))
|
||||
{
|
||||
len = IOVEC_MAX_ERR_MSG_LEN;
|
||||
iov[0].iov_base = (char *) IOVEC_MAX_ERR_MSG;
|
||||
iov[0].iov_len = len;
|
||||
total = len;
|
||||
iovcnt = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end (ap);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue