Skip to content

Commit b906df6

Browse files
os/exec: add closeOnce.WriteString method
Add an explicit WriteString method to closeOnce that acquires the writers lock. This overrides the one promoted from the embedded *os.File field. The promoted one naturally does not acquire the lock, and can therefore race with the Close method. Fixes #17647. Change-Id: I3460f2a0d503449481cfb2fd4628b4855ab0ecdf Reviewed-on: https://go-review.googlesource.com/33298 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 1b66b38 commit b906df6

File tree

3 files changed

+35
-2
lines changed

3 files changed

+35
-2
lines changed

src/cmd/dist/test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,7 @@ func (t *tester) runFlag(rx string) string {
10551055
func (t *tester) raceTest(dt *distTest) error {
10561056
t.addCmd(dt, "src", "go", "test", "-race", "-i", "runtime/race", "flag", "os/exec")
10571057
t.addCmd(dt, "src", "go", "test", "-race", t.runFlag("Output"), "runtime/race")
1058-
t.addCmd(dt, "src", "go", "test", "-race", "-short", t.runFlag("TestParse|TestEcho"), "flag", "os/exec")
1058+
t.addCmd(dt, "src", "go", "test", "-race", "-short", t.runFlag("TestParse|TestEcho|TestStdinCloseRace"), "flag", "os/exec")
10591059
// We don't want the following line, because it
10601060
// slows down all.bash (by 10 seconds on my laptop).
10611061
// The race builder should catch any error here, but doesn't.
@@ -1068,7 +1068,7 @@ func (t *tester) raceTest(dt *distTest) error {
10681068
}
10691069
if t.extLink() {
10701070
// Test with external linking; see issue 9133.
1071-
t.addCmd(dt, "src", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", t.runFlag("TestParse|TestEcho"), "flag", "os/exec")
1071+
t.addCmd(dt, "src", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", t.runFlag("TestParse|TestEcho|TestStdinCloseRace"), "flag", "os/exec")
10721072
}
10731073
return nil
10741074
}

src/os/exec/exec.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,13 @@ func (c *closeOnce) Write(b []byte) (int, error) {
579579
return n, err
580580
}
581581

582+
func (c *closeOnce) WriteString(s string) (int, error) {
583+
c.writers.RLock()
584+
n, err := c.File.WriteString(s)
585+
c.writers.RUnlock()
586+
return n, err
587+
}
588+
582589
// StdoutPipe returns a pipe that will be connected to the command's
583590
// standard output when the command starts.
584591
//

src/os/exec/exec_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,32 @@ func TestStdinClose(t *testing.T) {
246246
check("Wait", cmd.Wait())
247247
}
248248

249+
// Issue 17647.
250+
func TestStdinCloseRace(t *testing.T) {
251+
cmd := helperCommand(t, "stdinClose")
252+
stdin, err := cmd.StdinPipe()
253+
if err != nil {
254+
t.Fatalf("StdinPipe: %v", err)
255+
}
256+
if err := cmd.Start(); err != nil {
257+
t.Fatalf("Start: %v", err)
258+
}
259+
go func() {
260+
if err := cmd.Process.Kill(); err != nil {
261+
t.Errorf("Kill: %v", err)
262+
}
263+
}()
264+
go func() {
265+
io.Copy(stdin, strings.NewReader(stdinCloseTestString))
266+
if err := stdin.Close(); err != nil {
267+
t.Errorf("stdin.Close: %v", err)
268+
}
269+
}()
270+
if err := cmd.Wait(); err == nil {
271+
t.Fatalf("Wait: succeeded unexpectedly")
272+
}
273+
}
274+
249275
// Issue 5071
250276
func TestPipeLookPathLeak(t *testing.T) {
251277
fd0, lsof0 := numOpenFDS(t)

0 commit comments

Comments
 (0)