diff options
| author | Paul Eggert | 2019-08-21 17:18:33 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-08-21 17:21:06 -0700 |
| commit | 951ea375d52891f79b89794fbb9dca86ab8cd5a8 (patch) | |
| tree | 459efbdb4329bd8e5926f5f21b1e170505443bce | |
| parent | 11de1155f81fdac515b5465d31634c7b91a4d42a (diff) | |
| download | emacs-951ea375d52891f79b89794fbb9dca86ab8cd5a8.tar.gz emacs-951ea375d52891f79b89794fbb9dca86ab8cd5a8.zip | |
Don’t hard-loop on cycles in ‘read’ etc.
Problem for ‘read’ reported by Pip Cet in:
https://lists.gnu.org/r/emacs-devel/2019-08/msg00316.html
* src/fns.c (Frequire): Protect against circular current-load-list.
* src/lread.c (Fget_load_suffixes):
Protect against circular load-suffixes or load-file-rep-suffixes.
(Fload): Protect against circular loads-in-progress.
(openp): Protect against circular PATH and SUFFIXES.
(build_load_history): Protect against circular load-history or
current-load-list.
(readevalloop_eager_expand_eval): Protect against circular SUBFORMS.
(read1): Protect against circular data.
* test/src/lread-tests.el (lread-circular-hash): New test.
| -rw-r--r-- | src/fns.c | 9 | ||||
| -rw-r--r-- | src/lread.c | 68 | ||||
| -rw-r--r-- | test/src/lread-tests.el | 3 |
3 files changed, 43 insertions, 37 deletions
| @@ -2950,9 +2950,12 @@ suppressed. */) | |||
| 2950 | But not more than once in any file, | 2950 | But not more than once in any file, |
| 2951 | and not when we aren't loading or reading from a file. */ | 2951 | and not when we aren't loading or reading from a file. */ |
| 2952 | if (!from_file) | 2952 | if (!from_file) |
| 2953 | for (tem = Vcurrent_load_list; CONSP (tem); tem = XCDR (tem)) | 2953 | { |
| 2954 | if (NILP (XCDR (tem)) && STRINGP (XCAR (tem))) | 2954 | Lisp_Object tail = Vcurrent_load_list; |
| 2955 | from_file = 1; | 2955 | FOR_EACH_TAIL_SAFE (tail) |
| 2956 | if (NILP (XCDR (tail)) && STRINGP (XCAR (tail))) | ||
| 2957 | from_file = true; | ||
| 2958 | } | ||
| 2956 | 2959 | ||
| 2957 | if (from_file) | 2960 | if (from_file) |
| 2958 | { | 2961 | { |
diff --git a/src/lread.c b/src/lread.c index 1bfbf5aa865..e444830c99a 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -1064,18 +1064,13 @@ required. | |||
| 1064 | This uses the variables `load-suffixes' and `load-file-rep-suffixes'. */) | 1064 | This uses the variables `load-suffixes' and `load-file-rep-suffixes'. */) |
| 1065 | (void) | 1065 | (void) |
| 1066 | { | 1066 | { |
| 1067 | Lisp_Object lst = Qnil, suffixes = Vload_suffixes, suffix, ext; | 1067 | Lisp_Object lst = Qnil, suffixes = Vload_suffixes; |
| 1068 | while (CONSP (suffixes)) | 1068 | FOR_EACH_TAIL (suffixes) |
| 1069 | { | 1069 | { |
| 1070 | Lisp_Object exts = Vload_file_rep_suffixes; | 1070 | Lisp_Object exts = Vload_file_rep_suffixes; |
| 1071 | suffix = XCAR (suffixes); | 1071 | Lisp_Object suffix = XCAR (suffixes); |
| 1072 | suffixes = XCDR (suffixes); | 1072 | FOR_EACH_TAIL (exts) |
| 1073 | while (CONSP (exts)) | 1073 | lst = Fcons (concat2 (suffix, XCAR (exts)), lst); |
| 1074 | { | ||
| 1075 | ext = XCAR (exts); | ||
| 1076 | exts = XCDR (exts); | ||
| 1077 | lst = Fcons (concat2 (suffix, ext), lst); | ||
| 1078 | } | ||
| 1079 | } | 1074 | } |
| 1080 | return Fnreverse (lst); | 1075 | return Fnreverse (lst); |
| 1081 | } | 1076 | } |
| @@ -1290,8 +1285,8 @@ Return t if the file exists and loads successfully. */) | |||
| 1290 | the general case; the second load may do something different. */ | 1285 | the general case; the second load may do something different. */ |
| 1291 | { | 1286 | { |
| 1292 | int load_count = 0; | 1287 | int load_count = 0; |
| 1293 | Lisp_Object tem; | 1288 | Lisp_Object tem = Vloads_in_progress; |
| 1294 | for (tem = Vloads_in_progress; CONSP (tem); tem = XCDR (tem)) | 1289 | FOR_EACH_TAIL_SAFE (tem) |
| 1295 | if (!NILP (Fequal (found, XCAR (tem))) && (++load_count > 3)) | 1290 | if (!NILP (Fequal (found, XCAR (tem))) && (++load_count > 3)) |
| 1296 | signal_error ("Recursive load", Fcons (found, Vloads_in_progress)); | 1291 | signal_error ("Recursive load", Fcons (found, Vloads_in_progress)); |
| 1297 | record_unwind_protect (record_load_unwind, Vloads_in_progress); | 1292 | record_unwind_protect (record_load_unwind, Vloads_in_progress); |
| @@ -1611,7 +1606,8 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, | |||
| 1611 | 1606 | ||
| 1612 | CHECK_STRING (str); | 1607 | CHECK_STRING (str); |
| 1613 | 1608 | ||
| 1614 | for (tail = suffixes; CONSP (tail); tail = XCDR (tail)) | 1609 | tail = suffixes; |
| 1610 | FOR_EACH_TAIL_SAFE (tail) | ||
| 1615 | { | 1611 | { |
| 1616 | CHECK_STRING_CAR (tail); | 1612 | CHECK_STRING_CAR (tail); |
| 1617 | max_suffix_len = max (max_suffix_len, | 1613 | max_suffix_len = max (max_suffix_len, |
| @@ -1625,12 +1621,17 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, | |||
| 1625 | 1621 | ||
| 1626 | absolute = complete_filename_p (str); | 1622 | absolute = complete_filename_p (str); |
| 1627 | 1623 | ||
| 1624 | AUTO_LIST1 (just_use_str, Qnil); | ||
| 1625 | if (NILP (path)) | ||
| 1626 | path = just_use_str; | ||
| 1627 | |||
| 1628 | /* Go through all entries in the path and see whether we find the | 1628 | /* Go through all entries in the path and see whether we find the |
| 1629 | executable. */ | 1629 | executable. */ |
| 1630 | do { | 1630 | FOR_EACH_TAIL_SAFE (path) |
| 1631 | { | ||
| 1631 | ptrdiff_t baselen, prefixlen; | 1632 | ptrdiff_t baselen, prefixlen; |
| 1632 | 1633 | ||
| 1633 | if (NILP (path)) | 1634 | if (EQ (path, just_use_str)) |
| 1634 | filename = str; | 1635 | filename = str; |
| 1635 | else | 1636 | else |
| 1636 | filename = Fexpand_file_name (str, XCAR (path)); | 1637 | filename = Fexpand_file_name (str, XCAR (path)); |
| @@ -1663,8 +1664,9 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, | |||
| 1663 | memcpy (fn, SDATA (filename) + prefixlen, baselen); | 1664 | memcpy (fn, SDATA (filename) + prefixlen, baselen); |
| 1664 | 1665 | ||
| 1665 | /* Loop over suffixes. */ | 1666 | /* Loop over suffixes. */ |
| 1666 | for (tail = NILP (suffixes) ? list1 (empty_unibyte_string) : suffixes; | 1667 | AUTO_LIST1 (empty_string_only, empty_unibyte_string); |
| 1667 | CONSP (tail); tail = XCDR (tail)) | 1668 | tail = NILP (suffixes) ? empty_string_only : suffixes; |
| 1669 | FOR_EACH_TAIL_SAFE (tail) | ||
| 1668 | { | 1670 | { |
| 1669 | Lisp_Object suffix = XCAR (tail); | 1671 | Lisp_Object suffix = XCAR (tail); |
| 1670 | ptrdiff_t fnlen, lsuffix = SBYTES (suffix); | 1672 | ptrdiff_t fnlen, lsuffix = SBYTES (suffix); |
| @@ -1808,10 +1810,9 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, | |||
| 1808 | } | 1810 | } |
| 1809 | } | 1811 | } |
| 1810 | } | 1812 | } |
| 1811 | if (absolute || NILP (path)) | 1813 | if (absolute) |
| 1812 | break; | 1814 | break; |
| 1813 | path = XCDR (path); | 1815 | } |
| 1814 | } while (CONSP (path)); | ||
| 1815 | 1816 | ||
| 1816 | SAFE_FREE (); | 1817 | SAFE_FREE (); |
| 1817 | errno = last_errno; | 1818 | errno = last_errno; |
| @@ -1838,7 +1839,7 @@ build_load_history (Lisp_Object filename, bool entire) | |||
| 1838 | tail = Vload_history; | 1839 | tail = Vload_history; |
| 1839 | prev = Qnil; | 1840 | prev = Qnil; |
| 1840 | 1841 | ||
| 1841 | while (CONSP (tail)) | 1842 | FOR_EACH_TAIL (tail) |
| 1842 | { | 1843 | { |
| 1843 | tem = XCAR (tail); | 1844 | tem = XCAR (tail); |
| 1844 | 1845 | ||
| @@ -1861,22 +1862,19 @@ build_load_history (Lisp_Object filename, bool entire) | |||
| 1861 | { | 1862 | { |
| 1862 | tem2 = Vcurrent_load_list; | 1863 | tem2 = Vcurrent_load_list; |
| 1863 | 1864 | ||
| 1864 | while (CONSP (tem2)) | 1865 | FOR_EACH_TAIL (tem2) |
| 1865 | { | 1866 | { |
| 1866 | newelt = XCAR (tem2); | 1867 | newelt = XCAR (tem2); |
| 1867 | 1868 | ||
| 1868 | if (NILP (Fmember (newelt, tem))) | 1869 | if (NILP (Fmember (newelt, tem))) |
| 1869 | Fsetcar (tail, Fcons (XCAR (tem), | 1870 | Fsetcar (tail, Fcons (XCAR (tem), |
| 1870 | Fcons (newelt, XCDR (tem)))); | 1871 | Fcons (newelt, XCDR (tem)))); |
| 1871 | |||
| 1872 | tem2 = XCDR (tem2); | ||
| 1873 | maybe_quit (); | 1872 | maybe_quit (); |
| 1874 | } | 1873 | } |
| 1875 | } | 1874 | } |
| 1876 | } | 1875 | } |
| 1877 | else | 1876 | else |
| 1878 | prev = tail; | 1877 | prev = tail; |
| 1879 | tail = XCDR (tail); | ||
| 1880 | maybe_quit (); | 1878 | maybe_quit (); |
| 1881 | } | 1879 | } |
| 1882 | 1880 | ||
| @@ -1918,10 +1916,9 @@ readevalloop_eager_expand_eval (Lisp_Object val, Lisp_Object macroexpand) | |||
| 1918 | if (EQ (CAR_SAFE (val), Qprogn)) | 1916 | if (EQ (CAR_SAFE (val), Qprogn)) |
| 1919 | { | 1917 | { |
| 1920 | Lisp_Object subforms = XCDR (val); | 1918 | Lisp_Object subforms = XCDR (val); |
| 1921 | 1919 | val = Qnil; | |
| 1922 | for (val = Qnil; CONSP (subforms); subforms = XCDR (subforms)) | 1920 | FOR_EACH_TAIL (subforms) |
| 1923 | val = readevalloop_eager_expand_eval (XCAR (subforms), | 1921 | val = readevalloop_eager_expand_eval (XCAR (subforms), macroexpand); |
| 1924 | macroexpand); | ||
| 1925 | } | 1922 | } |
| 1926 | else | 1923 | else |
| 1927 | val = eval_sub (call2 (macroexpand, val, Qt)); | 1924 | val = eval_sub (call2 (macroexpand, val, Qt)); |
| @@ -2861,16 +2858,19 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) | |||
| 2861 | /* Now use params to make a new hash table and fill it. */ | 2858 | /* Now use params to make a new hash table and fill it. */ |
| 2862 | ht = Fmake_hash_table (param_count, params); | 2859 | ht = Fmake_hash_table (param_count, params); |
| 2863 | 2860 | ||
| 2864 | while (CONSP (data)) | 2861 | Lisp_Object last = data; |
| 2865 | { | 2862 | FOR_EACH_TAIL_SAFE (data) |
| 2863 | { | ||
| 2866 | key = XCAR (data); | 2864 | key = XCAR (data); |
| 2867 | data = XCDR (data); | 2865 | data = XCDR (data); |
| 2868 | if (!CONSP (data)) | 2866 | if (!CONSP (data)) |
| 2869 | error ("Odd number of elements in hash table data"); | 2867 | break; |
| 2870 | val = XCAR (data); | 2868 | val = XCAR (data); |
| 2871 | data = XCDR (data); | 2869 | last = XCDR (data); |
| 2872 | Fputhash (key, val, ht); | 2870 | Fputhash (key, val, ht); |
| 2873 | } | 2871 | } |
| 2872 | if (!NILP (last)) | ||
| 2873 | error ("Hash table data is not a list of even length"); | ||
| 2874 | 2874 | ||
| 2875 | return ht; | 2875 | return ht; |
| 2876 | } | 2876 | } |
diff --git a/test/src/lread-tests.el b/test/src/lread-tests.el index 82b75b195ca..ba5bfe0145d 100644 --- a/test/src/lread-tests.el +++ b/test/src/lread-tests.el | |||
| @@ -220,4 +220,7 @@ literals (Bug#20852)." | |||
| 220 | (* most-positive-fixnum most-positive-fixnum))) | 220 | (* most-positive-fixnum most-positive-fixnum))) |
| 221 | (should (= n (string-to-number (format "%d." n)))))) | 221 | (should (= n (string-to-number (format "%d." n)))))) |
| 222 | 222 | ||
| 223 | (ert-deftest lread-circular-hash () | ||
| 224 | (should-error (read "#s(hash-table data #0=(#0# . #0#))"))) | ||
| 225 | |||
| 223 | ;;; lread-tests.el ends here | 226 | ;;; lread-tests.el ends here |