From da7ee57f40069eda3488b2e51dfa878bdd5816af Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 16 Aug 2023 19:20:54 -0700 Subject: syscall: add support to get pidfd from ForkExec on Linux Add PidFD support, so that if the PidFD pointer in SysProcAttr is not nil, ForkExec (and thus all its users) obtains a pidfd from the kernel during clone(), and writes the result (or -1, if the functionality is not supported by the kernel) into *PidFD. The functionality to get pidfd is implemented for both clone3 and clone. For the latter, an extra argument to rawVforkSyscall is needed, thus the change in asm files. Add a trivial test case checking the obtained pidfd can be used to send a signal to a process, using pidfd_send_signal. To test clone3 code path, add a flag available to tests only. Updates #51246. Change-Id: I2212b69e1a657163c31b4a6245b076bc495777a3 Reviewed-on: https://go-review.googlesource.com/c/go/+/520266 Auto-Submit: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Cherry Mui Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Run-TryBot: Kirill Kolyshkin --- src/syscall/exec_linux_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'src/syscall/exec_linux_test.go') diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go index ca92a153f8..7883096b88 100644 --- a/src/syscall/exec_linux_test.go +++ b/src/syscall/exec_linux_test.go @@ -24,6 +24,7 @@ import ( "strings" "syscall" "testing" + "time" "unsafe" ) @@ -522,6 +523,73 @@ func TestCloneTimeNamespace(t *testing.T) { } } +func testPidFD(t *testing.T) error { + testenv.MustHaveExec(t) + + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + // Child: wait for a signal. + time.Sleep(time.Hour) + } + + exe, err := os.Executable() + if err != nil { + t.Fatal(err) + } + + var pidfd int + cmd := testenv.Command(t, exe, "-test.run=^TestPidFD$") + cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1") + cmd.SysProcAttr = &syscall.SysProcAttr{ + PidFD: &pidfd, + } + if err := cmd.Start(); err != nil { + return err + } + defer func() { + cmd.Process.Kill() + cmd.Wait() + }() + t.Log("got pidfd:", pidfd) + // If pidfd is not supported by the kernel, -1 is returned. + if pidfd == -1 { + t.Skip("pidfd not supported") + } + defer syscall.Close(pidfd) + + // Use pidfd to send a signal to the child. + sig := syscall.SIGINT + if _, _, e := syscall.Syscall(syscall.Sys_pidfd_send_signal, uintptr(pidfd), uintptr(sig), 0); e != 0 { + if e != syscall.EINVAL && testenv.SyscallIsNotSupported(e) { + t.Skip("pidfd_send_signal syscall not supported:", e) + } + t.Fatal("pidfd_send_signal syscall failed:", e) + } + // Check if the child received our signal. + err = cmd.Wait() + if cmd.ProcessState == nil || cmd.ProcessState.Sys().(syscall.WaitStatus).Signal() != sig { + t.Fatal("unexpected child error:", err) + } + return nil +} + +func TestPidFD(t *testing.T) { + if err := testPidFD(t); err != nil { + t.Fatal("can't start a process:", err) + } +} + +func TestPidFDClone3(t *testing.T) { + *syscall.ForceClone3 = true + defer func() { *syscall.ForceClone3 = false }() + + if err := testPidFD(t); err != nil { + if testenv.SyscallIsNotSupported(err) { + t.Skip("clone3 not supported:", err) + } + t.Fatal("can't start a process:", err) + } +} + type capHeader struct { version uint32 pid int32 -- cgit v1.3