From 41aa19662b6aa05b8ec70962f0c74f6f77098835 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 12 May 2020 10:57:56 +0200 Subject: [PATCH] libcontainer: honor seccomp errnoRet Signed-off-by: Giuseppe Scrivano --- libcontainer/configs/config.go | 7 ++- libcontainer/integration/seccomp_test.go | 72 ++++++++++++++++++++++++ libcontainer/seccomp/seccomp_linux.go | 12 +++- libcontainer/specconv/spec_linux.go | 7 ++- 4 files changed, 89 insertions(+), 9 deletions(-) diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 24989e9f..8c19c4a1 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -70,9 +70,10 @@ type Arg struct { // Syscall is a rule to match a syscall in Seccomp type Syscall struct { - Name string `json:"name"` - Action Action `json:"action"` - Args []*Arg `json:"args"` + Name string `json:"name"` + Action Action `json:"action"` + ErrnoRet *uint `json:"errnoRet"` + Args []*Arg `json:"args"` } // TODO Windows. Many of these fields should be factored out into those parts diff --git a/libcontainer/integration/seccomp_test.go b/libcontainer/integration/seccomp_test.go index 77f1a8d4..75319e7e 100644 --- a/libcontainer/integration/seccomp_test.go +++ b/libcontainer/integration/seccomp_test.go @@ -12,6 +12,78 @@ import ( libseccomp "github.com/seccomp/libseccomp-golang" ) +func TestSeccompDenyGetcwdWithErrno(t *testing.T) { + if testing.Short() { + return + } + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + errnoRet := uint(syscall.ESRCH) + + config := newTemplateConfig(rootfs) + config.Seccomp = &configs.Seccomp{ + DefaultAction: configs.Allow, + Syscalls: []*configs.Syscall{ + { + Name: "getcwd", + Action: configs.Errno, + ErrnoRet: &errnoRet, + }, + }, + } + + 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: No such process" + actual := strings.Trim(buffers.Stderr.String(), "\n") + if actual != expected { + t.Fatalf("Expected output %s but got %s\n", expected, actual) + } +} + func TestSeccompDenyGetcwd(t *testing.T) { if testing.Short() { return diff --git a/libcontainer/seccomp/seccomp_linux.go b/libcontainer/seccomp/seccomp_linux.go index 33afbbb9..73ddf3d1 100644 --- a/libcontainer/seccomp/seccomp_linux.go +++ b/libcontainer/seccomp/seccomp_linux.go @@ -38,7 +38,7 @@ func InitSeccomp(config *configs.Seccomp) error { return errors.New("cannot initialize Seccomp - nil config passed") } - defaultAction, err := getAction(config.DefaultAction) + defaultAction, err := getAction(config.DefaultAction, nil) if err != nil { return errors.New("error initializing seccomp - invalid default action") } @@ -102,17 +102,23 @@ func IsEnabled() bool { } // Convert Libcontainer Action to Libseccomp ScmpAction -func getAction(act configs.Action) (libseccomp.ScmpAction, error) { +func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) { switch act { case configs.Kill: return actKill, nil case configs.Errno: + if errnoRet != nil { + return libseccomp.ActErrno.SetReturnCode(int16(*errnoRet)), nil + } return actErrno, nil case configs.Trap: return actTrap, nil case configs.Allow: return actAllow, nil case configs.Trace: + if errnoRet != nil { + return libseccomp.ActTrace.SetReturnCode(int16(*errnoRet)), nil + } return actTrace, nil case configs.Log: return actLog, nil @@ -177,7 +183,7 @@ func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall) error { } // Convert the call's action to the libseccomp equivalent - callAct, err := getAction(call.Action) + callAct, err := getAction(call.Action, call.ErrnoRet) if err != nil { return fmt.Errorf("action in seccomp profile is invalid: %s", err) } diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 6df2e2af..2ad1651f 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -855,9 +855,10 @@ func SetupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) { for _, name := range call.Names { newCall := configs.Syscall{ - Name: name, - Action: newAction, - Args: []*configs.Arg{}, + Name: name, + Action: newAction, + ErrnoRet: call.ErrnoRet, + Args: []*configs.Arg{}, } // Loop through all the arguments of the syscall and convert them for _, arg := range call.Args {