Repository: opencontainers/runc
Pull request #2874 (2021-03-26 11:27:59)

script/check-config.sh: add SELinux and AppArmor
SELinux and AppArmor are always enabled since runc v1.0.0-rc93

Signed-off-by: Kenta Tada <Kenta.Tada@sony.com>


Patch:
diff --git a/init.go b/init.go
index 59ce1e8c..08351fdb 100644
--- a/init.go
+++ b/init.go
@@ -4,7 +4,6 @@ import (
 	"fmt"
 	"os"
 	"runtime"
-	"strconv"
 
 	"github.com/opencontainers/runc/libcontainer"
 	"github.com/opencontainers/runc/libcontainer/logs"
@@ -24,13 +23,8 @@ func init() {
 			panic(fmt.Sprintf("libcontainer: failed to parse log level: %q: %v", level, err))
 		}
 
-		logPipeFdStr := os.Getenv("_LIBCONTAINER_LOGPIPE")
-		logPipeFd, err := strconv.Atoi(logPipeFdStr)
-		if err != nil {
-			panic(fmt.Sprintf("libcontainer: failed to convert environment variable _LIBCONTAINER_LOGPIPE=%s to int: %s", logPipeFdStr, err))
-		}
 		err = logs.ConfigureLogging(logs.Config{
-			LogPipeFd: logPipeFd,
+			LogPipeFd: os.Getenv("_LIBCONTAINER_LOGPIPE"),
 			LogFormat: "json",
 			LogLevel:  logLevel,
 		})
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
index 93556a65..b5c7764a 100644
--- a/libcontainer/container_linux.go
+++ b/libcontainer/container_linux.go
@@ -357,24 +357,12 @@ type openResult struct {
 	err  error
 }
 
-func (c *linuxContainer) start(process *Process) (retErr error) {
+func (c *linuxContainer) start(process *Process) error {
 	parent, err := c.newParentProcess(process)
 	if err != nil {
 		return newSystemErrorWithCause(err, "creating new parent process")
 	}
-
-	logsDone := parent.forwardChildLogs()
-	if logsDone != nil {
-		defer func() {
-			// Wait for log forwarder to finish. This depends on
-			// runc init closing the _LIBCONTAINER_LOGPIPE log fd.
-			err := <-logsDone
-			if err != nil && retErr == nil {
-				retErr = newSystemErrorWithCause(err, "forwarding init logs")
-			}
-		}()
-	}
-
+	parent.forwardChildLogs()
 	if err := parent.start(); err != nil {
 		return newSystemErrorWithCause(err, "starting container process")
 	}
diff --git a/libcontainer/factory_linux.go b/libcontainer/factory_linux.go
index 972b0f2b..0b4cf59e 100644
--- a/libcontainer/factory_linux.go
+++ b/libcontainer/factory_linux.go
@@ -365,12 +365,6 @@ func (l *LinuxFactory) StartInitialization() (err error) {
 		defer consoleSocket.Close()
 	}
 
-	logPipeFdStr := os.Getenv("_LIBCONTAINER_LOGPIPE")
-	logPipeFd, err := strconv.Atoi(logPipeFdStr)
-	if err != nil {
-		return fmt.Errorf("unable to convert _LIBCONTAINER_LOGPIPE=%s to int: %s", logPipeFdStr, err)
-	}
-
 	// clear the current process's environment to clean any libcontainer
 	// specific env vars.
 	os.Clearenv()
@@ -393,7 +387,7 @@ func (l *LinuxFactory) StartInitialization() (err error) {
 		}
 	}()
 
-	i, err := newContainerInit(it, pipe, consoleSocket, fifofd, logPipeFd)
+	i, err := newContainerInit(it, pipe, consoleSocket, fifofd)
 	if err != nil {
 		return err
 	}
diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go
index 00f28e1f..c57af0ee 100644
--- a/libcontainer/init_linux.go
+++ b/libcontainer/init_linux.go
@@ -76,7 +76,7 @@ type initer interface {
 	Init() error
 }
 
-func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd, logFd int) (initer, error) {
+func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd int) (initer, error) {
 	var config *initConfig
 	if err := json.NewDecoder(pipe).Decode(&config); err != nil {
 		return nil, err
@@ -90,7 +90,6 @@ func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd,
 			pipe:          pipe,
 			consoleSocket: consoleSocket,
 			config:        config,
-			logFd:         logFd,
 		}, nil
 	case initStandard:
 		return &linuxStandardInit{
@@ -99,7 +98,6 @@ func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd,
 			parentPid:     unix.Getppid(),
 			config:        config,
 			fifoFd:        fifoFd,
-			logFd:         logFd,
 		}, nil
 	}
 	return nil, fmt.Errorf("unknown init type %q", t)
diff --git a/libcontainer/logs/logs.go b/libcontainer/logs/logs.go
index b114a5f5..1077e7b0 100644
--- a/libcontainer/logs/logs.go
+++ b/libcontainer/logs/logs.go
@@ -6,14 +6,14 @@ import (
 	"fmt"
 	"io"
 	"os"
+	"strconv"
 	"sync"
 
-	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
 
 var (
-	configureMutex sync.Mutex
+	configureMutex = sync.Mutex{}
 	// loggingConfigured will be set once logging has been configured via invoking `ConfigureLogging`.
 	// Subsequent invocations of `ConfigureLogging` would be no-op
 	loggingConfigured = false
@@ -23,46 +23,41 @@ type Config struct {
 	LogLevel    logrus.Level
 	LogFormat   string
 	LogFilePath string
-	LogPipeFd   int
+	LogPipeFd   string
 }
 
-func ForwardLogs(logPipe io.ReadCloser) chan error {
-	done := make(chan error, 1)
-	s := bufio.NewScanner(logPipe)
-
-	go func() {
-		for s.Scan() {
-			processEntry(s.Bytes())
+func ForwardLogs(logPipe io.Reader) {
+	lineReader := bufio.NewReader(logPipe)
+	for {
+		line, err := lineReader.ReadBytes('\n')
+		if len(line) > 0 {
+			processEntry(line)
 		}
-		if err := logPipe.Close(); err != nil {
-			logrus.Errorf("error closing log source: %v", err)
+		if err == io.EOF {
+			logrus.Debugf("log pipe has been closed: %+v", err)
+			return
 		}
-		// The only error we want to return is when reading from
-		// logPipe has failed.
-		done <- s.Err()
-		close(done)
-	}()
-
-	return done
+		if err != nil {
+			logrus.Errorf("log pipe read error: %+v", err)
+		}
+	}
 }
 
 func processEntry(text []byte) {
-	if len(text) == 0 {
-		return
-	}
-
-	var jl struct {
+	type jsonLog struct {
 		Level string `json:"level"`
 		Msg   string `json:"msg"`
 	}
+
+	var jl jsonLog
 	if err := json.Unmarshal(text, &jl); err != nil {
-		logrus.Errorf("failed to decode %q to json: %v", text, err)
+		logrus.Errorf("failed to decode %q to json: %+v", text, err)
 		return
 	}
 
 	lvl, err := logrus.ParseLevel(jl.Level)
 	if err != nil {
-		logrus.Errorf("failed to parse log level %q: %v", jl.Level, err)
+		logrus.Errorf("failed to parse log level %q: %v\n", jl.Level, err)
 		return
 	}
 	logrus.StandardLogger().Logf(lvl, jl.Msg)
@@ -73,15 +68,18 @@ func ConfigureLogging(config Config) error {
 	defer configureMutex.Unlock()
 
 	if loggingConfigured {
-		return errors.New("logging has already been configured")
+		logrus.Debug("logging has already been configured")
+		return nil
 	}
 
 	logrus.SetLevel(config.LogLevel)
 
-	// XXX: while 0 is a valid fd (usually stdin), here we assume
-	// that we never deliberately set LogPipeFd to 0.
-	if config.LogPipeFd > 0 {
-		logrus.SetOutput(os.NewFile(uintptr(config.LogPipeFd), "logpipe"))
+	if config.LogPipeFd != "" {
+		logPipeFdInt, err := strconv.Atoi(config.LogPipeFd)
+		if err != nil {
+			return fmt.Errorf("failed to convert _LIBCONTAINER_LOGPIPE environment variable value %q to int: %v", config.LogPipeFd, err)
+		}
+		logrus.SetOutput(os.NewFile(uintptr(logPipeFdInt), "logpipe"))
 	} else if config.LogFilePath != "" {
 		f, err := os.OpenFile(config.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0644)
 		if err != nil {
diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go
index 8315d709..6f282877 100644
--- a/libcontainer/process_linux.go
+++ b/libcontainer/process_linux.go
@@ -51,7 +51,7 @@ type parentProcess interface {
 
 	setExternalDescriptors(fds []string)
 
-	forwardChildLogs() chan error
+	forwardChildLogs()
 }
 
 type filePair struct {
@@ -251,8 +251,8 @@ func (p *setnsProcess) setExternalDescriptors(newFds []string) {
 	p.fds = newFds
 }
 
-func (p *setnsProcess) forwardChildLogs() chan error {
-	return logs.ForwardLogs(p.logFilePair.parent)
+func (p *setnsProcess) forwardChildLogs() {
+	go logs.ForwardLogs(p.logFilePair.parent)
 }
 
 type initProcess struct {
@@ -605,8 +605,8 @@ func (p *initProcess) setExternalDescriptors(newFds []string) {
 	p.fds = newFds
 }
 
-func (p *initProcess) forwardChildLogs() chan error {
-	return logs.ForwardLogs(p.logFilePair.parent)
+func (p *initProcess) forwardChildLogs() {
+	go logs.ForwardLogs(p.logFilePair.parent)
 }
 
 func getPipeFds(pid int) ([]string, error) {
diff --git a/libcontainer/restored_process.go b/libcontainer/restored_process.go
index 97565d7f..f861e82d 100644
--- a/libcontainer/restored_process.go
+++ b/libcontainer/restored_process.go
@@ -77,8 +77,7 @@ func (p *restoredProcess) setExternalDescriptors(newFds []string) {
 	p.fds = newFds
 }
 
-func (p *restoredProcess) forwardChildLogs() chan error {
-	return nil
+func (p *restoredProcess) forwardChildLogs() {
 }
 
 // nonChildProcess represents a process where the calling process is not
@@ -126,6 +125,5 @@ func (p *nonChildProcess) setExternalDescriptors(newFds []string) {
 	p.fds = newFds
 }
 
-func (p *nonChildProcess) forwardChildLogs() chan error {
-	return nil
+func (p *nonChildProcess) forwardChildLogs() {
 }
diff --git a/libcontainer/seccomp/fixtures/proc_self_status b/libcontainer/seccomp/fixtures/proc_self_status
new file mode 100644
index 00000000..0e0084f6
--- /dev/null
+++ b/libcontainer/seccomp/fixtures/proc_self_status
@@ -0,0 +1,47 @@
+Name:   cat
+State:  R (running)
+Tgid:   19383
+Ngid:   0
+Pid:    19383
+PPid:   19275
+TracerPid:  0
+Uid:    1000    1000    1000    1000
+Gid:    1000    1000    1000    1000
+FDSize: 256
+Groups: 24 25 27 29 30 44 46 102 104 108 111 1000 1001
+NStgid: 19383
+NSpid:  19383
+NSpgid: 19383
+NSsid:  19275
+VmPeak:     5944 kB
+VmSize:     5944 kB
+VmLck:         0 kB
+VmPin:         0 kB
+VmHWM:       744 kB
+VmRSS:       744 kB
+VmData:      324 kB
+VmStk:       136 kB
+VmExe:        48 kB
+VmLib:      1776 kB
+VmPTE:        32 kB
+VmPMD:        12 kB
+VmSwap:        0 kB
+Threads:    1
+SigQ:   0/30067
+SigPnd: 0000000000000000
+ShdPnd: 0000000000000000
+SigBlk: 0000000000000000
+SigIgn: 0000000000000080
+SigCgt: 0000000000000000
+CapInh: 0000000000000000
+CapPrm: 0000000000000000
+CapEff: 0000000000000000
+CapBnd: 0000003fffffffff
+CapAmb: 0000000000000000
+Seccomp:    0
+Cpus_allowed:   f
+Cpus_allowed_list:  0-3
+Mems_allowed:   00000000,00000001
+Mems_allowed_list:  0
+voluntary_ctxt_switches:    0
+nonvoluntary_ctxt_switches: 1
diff --git a/libcontainer/seccomp/seccomp_linux.go b/libcontainer/seccomp/seccomp_linux.go
index b6d19784..5e7b365c 100644
--- a/libcontainer/seccomp/seccomp_linux.go
+++ b/libcontainer/seccomp/seccomp_linux.go
@@ -3,8 +3,11 @@
 package seccomp
 
 import (
+	"bufio"
 	"errors"
 	"fmt"
+	"os"
+	"strings"
 
 	"github.com/opencontainers/runc/libcontainer/configs"
 	"github.com/opencontainers/runc/libcontainer/seccomp/patchbpf"
@@ -77,6 +80,24 @@ func InitSeccomp(config *configs.Seccomp) error {
 	return nil
 }
 
+// IsEnabled returns if the kernel has been configured to support seccomp.
+func IsEnabled() bool {
+	// Try to read from /proc/self/status for kernels > 3.8
+	s, err := parseStatusFile("/proc/self/status")
+	if err != nil {
+		// Check if Seccomp is supported, via CONFIG_SECCOMP.
+		if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
+			// Make sure the kernel has CONFIG_SECCOMP_FILTER.
+			if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
+				return true
+			}
+		}
+		return false
+	}
+	_, ok := s["Seccomp"]
+	return ok
+}
+
 // Convert Libcontainer Action to Libseccomp ScmpAction
 func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
 	switch act {
@@ -216,6 +237,33 @@ func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall) error {
 	return nil
 }
 
+func parseStatusFile(path string) (map[string]string, error) {
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	s := bufio.NewScanner(f)
+	status := make(map[string]string)
+
+	for s.Scan() {
+		text := s.Text()
+		parts := strings.Split(text, ":")
+
+		if len(parts) <= 1 {
+			continue
+		}
+
+		status[parts[0]] = parts[1]
+	}
+	if err := s.Err(); err != nil {
+		return nil, err
+	}
+
+	return status, nil
+}
+
 // Version returns major, minor, and micro.
 func Version() (uint, uint, uint) {
 	return libseccomp.GetLibraryVersion()
diff --git a/libcontainer/seccomp/seccomp_unsupported.go b/libcontainer/seccomp/seccomp_unsupported.go
index 8b7973e9..244886a4 100644
--- a/libcontainer/seccomp/seccomp_unsupported.go
+++ b/libcontainer/seccomp/seccomp_unsupported.go
@@ -18,6 +18,11 @@ func InitSeccomp(config *configs.Seccomp) error {
 	return nil
 }
 
+// IsEnabled returns false, because it is not supported.
+func IsEnabled() bool {
+	return false
+}
+
 // Version returns major, minor, and micro.
 func Version() (uint, uint, uint) {
 	return 0, 0, 0
diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go
index 97987f1d..6b1e9a6e 100644
--- a/libcontainer/setns_init_linux.go
+++ b/libcontainer/setns_init_linux.go
@@ -12,7 +12,6 @@ import (
 	"github.com/opencontainers/runc/libcontainer/system"
 	"github.com/opencontainers/selinux/go-selinux"
 	"github.com/pkg/errors"
-	"github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
 )
 
@@ -22,7 +21,6 @@ type linuxSetnsInit struct {
 	pipe          *os.File
 	consoleSocket *os.File
 	config        *initConfig
-	logFd         int
 }
 
 func (l *linuxSetnsInit) getSessionRingName() string {
@@ -88,11 +86,5 @@ func (l *linuxSetnsInit) Init() error {
 			return newSystemErrorWithCause(err, "init seccomp")
 		}
 	}
-	logrus.Debugf("setns_init: about to exec")
-	// Close the log pipe fd so the parent's ForwardLogs can exit.
-	if err := unix.Close(l.logFd); err != nil {
-		return newSystemErrorWithCause(err, "closing log pipe fd")
-	}
-
 	return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
 }
diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go
index d77022ad..7ec506c4 100644
--- a/libcontainer/standard_init_linux.go
+++ b/libcontainer/standard_init_linux.go
@@ -16,7 +16,6 @@ import (
 	"github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/opencontainers/selinux/go-selinux"
 	"github.com/pkg/errors"
-	"github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
 )
 
@@ -25,7 +24,6 @@ type linuxStandardInit struct {
 	consoleSocket *os.File
 	parentPid     int
 	fifoFd        int
-	logFd         int
 	config        *initConfig
 }
 
@@ -182,14 +180,7 @@ func (l *linuxStandardInit) Init() error {
 		return err
 	}
 	// Close the pipe to signal that we have completed our init.
-	logrus.Debugf("init: closing the pipe to signal completion")
 	l.pipe.Close()
-
-	// Close the log pipe fd so the parent's ForwardLogs can exit.
-	if err := unix.Close(l.logFd); err != nil {
-		return newSystemErrorWithCause(err, "closing log pipe fd")
-	}
-
 	// Wait for the FIFO to be opened on the other side before exec-ing the
 	// user process. We open it through /proc/self/fd/$fd, because the fd that
 	// was given to us was an O_PATH fd to the fifo itself. Linux allows us to
diff --git a/main.go b/main.go
index 2b549709..57d7f428 100644
--- a/main.go
+++ b/main.go
@@ -64,9 +64,8 @@ func main() {
 	}
 	v = append(v, "spec: "+specs.Version)
 	v = append(v, "go: "+runtime.Version())
-
-	major, minor, micro := seccomp.Version()
-	if major+minor+micro > 0 {
+	if seccomp.IsEnabled() {
+		major, minor, micro := seccomp.Version()
 		v = append(v, fmt.Sprintf("libseccomp: %d.%d.%d", major, minor, micro))
 	}
 	app.Version = strings.Join(v, "\n")
@@ -143,7 +142,7 @@ func main() {
 				fmt.Fprintln(os.Stderr, "the path in $XDG_RUNTIME_DIR must be writable by the user")
 				fatal(err)
 			}
-			if err := os.Chmod(root, os.FileMode(0o700)|os.ModeSticky); err != nil {
+			if err := os.Chmod(root, 0700|os.ModeSticky); err != nil {
 				fmt.Fprintln(os.Stderr, "you should check permission of the path in $XDG_RUNTIME_DIR")
 				fatal(err)
 			}
@@ -151,10 +150,6 @@ func main() {
 		if err := reviseRootDir(context); err != nil {
 			return err
 		}
-		// let init configure logging on its own
-		if args := context.Args(); args != nil && args.First() == "init" {
-			return nil
-		}
 		return logs.ConfigureLogging(createLogConfig(context))
 	}
 
@@ -181,9 +176,9 @@ func (f *FatalWriter) Write(p []byte) (n int, err error) {
 
 func createLogConfig(context *cli.Context) logs.Config {
 	logFilePath := context.GlobalString("log")
-	logPipeFd := 0
+	logPipeFd := ""
 	if logFilePath == "" {
-		logPipeFd = 2
+		logPipeFd = "2"
 	}
 	config := logs.Config{
 		LogPipeFd:   logPipeFd,
diff --git a/script/check-config.sh b/script/check-config.sh
index a7862009..0fe13912 100755
--- a/script/check-config.sh
+++ b/script/check-config.sh
@@ -271,5 +271,7 @@ flags=(
 	IP_VS_PROTO_TCP
 	IP_VS_PROTO_UDP
 	IP_VS_RR
+	SECURITY_SELINUX
+	SECURITY_APPARMOR
 )
 check_flags "${flags[@]}"


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 96cfcd86..704bf07c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -81,13 +81,6 @@ jobs:
     needs: [test]
     steps:
       - uses: actions/checkout@v2
-
-      - name: "Cache ~/.vagrant.d/boxes, using hash of Vagrantfile.fedora33"
-        uses: actions/cache@v2
-        with:
-          path: ~/.vagrant.d/boxes
-          key: vagrant-${{ hashFiles('Vagrantfile.fedora33') }}
-
       - name: prepare vagrant
         run: |
           ln -sf Vagrantfile.fedora33 Vagrantfile
@@ -123,13 +116,6 @@ jobs:
     needs: [test]
     steps:
       - uses: actions/checkout@v2
-
-      - name: "Cache ~/.vagrant.d/boxes, using hash of Vagrantfile.centos7"
-        uses: actions/cache@v2
-        with:
-          path: ~/.vagrant.d/boxes
-          key: vagrant-${{ hashFiles('Vagrantfile.centos7') }}
-
       - name: prepare vagrant
         run: |
           ln -sf Vagrantfile.centos7 Vagrantfile
diff --git a/libcontainer/container_linux_test.go b/libcontainer/container_linux_test.go
index 78071a92..2a9af6ad 100644
--- a/libcontainer/container_linux_test.go
+++ b/libcontainer/container_linux_test.go
@@ -139,8 +139,7 @@ func (m *mockProcess) externalDescriptors() []string {
 func (m *mockProcess) setExternalDescriptors(newFds []string) {
 }
 
-func (m *mockProcess) forwardChildLogs() chan error {
-	return nil
+func (m *mockProcess) forwardChildLogs() {
 }
 
 func TestGetContainerPids(t *testing.T) {
diff --git a/libcontainer/logs/logs_linux_test.go b/libcontainer/logs/logs_linux_test.go
index 7e2b59d9..ec9ae4fc 100644
--- a/libcontainer/logs/logs_linux_test.go
+++ b/libcontainer/logs/logs_linux_test.go
@@ -1,10 +1,10 @@
 package logs
 
 import (
-	"bytes"
-	"io"
+	"errors"
 	"io/ioutil"
 	"os"
+	"strings"
 	"testing"
 	"time"
 
@@ -12,110 +12,141 @@ import (
 )
 
 func TestLoggingToFile(t *testing.T) {
-	l := runLogForwarding(t)
+	logW, logFile, _ := runLogForwarding(t)
+	defer os.Remove(logFile)
+	defer logW.Close()
 
-	logToLogWriter(t, l, `{"level": "info","msg":"kitten"}`)
-	finish(t, l)
-	check(t, l, "kitten")
+	logToLogWriter(t, logW, `{"level": "info","msg":"kitten"}`)
+
+	logFileContent := waitForLogContent(t, logFile)
+	if !strings.Contains(logFileContent, "kitten") {
+		t.Fatalf("%s does not contain kitten", logFileContent)
+	}
 }
 
 func TestLogForwardingDoesNotStopOnJsonDecodeErr(t *testing.T) {
-	l := runLogForwarding(t)
+	logW, logFile, _ := runLogForwarding(t)
+	defer os.Remove(logFile)
+	defer logW.Close()
+
+	logToLogWriter(t, logW, "invalid-json-with-kitten")
 
-	logToLogWriter(t, l, "invalid-json-with-kitten")
-	checkWait(t, l, "failed to decode")
+	logFileContent := waitForLogContent(t, logFile)
+	if !strings.Contains(logFileContent, "failed to decode") {
+		t.Fatalf("%q does not contain decoding error", logFileContent)
+	}
+
+	truncateLogFile(t, logFile)
 
-	truncateLogFile(t, l.file)
+	logToLogWriter(t, logW, `{"level": "info","msg":"puppy"}`)
 
-	logToLogWriter(t, l, `{"level": "info","msg":"puppy"}`)
-	finish(t, l)
-	check(t, l, "puppy")
+	logFileContent = waitForLogContent(t, logFile)
+	if !strings.Contains(logFileContent, "puppy") {
+		t.Fatalf("%s does not contain puppy", logFileContent)
+	}
 }
 
 func TestLogForwardingDoesNotStopOnLogLevelParsingErr(t *testing.T) {
-	l := runLogForwarding(t)
+	logW, logFile, _ := runLogForwarding(t)
+	defer os.Remove(logFile)
+	defer logW.Close()
 
-	logToLogWriter(t, l, `{"level": "alert","msg":"puppy"}`)
-	checkWait(t, l, "failed to parse log level")
+	logToLogWriter(t, logW, `{"level": "alert","msg":"puppy"}`)
 
-	truncateLogFile(t, l.file)
+	logFileContent := waitForLogContent(t, logFile)
+	if !strings.Contains(logFileContent, "failed to parse log level") {
+		t.Fatalf("%q does not contain log level parsing error", logFileContent)
+	}
+
+	truncateLogFile(t, logFile)
+
+	logToLogWriter(t, logW, `{"level": "info","msg":"puppy"}`)
 
-	logToLogWriter(t, l, `{"level": "info","msg":"puppy"}`)
-	finish(t, l)
-	check(t, l, "puppy")
+	logFileContent = waitForLogContent(t, logFile)
+	if !strings.Contains(logFileContent, "puppy") {
+		t.Fatalf("%s does not contain puppy", logFileContent)
+	}
 }
 
 func TestLogForwardingStopsAfterClosingTheWriter(t *testing.T) {
-	l := runLogForwarding(t)
+	logW, logFile, doneForwarding := runLogForwarding(t)
+	defer os.Remove(logFile)
 
-	logToLogWriter(t, l, `{"level": "info","msg":"sync"}`)
+	logToLogWriter(t, logW, `{"level": "info","msg":"sync"}`)
 
-	// Do not use finish() here as we check done pipe ourselves.
-	l.w.Close()
+	logFileContent := waitForLogContent(t, logFile)
+	if !strings.Contains(logFileContent, "sync") {
+		t.Fatalf("%q does not contain sync message", logFileContent)
+	}
+
+	logW.Close()
 	select {
-	case <-l.done:
+	case <-doneForwarding:
 	case <-time.After(10 * time.Second):
 		t.Fatal("log forwarding did not stop after closing the pipe")
 	}
-
-	check(t, l, "sync")
 }
 
-func logToLogWriter(t *testing.T, l *log, message string) {
-	t.Helper()
-	_, err := l.w.Write([]byte(message + "\n"))
+func logToLogWriter(t *testing.T, logW *os.File, message string) {
+	_, err := logW.Write([]byte(message + "\n"))
 	if err != nil {
 		t.Fatalf("failed to write %q to log writer: %v", message, err)
 	}
 }
 
-type log struct {
-	w    io.WriteCloser
-	file string
-	done chan error
-}
-
-func runLogForwarding(t *testing.T) *log {
-	t.Helper()
+func runLogForwarding(t *testing.T) (*os.File, string, chan struct{}) {
 	logR, logW, err := os.Pipe()
 	if err != nil {
 		t.Fatal(err)
 	}
-	t.Cleanup(func() {
-		logR.Close()
-		logW.Close()
-	})
 
 	tempFile, err := ioutil.TempFile("", "")
 	if err != nil {
 		t.Fatal(err)
 	}
-	tempFile.Close()
 	logFile := tempFile.Name()
-	t.Cleanup(func() { os.Remove(logFile) })
 
 	logConfig := Config{LogLevel: logrus.InfoLevel, LogFormat: "json", LogFilePath: logFile}
+	return logW, logFile, startLogForwarding(t, logConfig, logR)
+}
+
+func startLogForwarding(t *testing.T, logConfig Config, logR *os.File) chan struct{} {
 	loggingConfigured = false
 	if err := ConfigureLogging(logConfig); err != nil {
 		t.Fatal(err)
 	}
+	doneForwarding := make(chan struct{})
+	go func() {
+		ForwardLogs(logR)
+		close(doneForwarding)
+	}()
+	return doneForwarding
+}
 
-	doneForwarding := ForwardLogs(logR)
+func waitForLogContent(t *testing.T, logFile string) string {
+	startTime := time.Now()
 
-	return &log{w: logW, done: doneForwarding, file: logFile}
-}
+	for {
+		if time.Now().After(startTime.Add(10 * time.Second)) {
+			t.Fatal(errors.New("No content in log file after 10 seconds"))
+			break
+		}
 
-func finish(t *testing.T, l *log) {
-	t.Helper()
-	l.w.Close()
-	if err := <-l.done; err != nil {
-		t.Fatalf("ForwardLogs: %v", err)
+		fileContent, err := ioutil.ReadFile(logFile)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if len(fileContent) == 0 {
+			continue
+		}
+		return string(fileContent)
 	}
+
+	return ""
 }
 
 func truncateLogFile(t *testing.T, logFile string) {
-	t.Helper()
-	file, err := os.OpenFile(logFile, os.O_RDWR, 0o600)
+	file, err := os.OpenFile(logFile, os.O_RDWR, 0666)
 	if err != nil {
 		t.Fatalf("failed to open log file: %v", err)
 		return
@@ -127,40 +158,3 @@ func truncateLogFile(t *testing.T, logFile string) {
 		t.Fatalf("failed to truncate log file: %v", err)
 	}
 }
-
-// check checks that file contains txt
-func check(t *testing.T, l *log, txt string) {
-	t.Helper()
-	contents, err := ioutil.ReadFile(l.file)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !bytes.Contains(contents, []byte(txt)) {
-		t.Fatalf("%q does not contain %q", string(contents), txt)
-	}
-}
-
-// checkWait checks that file contains txt. If the file is empty,
-// it waits until it's not.
-func checkWait(t *testing.T, l *log, txt string) {
-	t.Helper()
-	const (
-		delay = 100 * time.Millisecond
-		iter  = 3
-	)
-	for i := 0; ; i++ {
-		st, err := os.Stat(l.file)
-		if err != nil {
-			t.Fatal(err)
-		}
-		if st.Size() > 0 {
-			break
-		}
-		if i == iter {
-			t.Fatalf("waited %s for file %s to be non-empty but it still is", iter*delay, l.file)
-		}
-		time.Sleep(delay)
-	}
-
-	check(t, l, txt)
-}
diff --git a/libcontainer/seccomp/seccomp_linux_test.go b/libcontainer/seccomp/seccomp_linux_test.go
new file mode 100644
index 00000000..67a2ef6a
--- /dev/null
+++ b/libcontainer/seccomp/seccomp_linux_test.go
@@ -0,0 +1,17 @@
+// +build linux,cgo,seccomp
+
+package seccomp
+
+import "testing"
+
+func TestParseStatusFile(t *testing.T) {
+	s, err := parseStatusFile("fixtures/proc_self_status")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, ok := s["Seccomp"]; !ok {
+
+		t.Fatal("expected to find 'Seccomp' in the map but did not.")
+	}
+}
diff --git a/tests/integration/debug.bats b/tests/integration/debug.bats
index b2f87cf5..16f1b2fb 100644
--- a/tests/integration/debug.bats
+++ b/tests/integration/debug.bats
@@ -10,20 +10,16 @@ function teardown() {
 	teardown_bundle
 }
 
-function check_debug() {
-	[[ "$*" == *"nsexec started"* ]]
-	[[ "$*" == *"child process in init()"* ]]
-	[[ "$*" == *"init: closing the pipe to signal completion"* ]]
-}
-
 @test "global --debug" {
 	# run hello-world
 	runc --debug run test_hello
+	echo "${output}"
 	[ "$status" -eq 0 ]
 
 	# check expected debug output was sent to stderr
 	[[ "${output}" == *"level=debug"* ]]
-	check_debug "$output"
+	[[ "${output}" == *"nsexec started"* ]]
+	[[ "${output}" == *"child process in init()"* ]]
 }
 
 @test "global --debug to --log" {
@@ -34,11 +30,14 @@ function check_debug() {
 	# check output does not include debug info
 	[[ "${output}" != *"level=debug"* ]]
 
-	cat log.out >&2
+	# check log.out was generated
+	[ -e log.out ]
+
 	# check expected debug output was sent to log.out
 	output=$(cat log.out)
 	[[ "${output}" == *"level=debug"* ]]
-	check_debug "$output"
+	[[ "${output}" == *"nsexec started"* ]]
+	[[ "${output}" == *"child process in init()"* ]]
 }
 
 @test "global --debug to --log --log-format 'text'" {
@@ -49,11 +48,14 @@ function check_debug() {
 	# check output does not include debug info
 	[[ "${output}" != *"level=debug"* ]]
 
-	cat log.out >&2
+	# check log.out was generated
+	[ -e log.out ]
+
 	# check expected debug output was sent to log.out
 	output=$(cat log.out)
 	[[ "${output}" == *"level=debug"* ]]
-	check_debug "$output"
+	[[ "${output}" == *"nsexec started"* ]]
+	[[ "${output}" == *"child process in init()"* ]]
 }
 
 @test "global --debug to --log --log-format 'json'" {
@@ -64,9 +66,12 @@ function check_debug() {
 	# check output does not include debug info
 	[[ "${output}" != *"level=debug"* ]]
 
-	cat log.out >&2
+	# check log.out was generated
+	[ -e log.out ]
+
 	# check expected debug output was sent to log.out
 	output=$(cat log.out)
 	[[ "${output}" == *'"level":"debug"'* ]]
-	check_debug "$output"
+	[[ "${output}" == *"nsexec started"* ]]
+	[[ "${output}" == *"child process in init()"* ]]
 }
diff --git a/tests/integration/exec.bats b/tests/integration/exec.bats
index f80754a8..b7c9becb 100644
--- a/tests/integration/exec.bats
+++ b/tests/integration/exec.bats
@@ -132,38 +132,6 @@ function teardown() {
 	echo hello >preserve-fds.test
 	# fd 3 is used by bats, so we use 4
 	exec 4<preserve-fds.test
-	runc exec --preserve-fds=2 test_busybox cat /proc/self/fd/4
-	[ "$status" -eq 0 ]
+	output=$(__runc exec --preserve-fds=2 test_busybox cat /proc/self/fd/4)
 	[[ "${output}" == "hello" ]]
 }
-
-function check_exec_debug() {
-	[[ "$*" == *"nsexec started"* ]]
-	[[ "$*" == *"child process in init()"* ]]
-	[[ "$*" == *"setns_init: about to exec"* ]]
-}
-
-@test "runc --debug exec" {
-	runc run -d --console-socket "$CONSOLE_SOCKET" test
-	[ "$status" -eq 0 ]
-
-	runc --debug exec test true
-	[ "$status" -eq 0 ]
-	[[ "${output}" == *"level=debug"* ]]
-	check_exec_debug "$output"
-}
-
-@test "runc --debug --log exec" {
-	runc run -d --console-socket "$CONSOLE_SOCKET" test
-	[ "$status" -eq 0 ]
-
-	runc --debug --log log.out exec test true
-	# check output does not include debug info
-	[[ "${output}" != *"level=debug"* ]]
-
-	cat log.out >&2
-	# check expected debug output was sent to log.out
-	output=$(cat log.out)
-	[[ "${output}" == *"level=debug"* ]]
-	check_exec_debug "$output"
-}