emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] trunk r118095: Fix putenv race conditions with undefined b


From: Paul Eggert
Subject: [Emacs-diffs] trunk r118095: Fix putenv race conditions with undefined behavior.
Date: Sun, 12 Oct 2014 06:09:56 +0000
User-agent: Bazaar (2.6b2)

------------------------------------------------------------
revno: 118095
revision-id: address@hidden
parent: address@hidden
fixes bug: http://debbugs.gnu.org/8705
committer: Paul Eggert <address@hidden>
branch nick: trunk
timestamp: Sat 2014-10-11 23:09:50 -0700
message:
  Fix putenv race conditions with undefined behavior.
  
  Do all putenv calls before Emacs creates any threads.
  Use a safer way to modify the TZ environment variable in the
  presence of multiple threads.  For further thread-safety,
  prefer localtime_r and gmtime_r to localtime and gmtime,
  and prefer struct tm's tm_gmtoff (if available) to calling
  both localtime_r and gmtime_r.
  * configure.ac (LOCALTIME_CACHE): Remove.
  We needn't worry about SunOS 4 any more; Sun dropped support in 2003.
  All uses of LOCALTIME_CACHE removed.  This simplifies the fix.
  (tzalloc): Add check for this function.
  * admin/merge-gnulib (GNULIB_MODULES): Add time_r, since Emacs now
  calls localtime_r and gmtime_r directly.
  * src/dbusbind.c (Fdbus__init_bus): Move xputenv call from here ...
  (init_dbusbind): ... to this new function.
  * src/emacs.c (main) [HAVE_DBUS]: Call it before creating threads.
  * src/xterm.c (x_term_init): Move xputenv call from here ...
  (init_xterm): ... to this new function.
  * src/emacs.c (main) [USE_GTK]: Call it before creating threads.
  * src/editfns.c (HAVE_TM_GMTOFF): Default to false.
  (dump_tz_string): New constant.
  (init_editfns): Use it.  This centralizes the dump_tz stuff.
  Call set_time_zone_rule here, so that its xputenv is done
  before Emacs goes multithreaded.
  (mktime_z) [!HAVE_TZALLOC]: New function, which is typically
  thread-safe enough for Emacs.
  (format_time_string, Fdecode_time, Fcurrent_time_string)
  (Fcurrent_time_zone):
  Prefer localtime_r and gmtime_r, which are more thread-safe, to
  localtime and gmtime.  Remove now-unnecessary calls to block_input.
  (tm_gmtoff): New static function.
  (Fdecode_time, Fcurrent_time_zone): Use it.
  (Fencode_time): Use mktime_z, for better thread-safety.
  (set_time_zone_rule): Now static.  Rewrite to be mostly thread-safe,
  i.e., not quite thread-safe but good enough for Emacs typical usage.
  Do not reclaim storage that is in the environment; let it leak.
  Always call tzset, since localtime_r does not.
  * src/emacs.c (dump_tz, Fdump_emacs) [HAVE_TZSET]: Remove dump_tz stuff.
  This is now done in init_editfns.
  * src/systime.h (mktime_z, timezone_t, tzalloc, tzfree) [!HAVE_TZALLOC]:
  New macros and declarations, for platforms lacking tzalloc & friends.
modified:
  ChangeLog                      changelog-20091113204419-o5vbwnq5f7feedwu-1538
  admin/CPP-DEFINES              cppdefines-20091113204419-o5vbwnq5f7feedwu-8192
  admin/ChangeLog                changelog-20091113204419-o5vbwnq5f7feedwu-2226
  admin/merge-gnulib             mergegnulib-20120521022411-ndnoaiok33j6dn0g-1
  configure.ac                   
configure.in-20091113204419-o5vbwnq5f7feedwu-783
  src/ChangeLog                  changelog-20091113204419-o5vbwnq5f7feedwu-1438
  src/dbusbind.c                 dbusbind.c-20091113204419-o5vbwnq5f7feedwu-7961
  src/editfns.c                  editfns.c-20091113204419-o5vbwnq5f7feedwu-255
  src/emacs.c                    emacs.c-20091113204419-o5vbwnq5f7feedwu-241
  src/lisp.h                     lisp.h-20091113204419-o5vbwnq5f7feedwu-253
  src/systime.h                  systime.h-20091113204419-o5vbwnq5f7feedwu-510
  src/xterm.c                    xterm.c-20091113204419-o5vbwnq5f7feedwu-244
=== modified file 'ChangeLog'
--- a/ChangeLog 2014-10-06 06:21:13 +0000
+++ b/ChangeLog 2014-10-12 06:09:50 +0000
@@ -1,3 +1,11 @@
+2014-10-12  Paul Eggert  <address@hidden>
+
+       Fix putenv race conditions that can crash Emacs (Bug#8705).
+       * configure.ac (LOCALTIME_CACHE): Remove.
+       We needn't worry about SunOS 4 any more; Sun dropped support in 2003.
+       All uses of LOCALTIME_CACHE removed.  This simplifies the fix.
+       (tzalloc): Add check for this function.
+
 2014-10-06  Jan Djärv  <address@hidden>
 
        * configure.ac: Add -Wno-string-plus-int for clang.

=== modified file 'admin/CPP-DEFINES'
--- a/admin/CPP-DEFINES 2014-04-16 15:16:35 +0000
+++ b/admin/CPP-DEFINES 2014-10-12 06:09:50 +0000
@@ -368,7 +368,6 @@
 INTERNAL_TERMINAL
 IS_ANY_SEP
 IS_DIRECTORY_SEP
-LOCALTIME_CACHE
 MAIL_USE_FLOCK
 MAIL_USE_LOCKF
 MAIL_USE_POP

=== modified file 'admin/ChangeLog'
--- a/admin/ChangeLog   2014-10-07 20:17:09 +0000
+++ b/admin/ChangeLog   2014-10-12 06:09:50 +0000
@@ -1,3 +1,9 @@
+2014-10-12  Paul Eggert  <address@hidden>
+
+       Fix putenv race conditions with undefined behavior (Bug#8705).
+       * merge-gnulib (GNULIB_MODULES): Add time_r, since Emacs now
+       calls localtime_r and gmtime_r directly.
+
 2014-10-07  Glenn Morris  <address@hidden>
 
        * unidata/Makefile.in: Check for deleted uni- files.  (Bug#18489)

=== modified file 'admin/merge-gnulib'
--- a/admin/merge-gnulib        2014-08-30 22:59:39 +0000
+++ b/admin/merge-gnulib        2014-10-12 06:09:50 +0000
@@ -37,7 +37,7 @@
   pipe2 pselect pthread_sigmask putenv qacl readlink readlinkat
   sig2str socklen stat-time stdalign stdio
   strftime strtoimax strtoumax symlink sys_stat
-  sys_time time timer-time timespec-add timespec-sub
+  sys_time time time_r timer-time timespec-add timespec-sub
   unsetenv update-copyright utimens
   vla warnings
 '

=== modified file 'configure.ac'
--- a/configure.ac      2014-10-06 06:21:13 +0000
+++ b/configure.ac      2014-10-12 06:09:50 +0000
@@ -3915,43 +3915,7 @@
 
 AC_CHECK_HEADERS(valgrind/valgrind.h)
 
-AC_CHECK_FUNCS_ONCE(tzset)
-AC_MSG_CHECKING(whether localtime caches TZ)
-AC_CACHE_VAL(emacs_cv_localtime_cache,
-[if test x$ac_cv_func_tzset = xyes; then
-AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <time.h>
-char TZ_GMT0[] = "TZ=GMT0";
-char TZ_PST8[] = "TZ=PST8";
-main()
-{
-  time_t now = time ((time_t *) 0);
-  int hour_GMT0, hour_unset;
-  if (putenv (TZ_GMT0) != 0)
-    exit (1);
-  hour_GMT0 = localtime (&now)->tm_hour;
-  unsetenv("TZ");
-  hour_unset = localtime (&now)->tm_hour;
-  if (putenv (TZ_PST8) != 0)
-    exit (1);
-  if (localtime (&now)->tm_hour == hour_GMT0)
-    exit (1);
-  unsetenv("TZ");
-  if (localtime (&now)->tm_hour != hour_unset)
-    exit (1);
-  exit (0);
-}]])], emacs_cv_localtime_cache=no, emacs_cv_localtime_cache=yes,
-[# If we have tzset, assume the worst when cross-compiling.
-emacs_cv_localtime_cache=yes])
-else
-       # If we lack tzset, report that localtime does not cache TZ,
-       # since we can't invalidate the cache if we don't have tzset.
-       emacs_cv_localtime_cache=no
-fi])dnl
-AC_MSG_RESULT($emacs_cv_localtime_cache)
-if test $emacs_cv_localtime_cache = yes; then
-  AC_DEFINE(LOCALTIME_CACHE, 1,
-           [Define to 1 if localtime caches TZ.])
-fi
+AC_CHECK_FUNCS_ONCE(tzalloc tzset)
 
 ok_so_far=yes
 AC_CHECK_FUNC(socket, , ok_so_far=no)

=== modified file 'src/ChangeLog'
--- a/src/ChangeLog     2014-10-11 08:22:00 +0000
+++ b/src/ChangeLog     2014-10-12 06:09:50 +0000
@@ -1,3 +1,41 @@
+2014-10-12  Paul Eggert  <address@hidden>
+
+       Fix putenv race conditions with undefined behavior (Bug#8705).
+       Do all putenv calls before Emacs creates any threads.
+       Use a safer way to modify the TZ environment variable in the
+       presence of multiple threads.  For further thread-safety,
+       prefer localtime_r and gmtime_r to localtime and gmtime,
+       and prefer struct tm's tm_gmtoff (if available) to calling
+       both localtime_r and gmtime_r.
+       * dbusbind.c (Fdbus__init_bus): Move xputenv call from here ...
+       (init_dbusbind): ... to this new function.
+       * emacs.c (main) [HAVE_DBUS]: Call it before creating threads.
+       * xterm.c (x_term_init): Move xputenv call from here ...
+       (init_xterm): ... to this new function.
+       * emacs.c (main) [USE_GTK]: Call it before creating threads.
+       * editfns.c (HAVE_TM_GMTOFF): Default to false.
+       (dump_tz_string): New constant.
+       (init_editfns): Use it.  This centralizes the dump_tz stuff.
+       Call set_time_zone_rule here, so that its xputenv is done
+       before Emacs goes multithreaded.
+       (mktime_z) [!HAVE_TZALLOC]: New function, which is typically
+       thread-safe enough for Emacs.
+       (format_time_string, Fdecode_time, Fcurrent_time_string)
+       (Fcurrent_time_zone):
+       Prefer localtime_r and gmtime_r, which are more thread-safe, to
+       localtime and gmtime.  Remove now-unnecessary calls to block_input.
+       (tm_gmtoff): New static function.
+       (Fdecode_time, Fcurrent_time_zone): Use it.
+       (Fencode_time): Use mktime_z, for better thread-safety.
+       (set_time_zone_rule): Now static.  Rewrite to be mostly thread-safe,
+       i.e., not quite thread-safe but good enough for Emacs typical usage.
+       Do not reclaim storage that is in the environment; let it leak.
+       Always call tzset, since localtime_r does not.
+       * emacs.c (dump_tz, Fdump_emacs) [HAVE_TZSET]: Remove dump_tz stuff.
+       This is now done in init_editfns.
+       * systime.h (mktime_z, timezone_t, tzalloc, tzfree) [!HAVE_TZALLOC]:
+       New macros and declarations, for platforms lacking tzalloc & friends.
+
 2014-10-09  Paul Eggert  <address@hidden>
 
        * lisp.h (USE_STACK_STRING): Now true only if USE_STACK CONS.

=== modified file 'src/dbusbind.c'
--- a/src/dbusbind.c    2014-09-23 17:03:48 +0000
+++ b/src/dbusbind.c    2014-10-12 06:09:50 +0000
@@ -1054,6 +1054,7 @@
 
   /* Unset session environment.  */
 #if 0
+  /* This is buggy, since unsetenv is not thread-safe.  */
   if (XSYMBOL (QCdbus_session_bus) == data)
     {
       XD_DEBUG_MESSAGE ("unsetenv DBUS_SESSION_BUS_ADDRESS");
@@ -1219,9 +1220,6 @@
       XSETFASTINT (val, (intptr_t) connection);
       xd_registered_buses = Fcons (Fcons (bus, val), xd_registered_buses);
 
-      /* We do not want to abort.  */
-      xputenv ("DBUS_FATAL_WARNINGS=0");
-
       /* Cleanup.  */
       dbus_error_free (&derror);
     }
@@ -1738,6 +1736,13 @@
 
 
 void
+init_dbusbind (void)
+{
+  /* We do not want to abort.  */
+  xputenv ("DBUS_FATAL_WARNINGS=0");
+}
+
+void
 syms_of_dbusbind (void)
 {
 

=== modified file 'src/editfns.c'
--- a/src/editfns.c     2014-10-01 03:28:16 +0000
+++ b/src/editfns.c     2014-10-12 06:09:50 +0000
@@ -64,11 +64,17 @@
 extern Lisp_Object w32_get_internal_run_time (void);
 #endif
 
+static void set_time_zone_rule (char const *);
 static Lisp_Object format_time_string (char const *, ptrdiff_t, struct 
timespec,
                                       bool, struct tm *);
+static long int tm_gmtoff (struct tm *);
 static int tm_diff (struct tm *, struct tm *);
 static void update_buffer_properties (ptrdiff_t, ptrdiff_t);
 
+#ifndef HAVE_TM_GMTOFF
+# define HAVE_TM_GMTOFF false
+#endif
+
 static Lisp_Object Qbuffer_access_fontify_functions;
 
 /* Symbol for the text property used to mark fields.  */
@@ -79,15 +85,12 @@
 
 static Lisp_Object Qboundary;
 
-/* The startup value of the TZ environment variable so it can be
-   restored if the user calls set-time-zone-rule with a nil
-   argument.  If null, the TZ environment variable was unset.  */
+/* The startup value of the TZ environment variable; null if unset.  */
 static char const *initial_tz;
 
-/* True if the static variable tzvalbuf (defined in
-   set_time_zone_rule) is part of 'environ'.  */
-static bool tzvalbuf_in_environ;
-
+/* A valid but unlikely setting for the TZ environment variable.
+   It is OK (though a bit slower) if the user chooses this value.  */
+static char const dump_tz_string[] = "TZ=UtC0";
 
 void
 init_editfns (void)
@@ -101,13 +104,38 @@
   init_system_name ();
 
 #ifndef CANNOT_DUMP
-  /* Don't bother with this on initial start when just dumping out */
+  /* When just dumping out, set the time zone to a known unlikely value
+     and skip the rest of this function.  */
   if (!initialized)
-    return;
-#endif /* not CANNOT_DUMP */
-
-  initial_tz = getenv ("TZ");
-  tzvalbuf_in_environ = 0;
+    {
+# ifdef HAVE_TZSET
+      xputenv ((char *) dump_tz_string);
+      tzset ();
+# endif
+      return;
+    }
+#endif
+
+  char *tz = getenv ("TZ");
+  initial_tz = tz;
+
+#if !defined CANNOT_DUMP && defined HAVE_TZSET
+  /* If the execution TZ happens to be the same as the dump TZ,
+     change it to some other value and then change it back,
+     to force the underlying implementation to reload the TZ info.
+     This is needed on implementations that load TZ info from files,
+     since the TZ file contents may differ between dump and execution.  */
+  if (tz && strcmp (tz, &dump_tz_string[sizeof "TZ=" - 1]) == 0)
+    {
+      ++*tz;
+      tzset ();
+      --*tz;
+    }
+#endif
+
+  /* Call set_time_zone_rule now, so that its call to putenv is done
+     before multiple threads are active.  */
+  set_time_zone_rule (tz);
 
   pw = getpwuid (getuid ());
 #ifdef MSDOS
@@ -1373,6 +1401,30 @@
   error ("Specified time is not representable");
 }
 
+/* A substitute for mktime_z on platforms that lack it.  It's not
+   thread-safe, but should be good enough for Emacs in typical use.  */
+#ifndef HAVE_TZALLOC
+time_t
+mktime_z (timezone_t tz, struct tm *tm)
+{
+  char *oldtz = getenv ("TZ");
+  USE_SAFE_ALLOCA;
+  if (oldtz)
+    {
+      size_t oldtzsize = strlen (oldtz) + 1;
+      char *oldtzcopy = SAFE_ALLOCA (oldtzsize);
+      oldtz = strcpy (oldtzcopy, oldtz);
+    }
+  block_input ();
+  set_time_zone_rule (tz);
+  time_t t = mktime (tm);
+  set_time_zone_rule (oldtz);
+  unblock_input ();
+  SAFE_FREE ();
+  return t;
+}
+#endif
+
 /* Return the upper part of the time T (everything but the bottom 16 bits).  */
 static EMACS_INT
 hi_time (time_t t)
@@ -1768,39 +1820,28 @@
   size_t len;
   Lisp_Object bufstring;
   int ns = t.tv_nsec;
-  struct tm *tm;
   USE_SAFE_ALLOCA;
 
-  while (1)
+  tmp = ut ? gmtime_r (&t.tv_sec, tmp) : localtime_r (&t.tv_sec, tmp);
+  if (! tmp)
+    time_overflow ();
+  synchronize_system_time_locale ();
+
+  while (true)
     {
-      time_t *taddr = &t.tv_sec;
-      block_input ();
-
-      synchronize_system_time_locale ();
-
-      tm = ut ? gmtime (taddr) : localtime (taddr);
-      if (! tm)
-       {
-         unblock_input ();
-         time_overflow ();
-       }
-      *tmp = *tm;
-
       buf[0] = '\1';
-      len = emacs_nmemftime (buf, size, format, formatlen, tm, ut, ns);
+      len = emacs_nmemftime (buf, size, format, formatlen, tmp, ut, ns);
       if ((0 < len && len < size) || (len == 0 && buf[0] == '\0'))
        break;
 
       /* Buffer was too small, so make it bigger and try again.  */
-      len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tm, ut, ns);
-      unblock_input ();
+      len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tmp, ut, ns);
       if (STRING_BYTES_BOUND <= len)
        string_overflow ();
       size = len + 1;
       buf = SAFE_ALLOCA (size);
     }
 
-  unblock_input ();
   bufstring = make_unibyte_string (buf, len);
   SAFE_FREE ();
   return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0);
@@ -1824,38 +1865,30 @@
   (Lisp_Object specified_time)
 {
   time_t time_spec = lisp_seconds_argument (specified_time);
-  struct tm save_tm;
-  struct tm *decoded_time;
-  Lisp_Object list_args[9];
+  struct tm local_tm, gmt_tm;
 
-  block_input ();
-  decoded_time = localtime (&time_spec);
-  if (decoded_time)
-    save_tm = *decoded_time;
-  unblock_input ();
-  if (! (decoded_time
-        && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= save_tm.tm_year
-        && save_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
+  if (! (localtime_r (&time_spec, &local_tm)
+        && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= local_tm.tm_year
+        && local_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
     time_overflow ();
-  XSETFASTINT (list_args[0], save_tm.tm_sec);
-  XSETFASTINT (list_args[1], save_tm.tm_min);
-  XSETFASTINT (list_args[2], save_tm.tm_hour);
-  XSETFASTINT (list_args[3], save_tm.tm_mday);
-  XSETFASTINT (list_args[4], save_tm.tm_mon + 1);
-  /* On 64-bit machines an int is narrower than EMACS_INT, thus the
-     cast below avoids overflow in int arithmetics.  */
-  XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) save_tm.tm_year);
-  XSETFASTINT (list_args[6], save_tm.tm_wday);
-  list_args[7] = save_tm.tm_isdst ? Qt : Qnil;
-
-  block_input ();
-  decoded_time = gmtime (&time_spec);
-  if (decoded_time == 0)
-    list_args[8] = Qnil;
-  else
-    XSETINT (list_args[8], tm_diff (&save_tm, decoded_time));
-  unblock_input ();
-  return Flist (9, list_args);
+
+  /* Avoid overflow when INT_MAX < EMACS_INT_MAX.  */
+  EMACS_INT tm_year_base = TM_YEAR_BASE;
+
+  return Flist (9, ((Lisp_Object [])
+                   {make_number (local_tm.tm_sec),
+                    make_number (local_tm.tm_min),
+                    make_number (local_tm.tm_hour),
+                    make_number (local_tm.tm_mday),
+                    make_number (local_tm.tm_mon + 1),
+                    make_number (local_tm.tm_year + tm_year_base),
+                    make_number (local_tm.tm_wday),
+                    local_tm.tm_isdst ? Qt : Qnil,
+                    (HAVE_TM_GMTOFF
+                     ? make_number (tm_gmtoff (&local_tm))
+                     : gmtime_r (&time_spec, &gmt_tm)
+                     ? make_number (tm_diff (&local_tm, &gmt_tm))
+                     : Qnil)}));
 }
 
 /* Return OBJ - OFFSET, checking that OBJ is a valid fixnum and that
@@ -1911,18 +1944,12 @@
   if (CONSP (zone))
     zone = XCAR (zone);
   if (NILP (zone))
-    {
-      block_input ();
-      value = mktime (&tm);
-      unblock_input ();
-    }
+    value = mktime (&tm);
   else
     {
       static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d";
       char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)];
-      char *old_tzstring;
       const char *tzstring;
-      USE_SAFE_ALLOCA;
 
       if (EQ (zone, Qt))
        tzstring = "UTC0";
@@ -1939,29 +1966,13 @@
          tzstring = tzbuf;
        }
       else
+       tzstring = 0;
+
+      timezone_t tz = tzstring ? tzalloc (tzstring) : 0;
+      if (! tz)
        error ("Invalid time zone specification");
-
-      old_tzstring = getenv ("TZ");
-      if (old_tzstring)
-       {
-         char *buf = SAFE_ALLOCA (strlen (old_tzstring) + 1);
-         old_tzstring = strcpy (buf, old_tzstring);
-       }
-
-      block_input ();
-
-      /* Set TZ before calling mktime; merely adjusting mktime's returned
-        value doesn't suffice, since that would mishandle leap seconds.  */
-      set_time_zone_rule (tzstring);
-
-      value = mktime (&tm);
-
-      set_time_zone_rule (old_tzstring);
-#ifdef LOCALTIME_CACHE
-      tzset ();
-#endif
-      unblock_input ();
-      SAFE_FREE ();
+      value = mktime_z (tz, &tm);
+      tzfree (tz);
     }
 
   if (value == (time_t) -1)
@@ -1987,34 +1998,27 @@
   (Lisp_Object specified_time)
 {
   time_t value = lisp_seconds_argument (specified_time);
-  struct tm *tm;
-  char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1];
-  int len IF_LINT (= 0);
 
   /* Convert to a string in ctime format, except without the trailing
      newline, and without the 4-digit year limit.  Don't use asctime
      or ctime, as they might dump core if the year is outside the
      range -999 .. 9999.  */
-  block_input ();
-  tm = localtime (&value);
-  if (tm)
-    {
-      static char const wday_name[][4] =
-       { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
-      static char const mon_name[][4] =
-       { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-      printmax_t year_base = TM_YEAR_BASE;
-
-      len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd,
-                    wday_name[tm->tm_wday], mon_name[tm->tm_mon], tm->tm_mday,
-                    tm->tm_hour, tm->tm_min, tm->tm_sec,
-                    tm->tm_year + year_base);
-    }
-  unblock_input ();
-  if (! tm)
+  struct tm tm;
+  if (! localtime_r (&value, &tm))
     time_overflow ();
 
+  static char const wday_name[][4] =
+    { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+  static char const mon_name[][4] =
+    { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+  printmax_t year_base = TM_YEAR_BASE;
+  char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1];
+  int len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd,
+                    wday_name[tm.tm_wday], mon_name[tm.tm_mon], tm.tm_mday,
+                    tm.tm_hour, tm.tm_min, tm.tm_sec,
+                    tm.tm_year + year_base);
+
   return make_unibyte_string (buf, len);
 }
 
@@ -2041,6 +2045,17 @@
          + (a->tm_sec - b->tm_sec));
 }
 
+/* Yield A's UTC offset, or an unspecified value if unknown.  */
+static long int
+tm_gmtoff (struct tm *a)
+{
+#if HAVE_TM_GMTOFF
+  return a->tm_gmtoff;
+#else
+  return 0;
+#endif
+}
+
 DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 1, 0,
        doc: /* Return the offset and name for the local time zone.
 This returns a list of the form (OFFSET NAME).
@@ -2059,32 +2074,30 @@
   (Lisp_Object specified_time)
 {
   struct timespec value;
-  int offset;
-  struct tm *t;
-  struct tm localtm;
+  struct tm local_tm, gmt_tm;
   Lisp_Object zone_offset, zone_name;
 
   zone_offset = Qnil;
   value = make_timespec (lisp_seconds_argument (specified_time), 0);
-  zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &localtm);
-  block_input ();
-  t = gmtime (&value.tv_sec);
-  if (t)
-    offset = tm_diff (&localtm, t);
-  unblock_input ();
+  zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &local_tm);
 
-  if (t)
+  if (HAVE_TM_GMTOFF || gmtime_r (&value.tv_sec, &gmt_tm))
     {
+      long int offset = (HAVE_TM_GMTOFF
+                        ? tm_gmtoff (&local_tm)
+                        : tm_diff (&local_tm, &gmt_tm));
       zone_offset = make_number (offset);
       if (SCHARS (zone_name) == 0)
        {
          /* No local time zone name is available; use "+-NNNN" instead.  */
-         int m = offset / 60;
-         int am = offset < 0 ? - m : m;
-         char buf[sizeof "+00" + INT_STRLEN_BOUND (int)];
-         zone_name = make_formatted_string (buf, "%c%02d%02d",
+         long int m = offset / 60;
+         long int am = offset < 0 ? - m : m;
+         long int hour = am / 60;
+         int min = am % 60;
+         char buf[sizeof "+00" + INT_STRLEN_BOUND (long int)];
+         zone_name = make_formatted_string (buf, "%c%02ld%02d",
                                             (offset < 0 ? '-' : '+'),
-                                            am / 60, am % 60);
+                                            hour, min);
        }
     }
 
@@ -2123,12 +2136,12 @@
 
 /* Set the local time zone rule to TZSTRING.
 
-   This function is not thread-safe, partly because putenv, unsetenv
-   and tzset are not, and partly because of the static storage it
-   updates.  Other threads that invoke localtime etc. may be adversely
-   affected while this function is executing.  */
+   This function is not thread-safe, in theory because putenv is not,
+   but mostly because of the static storage it updates.  Other threads
+   that invoke localtime etc. may be adversely affected while this
+   function is executing.  */
 
-void
+static void
 set_time_zone_rule (const char *tzstring)
 {
   /* A buffer holding a string of the form "TZ=value", intended
@@ -2137,75 +2150,47 @@
   static ptrdiff_t tzvalbufsize;
 
   int tzeqlen = sizeof "TZ=" - 1;
-
-#ifdef LOCALTIME_CACHE
-  /* These two values are known to load tz files in buggy implementations,
-     i.e., Solaris 1 executables running under either Solaris 1 or Solaris 2.
-     Their values shouldn't matter in non-buggy implementations.
-     We don't use string literals for these strings,
-     since if a string in the environment is in readonly
-     storage, it runs afoul of bugs in SVR4 and Solaris 2.3.
-     See Sun bugs 1113095 and 1114114, ``Timezone routines
-     improperly modify environment''.  */
-
-  static char set_time_zone_rule_tz[][sizeof "TZ=GMT+0"]
-    = { "TZ=GMT+0", "TZ=GMT+1" };
-
-  /* In SunOS 4.1.3_U1 and 4.1.4, if TZ has a value like
-     "US/Pacific" that loads a tz file, then changes to a value like
-     "XXX0" that does not load a tz file, and then changes back to
-     its original value, the last change is (incorrectly) ignored.
-     Also, if TZ changes twice in succession to values that do
-     not load a tz file, tzset can dump core (see Sun bug#1225179).
-     The following code works around these bugs.  */
+  ptrdiff_t tzstringlen = tzstring ? strlen (tzstring) : 0;
+  char *tzval = tzvalbuf;
+  bool new_tzvalbuf = tzvalbufsize <= tzeqlen + tzstringlen;
+
+  if (new_tzvalbuf)
+    {
+      /* Do not attempt to free the old tzvalbuf, since another thread
+        may be using it.  In practice, the first allocation is large
+        enough and memory does not leak.  */
+      tzval = xpalloc (NULL, &tzvalbufsize,
+                      tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1);
+      tzvalbuf = tzval;
+      tzval[1] = 'Z';
+      tzval[2] = '=';
+    }
 
   if (tzstring)
     {
-      /* Temporarily set TZ to a value that loads a tz file
-        and that differs from tzstring.  */
-      bool eq0 = strcmp (tzstring, set_time_zone_rule_tz[0] + tzeqlen) == 0;
-      xputenv (set_time_zone_rule_tz[eq0]);
-    }
-  else
-    {
-      /* The implied tzstring is unknown, so temporarily set TZ to
-        two different values that each load a tz file.  */
-      xputenv (set_time_zone_rule_tz[0]);
-      tzset ();
-      xputenv (set_time_zone_rule_tz[1]);
-    }
-  tzset ();
-  tzvalbuf_in_environ = 0;
-#endif
-
-  if (!tzstring)
-    {
-      unsetenv ("TZ");
-      tzvalbuf_in_environ = 0;
-    }
-  else
-    {
-      ptrdiff_t tzstringlen = strlen (tzstring);
-
-      if (tzvalbufsize <= tzeqlen + tzstringlen)
-       {
-         unsetenv ("TZ");
-         tzvalbuf_in_environ = 0;
-         tzvalbuf = xpalloc (tzvalbuf, &tzvalbufsize,
-                             tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1);
-         memcpy (tzvalbuf, "TZ=", tzeqlen);
-       }
-
-      strcpy (tzvalbuf + tzeqlen, tzstring);
-
-      if (!tzvalbuf_in_environ)
-       {
-         xputenv (tzvalbuf);
-         tzvalbuf_in_environ = 1;
-       }
-    }
-
-#ifdef LOCALTIME_CACHE
+      /* Modify TZVAL in place.  Although this is dicey in a
+        multithreaded environment, we know of no portable alternative.
+        Calling putenv or setenv could crash some other thread.  */
+      tzval[0] = 'T';
+      strcpy (tzval + tzeqlen, tzstring);
+    }
+  else
+    {
+      /* Turn 'TZ=whatever' into an empty environment variable 'tZ='.
+        Although this is also dicey, calling unsetenv here can crash Emacs.
+        See Bug#8705.  */
+      tzval[0] = 't';
+      tzval[tzeqlen] = 0;
+    }
+
+  if (new_tzvalbuf)
+    {
+      /* Although this is not thread-safe, in practice this runs only
+        on startup when there is only one thread.  */
+      xputenv (tzval);
+    }
+
+#ifdef HAVE_TZSET
   tzset ();
 #endif
 }

=== modified file 'src/emacs.c'
--- a/src/emacs.c       2014-10-01 03:28:16 +0000
+++ b/src/emacs.c       2014-10-12 06:09:50 +0000
@@ -578,12 +578,6 @@
 }
 
 
-#ifdef HAVE_TZSET
-/* A valid but unlikely value for the TZ environment value.
-   It is OK (though a bit slower) if the user actually chooses this value.  */
-static char const dump_tz[] = "UtC0";
-#endif
-
 /* Test whether the next argument in ARGV matches SSTR or a prefix of
    LSTR (at least MINLEN characters).  If so, then if VALPTR is non-null
    (the argument is supposed to have a value) store in *VALPTR either
@@ -1548,8 +1542,23 @@
 
   init_charset ();
 
-  init_editfns (); /* init_process_emacs uses Voperating_system_release. */
-  init_process_emacs (); /* init_display uses add_keyboard_wait_descriptor. */
+  /* This calls putenv and so must precede init_process_emacs.  Also,
+     it sets Voperating_system_release, which init_process_emacs uses.  */
+  init_editfns ();
+
+  /* These two call putenv.  */
+#ifdef HAVE_DBUS
+  init_dbusbind ();
+#endif
+#ifdef USE_GTK
+  init_xterm ();
+#endif
+
+  /* This can create a thread that may call getenv, so it must follow
+     all calls to putenv and setenv.  Also, this sets up
+     add_keyboard_wait_descriptor, which init_display uses.  */
+  init_process_emacs ();
+
   init_keyboard ();    /* This too must precede init_sys_modes.  */
   if (!noninteractive)
     init_display ();   /* Determine terminal type.  Calls init_sys_modes.  */
@@ -1586,26 +1595,6 @@
                            build_string ("loadup.el"));
     }
 
-  if (initialized)
-    {
-#ifdef HAVE_TZSET
-      {
-       /* If the execution TZ happens to be the same as the dump TZ,
-          change it to some other value and then change it back,
-          to force the underlying implementation to reload the TZ info.
-          This is needed on implementations that load TZ info from files,
-          since the TZ file contents may differ between dump and execution.  */
-       char *tz = getenv ("TZ");
-       if (tz && !strcmp (tz, dump_tz))
-         {
-           ++*tz;
-           tzset ();
-           --*tz;
-         }
-      }
-#endif
-    }
-
   /* Set up for profiling.  This is known to work on FreeBSD,
      GNU/Linux and MinGW.  It might work on some other systems too.
      Give it a try and tell us if it works on your system.  To compile
@@ -1630,15 +1619,6 @@
 
   initialized = 1;
 
-#ifdef LOCALTIME_CACHE
-  /* Some versions of localtime have a bug.  They cache the value of the time
-     zone rather than looking it up every time.  Since localtime() is
-     called to bolt the undumping time into the undumped emacs, this
-     results in localtime ignoring the TZ environment variable.
-     This flushes the new TZ value into localtime.  */
-  tzset ();
-#endif /* defined (LOCALTIME_CACHE) */
-
   /* Enter editor command loop.  This never returns.  */
   Frecursive_edit ();
   /* NOTREACHED */
@@ -2119,14 +2099,6 @@
   tem = Vpurify_flag;
   Vpurify_flag = Qnil;
 
-#ifdef HAVE_TZSET
-  set_time_zone_rule (dump_tz);
-#ifndef LOCALTIME_CACHE
-  /* Force a tz reload, since set_time_zone_rule doesn't.  */
-  tzset ();
-#endif
-#endif
-
   fflush (stdout);
   /* Tell malloc where start of impure now is.  */
   /* Also arrange for warnings when nearly out of space.  */

=== modified file 'src/lisp.h'
--- a/src/lisp.h        2014-10-09 06:54:10 +0000
+++ b/src/lisp.h        2014-10-12 06:09:50 +0000
@@ -3990,7 +3990,6 @@
                                            ptrdiff_t, bool);
 extern void init_editfns (void);
 extern void syms_of_editfns (void);
-extern void set_time_zone_rule (const char *);
 
 /* Defined in buffer.c.  */
 extern bool mouse_face_overlay_overlaps (Lisp_Object);
@@ -4398,6 +4397,7 @@
 extern void syms_of_xselect (void);
 
 /* Defined in xterm.c.  */
+extern void init_xterm (void);
 extern void syms_of_xterm (void);
 #endif /* HAVE_X_WINDOWS */
 
@@ -4419,6 +4419,7 @@
 
 #ifdef HAVE_DBUS
 /* Defined in dbusbind.c.  */
+void init_dbusbind (void);
 void syms_of_dbusbind (void);
 #endif
 

=== modified file 'src/systime.h'
--- a/src/systime.h     2014-09-24 20:30:28 +0000
+++ b/src/systime.h     2014-10-12 06:09:50 +0000
@@ -93,6 +93,22 @@
 extern struct timespec lisp_time_argument (Lisp_Object);
 #endif
 
+#ifndef HAVE_TZALLOC
+# undef mktime_z
+# undef timezone_t
+# undef tzalloc
+# undef tzfree
+# define mktime_z emacs_mktime_z
+# define timezone_t emacs_timezone_t
+# define tzalloc emacs_tzalloc
+# define tzfree emacs_tzfree
+typedef char const *timezone_t;
+INLINE timezone_t tzalloc (char const *name) { return name; }
+INLINE void tzfree (timezone_t tz) { }
+/* Defined in editfns.c.  */
+extern time_t mktime_z (timezone_t, struct tm *);
+#endif
+
 INLINE_HEADER_END
 
 #endif /* EMACS_SYSTIME_H */

=== modified file 'src/xterm.c'
--- a/src/xterm.c       2014-10-03 02:20:52 +0000
+++ b/src/xterm.c       2014-10-12 06:09:50 +0000
@@ -10717,10 +10717,6 @@
 
         XSetLocaleModifiers ("");
 
-        /* Emacs can only handle core input events, so make sure
-           Gtk doesn't use Xinput or Xinput2 extensions.  */
-       xputenv ("GDK_CORE_DEVICE_EVENTS=1");
-
         /* Work around GLib bug that outputs a faulty warning. See
            https://bugzilla.gnome.org/show_bug.cgi?id=563627.  */
         id = g_log_set_handler ("GLib", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
@@ -11470,6 +11466,15 @@
   XSetIOErrorHandler (x_io_error_quitter);
 }
 
+#ifdef USE_GTK
+void
+init_xterm (void)
+{
+  /* Emacs can handle only core input events, so make sure
+     Gtk doesn't use Xinput or Xinput2 extensions.  */
+  xputenv ("GDK_CORE_DEVICE_EVENTS=1");
+}
+#endif
 
 void
 syms_of_xterm (void)


reply via email to

[Prev in Thread] Current Thread [Next in Thread]