mirror of
https://github.com/opencontainers/runc.git
synced 2026-11-06 05:17:05 +00:00
Do not catch SIGTTIN and SIGTTOU
When runc run or runc exec (without -d) is run as a background job of
a shell with job control, reading from (or writing to) the terminal
results in the kernel sending SIGTTIN (or SIGTTOU) to runc. The default
action for these signals is to stop the process, which is the expected
job control behavior.
Yet, runc catches all signals in order to forward them to the container.
If runc catches SIGTTIN, it forwards it to the container process, and
then the interrupted read is retried, resulting in another SIGTTIN, and
so on, so runc ends up in a busy loop.
Since commit d92dd226 the signal handler is set up in a separate
goroutine, so this only happens occasionally (when the signal handler
is set up before runc starts reading stdin); before that, it happened
every time.
SIGTTIN and SIGTTOU are job control signals of the host terminal, and
there is no point in forwarding those to the container. Exclude them
from the list of signals to catch, so the default action is used.
Note that signal.Reset can not be used to restore the default action,
since the Go runtime keeps its own handler installed for these signals
(which ignores them, resulting in the same busy loop).
Add an integration test.
Fixes #1430.
Assisted-by: Claude Opus 5.5
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
1 parent
c886e96a8f
commit
4ce6916007
2 files changed
+27
No files matched your search
@@ -37,6 +37,8 @@ func newSignalHandler() chan *signalHandler {
|
||||
switch sig := unix.Signal(i); sig {
|
||||
case unix.SIGCHLD:
|
||||
// Already set up above.
|
||||
case unix.SIGTTIN, unix.SIGTTOU:
|
||||
// Job control: use the default action (stop), see #1430.
|
||||
case unix.SIGURG:
|
||||
// SIGURG is used by the Go runtime for asynchronous
|
||||
// preemption, so runc receives it from time to time,
|
||||
|
||||
@@ -246,3 +246,28 @@ EOF
|
||||
# can not be removed, and the next test to use this name would fail.
|
||||
refute_output --partial "unable to destroy container"
|
||||
}
|
||||
|
||||
@test "runc run [SIGTTIN and SIGTTOU are not caught]" {
|
||||
update_config '.process.terminal = false | .process.args = ["sleep", "infinity"]'
|
||||
|
||||
runc run test_busybox </dev/null &
|
||||
local pid=$!
|
||||
wait_for_container 10 0.5 test_busybox running
|
||||
|
||||
# Make sure runc has set up the signal forwarding (SIGTERM is caught).
|
||||
sig_caught() {
|
||||
local cgt
|
||||
cgt=$(awk '/^SigCgt:/ {print $2}' "/proc/$pid/status")
|
||||
((0x$cgt >> ($1 - 1) & 1))
|
||||
}
|
||||
retry 10 0.5 sig_caught 15 # SIGTERM
|
||||
|
||||
# SIGTTIN and SIGTTOU should not be caught (and forwarded to the
|
||||
# container), so runc in a background process group is stopped
|
||||
# when accessing the terminal, see #1430.
|
||||
run ! sig_caught 21 # SIGTTIN
|
||||
run ! sig_caught 22 # SIGTTOU
|
||||
|
||||
runc kill test_busybox KILL
|
||||
wait "$pid" || true
|
||||
}
|
||||
Reference in new issue
Block a user