Skip to content

Commit ef87884

Browse files
authored
Merge pull request #147 from xibz/fix-leak
Fixes leak of open file during capture fifo
2 parents 1ee9961 + 9d143a3 commit ef87884

File tree

3 files changed

+64
-4
lines changed

3 files changed

+64
-4
lines changed

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 := captureFifoToFile(m.logger, logFifoPath, m.Cfg.FifoLogWriter); err != nil {
155+
if err := m.captureFifoToFile(m.logger, logFifoPath, m.Cfg.FifoLogWriter); err != nil {
156156
m.logger.Warnf("captureFifoToFile() returned %s. Continuing anyway.", err)
157157
}
158158
}

machine.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ func (m *Machine) setupLogging(ctx context.Context) error {
576576
return nil
577577
}
578578

579-
func captureFifoToFile(logger *log.Entry, fifoPath string, w io.Writer) error {
579+
func (m *Machine) captureFifoToFile(logger *log.Entry, fifoPath string, w io.Writer) error {
580580
// open the fifo pipe which will be used
581581
// to write its contents to a file.
582582
fd, err := syscall.Open(fifoPath, syscall.O_RDONLY|syscall.O_NONBLOCK, 0600)
@@ -591,6 +591,16 @@ func captureFifoToFile(logger *log.Entry, fifoPath string, w io.Writer) error {
591591

592592
logger.Debugf("Capturing %q to writer", fifoPath)
593593

594+
// this goroutine is to track the life of the application along with whether
595+
// or not the context has been cancelled which is signified by the exitCh. In
596+
// the event that the exitCh has been closed, we will close the fifo file.
597+
go func() {
598+
<-m.exitCh
599+
if err := fifoPipe.Close(); err != nil {
600+
logger.WithError(err).Debug("failed to close fifo")
601+
}
602+
}()
603+
594604
// Uses a goroutine to copy the contents of the fifo pipe to the io.Writer.
595605
// In the event that the goroutine finishes, which is caused by either the
596606
// context being closed or the application being closed, we will close the

machine_test.go

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package firecracker
1515

1616
import (
17+
"bytes"
1718
"context"
1819
"errors"
1920
"flag"
@@ -30,6 +31,7 @@ import (
3031
"testing"
3132
"time"
3233

34+
"github.com/sirupsen/logrus"
3335
"github.com/stretchr/testify/assert"
3436
"github.com/stretchr/testify/require"
3537

@@ -830,7 +832,10 @@ func TestCaptureFifoToFile(t *testing.T) {
830832
},
831833
}
832834

833-
if err := captureFifoToFile(fctesting.NewLogEntry(t), fifoPath, testWriter); err != nil {
835+
m := &Machine{
836+
exitCh: make(chan struct{}),
837+
}
838+
if err := m.captureFifoToFile(fctesting.NewLogEntry(t), fifoPath, testWriter); err != nil {
834839
t.Errorf("Unexpected error: %v", err)
835840
}
836841

@@ -869,7 +874,10 @@ func TestCaptureFifoToFile_nonblock(t *testing.T) {
869874
},
870875
}
871876

872-
if err := captureFifoToFile(fctesting.NewLogEntry(t), fifoPath, testWriter); err != nil {
877+
m := &Machine{
878+
exitCh: make(chan struct{}),
879+
}
880+
if err := m.captureFifoToFile(fctesting.NewLogEntry(t), fifoPath, testWriter); err != nil {
873881
t.Errorf("Unexpected error: %v", err)
874882
}
875883

@@ -1025,3 +1033,45 @@ func TestPID(t *testing.T) {
10251033
}
10261034

10271035
}
1036+
1037+
func TestCaptureFifoToFile_leak(t *testing.T) {
1038+
m := &Machine{
1039+
exitCh: make(chan struct{}),
1040+
}
1041+
1042+
fifoPath := filepath.Join(testDataPath, "TestCaptureFifoToFileLeak.fifo")
1043+
err := syscall.Mkfifo(fifoPath, 0700)
1044+
require.NoError(t, err, "failed to make fifo")
1045+
defer os.Remove(fifoPath)
1046+
1047+
fd, err := syscall.Open(fifoPath, syscall.O_RDWR|syscall.O_NONBLOCK, 0600)
1048+
require.NoError(t, err, "failed to open fifo path")
1049+
f := os.NewFile(uintptr(fd), fifoPath)
1050+
assert.NotNil(t, f, "failed to create new file")
1051+
go func() {
1052+
for {
1053+
select {
1054+
case <-m.exitCh:
1055+
break
1056+
default:
1057+
_, err := f.Write([]byte("A"))
1058+
assert.NoError(t, err, "failed to write bytes to fifo")
1059+
}
1060+
}
1061+
}()
1062+
1063+
buf := bytes.NewBuffer(nil)
1064+
1065+
loggerBuffer := bytes.NewBuffer(nil)
1066+
logger := fctesting.NewLogEntry(t)
1067+
logger.Logger.Level = logrus.WarnLevel
1068+
logger.Logger.Out = loggerBuffer
1069+
err = m.captureFifoToFile(logger, fifoPath, buf)
1070+
assert.NoError(t, err, "failed to capture fifo to file")
1071+
close(m.exitCh)
1072+
1073+
// wait sometime for the logs to populate
1074+
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)
1077+
}

0 commit comments

Comments
 (0)