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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.