Source file src/syscall/exec_pdeathsig_test.go

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build freebsd || linux
     6  
     7  package syscall_test
     8  
     9  import (
    10  	"bufio"
    11  	"fmt"
    12  	"internal/testenv"
    13  	"io"
    14  	"os"
    15  	"os/exec"
    16  	"os/signal"
    17  	"os/user"
    18  	"path/filepath"
    19  	"strconv"
    20  	"strings"
    21  	"syscall"
    22  	"testing"
    23  )
    24  
    25  // TestDeathSignalSetuid verifies that a command run with a different UID still
    26  // receives PDeathsig; it is a regression test for https://go.dev/issue/9686.
    27  func TestDeathSignalSetuid(t *testing.T) {
    28  	if testing.Short() {
    29  		t.Skipf("skipping test that copies its binary into temp dir")
    30  	}
    31  
    32  	// Copy the test binary to a location that another user can read/execute
    33  	// after we drop privileges.
    34  	//
    35  	// TODO(bcmills): Why do we believe that another users will be able to
    36  	// execute a binary in this directory? (It could be mounted noexec.)
    37  	tempDir, err := os.MkdirTemp("", "TestDeathSignal")
    38  	if err != nil {
    39  		t.Fatalf("cannot create temporary directory: %v", err)
    40  	}
    41  	defer os.RemoveAll(tempDir)
    42  	os.Chmod(tempDir, 0755)
    43  
    44  	tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
    45  
    46  	src, err := os.Open(os.Args[0])
    47  	if err != nil {
    48  		t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
    49  	}
    50  	defer src.Close()
    51  
    52  	dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
    53  	if err != nil {
    54  		t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
    55  	}
    56  	if _, err := io.Copy(dst, src); err != nil {
    57  		t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
    58  	}
    59  	err = dst.Close()
    60  	if err != nil {
    61  		t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
    62  	}
    63  
    64  	cmd := testenv.Command(t, tmpBinary)
    65  	cmd.Env = append(cmd.Environ(), "GO_DEATHSIG_PARENT=1")
    66  	chldStdin, err := cmd.StdinPipe()
    67  	if err != nil {
    68  		t.Fatalf("failed to create new stdin pipe: %v", err)
    69  	}
    70  	chldStdout, err := cmd.StdoutPipe()
    71  	if err != nil {
    72  		t.Fatalf("failed to create new stdout pipe: %v", err)
    73  	}
    74  	stderr := new(strings.Builder)
    75  	cmd.Stderr = stderr
    76  
    77  	err = cmd.Start()
    78  	defer func() {
    79  		chldStdin.Close()
    80  		cmd.Wait()
    81  		if stderr.Len() > 0 {
    82  			t.Logf("stderr:\n%s", stderr)
    83  		}
    84  	}()
    85  	if err != nil {
    86  		t.Fatalf("failed to start first child process: %v", err)
    87  	}
    88  
    89  	chldPipe := bufio.NewReader(chldStdout)
    90  
    91  	if got, err := chldPipe.ReadString('\n'); got == "start\n" {
    92  		syscall.Kill(cmd.Process.Pid, syscall.SIGTERM)
    93  
    94  		want := "ok\n"
    95  		if got, err = chldPipe.ReadString('\n'); got != want {
    96  			t.Fatalf("expected %q, received %q, %v", want, got, err)
    97  		}
    98  	} else if got == "skip\n" {
    99  		t.Skipf("skipping: parent could not run child program as selected user")
   100  	} else {
   101  		t.Fatalf("did not receive start from child, received %q, %v", got, err)
   102  	}
   103  }
   104  
   105  func deathSignalParent() {
   106  	var (
   107  		u   *user.User
   108  		err error
   109  	)
   110  	if os.Getuid() == 0 {
   111  		tryUsers := []string{"nobody"}
   112  		if testenv.Builder() != "" {
   113  			tryUsers = append(tryUsers, "gopher")
   114  		}
   115  		for _, name := range tryUsers {
   116  			u, err = user.Lookup(name)
   117  			if err == nil {
   118  				break
   119  			}
   120  			fmt.Fprintf(os.Stderr, "Lookup(%q): %v\n", name, err)
   121  		}
   122  	}
   123  	if u == nil {
   124  		// If we couldn't find an unprivileged user to run as, try running as
   125  		// the current user. (Empirically this still causes the call to Start to
   126  		// fail with a permission error if running as a non-root user on Linux.)
   127  		u, err = user.Current()
   128  		if err != nil {
   129  			fmt.Fprintln(os.Stderr, err)
   130  			os.Exit(1)
   131  		}
   132  	}
   133  
   134  	uid, err := strconv.ParseUint(u.Uid, 10, 32)
   135  	if err != nil {
   136  		fmt.Fprintf(os.Stderr, "invalid UID: %v\n", err)
   137  		os.Exit(1)
   138  	}
   139  	gid, err := strconv.ParseUint(u.Gid, 10, 32)
   140  	if err != nil {
   141  		fmt.Fprintf(os.Stderr, "invalid GID: %v\n", err)
   142  		os.Exit(1)
   143  	}
   144  
   145  	cmd := exec.Command(os.Args[0])
   146  	cmd.Env = append(os.Environ(),
   147  		"GO_DEATHSIG_PARENT=",
   148  		"GO_DEATHSIG_CHILD=1",
   149  	)
   150  	cmd.Stdin = os.Stdin
   151  	cmd.Stdout = os.Stdout
   152  	attrs := syscall.SysProcAttr{
   153  		Pdeathsig:  syscall.SIGUSR1,
   154  		Credential: &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)},
   155  	}
   156  	cmd.SysProcAttr = &attrs
   157  
   158  	fmt.Fprintf(os.Stderr, "starting process as user %q\n", u.Username)
   159  	if err := cmd.Start(); err != nil {
   160  		fmt.Fprintln(os.Stderr, err)
   161  		if testenv.SyscallIsNotSupported(err) {
   162  			fmt.Println("skip")
   163  			os.Exit(0)
   164  		}
   165  		os.Exit(1)
   166  	}
   167  	cmd.Wait()
   168  	os.Exit(0)
   169  }
   170  
   171  func deathSignalChild() {
   172  	c := make(chan os.Signal, 1)
   173  	signal.Notify(c, syscall.SIGUSR1)
   174  	go func() {
   175  		<-c
   176  		fmt.Println("ok")
   177  		os.Exit(0)
   178  	}()
   179  	fmt.Println("start")
   180  
   181  	buf := make([]byte, 32)
   182  	os.Stdin.Read(buf)
   183  
   184  	// We expected to be signaled before stdin closed
   185  	fmt.Println("not ok")
   186  	os.Exit(1)
   187  }
   188  

View as plain text