Selaa lähdekoodia

Fix zdump undefined behavior if !USE_LTZ

When compiled with -DUSE_LTZ=0, zdump modified pointers in
the environ vector, which POSIX says results in undefined behavior.
Fix this by using setenv if available, otherwise by arranging for
the modifications to occur to the vector before it's assigned
or re-assigned to ‘environ’.
* Makefile, NEWS: Mention this.
* private.h (HAVE_SETENV): Default to 1.
* zdump.c (tzalloc): If HAVE_SETENV, just use setenv; it’s simpler.
Otherwise, avoid modifying environ, environ[0], environ[0][0], etc.
while environ is in use by getenv etc.  Do this by keeping two
independent environ vectors; the original one and fakeenv.
If !HAVE_SETENV, return the initial environ array.
(tzalloc, tzfree): Give the getenv family a chance of getting the
correct answer, by calling tzset after each time the environment
is modified.
(tzfree): Restore the initial environ array.
Paul Eggert 3 vuotta sitten
vanhempi
sitoutus
fdd270534f
4 muutettua tiedostoa jossa 49 lisäystä ja 17 poistoa
  1. 1 0
      Makefile
  2. 4 0
      NEWS
  3. 4 0
      private.h
  4. 40 17
      zdump.c

+ 1 - 0
Makefile

@@ -223,6 +223,7 @@ LDLIBS=
 #  -DHAVE_MALLOC_ERRNO=0 if malloc etc. do not set errno on failure.
 #  -DHAVE_POSIX_DECLS=0 if your system's include files do not declare
 #	functions like 'link' or variables like 'tzname' required by POSIX
+#  -DHAVE_SETENV=0 if your system lacks the setenv function
 #  -DHAVE_SNPRINTF=0 if your system lacks the snprintf function
 #  -DHAVE_STDINT_H if you have a non-C99 compiler with <stdint.h>*
 #  -DHAVE_STRFTIME_L if <time.h> declares locale_t and strftime_l

+ 4 - 0
NEWS

@@ -73,6 +73,10 @@ Unreleased, experimental changes
     releases have been out of support since 2019.  This change affects
     only fat TZif files, as thin files never had the workaround.
 
+    zdump no longer modifies the environ vector when compiled on
+    platforms lacking tm_zone or when compiled with -DUSE_LTZ=0.
+    This avoid undefined behavior on POSIX platforms.
+
 
 Release 2022e - 2022-10-11 11:13:02 -0700
 

+ 4 - 0
private.h

@@ -87,6 +87,10 @@
 # define HAVE_POSIX_DECLS 1
 #endif
 
+#ifndef HAVE_SETENV
+# define HAVE_SETENV 1
+#endif
+
 #ifndef HAVE_STRDUP
 # define HAVE_STRDUP 1
 #endif

+ 40 - 17
zdump.c

@@ -228,33 +228,56 @@ mktime_z(timezone_t tz, struct tm *tmp)
 static timezone_t
 tzalloc(char const *val)
 {
+# if HAVE_SETENV
+  if (setenv("TZ", val, 1) != 0) {
+    perror("setenv");
+    exit(EXIT_FAILURE);
+  }
+  tzset();
+  return NULL;
+# else
+  enum { TZeqlen = 3 };
+  static char const TZeq[TZeqlen] = "TZ=";
   static char **fakeenv;
-  char **env = fakeenv;
-  char *env0;
-  if (! env) {
-    char **e = environ;
-    int to;
+  static size_t fakeenv0size;
+  void *freeable = NULL;
+  char **env = fakeenv, **initial_environ;
+  size_t valsize = strlen(val) + 1;
+  if (fakeenv0size < valsize) {
+    char **e = environ, **to;
+    ptrdiff_t initial_nenvptrs;  /* Counting the trailing NULL pointer.  */
 
     while (*e++)
       continue;
-    env = xmalloc(sumsize(sizeof *environ,
-			  (e - environ) * sizeof *environ));
-    to = 1;
-    for (e = environ; (env[to] = *e); e++)
-      to += strncmp(*e, "TZ=", 3) != 0;
+    initial_nenvptrs = e - environ;
+    fakeenv0size = sumsize(valsize, valsize);
+    fakeenv0size = max(fakeenv0size, 64);
+    freeable = env;
+    fakeenv = env =
+      xmalloc(sumsize(sumsize(sizeof *environ,
+			      initial_nenvptrs * sizeof *environ),
+		      sumsize(TZeqlen, fakeenv0size)));
+    to = env + 1;
+    for (e = environ; (*to = *e); e++)
+      to += strncmp(*e, TZeq, TZeqlen) != 0;
+    env[0] = memcpy(to + 1, TZeq, TZeqlen);
   }
-  env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val)));
-  env[0] = strcat(strcpy(env0, "TZ="), val);
-  environ = fakeenv = env;
+  memcpy(env[0] + TZeqlen, val, valsize);
+  initial_environ = environ;
+  environ = env;
   tzset();
-  return env;
+  free(freeable);
+  return initial_environ;
+# endif
 }
 
 static void
-tzfree(timezone_t env)
+tzfree(timezone_t initial_environ)
 {
-  environ = env + 1;
-  free(env[0]);
+# if !HAVE_SETENV
+  environ = initial_environ;
+  tzset();
+# endif
 }
 #endif /* ! USE_LOCALTIME_RZ */