diff options
| author | Brad Fitzpatrick <bradfitz@golang.org> | 2013-03-18 09:52:39 -0700 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2013-03-18 09:52:39 -0700 |
| commit | 9db0583007e1f644b16d957c2e567ad5e5922338 (patch) | |
| tree | 8a404141887fcefdec7a3f2d2d5db93cb5af3ca6 /src/pkg/os/exec | |
| parent | 7f50c23e2d18c445bfe790692e998ff91b37ddc2 (diff) | |
| download | go-9db0583007e1f644b16d957c2e567ad5e5922338.tar.xz | |
os/exec: fix fd leak with Std*Pipe + LookPath
If LookPath in Command fails, sets a sticky error, and then
StdinPipe, StdoutPipe, or StderrPipe were called, those pipe
fds were never cleaned up.
Fixes #5071
R=golang-dev, rogpeppe
CC=golang-dev
https://golang.org/cl/7799046
Diffstat (limited to 'src/pkg/os/exec')
| -rw-r--r-- | src/pkg/os/exec/exec.go | 2 | ||||
| -rw-r--r-- | src/pkg/os/exec/exec_test.go | 27 |
2 files changed, 29 insertions, 0 deletions
diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go index 8368491b0f..a3bbcf3005 100644 --- a/src/pkg/os/exec/exec.go +++ b/src/pkg/os/exec/exec.go @@ -235,6 +235,8 @@ func (c *Cmd) Run() error { // Start starts the specified command but does not wait for it to complete. func (c *Cmd) Start() error { if c.err != nil { + c.closeDescriptors(c.closeAfterStart) + c.closeDescriptors(c.closeAfterWait) return c.err } if c.Process != nil { diff --git a/src/pkg/os/exec/exec_test.go b/src/pkg/os/exec/exec_test.go index 611ac02676..dfcf4be231 100644 --- a/src/pkg/os/exec/exec_test.go +++ b/src/pkg/os/exec/exec_test.go @@ -151,6 +151,33 @@ func TestPipes(t *testing.T) { check("Wait", err) } +// Issue 5071 +func TestPipeLookPathLeak(t *testing.T) { + fd0 := numOpenFDS(t) + for i := 0; i < 4; i++ { + cmd := Command("something-that-does-not-exist-binary") + cmd.StdoutPipe() + cmd.StderrPipe() + cmd.StdinPipe() + if err := cmd.Run(); err == nil { + t.Fatal("unexpected success") + } + } + fdGrowth := numOpenFDS(t) - fd0 + if fdGrowth > 2 { + t.Errorf("leaked %d fds; want ~0", fdGrowth) + } +} + +func numOpenFDS(t *testing.T) int { + lsof, err := Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output() + if err != nil { + t.Skip("skipping test; error finding or running lsof") + return 0 + } + return bytes.Count(lsof, []byte("\n")) +} + var testedAlreadyLeaked = false // basefds returns the number of expected file descriptors |
