Skip to content

Commit

Permalink
syscall: fix accidental close of exec status pipe in StartProcess
Browse files Browse the repository at this point in the history
In syscall.forkAndExecInChild, blocks of code labelled Pass 1
and Pass 2 permute the file descriptors (if necessary) which are
passed to the child process.  If Pass 1 begins with fds = {0,2,1},
nextfd = 4 and pipe = 4, then the statement labelled "don't stomp
on pipe" is too late -- the pipe (which will be needed to pass
exec status back to the parent) will have been closed by the
preceding DUP call.

Moving the "don't stomp" test earlier ensures that the pipe is
protected.

Fixes #14979

Change-Id: I890c311527f6aa255be48b3277c1e84e2049ee22
Reviewed-on: https://go-review.googlesource.com/21184
Run-TryBot: David du Colombier <0intro@gmail.com>
Reviewed-by: David du Colombier <0intro@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information
Richard Miller authored and bradfitz committed Mar 29, 2016
1 parent 272df15 commit 1f0bebc
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 12 deletions.
6 changes: 3 additions & 3 deletions src/syscall/exec_bsd.go
Expand Up @@ -181,16 +181,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < int(i) {
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
if err1 != 0 {
goto childerror
}
RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
fd[i] = nextfd
nextfd++
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/syscall/exec_linux.go
Expand Up @@ -255,16 +255,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < int(i) {
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
_, _, err1 = RawSyscall(_SYS_dup, uintptr(fd[i]), uintptr(nextfd), 0)
if err1 != 0 {
goto childerror
}
RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
fd[i] = nextfd
nextfd++
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/syscall/exec_plan9.go
Expand Up @@ -270,16 +270,16 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < int(i) {
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(nextfd), 0)
if int32(r1) == -1 {
goto childerror
}

fd[i] = nextfd
nextfd++
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/syscall/exec_solaris.go
Expand Up @@ -178,16 +178,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < int(i) {
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(nextfd))
if err1 != 0 {
goto childerror
}
fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
fd[i] = nextfd
nextfd++
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/syscall/syscall_test.go
Expand Up @@ -6,6 +6,8 @@ package syscall_test

import (
"fmt"
"internal/testenv"
"os"
"syscall"
"testing"
)
Expand Down Expand Up @@ -45,3 +47,15 @@ func TestItoa(t *testing.T) {
t.Fatalf("itoa(%d) = %s, want %s", i, s, f)
}
}

// Check that permuting child process fds doesn't interfere with
// reporting of fork/exec status. See Issue 14979.
func TestExecErrPermutedFds(t *testing.T) {
testenv.MustHaveExec(t)

attr := &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stderr, os.Stdout}}
_, err := os.StartProcess("/", []string{"/"}, attr)
if err == nil {
t.Fatalf("StartProcess of invalid program returned err = nil")
}
}

0 comments on commit 1f0bebc

Please sign in to comment.