aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2012-05-02 16:25:46 -0700
committerPaul Eggert2012-05-02 16:25:46 -0700
commit7ed806a75c15de00d60ef37f347b5800370fb77c (patch)
tree544c50daa8065996076ab015e73788cc92ea0edf /src
parent99d2758343245de1ac452a9b8d1f4c1521f8c8d6 (diff)
downloademacs-7ed806a75c15de00d60ef37f347b5800370fb77c.tar.gz
emacs-7ed806a75c15de00d60ef37f347b5800370fb77c.zip
Fix race conditions involving setenv, gmtime, localtime, asctime.
Without this fix, interrupts could mess up code that uses these nonreentrant functions, since setting TZ invalidates existing tm_zone or tzname values, and since most of these functions return pointers to static storage. * editfns.c (format_time_string, Fdecode_time, Fencode_time) (Fcurrent_time_string, Fcurrent_time_zone, Fset_time_zone_rule): Grow the critical sections to include not just invoking localtime/gmtime, but also accessing these functions' results including their tm_zone values if any, and any related TZ setting. (format_time_string): Last arg is now struct tm *, not struct tm **, so that the struct tm is saved in the critical section. All callers changed. Simplify allocation of initial buffer, partly motivated by the fact that memory allocation needs to be outside the critical section.
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog18
-rw-r--r--src/editfns.c143
2 files changed, 97 insertions, 64 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 01e137d2fcc..bf297616e82 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,21 @@
12012-05-02 Paul Eggert <eggert@cs.ucla.edu>
2
3 Fix race conditions involving setenv, gmtime, localtime, asctime.
4 Without this fix, interrupts could mess up code that uses these
5 nonreentrant functions, since setting TZ invalidates existing
6 tm_zone or tzname values, and since most of these functions return
7 pointers to static storage.
8 * editfns.c (format_time_string, Fdecode_time, Fencode_time)
9 (Fcurrent_time_string, Fcurrent_time_zone, Fset_time_zone_rule):
10 Grow the critical sections to include not just invoking
11 localtime/gmtime, but also accessing these functions' results
12 including their tm_zone values if any, and any related TZ setting.
13 (format_time_string): Last arg is now struct tm *, not struct tm **,
14 so that the struct tm is saved in the critical section. All
15 callers changed. Simplify allocation of initial buffer, partly
16 motivated by the fact that memory allocation needs to be outside
17 the critical section.
18
12012-05-02 Dmitry Antipov <dmantipov@yandex.ru> 192012-05-02 Dmitry Antipov <dmantipov@yandex.ru>
2 20
3 * intervals.c (adjust_intervals_for_insertion): Initialize `newi' 21 * intervals.c (adjust_intervals_for_insertion): Initialize `newi'
diff --git a/src/editfns.c b/src/editfns.c
index a41565d8588..b52bc0c2a99 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -86,7 +86,7 @@ extern Lisp_Object w32_get_internal_run_time (void);
86 86
87static void time_overflow (void) NO_RETURN; 87static void time_overflow (void) NO_RETURN;
88static Lisp_Object format_time_string (char const *, ptrdiff_t, Lisp_Object, 88static Lisp_Object format_time_string (char const *, ptrdiff_t, Lisp_Object,
89 int, time_t *, struct tm **); 89 int, time_t *, struct tm *);
90static int tm_diff (struct tm *, struct tm *); 90static int tm_diff (struct tm *, struct tm *);
91static void update_buffer_properties (EMACS_INT, EMACS_INT); 91static void update_buffer_properties (EMACS_INT, EMACS_INT);
92 92
@@ -1704,7 +1704,7 @@ usage: (format-time-string FORMAT-STRING &optional TIME UNIVERSAL) */)
1704 (Lisp_Object format_string, Lisp_Object timeval, Lisp_Object universal) 1704 (Lisp_Object format_string, Lisp_Object timeval, Lisp_Object universal)
1705{ 1705{
1706 time_t t; 1706 time_t t;
1707 struct tm *tm; 1707 struct tm tm;
1708 1708
1709 CHECK_STRING (format_string); 1709 CHECK_STRING (format_string);
1710 format_string = code_convert_string_norecord (format_string, 1710 format_string = code_convert_string_norecord (format_string,
@@ -1715,54 +1715,55 @@ usage: (format-time-string FORMAT-STRING &optional TIME UNIVERSAL) */)
1715 1715
1716static Lisp_Object 1716static Lisp_Object
1717format_time_string (char const *format, ptrdiff_t formatlen, 1717format_time_string (char const *format, ptrdiff_t formatlen,
1718 Lisp_Object timeval, int ut, time_t *tval, struct tm **tmp) 1718 Lisp_Object timeval, int ut, time_t *tval, struct tm *tmp)
1719{ 1719{
1720 ptrdiff_t size; 1720 char buffer[4000];
1721 char *buf = buffer;
1722 size_t size = sizeof buffer;
1723 size_t len;
1724 Lisp_Object bufstring;
1721 int usec; 1725 int usec;
1722 int ns; 1726 int ns;
1723 struct tm *tm; 1727 struct tm *tm;
1728 USE_SAFE_ALLOCA;
1724 1729
1725 if (! (lisp_time_argument (timeval, tval, &usec) 1730 if (! (lisp_time_argument (timeval, tval, &usec)
1726 && 0 <= usec && usec < 1000000)) 1731 && 0 <= usec && usec < 1000000))
1727 error ("Invalid time specification"); 1732 error ("Invalid time specification");
1728 ns = usec * 1000; 1733 ns = usec * 1000;
1729 1734
1730 /* This is probably enough. */
1731 size = formatlen;
1732 if (size <= (STRING_BYTES_BOUND - 50) / 6)
1733 size = size * 6 + 50;
1734
1735 BLOCK_INPUT;
1736 tm = ut ? gmtime (tval) : localtime (tval);
1737 UNBLOCK_INPUT;
1738 if (! tm)
1739 time_overflow ();
1740 *tmp = tm;
1741
1742 synchronize_system_time_locale ();
1743
1744 while (1) 1735 while (1)
1745 { 1736 {
1746 char *buf = (char *) alloca (size + 1); 1737 BLOCK_INPUT;
1747 size_t result; 1738
1739 synchronize_system_time_locale ();
1740
1741 tm = ut ? gmtime (tval) : localtime (tval);
1742 if (! tm)
1743 {
1744 UNBLOCK_INPUT;
1745 time_overflow ();
1746 }
1747 *tmp = *tm;
1748 1748
1749 buf[0] = '\1'; 1749 buf[0] = '\1';
1750 BLOCK_INPUT; 1750 len = emacs_nmemftime (buf, size, format, formatlen, tm, ut, ns);
1751 result = emacs_nmemftime (buf, size, format, formatlen, tm, ut, ns); 1751 if ((0 < len && len < size) || (len == 0 && buf[0] == '\0'))
1752 UNBLOCK_INPUT; 1752 break;
1753 if ((result > 0 && result < size) || (result == 0 && buf[0] == '\0'))
1754 return code_convert_string_norecord (make_unibyte_string (buf, result),
1755 Vlocale_coding_system, 0);
1756 1753
1757 /* If buffer was too small, make it bigger and try again. */ 1754 /* Buffer was too small, so make it bigger and try again. */
1758 BLOCK_INPUT; 1755 len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tm, ut, ns);
1759 result = emacs_nmemftime (NULL, (size_t) -1, format, formatlen,
1760 tm, ut, ns);
1761 UNBLOCK_INPUT; 1756 UNBLOCK_INPUT;
1762 if (STRING_BYTES_BOUND <= result) 1757 if (STRING_BYTES_BOUND <= len)
1763 string_overflow (); 1758 string_overflow ();
1764 size = result + 1; 1759 size = len + 1;
1760 SAFE_ALLOCA (buf, char *, size);
1765 } 1761 }
1762
1763 UNBLOCK_INPUT;
1764 bufstring = make_unibyte_string (buf, len);
1765 SAFE_FREE ();
1766 return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0);
1766} 1767}
1767 1768
1768DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 1, 0, 1769DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 1, 0,
@@ -1792,31 +1793,32 @@ DOW and ZONE.) */)
1792 1793
1793 BLOCK_INPUT; 1794 BLOCK_INPUT;
1794 decoded_time = localtime (&time_spec); 1795 decoded_time = localtime (&time_spec);
1796 /* Make a copy, in case a signal handler modifies TZ or the struct. */
1797 if (decoded_time)
1798 save_tm = *decoded_time;
1795 UNBLOCK_INPUT; 1799 UNBLOCK_INPUT;
1796 if (! (decoded_time 1800 if (! (decoded_time
1797 && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= decoded_time->tm_year 1801 && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= save_tm.tm_year
1798 && decoded_time->tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE)) 1802 && save_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
1799 time_overflow (); 1803 time_overflow ();
1800 XSETFASTINT (list_args[0], decoded_time->tm_sec); 1804 XSETFASTINT (list_args[0], save_tm.tm_sec);
1801 XSETFASTINT (list_args[1], decoded_time->tm_min); 1805 XSETFASTINT (list_args[1], save_tm.tm_min);
1802 XSETFASTINT (list_args[2], decoded_time->tm_hour); 1806 XSETFASTINT (list_args[2], save_tm.tm_hour);
1803 XSETFASTINT (list_args[3], decoded_time->tm_mday); 1807 XSETFASTINT (list_args[3], save_tm.tm_mday);
1804 XSETFASTINT (list_args[4], decoded_time->tm_mon + 1); 1808 XSETFASTINT (list_args[4], save_tm.tm_mon + 1);
1805 /* On 64-bit machines an int is narrower than EMACS_INT, thus the 1809 /* On 64-bit machines an int is narrower than EMACS_INT, thus the
1806 cast below avoids overflow in int arithmetics. */ 1810 cast below avoids overflow in int arithmetics. */
1807 XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) decoded_time->tm_year); 1811 XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) save_tm.tm_year);
1808 XSETFASTINT (list_args[6], decoded_time->tm_wday); 1812 XSETFASTINT (list_args[6], save_tm.tm_wday);
1809 list_args[7] = (decoded_time->tm_isdst)? Qt : Qnil; 1813 list_args[7] = save_tm.tm_isdst ? Qt : Qnil;
1810 1814
1811 /* Make a copy, in case gmtime modifies the struct. */
1812 save_tm = *decoded_time;
1813 BLOCK_INPUT; 1815 BLOCK_INPUT;
1814 decoded_time = gmtime (&time_spec); 1816 decoded_time = gmtime (&time_spec);
1815 UNBLOCK_INPUT;
1816 if (decoded_time == 0) 1817 if (decoded_time == 0)
1817 list_args[8] = Qnil; 1818 list_args[8] = Qnil;
1818 else 1819 else
1819 XSETINT (list_args[8], tm_diff (&save_tm, decoded_time)); 1820 XSETINT (list_args[8], tm_diff (&save_tm, decoded_time));
1821 UNBLOCK_INPUT;
1820 return Flist (9, list_args); 1822 return Flist (9, list_args);
1821} 1823}
1822 1824
@@ -1898,21 +1900,23 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
1898 else 1900 else
1899 error ("Invalid time zone specification"); 1901 error ("Invalid time zone specification");
1900 1902
1903 BLOCK_INPUT;
1904
1901 /* Set TZ before calling mktime; merely adjusting mktime's returned 1905 /* Set TZ before calling mktime; merely adjusting mktime's returned
1902 value doesn't suffice, since that would mishandle leap seconds. */ 1906 value doesn't suffice, since that would mishandle leap seconds. */
1903 set_time_zone_rule (tzstring); 1907 set_time_zone_rule (tzstring);
1904 1908
1905 BLOCK_INPUT;
1906 value = mktime (&tm); 1909 value = mktime (&tm);
1907 UNBLOCK_INPUT;
1908 1910
1909 /* Restore TZ to previous value. */ 1911 /* Restore TZ to previous value. */
1910 newenv = environ; 1912 newenv = environ;
1911 environ = oldenv; 1913 environ = oldenv;
1912 xfree (newenv);
1913#ifdef LOCALTIME_CACHE 1914#ifdef LOCALTIME_CACHE
1914 tzset (); 1915 tzset ();
1915#endif 1916#endif
1917 UNBLOCK_INPUT;
1918
1919 xfree (newenv);
1916 } 1920 }
1917 1921
1918 if (value == (time_t) -1) 1922 if (value == (time_t) -1)
@@ -1939,24 +1943,29 @@ but this is considered obsolete. */)
1939{ 1943{
1940 time_t value; 1944 time_t value;
1941 struct tm *tm; 1945 struct tm *tm;
1942 register char *tem; 1946 char *tem = NULL;
1947 char buf[sizeof "Mon Apr 30 12:49:17 2012" - 1];
1943 1948
1944 if (! lisp_time_argument (specified_time, &value, NULL)) 1949 if (! lisp_time_argument (specified_time, &value, NULL))
1945 error ("Invalid time specification"); 1950 error ("Invalid time specification");
1946 1951
1947 /* Convert to a string, checking for out-of-range time stamps. 1952 /* Convert to a string, checking for out-of-range time stamps.
1953 Omit the trailing newline.
1948 Don't use 'ctime', as that might dump core if VALUE is out of 1954 Don't use 'ctime', as that might dump core if VALUE is out of
1949 range. */ 1955 range. */
1950 BLOCK_INPUT; 1956 BLOCK_INPUT;
1951 tm = localtime (&value); 1957 tm = localtime (&value);
1958 if (tm && TM_YEAR_IN_ASCTIME_RANGE (tm->tm_year))
1959 {
1960 tem = asctime (tm);
1961 if (tem)
1962 memcpy (buf, tem, sizeof buf);
1963 }
1952 UNBLOCK_INPUT; 1964 UNBLOCK_INPUT;
1953 if (! (tm && TM_YEAR_IN_ASCTIME_RANGE (tm->tm_year) && (tem = asctime (tm)))) 1965 if (! tem)
1954 time_overflow (); 1966 time_overflow ();
1955 1967
1956 /* Remove the trailing newline. */ 1968 return make_unibyte_string (buf, sizeof buf);
1957 tem[strlen (tem) - 1] = '\0';
1958
1959 return build_string (tem);
1960} 1969}
1961 1970
1962/* Yield A - B, measured in seconds. 1971/* Yield A - B, measured in seconds.
@@ -2000,22 +2009,22 @@ the data it can't find. */)
2000 (Lisp_Object specified_time) 2009 (Lisp_Object specified_time)
2001{ 2010{
2002 time_t value; 2011 time_t value;
2012 int offset;
2003 struct tm *t; 2013 struct tm *t;
2004 struct tm localtm; 2014 struct tm localtm;
2005 struct tm *localt;
2006 Lisp_Object zone_offset, zone_name; 2015 Lisp_Object zone_offset, zone_name;
2007 2016
2008 zone_offset = Qnil; 2017 zone_offset = Qnil;
2009 zone_name = format_time_string ("%Z", sizeof "%Z" - 1, specified_time, 2018 zone_name = format_time_string ("%Z", sizeof "%Z" - 1, specified_time,
2010 0, &value, &localt); 2019 0, &value, &localtm);
2011 localtm = *localt;
2012 BLOCK_INPUT; 2020 BLOCK_INPUT;
2013 t = gmtime (&value); 2021 t = gmtime (&value);
2022 if (t)
2023 offset = tm_diff (&localtm, t);
2014 UNBLOCK_INPUT; 2024 UNBLOCK_INPUT;
2015 2025
2016 if (t) 2026 if (t)
2017 { 2027 {
2018 int offset = tm_diff (&localtm, t);
2019 zone_offset = make_number (offset); 2028 zone_offset = make_number (offset);
2020 if (SCHARS (zone_name) == 0) 2029 if (SCHARS (zone_name) == 0)
2021 { 2030 {
@@ -2053,9 +2062,16 @@ only the former. */)
2053 (Lisp_Object tz) 2062 (Lisp_Object tz)
2054{ 2063{
2055 const char *tzstring; 2064 const char *tzstring;
2065 char **old_environbuf;
2066
2067 if (! (NILP (tz) || EQ (tz, Qt)))
2068 CHECK_STRING (tz);
2069
2070 BLOCK_INPUT;
2056 2071
2057 /* When called for the first time, save the original TZ. */ 2072 /* When called for the first time, save the original TZ. */
2058 if (!environbuf) 2073 old_environbuf = environbuf;
2074 if (!old_environbuf)
2059 initial_tz = (char *) getenv ("TZ"); 2075 initial_tz = (char *) getenv ("TZ");
2060 2076
2061 if (NILP (tz)) 2077 if (NILP (tz))
@@ -2063,15 +2079,14 @@ only the former. */)
2063 else if (EQ (tz, Qt)) 2079 else if (EQ (tz, Qt))
2064 tzstring = "UTC0"; 2080 tzstring = "UTC0";
2065 else 2081 else
2066 { 2082 tzstring = SSDATA (tz);
2067 CHECK_STRING (tz);
2068 tzstring = SSDATA (tz);
2069 }
2070 2083
2071 set_time_zone_rule (tzstring); 2084 set_time_zone_rule (tzstring);
2072 xfree (environbuf);
2073 environbuf = environ; 2085 environbuf = environ;
2074 2086
2087 UNBLOCK_INPUT;
2088
2089 xfree (old_environbuf);
2075 return Qnil; 2090 return Qnil;
2076} 2091}
2077 2092