Skip to content

Commit

Permalink
Fix race checking for process exit and waiting for exec fifo
Browse files Browse the repository at this point in the history
Signed-off-by: Jordan Liggitt <liggitt@google.com>
  • Loading branch information
liggitt committed Dec 18, 2019
1 parent 7496a96 commit 928cf56
Showing 1 changed file with 42 additions and 39 deletions.
81 changes: 42 additions & 39 deletions libcontainer/container_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,21 +266,23 @@ func (c *linuxContainer) Exec() error {
func (c *linuxContainer) exec() error {
path := filepath.Join(c.root, execFifoFilename)

fifoOpen := make(chan struct{})
select {
case <-awaitProcessExit(c.initProcess.pid(), fifoOpen):
return errors.New("container process is already dead")
case result := <-awaitFifoOpen(path):
close(fifoOpen)
if result.err != nil {
return result.err
}
f := result.file
defer f.Close()
if err := readFromExecFifo(f); err != nil {
return err
blockingFifoOpenCh := awaitFifoOpen(path)
for {
select {
case result := <-blockingFifoOpenCh:
return handleFifoResult(result)

case <-time.After(time.Millisecond * 100):
stat, err := system.Stat(c.initProcess.pid())
if err != nil || stat.State == system.Zombie {
// could be because process started, ran, and completed between our 100ms timeout and our system.Stat() check.
// see if the fifo exists and has data (with a non-blocking open, which will succeed if the writing process is complete).
if err := handleFifoResult(fifoOpen(path, false)); err != nil {
return errors.New("container process is already dead")
}
return nil
}
}
return os.Remove(path)
}
}

Expand All @@ -295,38 +297,39 @@ func readFromExecFifo(execFifo io.Reader) error {
return nil
}

func awaitProcessExit(pid int, exit <-chan struct{}) <-chan struct{} {
isDead := make(chan struct{})
go func() {
for {
select {
case <-exit:
return
case <-time.After(time.Millisecond * 100):
stat, err := system.Stat(pid)
if err != nil || stat.State == system.Zombie {
close(isDead)
return
}
}
}
}()
return isDead
}

func awaitFifoOpen(path string) <-chan openResult {
fifoOpened := make(chan openResult)
go func() {
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
fifoOpened <- openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}
return
}
fifoOpened <- openResult{file: f}
result := fifoOpen(path, true)
fifoOpened <- result
}()
return fifoOpened
}

func fifoOpen(path string, block bool) openResult {
flags := os.O_RDONLY
if !block {
flags |= syscall.O_NONBLOCK
}
f, err := os.OpenFile(path, flags, 0)
if err != nil {
return openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}
}
return openResult{file: f}
}

func handleFifoResult(result openResult) error {
if result.err != nil {
return result.err
}
f := result.file
defer f.Close()
if err := readFromExecFifo(f); err != nil {
return err
}
return os.Remove(f.Name())
}

type openResult struct {
file *os.File
err error
Expand Down

0 comments on commit 928cf56

Please sign in to comment.