fully implement documented $SIG{__WARN/DIE__} behavior #22995
+81
−34
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The documentation for %SIG (in perlvar) states:
This has never really been true.
There were two basic checks to prevent infinite recursion from a
__DIE__
or__WARN__
handler:$SIG{__DIE__}
references a subroutine that is currently active (somewhere on the call stack at the point of the exception), then die() unwinds the stack directly, bypassing the handler. (The same applies mutatis mutandis to$SIG{__WARN__}
/warn().)This behavior is wrong because the subroutine may have been invoked normally first (i.e. not via the %SIG machinery), so the handler should still kick in. This is bug GH $SIG{__DIE__} handler doesn't work when called explicitly #22984.
It also causes issues if the subroutine transfers control "sideways" via
goto &othersub
because then the registered handler is no longer considered "active" even though Perl code is still executing in the context of a__DIE__
/__WARN__
handler. Then, if the goto'd &othersub triggers a warning/exception, the__DIE__
/__WARN__
handler will be invoked recursively, eventually leading to a C stack overflow. This is bug GH Infinite recursion (+segfault) on die() after goto-ing out of __DIE__ handler #14527.$SIG{__WARN__}
(since c5be5b4) and$SIG{__DIE__}
(since 8b4094f) mitigates the latter issue by internally unsetting the DIE/WARN hooks for the duration of the handler call.Unfortunately, this is not a complete fix because any modification of
$SIG{__DIE__}
/$SIG{__WARN__}
within the handler, even seeming no-ops such as$SIG{__DIE__} = $SIG{__DIE__}
or{ local $SIG{__DIE__}; }
, will reïnstate the internal hooks, thus reärming the__DIE__
/__WARN__
handlers. This is bug GH Infinite recursion (+segfault) on warn() after localizing and goto-ing out of __WARN__ handler #22987.This patch adds two interpreter-global variables that record whether we are currently executing a
__DIE__
/__WARN__
handler. This fully replaces the old heuristics by a precise check that prevents recursive handler invocation and nothing more.Exporter::Heavy had to be patched because it relied on the old (buggy) behavior: It registered a
$SIG{__WARN__}
handler that would reässign$SIG{__WARN__}
and then call warn(), expecting the new handler to be called (i.e. two (nested) warn hooks to be active simultaneously). This is no longer possible with the new implementation.Fixes #22984, #22987.