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
This commit is contained in:
Paul Eggert 2014-10-11 23:09:50 -07:00
parent c1ec59da49
commit 4c4c5b9121
12 changed files with 278 additions and 279 deletions

View file

@ -1,3 +1,11 @@
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.

View file

@ -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

View file

@ -1,3 +1,9 @@
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)

View file

@ -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
'

View file

@ -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)

View file

@ -1,3 +1,41 @@
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.

View file

@ -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)
{

View file

@ -64,11 +64,17 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
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 @@ Lisp_Object Qfield;
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_editfns (void)
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 */
{
# ifdef HAVE_TZSET
xputenv ((char *) dump_tz_string);
tzset ();
# endif
return;
}
#endif
initial_tz = getenv ("TZ");
tzvalbuf_in_environ = 0;
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 @@ time_overflow (void)
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 @@ format_time_string (char const *format, ptrdiff_t formatlen,
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 @@ DOW and ZONE.) */)
(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 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
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 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
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 @@ but this is considered obsolete. */)
(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 @@ tm_diff (struct tm *a, struct tm *b)
+ (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 @@ the data it can't find. */)
(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 @@ only the former. */)
/* 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 @@ set_time_zone_rule (const char *tzstring)
static ptrdiff_t tzvalbufsize;
int tzeqlen = sizeof "TZ=" - 1;
ptrdiff_t tzstringlen = tzstring ? strlen (tzstring) : 0;
char *tzval = tzvalbuf;
bool new_tzvalbuf = tzvalbufsize <= tzeqlen + tzstringlen;
#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. */
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]);
/* 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
{
/* 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]);
/* 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;
}
tzset ();
tzvalbuf_in_environ = 0;
#endif
if (!tzstring)
if (new_tzvalbuf)
{
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;
}
/* Although this is not thread-safe, in practice this runs only
on startup when there is only one thread. */
xputenv (tzval);
}
#ifdef LOCALTIME_CACHE
#ifdef HAVE_TZSET
tzset ();
#endif
}

View file

@ -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. */

View file

@ -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

View file

@ -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 */

View file

@ -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)