-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
syscall: add support for SysProcAttr.Pdeathsig on FreeBSD
Fixes golang#46258 Change-Id: I63f70e67274a9df39c757243b99b12e50a9e4784
- Loading branch information
Showing
4 changed files
with
189 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// Copyright 2015 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. | ||
|
||
//go:build freebsd || linux | ||
// +build freebsd linux | ||
|
||
package syscall_test | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
"os/exec" | ||
"os/signal" | ||
"path/filepath" | ||
"syscall" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
if os.Getenv("GO_DEATHSIG_PARENT") == "1" { | ||
deathSignalParent() | ||
} else if os.Getenv("GO_DEATHSIG_CHILD") == "1" { | ||
deathSignalChild() | ||
} else if os.Getenv("GO_SYSCALL_NOERROR") == "1" { | ||
syscallNoError() | ||
} | ||
|
||
os.Exit(m.Run()) | ||
} | ||
|
||
func TestDeathSignal(t *testing.T) { | ||
if os.Getuid() != 0 { | ||
t.Skip("skipping root only test") | ||
} | ||
|
||
// Copy the test binary to a location that a non-root user can read/execute | ||
// after we drop privileges | ||
tempDir, err := os.MkdirTemp("", "TestDeathSignal") | ||
if err != nil { | ||
t.Fatalf("cannot create temporary directory: %v", err) | ||
} | ||
defer os.RemoveAll(tempDir) | ||
os.Chmod(tempDir, 0755) | ||
|
||
tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0])) | ||
|
||
src, err := os.Open(os.Args[0]) | ||
if err != nil { | ||
t.Fatalf("cannot open binary %q, %v", os.Args[0], err) | ||
} | ||
defer src.Close() | ||
|
||
dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) | ||
if err != nil { | ||
t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err) | ||
} | ||
if _, err := io.Copy(dst, src); err != nil { | ||
t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err) | ||
} | ||
err = dst.Close() | ||
if err != nil { | ||
t.Fatalf("failed to close test binary %q, %v", tmpBinary, err) | ||
} | ||
|
||
cmd := exec.Command(tmpBinary) | ||
cmd.Env = append(os.Environ(), "GO_DEATHSIG_PARENT=1") | ||
chldStdin, err := cmd.StdinPipe() | ||
if err != nil { | ||
t.Fatalf("failed to create new stdin pipe: %v", err) | ||
} | ||
chldStdout, err := cmd.StdoutPipe() | ||
if err != nil { | ||
t.Fatalf("failed to create new stdout pipe: %v", err) | ||
} | ||
cmd.Stderr = os.Stderr | ||
|
||
err = cmd.Start() | ||
defer cmd.Wait() | ||
if err != nil { | ||
t.Fatalf("failed to start first child process: %v", err) | ||
} | ||
|
||
chldPipe := bufio.NewReader(chldStdout) | ||
|
||
if got, err := chldPipe.ReadString('\n'); got == "start\n" { | ||
syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) | ||
|
||
go func() { | ||
time.Sleep(5 * time.Second) | ||
chldStdin.Close() | ||
}() | ||
|
||
want := "ok\n" | ||
if got, err = chldPipe.ReadString('\n'); got != want { | ||
t.Fatalf("expected %q, received %q, %v", want, got, err) | ||
} | ||
} else { | ||
t.Fatalf("did not receive start from child, received %q, %v", got, err) | ||
} | ||
} | ||
|
||
func deathSignalParent() { | ||
cmd := exec.Command(os.Args[0]) | ||
cmd.Env = append(os.Environ(), | ||
"GO_DEATHSIG_PARENT=", | ||
"GO_DEATHSIG_CHILD=1", | ||
) | ||
cmd.Stdin = os.Stdin | ||
cmd.Stdout = os.Stdout | ||
attrs := syscall.SysProcAttr{ | ||
Pdeathsig: syscall.SIGUSR1, | ||
// UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is | ||
// unused on Ubuntu | ||
Credential: &syscall.Credential{Uid: 99, Gid: 99}, | ||
} | ||
cmd.SysProcAttr = &attrs | ||
|
||
err := cmd.Start() | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "death signal parent error: %v\n", err) | ||
os.Exit(1) | ||
} | ||
cmd.Wait() | ||
os.Exit(0) | ||
} | ||
|
||
func deathSignalChild() { | ||
c := make(chan os.Signal, 1) | ||
signal.Notify(c, syscall.SIGUSR1) | ||
go func() { | ||
<-c | ||
fmt.Println("ok") | ||
os.Exit(0) | ||
}() | ||
fmt.Println("start") | ||
|
||
buf := make([]byte, 32) | ||
os.Stdin.Read(buf) | ||
|
||
// We expected to be signaled before stdin closed | ||
fmt.Println("not ok") | ||
os.Exit(1) | ||
} | ||
|
||
func syscallNoError() { | ||
// Test that the return value from SYS_GETEUID32 (which cannot fail) | ||
// doesn't get treated as an error (see https://golang.org/issue/22924) | ||
euid1, _, e := syscall.RawSyscall(syscall.Sys_GETEUID, 0, 0, 0) | ||
euid2, _ := syscall.RawSyscallNoError(syscall.Sys_GETEUID, 0, 0, 0) | ||
|
||
fmt.Println(uintptr(euid1), "/", int(e), "/", uintptr(euid2)) | ||
os.Exit(0) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters