From aad6b3b32c81795f86bc4a9e81aad94899daf520 Mon Sep 17 00:00:00 2001
From: cions <gh.cions@gmail.com>
Date: Tue, 24 Sep 2024 10:22:31 +0900
Subject: [PATCH] os: ignore SIGSYS in checkPidfd

In Android version 11 and earlier, pidfd-related system calls
are not allowed by the seccomp policy, which causes crashes due
to SIGSYS signals.

Fixes #69065
---
 src/os/pidfd_linux.go           | 16 ++++++++++++++++
 src/runtime/os_linux.go         | 13 +++++++++++--
 src/runtime/os_unix_nonlinux.go |  7 +++++++
 src/runtime/signal_unix.go      | 17 +++++++++++++++++
 4 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/src/os/pidfd_linux.go b/src/os/pidfd_linux.go
index 459d88cb445b42..0bfef7759cc679 100644
--- a/src/os/pidfd_linux.go
+++ b/src/os/pidfd_linux.go
@@ -18,6 +18,7 @@ package os
 import (
 	"errors"
 	"internal/syscall/unix"
+	"runtime"
 	"sync"
 	"syscall"
 	"unsafe"
@@ -151,6 +152,13 @@ var checkPidfdOnce = sync.OnceValue(checkPidfd)
 // execution environment in which the above system calls are restricted by
 // seccomp or a similar technology.
 func checkPidfd() error {
+	// In Android version < 12, pidfd-related system calls are not allowed
+	// by seccomp and trigger the SIGSYS signal. See issue #69065.
+	if runtime.GOOS == "android" {
+		ignoreSIGSYS()
+		defer restoreSIGSYS()
+	}
+
 	// Get a pidfd of the current process (opening of "/proc/self" won't
 	// work for waitid).
 	fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
@@ -192,3 +200,11 @@ func checkPidfd() error {
 //
 //go:linkname checkClonePidfd
 func checkClonePidfd() error
+
+// Provided by runtime.
+//
+//go:linkname ignoreSIGSYS
+func ignoreSIGSYS()
+
+//go:linkname restoreSIGSYS
+func restoreSIGSYS()
diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go
index 3911276cf2c7f4..e18ef8e7762361 100644
--- a/src/runtime/os_linux.go
+++ b/src/runtime/os_linux.go
@@ -879,8 +879,9 @@ func runPerThreadSyscall() {
 }
 
 const (
-	_SI_USER  = 0
-	_SI_TKILL = -6
+	_SI_USER     = 0
+	_SI_TKILL    = -6
+	_SYS_SECCOMP = 1
 )
 
 // sigFromUser reports whether the signal was sent because of a call
@@ -892,6 +893,14 @@ func (c *sigctxt) sigFromUser() bool {
 	return code == _SI_USER || code == _SI_TKILL
 }
 
+// sigFromSeccomp reports whether the signal was sent from seccomp.
+//
+//go:nosplit
+func (c *sigctxt) sigFromSeccomp() bool {
+	code := int32(c.sigcode())
+	return code == _SYS_SECCOMP
+}
+
 //go:nosplit
 func mprotect(addr unsafe.Pointer, n uintptr, prot int32) (ret int32, errno int32) {
 	r, _, err := syscall.Syscall6(syscall.SYS_MPROTECT, uintptr(addr), n, uintptr(prot), 0, 0, 0)
diff --git a/src/runtime/os_unix_nonlinux.go b/src/runtime/os_unix_nonlinux.go
index b98753b8fe12b7..0e8b61c3b11aa2 100644
--- a/src/runtime/os_unix_nonlinux.go
+++ b/src/runtime/os_unix_nonlinux.go
@@ -13,3 +13,10 @@ package runtime
 func (c *sigctxt) sigFromUser() bool {
 	return c.sigcode() == _SI_USER
 }
+
+// sigFromSeccomp reports whether the signal was sent from seccomp.
+//
+//go:nosplit
+func (c *sigctxt) sigFromSeccomp() bool {
+	return false
+}
diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index a6373093b5f531..a056e21a2f9cee 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -605,6 +605,19 @@ var crashing atomic.Int32
 var testSigtrap func(info *siginfo, ctxt *sigctxt, gp *g) bool
 var testSigusr1 func(gp *g) bool
 
+// sigsysIgnored is non-zero if we are currently ignoring SIGSYS. See issue #69065.
+var sigsysIgnored uint32
+
+//go:linkname ignoreSIGSYS os.ignoreSIGSYS
+func ignoreSIGSYS() {
+	atomic.Store(&sigsysIgnored, 1)
+}
+
+//go:linkname restoreSIGSYS os.restoreSIGSYS
+func restoreSIGSYS() {
+	atomic.Store(&sigsysIgnored, 0)
+}
+
 // sighandler is invoked when a signal occurs. The global g will be
 // set to a gsignal goroutine and we will be running on the alternate
 // signal stack. The parameter gp will be the value of the global g
@@ -715,6 +728,10 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
 		return
 	}
 
+	if sig == _SIGSYS && c.sigFromSeccomp() && atomic.Load(&sigsysIgnored) != 0 {
+		return
+	}
+
 	if flags&_SigKill != 0 {
 		dieFromSignal(sig)
 	}