Re: [PATCH] fix format security
Bruno Haible via Gnulib discussion list <[email protected]>
| Newsgroups | gmane.comp.lib.gnulib.bugs,gmane.editors.poke.devel |
|---|---|
| Message-ID | <3253920.OcfQ0rdVKJ@cagnes> |
Collin Funk wrote: > If that sounds okay, I'll figure out what version of GCC and Clang added > them and submit a patch. I've now fixed the warnings through these patches. Thanks for your help, Collin! 2026-08-26 Bruno Haible <[email protected]> gettext-h: Get rid of gcc and clang warnings also in C++ mode. * lib/gettext.h (gettext, dgettext, dcgettext, ngettext, dngettext, dcngettext, textdomain): In C++ mode, define them as aliases to inline functions. gettext-h: Get rid of gcc -Wunused-value warnings for textdomain. * lib/gettext.h (textdomain): Define as inline function. gettext-h: Get rid of -Wformat warnings also for ngettext, d[c]ngettext. * lib/gettext.h (ngettext, dngettext, dcngettext): Define as inline functions, like gettext, dgettext, dcgettext. gettext-h: Get rid of -Wformat-security warnings with clang. Idea by Collin Funk in <https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00292.html>. * lib/gettext.h (_LIBGETTEXT_INLINE): New macro. (gettext, dgettext, dcgettext): Use it. Declare as inline functions also with clang. Declare with __format_arg__ attribute.
0001-gettext-h-Get-rid-of-Wformat-security-warnings-with-.patch
(text/x-patch, 5.3 KB)
From 12806e284d93ef4a02a46d6fca22481a3865b1a4 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Thu, 27 Aug 2026 01:18:39 +0200 Subject: [PATCH 1/4] gettext-h: Get rid of -Wformat-security warnings with clang. Idea by Collin Funk in <https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00292.html>. * lib/gettext.h (_LIBGETTEXT_INLINE): New macro. (gettext, dgettext, dcgettext): Use it. Declare as inline functions also with clang. Declare with __format_arg__ attribute. --- ChangeLog | 9 +++++++++ lib/gettext.h | 48 ++++++++++++++++++++---------------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/ChangeLog b/ChangeLog index e22523135b..5a1bcbb7d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2026-08-26 Bruno Haible <[email protected]> + + gettext-h: Get rid of -Wformat-security warnings with clang. + Idea by Collin Funk in + <https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00292.html>. + * lib/gettext.h (_LIBGETTEXT_INLINE): New macro. + (gettext, dgettext, dcgettext): Use it. Declare as inline functions also + with clang. Declare with __format_arg__ attribute. + 2026-08-26 Bruno Haible <[email protected]> gettext-h: Fix compilation error (regression 2026-08-24). diff --git a/lib/gettext.h b/lib/gettext.h index 68826302c3..6c1a64006b 100644 --- a/lib/gettext.h +++ b/lib/gettext.h @@ -67,16 +67,13 @@ # endif /* Disabled NLS. */ -/* When gcc is used with option -Wformat=2, we need to silence - "warning: format not a string literal, argument types not checked [-Wformat-nonliteral]" +/* When gcc or clang is used with option -Wformat=2, we need to silence + gcc: "warning: format not a string literal, argument types not checked [-Wformat-nonliteral]" + clang: "warning: format string is not a string literal [-Wformat-nonliteral]", + "warning: format string is not a string literal (potentially insecure) [-Wformat-security]" warnings that would occur at every invocation of a *gettext function in a *printf format string position. - Do this with inline functions when possible, namely for gettext, dgettext, - dcgettext, which are known to gcc as "external built-ins". */ -/* When clang is used with option -Wformat=2, we need to silence - "warning: format string is not a string literal [-Wformat-nonliteral]" - warnings that would occur at every invocation of a *gettext function - in a *printf format string position. */ + Do this with inline functions when possible. */ /* These warnings would not occur with enabled NLS. */ /* A test case: ================================ foo.c ================================ @@ -106,21 +103,22 @@ printf (dcgettext ("toto", some_computed_string (), LC_MESSAGES)); } ======================================================================= - $CC -Wformat=2 -S foo.c - $CC -Wformat=2 -S -x c++ foo.c + $CC -Wall -Wformat=2 -S foo.c + $CC -Wall -Wformat=2 -S -x c++ foo.c */ -# if defined __GNUC__ && !defined __clang__ && !defined __cplusplus +# if (defined __GNUC__ || defined __clang__) && !defined __cplusplus +# if __GNUC__ + (__GNUC_MINOR__ >= 2) > 4 || defined __clang__ +# define _LIBGETTEXT_INLINE __always_inline__, __gnu_inline__ +# else +# define _LIBGETTEXT_INLINE __always_inline__ +# endif /* The return type 'const char *' serves the purpose of producing warnings for invalid uses of the value returned from these functions. */ -# if __GNUC__ >= 9 +# if defined __GNUC__ && __GNUC__ >= 9 && !defined __clang__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" # endif -# if __GNUC__ + (__GNUC_MINOR__ >= 2) > 4 -__attribute__ ((__always_inline__, __gnu_inline__)) -# else -__attribute__ ((__always_inline__)) -# endif +__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (1))) extern inline # if !defined(__sun) const @@ -134,11 +132,7 @@ gettext (const char *msgid) # endif msgid; } -# if __GNUC__ + (__GNUC_MINOR__ >= 2) > 4 -__attribute__ ((__always_inline__, __gnu_inline__)) -# else -__attribute__ ((__always_inline__)) -# endif +__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (2))) extern inline # if !defined(__sun) const @@ -153,11 +147,7 @@ dgettext (const char *domain, const char *msgid) # endif msgid; } -# if __GNUC__ + (__GNUC_MINOR__ >= 2) > 4 -__attribute__ ((__always_inline__, __gnu_inline__)) -# else -__attribute__ ((__always_inline__)) -# endif +__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (2))) extern inline # if !defined(__sun) const @@ -173,7 +163,7 @@ dcgettext (const char *domain, const char *msgid, int category) # endif msgid; } -# if __GNUC__ >= 9 +# if defined __GNUC__ && __GNUC__ >= 9 && !defined __clang__ # pragma GCC diagnostic pop # endif /* Silence -Wuseless-cast warnings. */ @@ -219,6 +209,7 @@ dcgettext (const char *domain, const char *msgid, int category) # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ ((void) _LIBGETTEXT_FUNCAST (int, Category), \ dngettext (Domainname, Msgid1, Msgid2, N)) + # endif # undef textdomain @@ -231,6 +222,7 @@ dcgettext (const char *domain, const char *msgid, int category) # define bind_textdomain_codeset(Domainname, Codeset) \ ((void) _LIBGETTEXT_FUNCAST (const char *, Domainname), \ _LIBGETTEXT_FUNCAST (const char *, Codeset)) + #endif -- 2.53.0
0002-gettext-h-Get-rid-of-Wformat-warnings-also-for-ngett.patch
(text/x-patch, 3.1 KB)
From c42e9f5d63ed6c6c7a5093dcb253319ae43bee5f Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Thu, 27 Aug 2026 01:21:03 +0200 Subject: [PATCH 2/4] gettext-h: Get rid of -Wformat warnings also for ngettext, d[c]ngettext. * lib/gettext.h (ngettext, dngettext, dcngettext): Define as inline functions, like gettext, dgettext, dcgettext. --- ChangeLog | 4 ++++ lib/gettext.h | 61 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5a1bcbb7d7..dbc64feedb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2026-08-26 Bruno Haible <[email protected]> + gettext-h: Get rid of -Wformat warnings also for ngettext, d[c]ngettext. + * lib/gettext.h (ngettext, dngettext, dcngettext): Define as inline + functions, like gettext, dgettext, dcgettext. + gettext-h: Get rid of -Wformat-security warnings with clang. Idea by Collin Funk in <https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00292.html>. diff --git a/lib/gettext.h b/lib/gettext.h index 6c1a64006b..e12a1e4ec5 100644 --- a/lib/gettext.h +++ b/lib/gettext.h @@ -163,25 +163,54 @@ dcgettext (const char *domain, const char *msgid, int category) # endif msgid; } +__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (1), __format_arg__ (2))) +extern inline +# if !defined(__sun) +const +# endif +char * +ngettext (const char *msgid1, const char *msgid2, unsigned long n) +{ + return +# ifdef __sun + (char *) +# endif + (n == 1 ? msgid1 : msgid2); +} +__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (2), __format_arg__ (3))) +extern inline +# if !defined(__sun) +const +# endif +char * +dngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned long n) +{ + (void) domain; + return +# ifdef __sun + (char *) +# endif + (n == 1 ? msgid1 : msgid2); +} +__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (2), __format_arg__ (3))) +extern inline +# if !defined(__sun) +const +# endif +char * +dcngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned long n, int category) +{ + (void) domain; + (void) category; + return +# ifdef __sun + (char *) +# endif + (n == 1 ? msgid1 : msgid2); +} # if defined __GNUC__ && __GNUC__ >= 9 && !defined __clang__ # pragma GCC diagnostic pop # endif -/* Silence -Wuseless-cast warnings. */ -# if __GNUC__ >= 14 -# pragma GCC diagnostic ignored "-Wuseless-cast" -# endif -/* Use plain casts to pacify -Wformat-nonliteral. - This ngettext implementation does not evaluate all arguments, - as doing so would trigger -Wformat-nonliteral false positives. */ -# undef ngettext -# define ngettext(Msgid1, Msgid2, N) \ - ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) -# undef dngettext -# define dngettext(Domainname, Msgid1, Msgid2, N) \ - ((void) (const char *) (Domainname), ngettext (Msgid1, Msgid2, N)) -# undef dcngettext -# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ - ((void) (int) (Category), dngettext (Domainname, Msgid1, Msgid2, N)) # else -- 2.53.0
0003-gettext-h-Get-rid-of-gcc-Wunused-value-warnings-for-.patch
(text/x-patch, 2 KB)
From 3513989c286d3204dbe3d575fcff08b5c2d62f55 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Thu, 27 Aug 2026 01:24:47 +0200 Subject: [PATCH 3/4] gettext-h: Get rid of gcc -Wunused-value warnings for textdomain. * lib/gettext.h (textdomain): Define as inline function. --- ChangeLog | 3 +++ lib/gettext.h | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index dbc64feedb..7422f6c058 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2026-08-26 Bruno Haible <[email protected]> + gettext-h: Get rid of gcc -Wunused-value warnings for textdomain. + * lib/gettext.h (textdomain): Define as inline function. + gettext-h: Get rid of -Wformat warnings also for ngettext, d[c]ngettext. * lib/gettext.h (ngettext, dngettext, dcngettext): Define as inline functions, like gettext, dgettext, dcgettext. diff --git a/lib/gettext.h b/lib/gettext.h index e12a1e4ec5..24150dfd44 100644 --- a/lib/gettext.h +++ b/lib/gettext.h @@ -208,6 +208,12 @@ dcngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned # endif (n == 1 ? msgid1 : msgid2); } +__attribute__ ((_LIBGETTEXT_INLINE)) +extern inline const char * +textdomain (const char *domainname) +{ + return domainname; +} # if defined __GNUC__ && __GNUC__ >= 9 && !defined __clang__ # pragma GCC diagnostic pop # endif @@ -238,11 +244,11 @@ dcngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ ((void) _LIBGETTEXT_FUNCAST (int, Category), \ dngettext (Domainname, Msgid1, Msgid2, N)) +# undef textdomain +# define textdomain(Domainname) _LIBGETTEXT_FUNCAST (const char *, Domainname) # endif -# undef textdomain -# define textdomain(Domainname) _LIBGETTEXT_FUNCAST (const char *, Domainname) # undef bindtextdomain # define bindtextdomain(Domainname, Dirname) \ ((void) _LIBGETTEXT_FUNCAST (const char *, Domainname), \ -- 2.53.0
0004-gettext-h-Get-rid-of-gcc-and-clang-warnings-also-in-.patch
(text/x-patch, 3.1 KB)
From ec224ee81bbdcebfd136186e777f0dae85308006 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Thu, 27 Aug 2026 01:29:08 +0200 Subject: [PATCH 4/4] gettext-h: Get rid of gcc and clang warnings also in C++ mode. * lib/gettext.h (gettext, dgettext, dcgettext, ngettext, dngettext, dcngettext, textdomain): In C++ mode, define them as aliases to inline functions. --- ChangeLog | 5 ++++ lib/gettext.h | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/ChangeLog b/ChangeLog index 7422f6c058..c56a9fc010 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2026-08-26 Bruno Haible <[email protected]> + gettext-h: Get rid of gcc and clang warnings also in C++ mode. + * lib/gettext.h (gettext, dgettext, dcgettext, ngettext, dngettext, + dcngettext, textdomain): In C++ mode, define them as aliases to inline + functions. + gettext-h: Get rid of gcc -Wunused-value warnings for textdomain. * lib/gettext.h (textdomain): Define as inline function. diff --git a/lib/gettext.h b/lib/gettext.h index 24150dfd44..f201b98227 100644 --- a/lib/gettext.h +++ b/lib/gettext.h @@ -218,6 +218,70 @@ textdomain (const char *domainname) # pragma GCC diagnostic pop # endif +# elif (defined __GNUC__ || defined __clang__) && defined __cplusplus + +__attribute__ ((__format_arg__ (1))) +static inline const char * +_libgettext_gettext (const char *msgid) +{ + return msgid; +} +# undef gettext +# define gettext _libgettext_gettext +__attribute__ ((__format_arg__ (2))) +static inline const char * +_libgettext_dgettext (const char *domain, const char *msgid) +{ + (void) domain; + return msgid; +} +# undef dgettext +# define dgettext _libgettext_dgettext +__attribute__ ((__format_arg__ (2))) +static inline const char * +_libgettext_dcgettext (const char *domain, const char *msgid, int category) +{ + (void) domain; + (void) category; + return msgid; +} +# undef dcgettext +# define dcgettext _libgettext_dcgettext +__attribute__ ((__format_arg__ (1), __format_arg__ (2))) +static inline const char * +_libgettext_ngettext (const char *msgid1, const char *msgid2, unsigned long n) +{ + return (n == 1 ? msgid1 : msgid2); +} +# undef ngettext +# define ngettext _libgettext_ngettext +__attribute__ ((__format_arg__ (2), __format_arg__ (3))) +static inline const char * +_libgettext_dngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned long n) +{ + (void) domain; + return (n == 1 ? msgid1 : msgid2); +} +# undef dngettext +# define dngettext _libgettext_dngettext +__attribute__ ((__format_arg__ (2), __format_arg__ (3))) +static inline const char * +_libgettext_dcngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned long n, int category) +{ + (void) domain; + (void) category; + return (n == 1 ? msgid1 : msgid2); +} +# undef dcngettext +# define dcngettext _libgettext_dcngettext +static inline const char * +_libgettext_textdomain (const char *domainname) +{ + return domainname; +} +# undef textdomain +# define textdomain _libgettext_textdomain + # else # undef gettext -- 2.53.0