mirror of
				https://github.com/opencontainers/runc.git
				synced 2025-10-31 02:56:25 +08:00 
			
		
		
		
	 bd3c4f844a
			
		
	
	bd3c4f844a
	
	
	
		
			
			There is a race in runc exec when the init process stops just before the check for the container status. It is then wrongly assumed that we are trying to start an init process instead of an exec process. This commit add an Init field to libcontainer Process to distinguish between init and exec processes to prevent this race. Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
		
			
				
	
	
		
			423 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			423 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build linux,cgo,seccomp
 | |
| 
 | |
| package integration
 | |
| 
 | |
| import (
 | |
| 	"strings"
 | |
| 	"syscall"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/opencontainers/runc/libcontainer"
 | |
| 	"github.com/opencontainers/runc/libcontainer/configs"
 | |
| 	libseccomp "github.com/seccomp/libseccomp-golang"
 | |
| )
 | |
| 
 | |
| func TestSeccompDenyGetcwd(t *testing.T) {
 | |
| 	if testing.Short() {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rootfs, err := newRootfs()
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer remove(rootfs)
 | |
| 
 | |
| 	config := newTemplateConfig(rootfs)
 | |
| 	config.Seccomp = &configs.Seccomp{
 | |
| 		DefaultAction: configs.Allow,
 | |
| 		Syscalls: []*configs.Syscall{
 | |
| 			{
 | |
| 				Name:   "getcwd",
 | |
| 				Action: configs.Errno,
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	container, err := newContainer(config)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer container.Destroy()
 | |
| 
 | |
| 	buffers := newStdBuffers()
 | |
| 	pwd := &libcontainer.Process{
 | |
| 		Cwd:    "/",
 | |
| 		Args:   []string{"pwd"},
 | |
| 		Env:    standardEnvironment,
 | |
| 		Stdin:  buffers.Stdin,
 | |
| 		Stdout: buffers.Stdout,
 | |
| 		Stderr: buffers.Stderr,
 | |
| 		Init:   true,
 | |
| 	}
 | |
| 
 | |
| 	err = container.Run(pwd)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	ps, err := pwd.Wait()
 | |
| 	if err == nil {
 | |
| 		t.Fatal("Expecting error (negative return code); instead exited cleanly!")
 | |
| 	}
 | |
| 
 | |
| 	var exitCode int
 | |
| 	status := ps.Sys().(syscall.WaitStatus)
 | |
| 	if status.Exited() {
 | |
| 		exitCode = status.ExitStatus()
 | |
| 	} else if status.Signaled() {
 | |
| 		exitCode = -int(status.Signal())
 | |
| 	} else {
 | |
| 		t.Fatalf("Unrecognized exit reason!")
 | |
| 	}
 | |
| 
 | |
| 	if exitCode == 0 {
 | |
| 		t.Fatalf("Getcwd should fail with negative exit code, instead got %d!", exitCode)
 | |
| 	}
 | |
| 
 | |
| 	expected := "pwd: getcwd: Operation not permitted"
 | |
| 	actual := strings.Trim(buffers.Stderr.String(), "\n")
 | |
| 	if actual != expected {
 | |
| 		t.Fatalf("Expected output %s but got %s\n", expected, actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSeccompPermitWriteConditional(t *testing.T) {
 | |
| 	if testing.Short() {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rootfs, err := newRootfs()
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer remove(rootfs)
 | |
| 
 | |
| 	config := newTemplateConfig(rootfs)
 | |
| 	config.Seccomp = &configs.Seccomp{
 | |
| 		DefaultAction: configs.Allow,
 | |
| 		Syscalls: []*configs.Syscall{
 | |
| 			{
 | |
| 				Name:   "write",
 | |
| 				Action: configs.Errno,
 | |
| 				Args: []*configs.Arg{
 | |
| 					{
 | |
| 						Index: 0,
 | |
| 						Value: 2,
 | |
| 						Op:    configs.EqualTo,
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	container, err := newContainer(config)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer container.Destroy()
 | |
| 
 | |
| 	buffers := newStdBuffers()
 | |
| 	dmesg := &libcontainer.Process{
 | |
| 		Cwd:    "/",
 | |
| 		Args:   []string{"busybox", "ls", "/"},
 | |
| 		Env:    standardEnvironment,
 | |
| 		Stdin:  buffers.Stdin,
 | |
| 		Stdout: buffers.Stdout,
 | |
| 		Stderr: buffers.Stderr,
 | |
| 		Init:   true,
 | |
| 	}
 | |
| 
 | |
| 	err = container.Run(dmesg)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if _, err := dmesg.Wait(); err != nil {
 | |
| 		t.Fatalf("%s: %s", err, buffers.Stderr)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSeccompDenyWriteConditional(t *testing.T) {
 | |
| 	if testing.Short() {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Only test if library version is v2.2.1 or higher
 | |
| 	// Conditional filtering will always error in v2.2.0 and lower
 | |
| 	major, minor, micro := libseccomp.GetLibraryVersion()
 | |
| 	if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rootfs, err := newRootfs()
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer remove(rootfs)
 | |
| 
 | |
| 	config := newTemplateConfig(rootfs)
 | |
| 	config.Seccomp = &configs.Seccomp{
 | |
| 		DefaultAction: configs.Allow,
 | |
| 		Syscalls: []*configs.Syscall{
 | |
| 			{
 | |
| 				Name:   "write",
 | |
| 				Action: configs.Errno,
 | |
| 				Args: []*configs.Arg{
 | |
| 					{
 | |
| 						Index: 0,
 | |
| 						Value: 2,
 | |
| 						Op:    configs.EqualTo,
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	container, err := newContainer(config)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer container.Destroy()
 | |
| 
 | |
| 	buffers := newStdBuffers()
 | |
| 	dmesg := &libcontainer.Process{
 | |
| 		Cwd:    "/",
 | |
| 		Args:   []string{"busybox", "ls", "does_not_exist"},
 | |
| 		Env:    standardEnvironment,
 | |
| 		Stdin:  buffers.Stdin,
 | |
| 		Stdout: buffers.Stdout,
 | |
| 		Stderr: buffers.Stderr,
 | |
| 		Init:   true,
 | |
| 	}
 | |
| 
 | |
| 	err = container.Run(dmesg)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	ps, err := dmesg.Wait()
 | |
| 	if err == nil {
 | |
| 		t.Fatal("Expecting negative return, instead got 0!")
 | |
| 	}
 | |
| 
 | |
| 	var exitCode int
 | |
| 	status := ps.Sys().(syscall.WaitStatus)
 | |
| 	if status.Exited() {
 | |
| 		exitCode = status.ExitStatus()
 | |
| 	} else if status.Signaled() {
 | |
| 		exitCode = -int(status.Signal())
 | |
| 	} else {
 | |
| 		t.Fatalf("Unrecognized exit reason!")
 | |
| 	}
 | |
| 
 | |
| 	if exitCode == 0 {
 | |
| 		t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
 | |
| 	}
 | |
| 
 | |
| 	// We're denying write to stderr, so we expect an empty buffer
 | |
| 	expected := ""
 | |
| 	actual := strings.Trim(buffers.Stderr.String(), "\n")
 | |
| 	if actual != expected {
 | |
| 		t.Fatalf("Expected output %s but got %s\n", expected, actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSeccompPermitWriteMultipleConditions(t *testing.T) {
 | |
| 	if testing.Short() {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rootfs, err := newRootfs()
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer remove(rootfs)
 | |
| 
 | |
| 	config := newTemplateConfig(rootfs)
 | |
| 	config.Seccomp = &configs.Seccomp{
 | |
| 		DefaultAction: configs.Allow,
 | |
| 		Syscalls: []*configs.Syscall{
 | |
| 			{
 | |
| 				Name:   "write",
 | |
| 				Action: configs.Errno,
 | |
| 				Args: []*configs.Arg{
 | |
| 					{
 | |
| 						Index: 0,
 | |
| 						Value: 2,
 | |
| 						Op:    configs.EqualTo,
 | |
| 					},
 | |
| 					{
 | |
| 						Index: 2,
 | |
| 						Value: 0,
 | |
| 						Op:    configs.NotEqualTo,
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	buffers, exitCode, err := runContainer(config, "", "ls", "/")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("%s: %s", buffers, err)
 | |
| 	}
 | |
| 	if exitCode != 0 {
 | |
| 		t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers)
 | |
| 	}
 | |
| 	// We don't need to verify the actual thing printed
 | |
| 	// Just that something was written to stdout
 | |
| 	if len(buffers.Stdout.String()) == 0 {
 | |
| 		t.Fatalf("Nothing was written to stdout, write call failed!\n")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSeccompDenyWriteMultipleConditions(t *testing.T) {
 | |
| 	if testing.Short() {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Only test if library version is v2.2.1 or higher
 | |
| 	// Conditional filtering will always error in v2.2.0 and lower
 | |
| 	major, minor, micro := libseccomp.GetLibraryVersion()
 | |
| 	if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rootfs, err := newRootfs()
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer remove(rootfs)
 | |
| 
 | |
| 	config := newTemplateConfig(rootfs)
 | |
| 	config.Seccomp = &configs.Seccomp{
 | |
| 		DefaultAction: configs.Allow,
 | |
| 		Syscalls: []*configs.Syscall{
 | |
| 			{
 | |
| 				Name:   "write",
 | |
| 				Action: configs.Errno,
 | |
| 				Args: []*configs.Arg{
 | |
| 					{
 | |
| 						Index: 0,
 | |
| 						Value: 2,
 | |
| 						Op:    configs.EqualTo,
 | |
| 					},
 | |
| 					{
 | |
| 						Index: 2,
 | |
| 						Value: 0,
 | |
| 						Op:    configs.NotEqualTo,
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist")
 | |
| 	if err == nil {
 | |
| 		t.Fatalf("Expecting error return, instead got 0")
 | |
| 	}
 | |
| 	if exitCode == 0 {
 | |
| 		t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
 | |
| 	}
 | |
| 
 | |
| 	expected := ""
 | |
| 	actual := strings.Trim(buffers.Stderr.String(), "\n")
 | |
| 	if actual != expected {
 | |
| 		t.Fatalf("Expected output %s but got %s\n", expected, actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSeccompMultipleConditionSameArgDeniesStdout(t *testing.T) {
 | |
| 	if testing.Short() {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rootfs, err := newRootfs()
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer remove(rootfs)
 | |
| 
 | |
| 	// Prevent writing to both stdout and stderr
 | |
| 	config := newTemplateConfig(rootfs)
 | |
| 	config.Seccomp = &configs.Seccomp{
 | |
| 		DefaultAction: configs.Allow,
 | |
| 		Syscalls: []*configs.Syscall{
 | |
| 			{
 | |
| 				Name:   "write",
 | |
| 				Action: configs.Errno,
 | |
| 				Args: []*configs.Arg{
 | |
| 					{
 | |
| 						Index: 0,
 | |
| 						Value: 1,
 | |
| 						Op:    configs.EqualTo,
 | |
| 					},
 | |
| 					{
 | |
| 						Index: 0,
 | |
| 						Value: 2,
 | |
| 						Op:    configs.EqualTo,
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	buffers, exitCode, err := runContainer(config, "", "ls", "/")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("%s: %s", buffers, err)
 | |
| 	}
 | |
| 	if exitCode != 0 {
 | |
| 		t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers)
 | |
| 	}
 | |
| 	// Verify that nothing was printed
 | |
| 	if len(buffers.Stdout.String()) != 0 {
 | |
| 		t.Fatalf("Something was written to stdout, write call succeeded!\n")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestSeccompMultipleConditionSameArgDeniesStderr(t *testing.T) {
 | |
| 	if testing.Short() {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	rootfs, err := newRootfs()
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	defer remove(rootfs)
 | |
| 
 | |
| 	// Prevent writing to both stdout and stderr
 | |
| 	config := newTemplateConfig(rootfs)
 | |
| 	config.Seccomp = &configs.Seccomp{
 | |
| 		DefaultAction: configs.Allow,
 | |
| 		Syscalls: []*configs.Syscall{
 | |
| 			{
 | |
| 				Name:   "write",
 | |
| 				Action: configs.Errno,
 | |
| 				Args: []*configs.Arg{
 | |
| 					{
 | |
| 						Index: 0,
 | |
| 						Value: 1,
 | |
| 						Op:    configs.EqualTo,
 | |
| 					},
 | |
| 					{
 | |
| 						Index: 0,
 | |
| 						Value: 2,
 | |
| 						Op:    configs.EqualTo,
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist")
 | |
| 	if err == nil {
 | |
| 		t.Fatalf("Expecting error return, instead got 0")
 | |
| 	}
 | |
| 	if exitCode == 0 {
 | |
| 		t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
 | |
| 	}
 | |
| 	// Verify nothing was printed
 | |
| 	if len(buffers.Stderr.String()) != 0 {
 | |
| 		t.Fatalf("Something was written to stderr, write call succeeded!\n")
 | |
| 	}
 | |
| }
 |