diff --git a/libcontainer/seccomp/seccomp_linux.go b/libcontainer/seccomp/seccomp_linux.go index 23b8610481c..d64f4970ffd 100644 --- a/libcontainer/seccomp/seccomp_linux.go +++ b/libcontainer/seccomp/seccomp_linux.go @@ -36,7 +36,7 @@ func InitSeccomp(config *configs.Seccomp) (int, error) { defaultAction, err := getAction(config.DefaultAction, config.DefaultErrnoRet) if err != nil { - return -1, errors.New("error initializing seccomp - invalid default action") + return -1, fmt.Errorf("error initializing seccomp - invalid default action: %w", err) } // Ignore the error since pre-2.4 libseccomp is treated as API level 0. @@ -197,6 +197,13 @@ func FlagSupported(flag specs.LinuxSeccompFlag) error { // Convert Libcontainer Action to Libseccomp ScmpAction func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) { + if errnoRet != nil && act != configs.Errno && act != configs.Trace { + switch act { + case configs.Kill, configs.Trap, configs.Allow, configs.Log, configs.Notify, configs.KillThread, configs.KillProcess: + return libseccomp.ActInvalid, errors.New("errnoRet is only supported for SCMP_ACT_ERRNO and SCMP_ACT_TRACE") + } + } + switch act { case configs.Kill, configs.KillThread: return libseccomp.ActKillThread, nil diff --git a/libcontainer/seccomp/seccomp_linux_test.go b/libcontainer/seccomp/seccomp_linux_test.go new file mode 100644 index 00000000000..fc3797b24d9 --- /dev/null +++ b/libcontainer/seccomp/seccomp_linux_test.go @@ -0,0 +1,80 @@ +//go:build cgo && seccomp + +package seccomp + +import ( + "strings" + "testing" + + "github.com/opencontainers/runc/libcontainer/configs" +) + +func TestGetActionErrnoRet(t *testing.T) { + errnoRet := uint(1) + tests := []struct { + name string + action configs.Action + wantErr bool + }{ + {name: "kill", action: configs.Kill, wantErr: true}, + {name: "errno", action: configs.Errno}, + {name: "trap", action: configs.Trap, wantErr: true}, + {name: "allow", action: configs.Allow, wantErr: true}, + {name: "trace", action: configs.Trace}, + {name: "log", action: configs.Log, wantErr: true}, + {name: "notify", action: configs.Notify, wantErr: true}, + {name: "kill thread", action: configs.KillThread, wantErr: true}, + {name: "kill process", action: configs.KillProcess, wantErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := getAction(tt.action, &errnoRet) + if tt.wantErr && err == nil { + t.Fatal("expected errnoRet to be rejected for this action") + } + if !tt.wantErr && err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + } +} + +func TestInitSeccompRejectsErrnoRetForAllow(t *testing.T) { + errnoRet := uint(1) + tests := []struct { + name string + config *configs.Seccomp + }{ + { + name: "default action", + config: &configs.Seccomp{ + DefaultAction: configs.Allow, + DefaultErrnoRet: &errnoRet, + }, + }, + { + name: "syscall action", + config: &configs.Seccomp{ + DefaultAction: configs.Allow, + Syscalls: []*configs.Syscall{{ + Name: "getpid", + Action: configs.Allow, + ErrnoRet: &errnoRet, + }}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := InitSeccomp(tt.config) + if err == nil { + t.Fatal("expected errnoRet with SCMP_ACT_ALLOW to be rejected") + } + if !strings.Contains(err.Error(), "errnoRet is only supported") { + t.Fatalf("unexpected error: %v", err) + } + }) + } +}