diff --git a/include/stdio.h b/include/stdio.h index e48d709919..3d917dba5b 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -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))); diff --git a/posix/Makefile b/posix/Makefile index c0e224236a..36b8b14c46 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -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 diff --git a/posix/tst-libc-message.c b/posix/tst-libc-message.c new file mode 100644 index 0000000000..b85195e338 --- /dev/null +++ b/posix/tst-libc-message.c @@ -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 + . */ + +#include +#include + +#include +#include + +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 diff --git a/sysdeps/posix/libc_fatal.c b/sysdeps/posix/libc_fatal.c index d90cc6c681..6f75197e96 100644 --- a/sysdeps/posix/libc_fatal.c +++ b/sysdeps/posix/libc_fatal.c @@ -16,23 +16,13 @@ License along with the GNU C Library; if not, see . */ -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include #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);