diff options
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 | ||