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

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

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

Eli Zaretskii
> From: 路客 <[hidden email]>
> Date: Wed, 17 Feb 2021 18:13:56 +0800
>
> ----- code begin -----
> (#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")))
> ----- code end -----
>
> Try to `read' or `eval' this block of code (C-x C-e) will immediately
> crash Emacs 26.3 or 27.1.50; however, older Emacs 26.0.50 works well
> by entering the debugger with an error like:

It's an infinite recursion in substitute_object_recurse, called by
lread--substitute-object-in-subtree.



Reply | Threaded
Open this post in threaded view
|

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

路客
> 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?
Shouldn't the read() function try to prevent itself from crashing?



Reply | Threaded
Open this post in threaded view
|

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

Eli Zaretskii
> From: 路客 <[hidden email]>
> Date: Thu, 18 Feb 2021 09:56:06 +0800
> Cc: [hidden email]
>
> > 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?



Reply | Threaded
Open this post in threaded view
|

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

Eli Zaretskii
> From: Andreas Schwab <[hidden email]>
> Cc: 路客 <[hidden email]>,  [hidden email],
>   Stefan Monnier
>  <[hidden email]>
> Date: Thu, 18 Feb 2021 15:45:01 +0100
>
> On Feb 18 2021, Eli Zaretskii wrote:
>
> > Does anyone see how to stop this infinite recursion, except by
> > counting recursive invocation levels and bailing out at some arbitrary
> > depth?
>
> Shouldn't the subst->seen list prevent recursion?

Maybe, but the insertion into the 'seen' list is conditioned on #n=
being in 'completed'.  Or maybe I don't understand the logic of the
code well enough.