aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2017-07-09 16:04:02 -0700
committerPaul Eggert2017-07-09 16:05:13 -0700
commit083940a93df17c6e50d6523e30d56ca3d179f688 (patch)
tree7192d741b6d66943c4f4fd38436aaf8960e6299a /src
parentce6773aad5c71f6c486244a6fc9fcb69fc99784d (diff)
downloademacs-083940a93df17c6e50d6523e30d56ca3d179f688.tar.gz
emacs-083940a93df17c6e50d6523e30d56ca3d179f688.zip
Fix core dump in substitute-object-in-subtree
Without this fix, (substitute-object-in-subtree #0=(#0# 'a) 'a) would dump core, since the C code would recurse indefinitely through the infinite structure. This patch adds an argument to the function, and renames it to lread--substitute-object-in-subtree as the function is not general-purpose and should not be relied on by outside code. See Bug#23660. * src/intervals.c (traverse_intervals_noorder): ARG is now void *, not Lisp_Object, so that callers need not cons unnecessarily. All callers changed. Also, remove related #if-0 code that was “temporary” in the early 1990s and has not been compilable for some time. * src/lread.c (struct subst): New type, for substitution closure data. (seen_list): Remove this static var, as this info is now part of struct subst. All uses removed. (Flread__substitute_object_in_subtree): Rename from Fsubstitute_object_in_subtree, and give it a 3rd arg so that it doesn’t dump core when called from the top level with an already-cyclic structure. All callers changed. (SUBSTITUTE): Remove. All callers expanded and then simplified. (substitute_object_recurse): Take a single argument SUBST rather than a pair OBJECT and PLACEHOLDER, so that its address can be passed around as part of a closure; this avoids the need for an AUTO_CONS call. All callers changed. If the COMPLETED component is t, treat every subobject as potentially circular. (substitute_in_interval): Take a struct subst * rather than a Lisp_Object, for the closure data. All callers changed. * test/src/lread-tests.el (lread-lread--substitute-object-in-subtree): New test, to check that the core dump does not reoccur.
Diffstat (limited to 'src')
-rw-r--r--src/alloc.c4
-rw-r--r--src/intervals.c66
-rw-r--r--src/intervals.h3
-rw-r--r--src/lread.c110
-rw-r--r--src/print.c6
5 files changed, 53 insertions, 136 deletions
diff --git a/src/alloc.c b/src/alloc.c
index ac3de83b2b6..2d785d5b9a4 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -1553,7 +1553,7 @@ make_interval (void)
1553/* Mark Lisp objects in interval I. */ 1553/* Mark Lisp objects in interval I. */
1554 1554
1555static void 1555static void
1556mark_interval (register INTERVAL i, Lisp_Object dummy) 1556mark_interval (INTERVAL i, void *dummy)
1557{ 1557{
1558 /* Intervals should never be shared. So, if extra internal checking is 1558 /* Intervals should never be shared. So, if extra internal checking is
1559 enabled, GC aborts if it seems to have visited an interval twice. */ 1559 enabled, GC aborts if it seems to have visited an interval twice. */
@@ -1567,7 +1567,7 @@ mark_interval (register INTERVAL i, Lisp_Object dummy)
1567#define MARK_INTERVAL_TREE(i) \ 1567#define MARK_INTERVAL_TREE(i) \
1568 do { \ 1568 do { \
1569 if (i && !i->gcmarkbit) \ 1569 if (i && !i->gcmarkbit) \
1570 traverse_intervals_noorder (i, mark_interval, Qnil); \ 1570 traverse_intervals_noorder (i, mark_interval, NULL); \
1571 } while (0) 1571 } while (0)
1572 1572
1573/*********************************************************************** 1573/***********************************************************************
diff --git a/src/intervals.c b/src/intervals.c
index d17d80ac865..0089ecb8dde 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -224,7 +224,8 @@ intervals_equal (INTERVAL i0, INTERVAL i1)
224 Pass FUNCTION two args: an interval, and ARG. */ 224 Pass FUNCTION two args: an interval, and ARG. */
225 225
226void 226void
227traverse_intervals_noorder (INTERVAL tree, void (*function) (INTERVAL, Lisp_Object), Lisp_Object arg) 227traverse_intervals_noorder (INTERVAL tree, void (*function) (INTERVAL, void *),
228 void *arg)
228{ 229{
229 /* Minimize stack usage. */ 230 /* Minimize stack usage. */
230 while (tree) 231 while (tree)
@@ -257,69 +258,6 @@ traverse_intervals (INTERVAL tree, ptrdiff_t position,
257 } 258 }
258} 259}
259 260
260#if 0
261
262static int icount;
263static int idepth;
264static int zero_length;
265
266/* These functions are temporary, for debugging purposes only. */
267
268INTERVAL search_interval, found_interval;
269
270void
271check_for_interval (INTERVAL i)
272{
273 if (i == search_interval)
274 {
275 found_interval = i;
276 icount++;
277 }
278}
279
280INTERVAL
281search_for_interval (INTERVAL i, INTERVAL tree)
282{
283 icount = 0;
284 search_interval = i;
285 found_interval = NULL;
286 traverse_intervals_noorder (tree, &check_for_interval, Qnil);
287 return found_interval;
288}
289
290static void
291inc_interval_count (INTERVAL i)
292{
293 icount++;
294 if (LENGTH (i) == 0)
295 zero_length++;
296 if (depth > idepth)
297 idepth = depth;
298}
299
300int
301count_intervals (INTERVAL i)
302{
303 icount = 0;
304 idepth = 0;
305 zero_length = 0;
306 traverse_intervals_noorder (i, &inc_interval_count, Qnil);
307
308 return icount;
309}
310
311static INTERVAL
312root_interval (INTERVAL interval)
313{
314 register INTERVAL i = interval;
315
316 while (! ROOT_INTERVAL_P (i))
317 i = INTERVAL_PARENT (i);
318
319 return i;
320}
321#endif
322
323/* Assuming that a left child exists, perform the following operation: 261/* Assuming that a left child exists, perform the following operation:
324 262
325 A B 263 A B
diff --git a/src/intervals.h b/src/intervals.h
index a0da6f37801..9140e0c17ab 100644
--- a/src/intervals.h
+++ b/src/intervals.h
@@ -242,8 +242,7 @@ extern void traverse_intervals (INTERVAL, ptrdiff_t,
242 void (*) (INTERVAL, Lisp_Object), 242 void (*) (INTERVAL, Lisp_Object),
243 Lisp_Object); 243 Lisp_Object);
244extern void traverse_intervals_noorder (INTERVAL, 244extern void traverse_intervals_noorder (INTERVAL,
245 void (*) (INTERVAL, Lisp_Object), 245 void (*) (INTERVAL, void *), void *);
246 Lisp_Object);
247extern INTERVAL split_interval_right (INTERVAL, ptrdiff_t); 246extern INTERVAL split_interval_right (INTERVAL, ptrdiff_t);
248extern INTERVAL split_interval_left (INTERVAL, ptrdiff_t); 247extern INTERVAL split_interval_left (INTERVAL, ptrdiff_t);
249extern INTERVAL find_interval (INTERVAL, ptrdiff_t); 248extern INTERVAL find_interval (INTERVAL, ptrdiff_t);
diff --git a/src/lread.c b/src/lread.c
index 8e7cd3c5510..4d1a27d1c1d 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -595,6 +595,20 @@ read_emacs_mule_char (int c, int (*readbyte) (int, Lisp_Object), Lisp_Object rea
595} 595}
596 596
597 597
598/* An in-progress substitution of OBJECT for PLACEHOLDER. */
599struct subst
600{
601 Lisp_Object object;
602 Lisp_Object placeholder;
603
604 /* Hash table of subobjects of OBJECT that might be circular. If
605 Qt, all such objects might be circular. */
606 Lisp_Object completed;
607
608 /* List of subobjects of OBJECT that have already been visited. */
609 Lisp_Object seen;
610};
611
598static Lisp_Object read_internal_start (Lisp_Object, Lisp_Object, 612static Lisp_Object read_internal_start (Lisp_Object, Lisp_Object,
599 Lisp_Object); 613 Lisp_Object);
600static Lisp_Object read0 (Lisp_Object); 614static Lisp_Object read0 (Lisp_Object);
@@ -603,9 +617,8 @@ static Lisp_Object read1 (Lisp_Object, int *, bool);
603static Lisp_Object read_list (bool, Lisp_Object); 617static Lisp_Object read_list (bool, Lisp_Object);
604static Lisp_Object read_vector (Lisp_Object, bool); 618static Lisp_Object read_vector (Lisp_Object, bool);
605 619
606static Lisp_Object substitute_object_recurse (Lisp_Object, Lisp_Object, 620static Lisp_Object substitute_object_recurse (struct subst *, Lisp_Object);
607 Lisp_Object); 621static void substitute_in_interval (INTERVAL, void *);
608static void substitute_in_interval (INTERVAL, Lisp_Object);
609 622
610 623
611/* Get a character from the tty. */ 624/* Get a character from the tty. */
@@ -3107,7 +3120,8 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
3107 } 3120 }
3108 else 3121 else
3109 { 3122 {
3110 Fsubstitute_object_in_subtree (tem, placeholder); 3123 Flread__substitute_object_in_subtree
3124 (tem, placeholder, read_objects_completed);
3111 3125
3112 /* ...and #n# will use the real value from now on. */ 3126 /* ...and #n# will use the real value from now on. */
3113 i = hash_lookup (h, number, &hash); 3127 i = hash_lookup (h, number, &hash);
@@ -3513,26 +3527,16 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
3513 } 3527 }
3514} 3528}
3515 3529
3516 3530DEFUN ("lread--substitute-object-in-subtree",
3517/* List of nodes we've seen during substitute_object_in_subtree. */ 3531 Flread__substitute_object_in_subtree,
3518static Lisp_Object seen_list; 3532 Slread__substitute_object_in_subtree, 3, 3, 0,
3519 3533 doc: /* In OBJECT, replace every occurrence of PLACEHOLDER with OBJECT.
3520DEFUN ("substitute-object-in-subtree", Fsubstitute_object_in_subtree, 3534COMPLETED is a hash table of objects that might be circular, or is t
3521 Ssubstitute_object_in_subtree, 2, 2, 0, 3535if any object might be circular. */)
3522 doc: /* Replace every reference to PLACEHOLDER in OBJECT with OBJECT. */) 3536 (Lisp_Object object, Lisp_Object placeholder, Lisp_Object completed)
3523 (Lisp_Object object, Lisp_Object placeholder)
3524{ 3537{
3525 Lisp_Object check_object; 3538 struct subst subst = { object, placeholder, completed, Qnil };
3526 3539 Lisp_Object check_object = substitute_object_recurse (&subst, object);
3527 /* We haven't seen any objects when we start. */
3528 seen_list = Qnil;
3529
3530 /* Make all the substitutions. */
3531 check_object
3532 = substitute_object_recurse (object, placeholder, object);
3533
3534 /* Clear seen_list because we're done with it. */
3535 seen_list = Qnil;
3536 3540
3537 /* The returned object here is expected to always eq the 3541 /* The returned object here is expected to always eq the
3538 original. */ 3542 original. */
@@ -3541,26 +3545,12 @@ DEFUN ("substitute-object-in-subtree", Fsubstitute_object_in_subtree,
3541 return Qnil; 3545 return Qnil;
3542} 3546}
3543 3547
3544/* Feval doesn't get called from here, so no gc protection is needed. */
3545#define SUBSTITUTE(get_val, set_val) \
3546 do { \
3547 Lisp_Object old_value = get_val; \
3548 Lisp_Object true_value \
3549 = substitute_object_recurse (object, placeholder, \
3550 old_value); \
3551 \
3552 if (!EQ (old_value, true_value)) \
3553 { \
3554 set_val; \
3555 } \
3556 } while (0)
3557
3558static Lisp_Object 3548static Lisp_Object
3559substitute_object_recurse (Lisp_Object object, Lisp_Object placeholder, Lisp_Object subtree) 3549substitute_object_recurse (struct subst *subst, Lisp_Object subtree)
3560{ 3550{
3561 /* If we find the placeholder, return the target object. */ 3551 /* If we find the placeholder, return the target object. */
3562 if (EQ (placeholder, subtree)) 3552 if (EQ (subst->placeholder, subtree))
3563 return object; 3553 return subst->object;
3564 3554
3565 /* For common object types that can't contain other objects, don't 3555 /* For common object types that can't contain other objects, don't
3566 bother looking them up; we're done. */ 3556 bother looking them up; we're done. */
@@ -3570,15 +3560,16 @@ substitute_object_recurse (Lisp_Object object, Lisp_Object placeholder, Lisp_Obj
3570 return subtree; 3560 return subtree;
3571 3561
3572 /* If we've been to this node before, don't explore it again. */ 3562 /* If we've been to this node before, don't explore it again. */
3573 if (!EQ (Qnil, Fmemq (subtree, seen_list))) 3563 if (!EQ (Qnil, Fmemq (subtree, subst->seen)))
3574 return subtree; 3564 return subtree;
3575 3565
3576 /* If this node can be the entry point to a cycle, remember that 3566 /* If this node can be the entry point to a cycle, remember that
3577 we've seen it. It can only be such an entry point if it was made 3567 we've seen it. It can only be such an entry point if it was made
3578 by #n=, which means that we can find it as a value in 3568 by #n=, which means that we can find it as a value in
3579 read_objects_completed. */ 3569 COMPLETED. */
3580 if (hash_lookup (XHASH_TABLE (read_objects_completed), subtree, NULL) >= 0) 3570 if (EQ (subst->completed, Qt)
3581 seen_list = Fcons (subtree, seen_list); 3571 || hash_lookup (XHASH_TABLE (subst->completed), subtree, NULL) >= 0)
3572 subst->seen = Fcons (subtree, subst->seen);
3582 3573
3583 /* Recurse according to subtree's type. 3574 /* Recurse according to subtree's type.
3584 Every branch must return a Lisp_Object. */ 3575 Every branch must return a Lisp_Object. */
@@ -3605,19 +3596,15 @@ substitute_object_recurse (Lisp_Object object, Lisp_Object placeholder, Lisp_Obj
3605 if (SUB_CHAR_TABLE_P (subtree)) 3596 if (SUB_CHAR_TABLE_P (subtree))
3606 i = 2; 3597 i = 2;
3607 for ( ; i < length; i++) 3598 for ( ; i < length; i++)
3608 SUBSTITUTE (AREF (subtree, i), 3599 ASET (subtree, i,
3609 ASET (subtree, i, true_value)); 3600 substitute_object_recurse (subst, AREF (subtree, i)));
3610 return subtree; 3601 return subtree;
3611 } 3602 }
3612 3603
3613 case Lisp_Cons: 3604 case Lisp_Cons:
3614 { 3605 XSETCAR (subtree, substitute_object_recurse (subst, XCAR (subtree)));
3615 SUBSTITUTE (XCAR (subtree), 3606 XSETCDR (subtree, substitute_object_recurse (subst, XCDR (subtree)));
3616 XSETCAR (subtree, true_value)); 3607 return subtree;
3617 SUBSTITUTE (XCDR (subtree),
3618 XSETCDR (subtree, true_value));
3619 return subtree;
3620 }
3621 3608
3622 case Lisp_String: 3609 case Lisp_String:
3623 { 3610 {
@@ -3625,11 +3612,8 @@ substitute_object_recurse (Lisp_Object object, Lisp_Object placeholder, Lisp_Obj
3625 substitute_in_interval contains part of the logic. */ 3612 substitute_in_interval contains part of the logic. */
3626 3613
3627 INTERVAL root_interval = string_intervals (subtree); 3614 INTERVAL root_interval = string_intervals (subtree);
3628 AUTO_CONS (arg, object, placeholder);
3629
3630 traverse_intervals_noorder (root_interval, 3615 traverse_intervals_noorder (root_interval,
3631 &substitute_in_interval, arg); 3616 substitute_in_interval, subst);
3632
3633 return subtree; 3617 return subtree;
3634 } 3618 }
3635 3619
@@ -3641,12 +3625,10 @@ substitute_object_recurse (Lisp_Object object, Lisp_Object placeholder, Lisp_Obj
3641 3625
3642/* Helper function for substitute_object_recurse. */ 3626/* Helper function for substitute_object_recurse. */
3643static void 3627static void
3644substitute_in_interval (INTERVAL interval, Lisp_Object arg) 3628substitute_in_interval (INTERVAL interval, void *arg)
3645{ 3629{
3646 Lisp_Object object = Fcar (arg); 3630 set_interval_plist (interval,
3647 Lisp_Object placeholder = Fcdr (arg); 3631 substitute_object_recurse (arg, interval->plist));
3648
3649 SUBSTITUTE (interval->plist, set_interval_plist (interval, true_value));
3650} 3632}
3651 3633
3652 3634
@@ -4744,7 +4726,7 @@ syms_of_lread (void)
4744{ 4726{
4745 defsubr (&Sread); 4727 defsubr (&Sread);
4746 defsubr (&Sread_from_string); 4728 defsubr (&Sread_from_string);
4747 defsubr (&Ssubstitute_object_in_subtree); 4729 defsubr (&Slread__substitute_object_in_subtree);
4748 defsubr (&Sintern); 4730 defsubr (&Sintern);
4749 defsubr (&Sintern_soft); 4731 defsubr (&Sintern_soft);
4750 defsubr (&Sunintern); 4732 defsubr (&Sunintern);
@@ -5057,8 +5039,6 @@ that are loaded before your customizations are read! */);
5057 read_objects_map = Qnil; 5039 read_objects_map = Qnil;
5058 staticpro (&read_objects_completed); 5040 staticpro (&read_objects_completed);
5059 read_objects_completed = Qnil; 5041 read_objects_completed = Qnil;
5060 staticpro (&seen_list);
5061 seen_list = Qnil;
5062 5042
5063 Vloads_in_progress = Qnil; 5043 Vloads_in_progress = Qnil;
5064 staticpro (&Vloads_in_progress); 5044 staticpro (&Vloads_in_progress);
diff --git a/src/print.c b/src/print.c
index 50c75d7712c..b6ea3ff62a5 100644
--- a/src/print.c
+++ b/src/print.c
@@ -566,7 +566,7 @@ temp_output_buffer_setup (const char *bufname)
566 566
567static void print (Lisp_Object, Lisp_Object, bool); 567static void print (Lisp_Object, Lisp_Object, bool);
568static void print_preprocess (Lisp_Object); 568static void print_preprocess (Lisp_Object);
569static void print_preprocess_string (INTERVAL, Lisp_Object); 569static void print_preprocess_string (INTERVAL, void *);
570static void print_object (Lisp_Object, Lisp_Object, bool); 570static void print_object (Lisp_Object, Lisp_Object, bool);
571 571
572DEFUN ("terpri", Fterpri, Sterpri, 0, 2, 0, 572DEFUN ("terpri", Fterpri, Sterpri, 0, 2, 0,
@@ -1214,7 +1214,7 @@ print_preprocess (Lisp_Object obj)
1214 case Lisp_String: 1214 case Lisp_String:
1215 /* A string may have text properties, which can be circular. */ 1215 /* A string may have text properties, which can be circular. */
1216 traverse_intervals_noorder (string_intervals (obj), 1216 traverse_intervals_noorder (string_intervals (obj),
1217 print_preprocess_string, Qnil); 1217 print_preprocess_string, NULL);
1218 break; 1218 break;
1219 1219
1220 case Lisp_Cons: 1220 case Lisp_Cons:
@@ -1263,7 +1263,7 @@ Fills `print-number-table'. */)
1263} 1263}
1264 1264
1265static void 1265static void
1266print_preprocess_string (INTERVAL interval, Lisp_Object arg) 1266print_preprocess_string (INTERVAL interval, void *arg)
1267{ 1267{
1268 print_preprocess (interval->plist); 1268 print_preprocess (interval->plist);
1269} 1269}