From 53549b9b9198f1309f9559cf377e344cf5ea784c Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sat, 22 Jun 2024 12:17:51 +0200 Subject: [PATCH] xvasprintf: Guarantee a non-NULL result. * lib/xvasprintf.h: Clarify the programmer's responsibilities. (xasprintf, xvasprintf): Declare as returning non-NULL. * lib/xvasprintf.c: Include . (xstrcat): Allow results longer than INT_MAX characters. Upon size overflow, invoke xalloc_die. (xvasprintf): Call vazsprintf instead of vasprintf. When some other error occurs, emit an error message and abort. * m4/strerrorname_np.m4 (gl_CHECK_STRERRORNAME_NP): New macro, extracted from gl_FUNC_STRERRORNAME_NP. (gl_FUNC_STRERRORNAME_NP): Invoke it. (gl_OPTIONAL_STRERRORNAME_NP): New macro. * m4/xvasprintf.m4 (gl_XVASPRINTF): Invoke gl_OPTIONAL_STRERRORNAME_NP. * modules/xvasprintf (Files): Add m4/strerrorname_np.m4. (Depends-on): Add extensions, vazsprintf. Remove vasprintf. * NEWS: Mention the change. --- ChangeLog | 19 ++++++++++++++++++ NEWS | 4 ++++ lib/xvasprintf.c | 46 ++++++++++++++++++++++++++++++++----------- lib/xvasprintf.h | 25 +++++++++++++++-------- m4/strerrorname_np.m4 | 33 ++++++++++++++++++++++++++----- m4/xvasprintf.m4 | 6 ++++-- modules/xvasprintf | 4 +++- 7 files changed, 110 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7c720ba885..ef21dcc222 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2024-06-22 Bruno Haible + + xvasprintf: Guarantee a non-NULL result. + * lib/xvasprintf.h: Clarify the programmer's responsibilities. + (xasprintf, xvasprintf): Declare as returning non-NULL. + * lib/xvasprintf.c: Include . + (xstrcat): Allow results longer than INT_MAX characters. Upon size + overflow, invoke xalloc_die. + (xvasprintf): Call vazsprintf instead of vasprintf. When some other + error occurs, emit an error message and abort. + * m4/strerrorname_np.m4 (gl_CHECK_STRERRORNAME_NP): New macro, extracted + from gl_FUNC_STRERRORNAME_NP. + (gl_FUNC_STRERRORNAME_NP): Invoke it. + (gl_OPTIONAL_STRERRORNAME_NP): New macro. + * m4/xvasprintf.m4 (gl_XVASPRINTF): Invoke gl_OPTIONAL_STRERRORNAME_NP. + * modules/xvasprintf (Files): Add m4/strerrorname_np.m4. + (Depends-on): Add extensions, vazsprintf. Remove vasprintf. + * NEWS: Mention the change. + 2024-06-22 Bruno Haible vasprintf: Make return convention consistent with other modules. diff --git a/NEWS b/NEWS index 57540c8375..36ffbb64f8 100644 --- a/NEWS +++ b/NEWS @@ -74,6 +74,10 @@ User visible incompatible changes Date Modules Changes +2024-06-22 xvasprintf It is now the programmer's responsibility to pass + a valid format string without %ls, %lc directives + and that all widths are >= -INT_MAX and <= INT_MAX. + 2024-05-16 putenv This module is renamed to 'putenv-gnu'. 2024-02-21 *printf-posix These modules no longer support the 'n' directive diff --git a/lib/xvasprintf.c b/lib/xvasprintf.c index 6cf6d36a58..97f24eb297 100644 --- a/lib/xvasprintf.c +++ b/lib/xvasprintf.c @@ -21,8 +21,9 @@ #include #include -#include #include +#include +#include #include "xalloc.h" @@ -48,14 +49,10 @@ xstrcat (size_t argcount, va_list args) } va_end (ap); - /* Test for overflow in the summing pass above or in (totalsize + 1) below. - Also, don't return a string longer than INT_MAX, for consistency with - vasprintf(). */ - if (totalsize == SIZE_MAX || totalsize > INT_MAX) - { - errno = EOVERFLOW; - return NULL; - } + /* Test for overflow in the summing pass above or in (totalsize + 1) + below. */ + if (totalsize == SIZE_MAX) + xalloc_die (); /* Allocate and fill the result string. */ result = XNMALLOC (totalsize + 1, char); @@ -99,11 +96,38 @@ xvasprintf (const char *format, va_list args) } } - if (vasprintf (&result, format, args) < 0) + if (vazsprintf (&result, format, args) < 0) { if (errno == ENOMEM) xalloc_die (); - return NULL; + else + { + /* The programmer ought to have ensured that none of the other errors + can occur. */ + int err = errno; + char errbuf[20]; + const char *errname; +#if HAVE_WORKING_STRERRORNAME_NP + errname = strerrorname_np (err); + if (errname == NULL) +#else + if (err == EINVAL) + errname = "EINVAL"; + else if (err == EILSEQ) + errname = "EILSEQ"; + else if (err == EOVERFLOW) + errname = "EOVERFLOW"; + else +#endif + { + sprintf (errbuf, "%d", err); + errname = errbuf; + } + fprintf (stderr, "vasprintf failed! format=\"%s\", errno=%s\n", + format, errname); + fflush (stderr); + abort (); + } } return result; diff --git a/lib/xvasprintf.h b/lib/xvasprintf.h index 937f97ba4b..fac633c549 100644 --- a/lib/xvasprintf.h +++ b/lib/xvasprintf.h @@ -17,7 +17,8 @@ #ifndef _XVASPRINTF_H #define _XVASPRINTF_H -/* This file uses _GL_ATTRIBUTE_FORMAT, _GL_ATTRIBUTE_MALLOC. */ +/* This file uses _GL_ATTRIBUTE_FORMAT, _GL_ATTRIBUTE_MALLOC, + _GL_ATTRIBUTE_RETURNS_NONNULL. */ #if !_GL_CONFIG_H_INCLUDED #error "Please include config.h first." #endif @@ -35,19 +36,27 @@ extern "C" { #endif -/* Write formatted output to a string dynamically allocated with malloc(), - and return it. Upon [ENOMEM] memory allocation error, call xalloc_die. - On some other error - - [EOVERFLOW] resulting string length is > INT_MAX, +/* Prints formatted output to a string dynamically allocated with malloc(), + and returns it. Upon [ENOMEM] memory allocation error, it calls xalloc_die. + + It is the responsibility of the programmer to ensure that + - the format string is valid, + - the format string does not use %ls or %lc directives, and + - all widths in the format string and passed as arguments are >= -INT_MAX + and <= INT_MAX, + so that other errors - [EINVAL] invalid format string, - [EILSEQ] error during conversion between wide and multibyte characters, - return NULL. */ + - [EOVERFLOW] some specified width is > INT_MAX, + cannot occur. */ extern char *xasprintf (const char *format, ...) _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE - _GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_STANDARD, 1, 2)); + _GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_STANDARD, 1, 2)) + _GL_ATTRIBUTE_RETURNS_NONNULL; extern char *xvasprintf (const char *format, va_list args) _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE - _GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_STANDARD, 1, 0)); + _GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_STANDARD, 1, 0)) + _GL_ATTRIBUTE_RETURNS_NONNULL; #ifdef __cplusplus } diff --git a/m4/strerrorname_np.m4 b/m4/strerrorname_np.m4 index 9725155bae..ac0211715a 100644 --- a/m4/strerrorname_np.m4 +++ b/m4/strerrorname_np.m4 @@ -1,5 +1,5 @@ # strerrorname_np.m4 -# serial 5 +# serial 6 dnl Copyright (C) 2020-2024 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -9,6 +9,21 @@ AC_DEFUN([gl_FUNC_STRERRORNAME_NP], [ AC_REQUIRE([gl_STRING_H_DEFAULTS]) + AC_REQUIRE([gl_CHECK_STRERRORNAME_NP]) + if test $ac_cv_func_strerrorname_np = yes; then + case "$gl_cv_func_strerrorname_np_works" in + *yes) ;; + *) REPLACE_STRERRORNAME_NP=1 ;; + esac + else + HAVE_STRERRORNAME_NP=0 + fi +]) + +# Check for a working strerrorname_np function. +# Sets ac_cv_func_strerrorname_np, gl_cv_func_strerrorname_np_works. +AC_DEFUN([gl_CHECK_STRERRORNAME_NP], +[ dnl Persuade glibc to declare strerrorname_np(). AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) @@ -54,11 +69,19 @@ AC_DEFUN([gl_FUNC_STRERRORNAME_NP], esac ]) ]) + fi +]) + +# Prerequisite for using strerrorname_np when available. +AC_DEFUN_ONCE([gl_OPTIONAL_STRERRORNAME_NP], +[ + AC_REQUIRE([gl_CHECK_STRERRORNAME_NP]) + if test $ac_cv_func_strerrorname_np = yes; then case "$gl_cv_func_strerrorname_np_works" in - *yes) ;; - *) REPLACE_STRERRORNAME_NP=1 ;; + *yes) + AC_DEFINE([HAVE_WORKING_STRERRORNAME_NP], [1], + [Define to 1 if the function strerrorname_np exists and works.]) + ;; esac - else - HAVE_STRERRORNAME_NP=0 fi ]) diff --git a/m4/xvasprintf.m4 b/m4/xvasprintf.m4 index f492e990cb..a3c15966d9 100644 --- a/m4/xvasprintf.m4 +++ b/m4/xvasprintf.m4 @@ -1,9 +1,11 @@ # xvasprintf.m4 -# serial 2 +# serial 3 dnl Copyright (C) 2006, 2009-2024 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Prerequisites of lib/xvasprintf.c. -AC_DEFUN([gl_XVASPRINTF], [:]) +AC_DEFUN([gl_XVASPRINTF], [ + gl_OPTIONAL_STRERRORNAME_NP +]) diff --git a/modules/xvasprintf b/modules/xvasprintf index b322890876..bb537ce3ee 100644 --- a/modules/xvasprintf +++ b/modules/xvasprintf @@ -7,10 +7,12 @@ lib/xvasprintf.c lib/xasprintf.c lib/xalloc.h m4/xvasprintf.m4 +m4/strerrorname_np.m4 Depends-on: +extensions stdio -vasprintf +vazsprintf xalloc xalloc-die extern-inline -- 2.39.5