Mercurial > hg > xemacs-beta
changeset 5241:d579d76f3dcc
Be more careful about side-effects from Lisp code, #'reduce
src/ChangeLog addition:
2010-07-24 Aidan Kehoe <kehoea@parhasard.net>
* lisp.h (PARSE_KEYWORDS):
Always accept a nil :allow-other-keys keyword argument, as
described in the ALLOW-OTHER-KEYS-NIL Common Lisp issue writeup,
and as necessary for Paul Dietz' tests for #'reduce.
* fns.c (mapping_interaction_error): New.
(Freduce): Call mapping_interaction_error when KEY or FUNCTION
have modified a string SEQUENCE such that the byte length of the
string has changed, or such that the current cursor pointer
doesn't point to the beginning of a character.
Cf. the MAPPING-DESTRUCTIVE-INTERACTION Common Lisp issue
writeup.
When traversing a list, GCPRO the part of it we still have to
traverse, to avoid any crashes if FUNCTION or KEY amputate it
behind us and force a garbage collection.
tests/ChangeLog addition:
2010-07-24 Aidan Kehoe <kehoea@parhasard.net>
* automated/lisp-tests.el:
Test a couple of things #'reduce was just made more careful
about.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Sat, 24 Jul 2010 15:56:57 +0100 |
parents | fca0cf0971de |
children | f3eca926258e |
files | src/ChangeLog src/fns.c src/lisp.h tests/ChangeLog tests/automated/lisp-tests.el |
diffstat | 5 files changed, 121 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/src/ChangeLog Tue Jul 13 10:20:22 2010 +0200 +++ b/src/ChangeLog Sat Jul 24 15:56:57 2010 +0100 @@ -1,3 +1,21 @@ +2010-07-24 Aidan Kehoe <kehoea@parhasard.net> + + * lisp.h (PARSE_KEYWORDS): + Always accept a nil :allow-other-keys keyword argument, as + described in the ALLOW-OTHER-KEYS-NIL Common Lisp issue writeup, + and as necessary for Paul Dietz' tests for #'reduce. + + * fns.c (mapping_interaction_error): New. + (Freduce): Call mapping_interaction_error when KEY or FUNCTION + have modified a string SEQUENCE such that the byte length of the + string has changed, or such that the current cursor pointer + doesn't point to the beginning of a character. + Cf. the MAPPING-DESTRUCTIVE-INTERACTION Common Lisp issue + writeup. + When traversing a list, GCPRO the part of it we still have to + traverse, to avoid any crashes if FUNCTION or KEY amputate it + behind us and force a garbage collection. + 2010-06-05 Marcus Crestani <crestani@informatik.uni-tuebingen.de> * gc.c:
--- a/src/fns.c Tue Jul 13 10:20:22 2010 +0200 +++ b/src/fns.c Sat Jul 24 15:56:57 2010 +0100 @@ -65,6 +65,12 @@ static int internal_old_equal (Lisp_Object, Lisp_Object, int); Lisp_Object safe_copy_tree (Lisp_Object arg, Lisp_Object vecp, int depth); +static DOESNT_RETURN +mapping_interaction_error (Lisp_Object func, Lisp_Object object) +{ + invalid_state_2 ("object modified while traversing it", func, object); +} + static Lisp_Object mark_bit_vector (Lisp_Object UNUSED (obj)) { @@ -4995,21 +5001,31 @@ starting++; startp = XSTRING_DATA (sequence); cursor = startp + cursor_offset; + + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qreduce, sequence); + } + INC_IBYTEPTR (cursor); cursor_offset = cursor - startp; } while (cursor_offset < byte_len && starting < ending) { - if (cursor_offset > XSTRING_LENGTH (sequence)) + accum = call2 (function, accum, + KEY (key, make_char (itext_ichar (cursor)))); + + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) { - invalid_state ("sequence modified during reduce", sequence); + mapping_interaction_error (Qreduce, sequence); } - startp = XSTRING_DATA (sequence); - cursor = startp + cursor_offset; - accum = call2 (function, accum, - KEY (key, make_char (itext_ichar (cursor)))); INC_IBYTEPTR (cursor); cursor_offset = cursor - startp; ++starting; @@ -5018,7 +5034,7 @@ else { Elemcount len = string_char_length (sequence); - Bytecount cursor_offset; + Bytecount cursor_offset, byte_len = XSTRING_LENGTH (sequence); const Ibyte *cursor; ending = min (ending, len); @@ -5035,6 +5051,13 @@ ending--; if (ending > 0) { + cursor = XSTRING_DATA (sequence) + cursor_offset; + + if (!valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qreduce, sequence); + } + DEC_IBYTEPTR (cursor); cursor_offset = cursor - XSTRING_DATA (sequence); } @@ -5042,18 +5065,19 @@ for (ii = ending - 1; ii >= starting; --ii) { - if (cursor_offset > XSTRING_LENGTH (sequence)) - { - invalid_state ("sequence modified during reduce", sequence); - } - - cursor = XSTRING_DATA (sequence) + cursor_offset; accum = call2 (function, KEY (key, make_char (itext_ichar (cursor))), accum); - if (ii > 1) + if (ii > 0) { cursor = XSTRING_DATA (sequence) + cursor_offset; + + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qreduce, sequence); + } + DEC_IBYTEPTR (cursor); cursor_offset = cursor - XSTRING_DATA (sequence); } @@ -5064,6 +5088,11 @@ { if (NILP (from_end)) { + struct gcpro gcpro1; + Lisp_Object tailed = Qnil; + + GCPRO1 (tailed); + if (!UNBOUNDP (initial_value)) { accum = initial_value; @@ -5073,6 +5102,9 @@ Elemcount counting = 0; EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) { + /* KEY may amputate the list behind us; make sure what + remains to be processed is still reachable. */ + tailed = tail; if (counting == starting) { accum = KEY (key, elt); @@ -5089,6 +5121,10 @@ EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) { + /* KEY or FUNCTION may amputate the list behind us; make + sure what remains to be processed is still + reachable. */ + tailed = tail; if (counting >= starting) { if (counting < ending) @@ -5103,6 +5139,8 @@ ++counting; } } + + UNGCPRO; } else {
--- a/src/lisp.h Tue Jul 13 10:20:22 2010 +0200 +++ b/src/lisp.h Sat Jul 24 15:56:57 2010 +0100 @@ -3577,9 +3577,18 @@ { \ continue; \ } \ - else if (!(pk_allow_other_keys \ - = non_nil_allow_other_keys_p (keywords_offset, \ - nargs, args))) \ + else if ((pk_allow_other_keys \ + = non_nil_allow_other_keys_p (keywords_offset, \ + nargs, args))) \ + { \ + continue; \ + } \ + else if (EQ (pk_key, Q_allow_other_keys) && \ + NILP (pk_value)) \ + { \ + continue; \ + } \ + else \ { \ invalid_keyword_argument (function, pk_key); \ } \
--- a/tests/ChangeLog Tue Jul 13 10:20:22 2010 +0200 +++ b/tests/ChangeLog Sat Jul 24 15:56:57 2010 +0100 @@ -1,3 +1,9 @@ +2010-07-24 Aidan Kehoe <kehoea@parhasard.net> + + * automated/lisp-tests.el: + Test a couple of things #'reduce was just made more careful + about. + 2010-06-13 Stephen J. Turnbull <stephen@xemacs.org> * gtk/event-stream-tests.el:
--- a/tests/automated/lisp-tests.el Tue Jul 13 10:20:22 2010 +0200 +++ b/tests/automated/lisp-tests.el Sat Jul 24 15:56:57 2010 +0100 @@ -2341,4 +2341,37 @@ (gethash hashed-bignum hashing)) "checking hashing works correctly with #'eql tests and bignums")))) +;; +(when (decode-char 'ucs #x0192) + (Check-Error + invalid-state + (let ((str "aaaaaaaaaaaaa") + (called 0) + modified) + (reduce #'+ str + :key #'(lambda (object) + (prog1 + object + (incf called) + (or modified + (and (> called 5) + (setq modified + (fill str (read #r"?\u0192"))))))))))) + +(Assert + (eql 55 + (let ((sequence '(1 2 3 4 5 6 7 8 9 10)) + (called 0) + modified) + (reduce #'+ + sequence + :key + #'(lambda (object) (prog1 + object + (incf called) + (and (eql called 5) + (setcdr (nthcdr 3 sequence) nil)) + (garbage-collect)))))) + "checking we can amputate lists without crashing #'reduce") + ;;; end of lisp-tests.el