Commit 4c4c5b91 authored by Paul Eggert's avatar Paul Eggert

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.

Fixes: debbugs:8705
parent c1ec59da
2014-10-12 Paul Eggert <eggert@cs.ucla.edu>
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 <jan.h.d@swipnet.se>
* configure.ac: Add -Wno-string-plus-int for clang.
......
......@@ -368,7 +368,6 @@ HAVE___FPENDING
INTERNAL_TERMINAL
IS_ANY_SEP
IS_DIRECTORY_SEP
LOCALTIME_CACHE
MAIL_USE_FLOCK
MAIL_USE_LOCKF
MAIL_USE_POP
......
2014-10-12 Paul Eggert <eggert@cs.ucla.edu>
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 <rgm@gnu.org>
* unidata/Makefile.in: Check for deleted uni- files. (Bug#18489)
......
......@@ -37,7 +37,7 @@ GNULIB_MODULES='
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
'
......
......@@ -3915,43 +3915,7 @@ AC_SUBST(KRB4LIB)
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)
......
2014-10-12 Paul Eggert <eggert@cs.ucla.edu>
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 <eggert@cs.ucla.edu>
* lisp.h (USE_STACK_STRING): Now true only if USE_STACK CONS.
......
......@@ -1054,6 +1054,7 @@ xd_remove_watch (DBusWatch *watch, void *data)
/* 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 @@ this connection to those buses. */)
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);
}
......@@ -1737,6 +1735,13 @@ xd_read_queued_messages (int fd, void *data)
}
void
init_dbusbind (void)
{
/* We do not want to abort. */
xputenv ("DBUS_FATAL_WARNINGS=0");
}
void
syms_of_dbusbind (void)
{
......
This diff is collapsed.
......@@ -578,12 +578,6 @@ DEFUN ("invocation-directory", Finvocation_directory, Sinvocation_directory,
}
#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 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
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 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
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 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
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 @@ You must run Emacs in batch mode in order to dump it. */)
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. */
......
......@@ -3990,7 +3990,6 @@ extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t,
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_xsmfns (void);
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 @@ extern void syms_of_decompress (void);
#ifdef HAVE_DBUS
/* Defined in dbusbind.c. */
void init_dbusbind (void);
void syms_of_dbusbind (void);
#endif
......
......@@ -93,6 +93,22 @@ extern bool decode_time_components (Lisp_Object, Lisp_Object, Lisp_Object,
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 */
......@@ -10717,10 +10717,6 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
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 @@ x_initialize (void)
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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment