Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/sentry/control/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ go_library(
deps = [
":control_api_go_proto",
"//pkg/abi/linux",
"//pkg/bpf",
"//pkg/cleanup",
"//pkg/context",
"//pkg/eventchannel",
Expand Down
12 changes: 12 additions & 0 deletions pkg/sentry/control/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"time"

"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bpf"
"gvisor.dev/gvisor/pkg/cleanup"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
Expand Down Expand Up @@ -152,6 +153,10 @@ type ExecArgs struct {

// Limits is the limit set for the process being executed.
Limits *limits.LimitSet

// SeccompProgram is an optional seccomp BPF program to install on the
// new process.
SeccompProgram *bpf.Program
}

// String prints the arguments as a string.
Expand Down Expand Up @@ -319,6 +324,13 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
return nil, 0, nil, err
}

if args.SeccompProgram != nil {
task := tg.Leader()
if err := task.AppendSyscallFilter(*args.SeccompProgram, true); err != nil {
return nil, 0, nil, fmt.Errorf("appending seccomp filters: %w", err)
}
}

// Start the newly created process.
proc.Kernel.StartProcess(tg)

Expand Down
1 change: 1 addition & 0 deletions runsc/boot/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go_library(
"restore.go",
"restore_impl.go",
"seccheck.go",
"seccomp.go",
"strace.go",
"vfs.go",
],
Expand Down
42 changes: 19 additions & 23 deletions runsc/boot/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bpf"
"gvisor.dev/gvisor/pkg/cleanup"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/coverage"
Expand Down Expand Up @@ -84,7 +83,6 @@ import (
"gvisor.dev/gvisor/runsc/config"
"gvisor.dev/gvisor/runsc/profile"
"gvisor.dev/gvisor/runsc/specutils"
"gvisor.dev/gvisor/runsc/specutils/seccomp"

// Top-level inet providers.
"gvisor.dev/gvisor/pkg/sentry/socket/hostinet"
Expand Down Expand Up @@ -1361,27 +1359,15 @@ func (l *Loader) createContainerProcess(info *containerInfo) (*kernel.ThreadGrou
info.procArgs.FDTable.DecRef(ctx)

// Install seccomp filters with the new task if there are any.
if info.conf.OCISeccomp {
if info.spec.Linux != nil && info.spec.Linux.Seccomp != nil {
program, err := seccomp.BuildProgram(info.spec.Linux.Seccomp)
if err != nil {
return nil, nil, fmt.Errorf("building seccomp program: %w", err)
}

if log.IsLogging(log.Debug) {
out, _ := bpf.DecodeProgram(program)
log.Debugf("Installing OCI seccomp filters\nProgram:\n%s", out)
}

task := tg.Leader()
// NOTE: It seems Flags are ignored by runc so we ignore them too.
if err := task.AppendSyscallFilter(program, true); err != nil {
return nil, nil, fmt.Errorf("appending seccomp filters: %w", err)
}
}
} else {
if info.spec.Linux != nil && info.spec.Linux.Seccomp != nil {
log.Warningf("Seccomp spec is being ignored")
program, err := buildOCISeccompProgram(info.conf, info.spec)
if err != nil {
return nil, nil, err
}
if program != nil {
task := tg.Leader()
// NOTE: It seems Flags are ignored by runc so we ignore them too.
if err := task.AppendSyscallFilter(*program, true); err != nil {
return nil, nil, fmt.Errorf("appending seccomp filters: %w", err)
}
}

Expand Down Expand Up @@ -1524,6 +1510,16 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) {
return 0, fmt.Errorf("creating limits: %w", err)
}

containerName := l.k.ContainerName(args.ContainerID)
spec := l.containerSpecs[containerName]
if spec != nil {
seccompProgram, err := buildOCISeccompProgram(l.root.conf, spec)
if err != nil {
return 0, err
}
args.SeccompProgram = seccompProgram
}

// Start the process.
proc := control.Proc{Kernel: l.k}
newTG, tgid, ttyFile, err := control.ExecAsync(&proc, args)
Expand Down
51 changes: 51 additions & 0 deletions runsc/boot/seccomp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2026 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package boot

import (
"fmt"

specs "github.com/opencontainers/runtime-spec/specs-go"

"gvisor.dev/gvisor/pkg/bpf"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/runsc/config"
"gvisor.dev/gvisor/runsc/specutils/seccomp"
)

func buildOCISeccompProgram(conf *config.Config, spec *specs.Spec) (*bpf.Program, error) {
if !conf.OCISeccomp {
if spec.Linux != nil && spec.Linux.Seccomp != nil {
log.Warningf("Seccomp spec is being ignored because oci-seccomp is disabled")
}
return nil, nil
}

if spec.Linux == nil || spec.Linux.Seccomp == nil {
return nil, nil
}

program, err := seccomp.BuildProgram(spec.Linux.Seccomp)
if err != nil {
return nil, fmt.Errorf("building seccomp program: %w", err)
}

if log.IsLogging(log.Debug) {
out, _ := bpf.DecodeProgram(program)
log.Debugf("Installing OCI seccomp filters\nProgram:\n%s", out)
}

return &program, nil
}
70 changes: 70 additions & 0 deletions runsc/container/multi_container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3309,3 +3309,73 @@ func TestFSCheckpointCommand(t *testing.T) {
t.Errorf("Error waiting for FS restore: %v", err)
}
}

func TestMultiContainerExecSeccomp(t *testing.T) {
rootDir, cleanup, err := testutil.SetupRootDir()
if err != nil {
t.Fatalf("error creating root dir: %v", err)
}
defer cleanup()

conf := testutil.TestConfig(t)
conf.RootDir = rootDir
conf.OCISeccomp = true

testSpecs, ids := createSpecs(sleepCmd, sleepCmd)

// Container 0 (sandbox root): Block "uname" syscall.
testSpecs[0].Linux = &specs.Linux{
Seccomp: &specs.LinuxSeccomp{
DefaultAction: specs.ActAllow,
Syscalls: []specs.LinuxSyscall{
{
Names: []string{"uname"},
Action: specs.ActErrno,
},
},
},
}

// Container 1 (sub-container): Block "getdents64" syscall.
testSpecs[1].Linux = &specs.Linux{
Seccomp: &specs.LinuxSeccomp{
DefaultAction: specs.ActAllow,
Syscalls: []specs.LinuxSyscall{
{
Names: []string{"getdents64"},
Action: specs.ActErrno,
},
},
},
}

containers, cleanup, err := startContainers(conf, testSpecs, ids)
if err != nil {
t.Fatalf("error starting containers: %v", err)
}
defer cleanup()

// Container 0: "uname" should be blocked.
_, err = executeCombinedOutput(conf, containers[0], nil, "/bin/uname")
if err == nil {
t.Errorf("uname in container 0 should have failed, but succeeded")
}

// Container 0: "getdents64" should not be blocked.
_, err = executeCombinedOutput(conf, containers[0], nil, "/bin/ls", "/")
if err != nil {
t.Errorf("ls in container 0 should have succeeded: %v", err)
}

// Container 1: "getdents64" should be blocked.
_, err = executeCombinedOutput(conf, containers[1], nil, "/bin/ls", "/")
if err == nil {
t.Errorf("ls in container 1 should have failed, but succeeded")
}

// Container 1: "uname" should not be blocked.
_, err = executeCombinedOutput(conf, containers[1], nil, "/bin/uname")
if err != nil {
t.Errorf("uname in container 1 should have succeeded: %v", err)
}
}
Loading