Skip to content

Commit 91d65a8

Browse files
authored
Merge pull request #157 from xibz/fix-race-fifo
Fixes race with io.Copy and O_NONBLOCK file
2 parents 654fb05 + 3e16a8b commit 91d65a8

File tree

5 files changed

+26
-14
lines changed

5 files changed

+26
-14
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/firecracker-microvm/firecracker-go-sdk
33
go 1.11
44

55
require (
6+
github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c
67
github.com/containernetworking/cni v0.7.2-0.20190807151350-8c6c47d1c7fc
78
github.com/containernetworking/plugins v0.8.2
89
github.com/go-openapi/errors v0.17.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:C
88
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
99
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
1010
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
11+
github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c h1:KFbqHhDeaHM7IfFtXHfUHMDaUStpM2YwBR+iJCIOsKk=
12+
github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
1113
github.com/containernetworking/cni v0.7.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
1214
github.com/containernetworking/cni v0.7.2-0.20190807151350-8c6c47d1c7fc h1:zUNdrf9w09mWodVhZ9hX4Yk4Uu84n/OgdfPattAwwt8=
1315
github.com/containernetworking/cni v0.7.2-0.20190807151350-8c6c47d1c7fc/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=

handlers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ var CreateLogFilesHandler = Handler{
152152
)
153153

154154
if m.Cfg.FifoLogWriter != nil {
155-
if err := m.captureFifoToFile(m.logger, logFifoPath, m.Cfg.FifoLogWriter); err != nil {
155+
if err := m.captureFifoToFile(ctx, m.logger, logFifoPath, m.Cfg.FifoLogWriter); err != nil {
156156
m.logger.Warnf("captureFifoToFile() returned %s. Continuing anyway.", err)
157157
}
158158
}

machine.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"syscall"
2828
"time"
2929

30+
"github.com/containerd/fifo"
3031
"github.com/containernetworking/plugins/pkg/ns"
3132
"github.com/gofrs/uuid"
3233
"github.com/hashicorp/go-multierror"
@@ -582,19 +583,14 @@ func (m *Machine) setupLogging(ctx context.Context) error {
582583
return nil
583584
}
584585

585-
func (m *Machine) captureFifoToFile(logger *log.Entry, fifoPath string, w io.Writer) error {
586+
func (m *Machine) captureFifoToFile(ctx context.Context, logger *log.Entry, fifoPath string, w io.Writer) error {
586587
// open the fifo pipe which will be used
587588
// to write its contents to a file.
588-
fd, err := syscall.Open(fifoPath, syscall.O_RDONLY|syscall.O_NONBLOCK, 0600)
589+
fifoPipe, err := fifo.OpenFifo(ctx, fifoPath, syscall.O_RDONLY|syscall.O_NONBLOCK, 0600)
589590
if err != nil {
590591
return fmt.Errorf("Failed to open fifo path at %q: %v", fifoPath, err)
591592
}
592593

593-
fifoPipe := os.NewFile(uintptr(fd), fifoPath)
594-
if fifoPipe == nil {
595-
return fmt.Errorf("Invalid file descriptor")
596-
}
597-
598594
logger.Debugf("Capturing %q to writer", fifoPath)
599595

600596
// this goroutine is to track the life of the application along with whether
@@ -623,7 +619,7 @@ func (m *Machine) captureFifoToFile(logger *log.Entry, fifoPath string, w io.Wri
623619
}()
624620

625621
if _, err := io.Copy(w, fifoPipe); err != nil {
626-
logger.Warnf("io.Copy failed to copy contents of fifo pipe: %v", err)
622+
logger.WithError(err).Warn("io.Copy failed to copy contents of fifo pipe")
627623
}
628624
}()
629625

machine_test.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ func TestCaptureFifoToFile(t *testing.T) {
835835
m := &Machine{
836836
exitCh: make(chan struct{}),
837837
}
838-
if err := m.captureFifoToFile(fctesting.NewLogEntry(t), fifoPath, testWriter); err != nil {
838+
if err := m.captureFifoToFile(context.Background(), fctesting.NewLogEntry(t), fifoPath, testWriter); err != nil {
839839
t.Errorf("Unexpected error: %v", err)
840840
}
841841

@@ -877,12 +877,19 @@ func TestCaptureFifoToFile_nonblock(t *testing.T) {
877877
m := &Machine{
878878
exitCh: make(chan struct{}),
879879
}
880-
if err := m.captureFifoToFile(fctesting.NewLogEntry(t), fifoPath, testWriter); err != nil {
880+
if err := m.captureFifoToFile(context.Background(), fctesting.NewLogEntry(t), fifoPath, testWriter); err != nil {
881881
t.Errorf("Unexpected error: %v", err)
882882
}
883883

884884
defer os.Remove(logPath)
885885

886+
// we sleep here to check to see the io.Copy is working properly in
887+
// captureFifoToFile. This is due to the fifo being opened with O_NONBLOCK,
888+
// which causes io.Copy to exit immediately with no error.
889+
//
890+
// https://github.com/firecracker-microvm/firecracker-go-sdk/issues/156
891+
time.Sleep(250 * time.Millisecond)
892+
886893
f, err := os.OpenFile(fifoPath, os.O_RDWR, 0600)
887894
if err != nil {
888895
t.Fatalf("Failed to open file, %q: %v", fifoPath, err)
@@ -1066,14 +1073,20 @@ func TestCaptureFifoToFile_leak(t *testing.T) {
10661073
logger := fctesting.NewLogEntry(t)
10671074
logger.Logger.Level = logrus.WarnLevel
10681075
logger.Logger.Out = loggerBuffer
1069-
err = m.captureFifoToFile(logger, fifoPath, buf)
1076+
err = m.captureFifoToFile(context.Background(), logger, fifoPath, buf)
10701077
assert.NoError(t, err, "failed to capture fifo to file")
10711078
close(m.exitCh)
10721079

10731080
// wait sometime for the logs to populate
10741081
time.Sleep(250 * time.Millisecond)
1075-
expected := `io.Copy failed to copy contents of fifo pipe: read testdata/TestCaptureFifoToFileLeak.fifo: file already closed`
1076-
assert.Contains(t, loggerBuffer.String(), expected)
1082+
expectedClosedFifo := `reading from a closed fifo`
1083+
expectedClosedFile := `file already closed`
1084+
logs := loggerBuffer.String()
1085+
if !(strings.Contains(logs, expectedClosedFifo) ||
1086+
strings.Contains(logs, expectedClosedFile)) {
1087+
1088+
t.Errorf("logs did not container a closed fifo error or closed file")
1089+
}
10771090
}
10781091

10791092
func TestWaitWithKill(t *testing.T) {

0 commit comments

Comments
 (0)