diff options
| author | Paul Eggert | 2012-05-02 16:25:46 -0700 |
|---|---|---|
| committer | Paul Eggert | 2012-05-02 16:25:46 -0700 |
| commit | 7ed806a75c15de00d60ef37f347b5800370fb77c (patch) | |
| tree | 544c50daa8065996076ab015e73788cc92ea0edf /src | |
| parent | 99d2758343245de1ac452a9b8d1f4c1521f8c8d6 (diff) | |
| download | emacs-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/ChangeLog | 18 | ||||
| -rw-r--r-- | src/editfns.c | 143 |
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 @@ | |||
| 1 | 2012-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 | |||
| 1 | 2012-05-02 Dmitry Antipov <dmantipov@yandex.ru> | 19 | 2012-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 | ||
| 87 | static void time_overflow (void) NO_RETURN; | 87 | static void time_overflow (void) NO_RETURN; |
| 88 | static Lisp_Object format_time_string (char const *, ptrdiff_t, Lisp_Object, | 88 | static Lisp_Object format_time_string (char const *, ptrdiff_t, Lisp_Object, |
| 89 | int, time_t *, struct tm **); | 89 | int, time_t *, struct tm *); |
| 90 | static int tm_diff (struct tm *, struct tm *); | 90 | static int tm_diff (struct tm *, struct tm *); |
| 91 | static void update_buffer_properties (EMACS_INT, EMACS_INT); | 91 | static 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 | ||
| 1716 | static Lisp_Object | 1716 | static Lisp_Object |
| 1717 | format_time_string (char const *format, ptrdiff_t formatlen, | 1717 | format_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 | ||
| 1768 | DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 1, 0, | 1769 | DEFUN ("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 | ||