1
0
mirror of https://github.com/golang/go synced 2024-11-20 05:44:44 -07:00

syscall: fix a number of exec bugs on Plan 9

1. Readdirnames was erroneously returning an
   empty slice on every invocation.

2. The logic for determining which files to
   close before exec was incorrect.  If the
   set of files to be kept open (provided by
   the caller) did not include the files
   opened at startup, those files would be
   accidentally closed.

I also cleaned up readdupdevice while I was
in the vicinity.

R=golang-dev, seed, rsc
CC=golang-dev
https://golang.org/cl/6016044
This commit is contained in:
Anthony Martin 2012-04-26 02:59:13 -07:00
parent 42aa9abae9
commit 5491623406

View File

@ -86,66 +86,60 @@ func gstring(b []byte) (string, []byte) {
// readdirnames returns the names of files inside the directory represented by dirfd. // readdirnames returns the names of files inside the directory represented by dirfd.
func readdirnames(dirfd int) (names []string, err error) { func readdirnames(dirfd int) (names []string, err error) {
result := make([]string, 0, 100) names = make([]string, 0, 100)
var buf [STATMAX]byte var buf [STATMAX]byte
for { for {
n, e := Read(dirfd, buf[:]) n, e := Read(dirfd, buf[:])
if e != nil { if e != nil {
return []string{}, e return nil, e
} }
if n == 0 { if n == 0 {
break break
} }
for i := 0; i < n; { for i := 0; i < n; {
m, _ := gbit16(buf[i:]) m, _ := gbit16(buf[i:])
m += 2 m += 2
if m < STATFIXLEN { if m < STATFIXLEN {
return []string{}, NewError("malformed stat buffer") return nil, NewError("malformed stat buffer")
} }
name, _ := gstring(buf[i+41:]) s, _ := gstring(buf[i+41:])
result = append(result, name) names = append(names, s)
i += int(m) i += int(m)
} }
} }
return []string{}, nil return
} }
// readdupdevice returns a list of currently opened fds (excluding stdin, stdout, stderr) from the dup device #d. // readdupdevice returns a list of currently opened fds (excluding stdin, stdout, stderr) from the dup device #d.
// ForkLock should be write locked before calling, so that no new fds would be created while the fd list is being read. // ForkLock should be write locked before calling, so that no new fds would be created while the fd list is being read.
func readdupdevice() (fds []int, err error) { func readdupdevice() (fds []int, err error) {
dupdevfd, err := Open("#d", O_RDONLY) dupdevfd, err := Open("#d", O_RDONLY)
if err != nil { if err != nil {
return return
} }
defer Close(dupdevfd) defer Close(dupdevfd)
fileNames, err := readdirnames(dupdevfd) names, err := readdirnames(dupdevfd)
if err != nil { if err != nil {
return return
} }
fds = make([]int, 0, len(fileNames)>>1) fds = make([]int, 0, len(names)/2)
for _, fdstr := range fileNames { for _, name := range names {
if l := len(fdstr); l > 2 && fdstr[l-3] == 'c' && fdstr[l-2] == 't' && fdstr[l-1] == 'l' { if n := len(name); n > 3 && name[n-3:n] == "ctl" {
continue continue
} }
fd := int(atoi([]byte(name)))
fd := int(atoi([]byte(fdstr))) switch fd {
case 0, 1, 2, dupdevfd:
if fd == 0 || fd == 1 || fd == 2 || fd == dupdevfd {
continue continue
} }
fds = append(fds, fd) fds = append(fds, fd)
} }
return
return fds[0:len(fds)], nil
} }
var startupFds []int var startupFds []int
@ -282,14 +276,13 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
if fd[i] == int(i) { if fd[i] == int(i) {
continue continue
} }
r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(i), 0) r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(i), 0)
if int(r1) == -1 { if int(r1) == -1 {
goto childerror goto childerror
} }
} }
// Pass 3: close fds that were dup-ed // Pass 3: close fd[i] if it was moved in the previous pass.
for i = 0; i < len(fd); i++ { for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] != int(i) { if fd[i] >= 0 && fd[i] != int(i) {
RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0) RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0)
@ -406,39 +399,32 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
// get a list of open fds, excluding stdin,stdout and stderr that need to be closed in the child. // get a list of open fds, excluding stdin,stdout and stderr that need to be closed in the child.
// no new fds can be created while we hold the ForkLock for writing. // no new fds can be created while we hold the ForkLock for writing.
openFds, e := readdupdevice() openFds, e := readdupdevice()
if e != nil { if e != nil {
ForkLock.Unlock() ForkLock.Unlock()
return 0, e return 0, e
} }
fdsToClose := make([]int, 0, len(openFds)) fdsToClose := make([]int, 0, len(openFds))
// exclude fds opened from startup from the list of fds to be closed.
for _, fd := range openFds { for _, fd := range openFds {
isReserved := false doClose := true
for _, reservedFd := range startupFds {
if fd == reservedFd { // exclude files opened at startup.
isReserved = true for _, sfd := range startupFds {
if fd == sfd {
doClose = false
break break
} }
} }
if !isReserved { // exclude files explicitly requested by the caller.
fdsToClose = append(fdsToClose, fd) for _, rfd := range attr.Files {
} if fd == int(rfd) {
} doClose = false
// exclude fds requested by the caller from the list of fds to be closed.
for _, fd := range openFds {
isReserved := false
for _, reservedFd := range attr.Files {
if fd == int(reservedFd) {
isReserved = true
break break
} }
} }
if !isReserved { if doClose {
fdsToClose = append(fdsToClose, fd) fdsToClose = append(fdsToClose, fd)
} }
} }