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);