Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit bce10eb

Browse files
committed
syscall: exec_linux: add test for F_DUPFD_CLOEXEC behaviour
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
1 parent bb99daf commit bce10eb

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

‎src/syscall/exec_linux_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package syscall_test
88

99
import (
1010
"bytes"
11+
"errors"
1112
"flag"
1213
"fmt"
1314
"internal/testenv"
@@ -673,3 +674,78 @@ func testAmbientCaps(t *testing.T, userns bool) {
673674
t.Fatal(err.Error())
674675
}
675676
}
677+
678+
// Test to ensure that ForkExec doesn't clobber file descriptors not included
679+
// in cmd.ExtraFiles. See https://golang.org/issue/61751.
680+
func TestExtraFilesNoClobber(t *testing.T) {
681+
testenv.MustHaveExec(t)
682+
683+
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
684+
// Successfully managed to self-exec!
685+
os.Exit(0)
686+
}
687+
688+
exe, err := os.Executable()
689+
if err != nil {
690+
t.Fatal(err)
691+
}
692+
693+
// Grab a handle to our /proc/self/exe.
694+
exeFileOriginal, err := os.Open(exe)
695+
if err != nil {
696+
t.Fatal(err)
697+
}
698+
defer exeFileOriginal.Close()
699+
700+
// A file we cannot execute.
701+
devNullOriginal, err := os.Open("/dev/null")
702+
if err != nil {
703+
t.Fatal(err)
704+
}
705+
defer devNullOriginal.Close()
706+
if _, err := exec.LookPath(devNullOriginal.Name()); err == nil {
707+
t.Skip("skipping test -- /dev/null is executable")
708+
}
709+
710+
// Change the file descriptors such that devNull is a large descriptor and
711+
// exeFile is one higher. Before https://golang.org/cl/515799, this would
712+
// cause ForkExec to clobber the descriptor.
713+
//
714+
// Unfortunately we can't really have a generic test for clobbering because
715+
// we can only detect the clobbering of a single file descriptor using this
716+
// method. It might be possible use {Ptrace: true} to detect clobbering but
717+
// the behaviour of F_DUPFD_CLOEXEC is not guaranteed (and you never know
718+
// if some other test has opened a file, throwing off the fd calculations).
719+
devNullFd := 9000
720+
exeFileFd := devNullFd + 1
721+
722+
if err := syscall.Dup2(int(devNullOriginal.Fd()), devNullFd); err != nil {
723+
t.Fatalf("dup %s to %d failed: %v", devNullOriginal.Name(), devNullFd, err)
724+
}
725+
devNull := os.NewFile(uintptr(devNullFd), "/dev/null (dup'd)")
726+
defer devNull.Close()
727+
728+
if err := syscall.Dup2(int(exeFileOriginal.Fd()), exeFileFd); err != nil {
729+
t.Fatalf("dup %s to %d failed: %v", exeFileOriginal.Name(), exeFileFd, err)
730+
}
731+
exeFile := os.NewFile(uintptr(exeFileFd), exeFileOriginal.Name()+" (dup'd)")
732+
defer exeFile.Close()
733+
734+
// Try to run exeFile through /proc/self/fd/$n.
735+
exePath := fmt.Sprintf("/proc/self/fd/%d", exeFile.Fd())
736+
if _, err := os.Stat(exePath); err != nil {
737+
t.Skipf("skipping test -- cannot resolve %s", exePath)
738+
}
739+
cmd := testenv.Command(t, exePath, "-test.run="+t.Name())
740+
cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
741+
cmd.Stdout = os.Stdout
742+
cmd.Stderr = os.Stderr
743+
cmd.ExtraFiles = []*os.File{devNull}
744+
if err := cmd.Run(); err != nil {
745+
if errors.Is(err, os.ErrPermission) {
746+
t.Fatalf("fd %d was clobbered during exec: execve %s: %v", exeFileFd, exePath, err)
747+
}
748+
t.Fatal(err)
749+
}
750+
runtime.KeepAlive(exeFile)
751+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /