Skip to content

Fix _handle_signals TypeError when SIGTERM is delivered to a forked child - #70123

Open
dwoz wants to merge 2 commits into
saltstack:3006.xfrom
dwoz:dwoz/fix/handle-signals-default-int-handler-typeerror
Open

Fix _handle_signals TypeError when SIGTERM is delivered to a forked child#70123
dwoz wants to merge 2 commits into
saltstack:3006.xfrom
dwoz:dwoz/fix/handle-signals-default-int-handler-typeerror

Conversation

@dwoz

@dwoz dwoz commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a decade-old bug in salt.utils.process.ProcessManager._handle_signals:

return signal.default_int_handler(signal.SIGTERM)(*args)

signal.default_int_handler requires (signum, frame). The buggy line calls it with a single positional arg, catches the resulting TypeError nowhere, and kills the receiving process with an unhandled exception instead of the intended clean shutdown.

Traceback observed in the wild on Salt 3008.1 when SIGTERM was delivered to the master:

File "salt/utils/process.py", line 817, in _handle_signals
    return signal.default_int_handler(signal.SIGTERM)(*args)
TypeError: default_int_handler expected 2 arguments, got 1

MasterPubServerChannel._publish_daemon crashed and ProcessManager did NOT respawn it.

Why is the elif arm reachable?

signal.getsignal(SIGTERM) returns signal.Handlers.SIG_DFL (an enum, not callable and not None) in a fresh interpreter, so the first two arms of the if fall through to the buggy one.

Fix

Pass through the received (signum, frame) tuple. default_int_handler raises KeyboardInterrupt regardless of signum, matching the original intent.

Test

tests/pytests/functional/utils/test_process.py::test_handle_signals_default_int_handler_typeerror forces the child branch, sets _sigterm_handler to SIG_DFL, and asserts _handle_signals raises KeyboardInterrupt (was TypeError before this patch).

Backporting: bug also present on 3007.x, 3008.x, master; will forward-port via merge-forwards.

signal.default_int_handler(signal.SIGTERM)(*args) called
default_int_handler with a single positional argument (it requires
(signum, frame)), raised TypeError, and killed the receiving process
with an unhandled exception instead of triggering the intended
KeyboardInterrupt-based clean shutdown.

Observed in the wild on 3008.1's MasterPubServerChannel._publish_daemon
when SIGTERM was delivered to the master. The daemon crashed and
ProcessManager did not respawn it.

The elif arm is reachable in the common case: signal.getsignal(SIGTERM)
returns signal.Handlers.SIG_DFL, which is not callable and not None, so
the first two arms fall through to the buggy call.

Pass through the received (signum, frame) tuple; default_int_handler
raises KeyboardInterrupt regardless of signum, matching the intent.
@dwoz
dwoz requested a review from a team as a code owner August 23, 2026 04:29
@dwoz dwoz added the test:full Run the full test suite label Aug 23, 2026
@dwoz dwoz added this to the Sulphur v3006.28 milestone Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:full Run the full test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant