From 9db0583007e1f644b16d957c2e567ad5e5922338 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 18 Mar 2013 09:52:39 -0700 Subject: [PATCH] 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 --- src/pkg/os/exec/exec.go | 2 ++ src/pkg/os/exec/exec_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) 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 -- 2.50.0