Subject: Re: bug#46586: 26.3, 27.1.50; Emacs crash in a backtrace (core) dump (a long standing issue)

From: Eli Zaretskii <[email-masked]>
Date: Thu, 18 Feb 2021 16:16:23 +0200

> From: 路客 <[email-masked]>
> Date: Thu, 18 Feb 2021 09:56:06 +0800
> Cc: [email-masked]
> 
> > It's an infinite recursion in substitute_object_recurse, called by
> > lread--substitute-object-in-subtree.
> 
> I see, but why is Emacs 26.0.50 or earlier able to catch this issue?

The related code was refactored since then.  (And I'm not sure Emacs
26.0.50 indeed identified the problem correctly, see below.  So it
could be just sheer luck that it didn't crash back then.)

> Shouldn't the read() function try to prevent itself from crashing?

It should, so this is a bug.

But how did such a form get originated?  It looks like it's indeed
self-referential, and thus is got to trigger infinite recursion:

> (#1=(#("000008964 .gnus.el" 0 18 (r #1#))
> (def #2=#("000008964 .gnus.el" 0 18
> (r
> (#2#
> (def #3=#("000006393 .gnus.el" 0 18
> (r #4=(#3#
> (def
> #("000006393 .gnus.el" 0 18 (r #4#)) "x"))))"x"))))"x")))

The last part references itself: it seems to define a string with a
text property that is the same string.

Stepping through the code in substitute_object_recurse, I see that we
end up recursively expanding this string:

  #("000006393 .gnus.el" 0 18 (r (#("000006393 .gnus.el" 0 18 (r #2)) (def #0 "x"))))

Which then yields this:

  #("000006393 .gnus.el" 0 18 (r (#0 (def #("000006393 .gnus.el" 0 18 (r #2)) "x"))))

And that again yields

  #("000006393 .gnus.el" 0 18 (r (#("000006393 .gnus.el" 0 18 (r #2)) (def #0 "x"))))

Etc. etc., ad nauseam (or, rather, until we exhaust the C run-time
stack and segfault).

Does anyone see how to stop this infinite recursion, except by
counting recursive invocation levels and bailing out at some arbitrary
depth?