Hello gnulib team,

An enterprising Gentoo user reported compilation failures of
projects using gnulib when -Werror=format-security is enabled:

https://bugs.gentoo.org/955689
https://bugs.gentoo.org/955947

Further inspection & trying to reproduce this in gnulib revealed that
this requires a combination of -Werror=security and --disable-nls.

To reproduce in gnulib:

- clone gnulib

- create a test dir:
  ./gnulib-tool --create-testdir --symlink --dir mytests
  (you can interrupt when it starts to configure)

- reconfigure the project:
  cd mytests && ./configure --disable-nls CFLAGS="-pipe -Werror=format-security 
-O"

- make will fail with an error like:
clean-temp.c: In function 'create_temp_dir':
clean-temp.c:234:7: error: format not a string literal and no format arguments 
[-Werror=format-security]
  234 |       error (0, errno,
      |       ^~~~~
clean-temp.c:234:7: error: format not a string literal and no format arguments 
[-Werror=format-security]

The errors cannot be reproduced when nls is enabled, which suggests
an implementation difference in the generated error() macros depending
on the nls feature.

Attached is a patch which fixes these failures, based on top of last
night's gnulib git; they make the tests compile/run and were used to
resolve the aforementioned problems in gettext and m4, though they
obviously affect any gnulib consuming projects.

I hope you find these useful!

thanks,
Holger
(please cc: my on replies)
diff --git a/lib/clean-temp.c b/lib/clean-temp.c
index e8d0cf3113..568faf2aa3 100644
--- a/lib/clean-temp.c
+++ b/lib/clean-temp.c
@@ -233,7 +233,7 @@ create_temp_dir (const char *prefix, const char *parentdir,
   xtemplate = (char *) xmalloca (PATH_MAX);
   if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL))
     {
-      error (0, errno,
+      error (0, errno, "%s",
              _("cannot find a temporary directory, try setting $TMPDIR"));
       goto quit;
     }
diff --git a/lib/csharpcomp.c b/lib/csharpcomp.c
index 5b6c9684d9..83f79e69c5 100644
--- a/lib/csharpcomp.c
+++ b/lib/csharpcomp.c
@@ -223,7 +223,7 @@ compile_csharp_using_mono (const char * const *sources,
          line if it starts with "Compilation succeeded".  */
       fp = fdopen (fd[0], "r");
       if (fp == NULL)
-        error (EXIT_FAILURE, errno, _("fdopen() failed"));
+        error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
       line[0] = NULL; linesize[0] = 0;
       line[1] = NULL; linesize[1] = 0;
       l = 0;
@@ -372,7 +372,7 @@ compile_csharp_using_dotnet (const char * const *sources,
             /* Retrieve its result.  */
             FILE *fp = fdopen (fd[0], "r");
             if (fp == NULL)
-              error (EXIT_FAILURE, errno, _("fdopen() failed"));
+              error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
             char *line = NULL;
             size_t linesize = 0;
@@ -464,7 +464,7 @@ compile_csharp_using_dotnet (const char * const *sources,
             /* Retrieve its result.  */
             FILE *fp = fdopen (fd[0], "r");
             if (fp == NULL)
-              error (EXIT_FAILURE, errno, _("fdopen() failed"));
+              error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
             char *line = NULL;
             size_t linesize = 0;
@@ -1086,6 +1086,6 @@ compile_csharp_class (const char * const *sources,
   if (result >= 0)
     return (bool) result;
 
-  error (0, 0, _("C# compiler not found, try installing mono or dotnet"));
+  error (0, 0, "%s", _("C# compiler not found, try installing mono or dotnet"));
   return true;
 }
diff --git a/lib/csharpexec.c b/lib/csharpexec.c
index 13a05a7e73..51573d4cd6 100644
--- a/lib/csharpexec.c
+++ b/lib/csharpexec.c
@@ -394,7 +394,7 @@ execute_csharp_using_dotnet (const char *assembly_path,
                 /* Retrieve its result.  */
                 FILE *fp = fdopen (fd[0], "r");
                 if (fp == NULL)
-                  error (EXIT_FAILURE, errno, _("fdopen() failed"));
+                  error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
                 char *line = NULL;
                 size_t linesize = 0;
@@ -678,6 +678,6 @@ execute_csharp_program (const char *assembly_path,
     return (bool) result;
 
   if (!quiet)
-    error (0, 0, _("C# virtual machine not found, try installing mono or dotnet"));
+    error (0, 0, "%s", _("C# virtual machine not found, try installing mono or dotnet"));
   return true;
 }
diff --git a/lib/javacomp.c b/lib/javacomp.c
index 33ec8eb63e..49e394a785 100644
--- a/lib/javacomp.c
+++ b/lib/javacomp.c
@@ -100,7 +100,7 @@ default_target_version (void)
                && java_version_cache[2] >= '1' && java_version_cache[2] <= '5'
                && java_version_cache[3] == '\0')
         {
-          error (0, 0, _("The java program is too old. Cannot compile Java code for this old version any more."));
+          error (0, 0, "%s", _("The java program is too old. Cannot compile Java code for this old version any more."));
           java_version_cache = "1.6";
         }
       else if ((java_version_cache[0] == '1'
@@ -144,7 +144,7 @@ source_version_index (const char *source_version)
            && (source_version[1] >= '0' && source_version[1] <= '9')
            && source_version[2] == '\0')
     return (source_version[0] - '1') * 10 + source_version[1] - '0' + 4;
-  error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
+  error (EXIT_FAILURE, 0, "%s", _("invalid source_version argument to compile_java_class"));
   return 0;
 }
 
@@ -165,7 +165,7 @@ target_version_index (const char *target_version)
            && (target_version[1] >= '0' && target_version[1] <= '9')
            && target_version[2] == '\0')
     return (target_version[0] - '1') * 10 + target_version[1] - '0' + 4;
-  error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
+  error (EXIT_FAILURE, 0, "%s", _("invalid target_version argument to compile_java_class"));
   return 0;
 }
 
@@ -352,7 +352,7 @@ execute_and_read_line (const char *progname,
   /* Retrieve its result.  */
   fp = fdopen (fd[0], "r");
   if (fp == NULL)
-    error (EXIT_FAILURE, errno, _("fdopen() failed"));
+    error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
   line = NULL; linesize = 0;
   linelen = getline (&line, &linesize, fp);
@@ -1130,7 +1130,7 @@ compile_java_class (const char * const *java_sources,
         }
     }
 
-  error (0, 0, _("Java compiler not found, try setting $JAVAC"));
+  error (0, 0, "%s", _("Java compiler not found, try setting $JAVAC"));
   err = true;
 
  done2:
diff --git a/lib/javaexec.c b/lib/javaexec.c
index 1158971ff9..2633a0a166 100644
--- a/lib/javaexec.c
+++ b/lib/javaexec.c
@@ -306,7 +306,7 @@ execute_java_class (const char *class_name,
   }
 
   if (!quiet)
-    error (0, 0, _("Java virtual machine not found, try setting $JAVA"));
+    error (0, 0, "%s", _("Java virtual machine not found, try setting $JAVA"));
   err = true;
 
  done2:
diff --git a/lib/javaversion.c b/lib/javaversion.c
index bd680354a4..70fe507752 100644
--- a/lib/javaversion.c
+++ b/lib/javaversion.c
@@ -72,7 +72,7 @@ execute_and_read_line (const char *progname,
   /* Retrieve its result.  */
   fp = fdopen (fd[0], "r");
   if (fp == NULL)
-    error (EXIT_FAILURE, errno, _("fdopen() failed"));
+    error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
   line = NULL; linesize = 0;
   linelen = getline (&line, &linesize, fp);
diff --git a/lib/spawn-pipe.c b/lib/spawn-pipe.c
index 098352e2d5..3dfb6499ba 100644
--- a/lib/spawn-pipe.c
+++ b/lib/spawn-pipe.c
@@ -215,10 +215,10 @@ create_pipe (const char *progname,
        process is running.  */
   if (pipe_stdout)
     if (pipe2_safer (ifd, O_BINARY | O_CLOEXEC) < 0)
-      error (EXIT_FAILURE, errno, _("cannot create pipe"));
+      error (EXIT_FAILURE, errno, "%s", _("cannot create pipe"));
   if (pipe_stdin)
     if (pipe2_safer (ofd, O_BINARY | O_CLOEXEC) < 0)
-      error (EXIT_FAILURE, errno, _("cannot create pipe"));
+      error (EXIT_FAILURE, errno, "%s", _("cannot create pipe"));
 /* Data flow diagram:
  *
  *           write        system         read
diff --git a/lib/vc-mtime.c b/lib/vc-mtime.c
index 77acbabba4..80b221aa06 100644
--- a/lib/vc-mtime.c
+++ b/lib/vc-mtime.c
@@ -83,7 +83,7 @@ is_git_present (void)
           /* Retrieve its result.  */
           FILE *fp = fdopen (fd[0], "r");
           if (fp == NULL)
-            error (EXIT_FAILURE, errno, _("fdopen() failed"));
+            error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
           char *line = NULL;
           size_t linesize = 0;
@@ -233,7 +233,7 @@ git_mtime (struct timespec *mtime, const char *filename)
 
   fp = fdopen (fd[0], "r");
   if (fp == NULL)
-    error (EXIT_FAILURE, errno, _("fdopen() failed"));
+    error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
   line = NULL; linesize = 0;
   linelen = getline (&line, &linesize, fp);
@@ -337,7 +337,7 @@ abs_git_checkout (void)
   /* Retrieve its result.  */
   FILE *fp = fdopen (fd[0], "r");
   if (fp == NULL)
-    error (EXIT_FAILURE, errno, _("fdopen() failed"));
+    error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
   char *line = NULL;
   size_t linesize = 0;
@@ -653,7 +653,7 @@ max_vc_mtime (struct timespec *max_of_mtimes,
                                names (because we have already relativized them).  */
                             FILE *fp = fdopen (fd[0], "r");
                             if (fp == NULL)
-                              error (EXIT_FAILURE, errno, _("fdopen() failed"));
+                              error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
                             char *fn = NULL;
                             size_t fn_size = 0;
@@ -778,7 +778,7 @@ max_vc_mtime (struct timespec *max_of_mtimes,
                                checkout dir, not to currdir!  */
                             FILE *fp = fdopen (fd[0], "r");
                             if (fp == NULL)
-                              error (EXIT_FAILURE, errno, _("fdopen() failed"));
+                              error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
                             char *fn = NULL;
                             size_t fn_size = 0;
@@ -867,7 +867,7 @@ max_vc_mtime (struct timespec *max_of_mtimes,
                                single line, containing a positive integer.  */
                             FILE *fp = fdopen (fd[0], "r");
                             if (fp == NULL)
-                              error (EXIT_FAILURE, errno, _("fdopen() failed"));
+                              error (EXIT_FAILURE, errno, "%s", _("fdopen() failed"));
 
                             char *line = NULL;
                             size_t linesize = 0;
diff --git a/lib/xsetenv.c b/lib/xsetenv.c
index 7dfa3fcd87..9d78a2a7f7 100644
--- a/lib/xsetenv.c
+++ b/lib/xsetenv.c
@@ -34,5 +34,5 @@ void
 xsetenv (const char *name, const char *value, int replace)
 {
   if (setenv (name, value, replace) < 0)
-    error (EXIT_FAILURE, 0, _("memory exhausted"));
+    error (EXIT_FAILURE, 0, "%s", _("memory exhausted"));
 }

Reply via email to