go: os/exec: race on cmd.Wait() might lead to panic
The current implementation of cmd.Wait has a race condition: if multiple goroutines call it, it might cause a panic.
In the first part of the method, copied below, two concurrent calls might check c.finished, get false, set it to true, invoke c.Process.Wait() and close c.waitDone before any error checking is performed. c.waitDone is a chan struct{}, and a double close will cause a panic.
func (c *Cmd) Wait() error {
if c.Process == nil {
return errors.New("exec: not started")
}
if c.finished {
return errors.New("exec: Wait was already called")
}
c.finished = true
state, err := c.Process.Wait()
if c.waitDone != nil {
close(c.waitDone)
}
//[...]
Since waiting is a synchronization primitive I’d expect one of the two:
- The documentation should state that this is not safe for concurrent use (probably the best approach here)
- Some form of synchronization to prevent races. I would either atomically CAS
c.finished(but I’m not a fan of atomics and would require to change type to some sort of int) or protectcwith a mutex, which would be my suggested solution for this case
I would happily send in CLs in both cases.
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 1
- Comments: 16 (15 by maintainers)
In general we only document the abnormal cases. The assumption when unstated is that things are not safe for concurrent use, that zero values are not usable, that implementations implement interfaces faithfully, and that only one return values is non-zero and meaningful.
That said, in this case I agree it would be a reasonable assumption for users to think that two concurrent Wait calls are valid, so I wouldn’t object to docs or a fix here to allow concurrent calls.