aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/testdata
diff options
context:
space:
mode:
authorKir Kolyshkin <kolyshkin@gmail.com>2026-03-24 22:18:47 -0700
committerKirill Kolyshkin <kolyshkin@gmail.com>2026-03-31 12:38:51 -0700
commit974764364aa09a34cad2d74a6b7c52c12a136ea3 (patch)
tree39a8c03db84a8dc82f3ac56f3bef9cbd220d02b5 /src/runtime/testdata
parent2000e27ea6a644ea3623db201d8ba2818e8f5838 (diff)
downloadgo-974764364aa09a34cad2d74a6b7c52c12a136ea3.tar.xz
runtime: unix: sane exit in dieFromSignal for pid 1
A curious bug was reported to kubernetes[1] and runc[2] recently: sometimes runc init reports exit status of 2. Turns out, Go runtime assumes that on any UNIX system signals such as SIGTERM (or any other that has _sigKill flag set in sigtable) with no signal handler set up, will result in kernel terminating the program. This is true, except for PID 1 which gets a custom treatment from the kernel. As a result, when a Go program that runs as PID 1 (which is easy to achieve in Linux by using a new PID namespace) receives such a signal, Go runtime calls dieFromSignal which falls through all the way to exit(2), which is very confusing to a user. This issue can be worked around by the program by adding custom handlers for SIGTERM/SIGINT/SIGHUP, but that requires a goroutine to handle those signals, which, in case of runc, unnecessarily raises its NPROC/pid.max requirement (see discussion at [2]). Since practically exit(2) in dieFromSignal can only happen when the process is running as PID 1, replace it with exit(128+sig) to mimic the shell convention when a child is terminated by a signal. Add a test case which demonstrates the issue and validates the fix (albeit only on Linux). [An earlier version of this patch used to do nothing in dieFromSignal for PID 1 case, but such behavior might be a breaking change for a Go program running in a Linux container as PID 1.] Fixes #78442 [1]: https://github.com/kubernetes/kubernetes/issues/135713 [2]: https://github.com/opencontainers/runc/pull/5189 Change-Id: I196e09e4b5ce84ce2c747a0c2d1fc6e9cf3a6131 Reviewed-on: https://go-review.googlesource.com/c/go/+/759040 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Junyang Shao <shaojunyang@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Diffstat (limited to 'src/runtime/testdata')
-rw-r--r--src/runtime/testdata/testprog/signal_pid1.go26
1 files changed, 26 insertions, 0 deletions
diff --git a/src/runtime/testdata/testprog/signal_pid1.go b/src/runtime/testdata/testprog/signal_pid1.go
new file mode 100644
index 0000000000..7446056e2d
--- /dev/null
+++ b/src/runtime/testdata/testprog/signal_pid1.go
@@ -0,0 +1,26 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "time"
+)
+
+func init() {
+ register("SignalPid1", SignalPid1)
+}
+
+// SignalPid1 is a helper for TestSignalPid1.
+func SignalPid1() {
+ if os.Getpid() != 1 {
+ fmt.Fprintln(os.Stderr, "I am not PID 1")
+ return
+ }
+ fmt.Println("ready")
+
+ time.Sleep(time.Hour)
+}