Skip to content

Commit

Permalink
Use seccomp_unotify to catch syscalls that violate filterss
Browse files Browse the repository at this point in the history
The main benefit of seccomp_unotify is an ability to resume the trapped syscall.
We have seen a case when a blocked syscall was in the msan internals that
prevented the Go runtime from reaching the SIGSYS handler and producing a panic
report.

PiperOrigin-RevId: 713139953
  • Loading branch information
avagin authored and gvisor-bot committed Jan 17, 2025
1 parent d22dedf commit 035059b
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 12 deletions.
1 change: 1 addition & 0 deletions pkg/abi/linux/seccomp.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (

SECCOMP_FILTER_FLAG_TSYNC = 1
SECCOMP_FILTER_FLAG_NEW_LISTENER = 1 << 3
SECCOMP_FILTER_FLAG_TSYNC_ESRCH = 1 << 4

SECCOMP_USER_NOTIF_FLAG_CONTINUE = 1

Expand Down
1 change: 1 addition & 0 deletions pkg/seccomp/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/bpf",
"//pkg/hostsyscall",
"//pkg/log",
"@org_golang_x_sys//unix:go_default_library",
],
Expand Down
3 changes: 2 additions & 1 deletion pkg/seccomp/seccomp.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func Install(rules SyscallRules, denyRules SyscallRules, options ProgramOptions)

log.Infof("Installing seccomp filters for %d syscalls (action=%v)", rules.Size(), options.DefaultAction)

listen := options.DefaultAction == linux.SECCOMP_RET_USER_NOTIF
instrs, _, err := BuildProgram([]RuleSet{
{
Rules: denyRules,
Expand All @@ -82,7 +83,7 @@ func Install(rules SyscallRules, denyRules SyscallRules, options ProgramOptions)
}

// Perform the actual installation.
if err := SetFilter(instrs); err != nil {
if err := SetFilter(instrs, listen); err != nil {
return fmt.Errorf("failed to set filter: %v", err)
}

Expand Down
47 changes: 39 additions & 8 deletions pkg/seccomp/seccomp_unsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ package seccomp

import (
"fmt"
"os"
"runtime"
"unsafe"

"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bpf"
"gvisor.dev/gvisor/pkg/hostsyscall"
"gvisor.dev/gvisor/pkg/log"
)

// SetFilter installs the given BPF program.
func SetFilter(instrs []bpf.Instruction) error {
func SetFilter(instrs []bpf.Instruction, listen bool) error {
// PR_SET_NO_NEW_PRIVS is required in order to enable seccomp. See
// seccomp(2) for details.
//
Expand All @@ -44,16 +47,44 @@ func SetFilter(instrs []bpf.Instruction) error {
Len: uint16(len(instrs)),
Filter: (*linux.BPFInstruction)(unsafe.Pointer(&instrs[0])),
}
tid, errno := seccomp(linux.SECCOMP_SET_MODE_FILTER, linux.SECCOMP_FILTER_FLAG_TSYNC, unsafe.Pointer(&sockProg))
flags := linux.SECCOMP_FILTER_FLAG_TSYNC
if listen {
flags |= linux.SECCOMP_FILTER_FLAG_NEW_LISTENER | linux.SECCOMP_FILTER_FLAG_TSYNC_ESRCH
}
fd, errno := seccomp(linux.SECCOMP_SET_MODE_FILTER, uint32(flags), unsafe.Pointer(&sockProg))
if errno != 0 {
return errno
}
// "On error, if SECCOMP_FILTER_FLAG_TSYNC was used, the return value is
// the ID of the thread that caused the synchronization failure. (This ID
// is a kernel thread ID of the type returned by clone(2) and gettid(2).)"
// - seccomp(2)
if tid != 0 {
return fmt.Errorf("couldn't synchronize filter to TID %d", tid)
if listen {
f := os.NewFile(fd, "seccomp_notify")
go func() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
for {
req := linux.SeccompNotif{}
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(f.Fd()),
uintptr(linux.SECCOMP_IOCTL_NOTIF_RECV),
uintptr(unsafe.Pointer(&req)))
if errno != 0 {
if errno == unix.EINTR {
continue
}
panic(fmt.Sprintf("SECCOMP_IOCTL_NOTIF_RECV failed with %d", errno))
}
log.Warningf("Seccomp violation: %#v", req)
resp := linux.SeccompNotifResp{
ID: req.ID,
Flags: linux.SECCOMP_USER_NOTIF_FLAG_CONTINUE,
}
errno = hostsyscall.RawSyscallErrno(unix.SYS_IOCTL, uintptr(f.Fd()),
uintptr(linux.SECCOMP_IOCTL_NOTIF_SEND),
uintptr(unsafe.Pointer(&resp)))

if errno != 0 {
panic(fmt.Sprintf("SECCOMP_IOCTL_NOTIF_SEND failed with %d", errno))
}
}
}()
}
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/sentry/platform/kvm/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ func seccompMmapRules(m *machine) {
panic(fmt.Sprintf("failed to build rules: %v", err))
}
// Perform the actual installation.
if err := seccomp.SetFilter(instrs); err != nil {
if err := seccomp.SetFilter(instrs, false); err != nil {
panic(fmt.Sprintf("failed to set filter: %v", err))
}
})
Expand Down
1 change: 1 addition & 0 deletions runsc/boot/filter/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ go_library(
"//pkg/seccomp/precompiledseccomp",
"//pkg/sync",
"//runsc/boot/filter/config",
"@org_golang_x_sys//unix:go_default_library",
],
)

Expand Down
12 changes: 10 additions & 2 deletions runsc/boot/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ import (
// violation.
const debugFilter = false

// If both debugFilter and debugFilterDontPanic are set to `true`, seccomp
// violations will be printed to the log without blocking syscalls.
const debugFilterDontPanic = false

// Options is a re-export of the config Options type under this package.
type Options = config.Options

Expand All @@ -48,12 +52,16 @@ func Install(opt Options) error {
if err != nil {
return fmt.Errorf("cannot render precompiled program for options %v / vars %v: %w", key, vars, err)
}
return seccomp.SetFilter(insns)
return seccomp.SetFilter(insns, false)
}
seccompOpts := config.SeccompOptions(opt)
if debugFilter {
log.Infof("Seccomp filter debugging is enabled; seccomp failures will result in a panic stack trace.")
seccompOpts.DefaultAction = linux.SECCOMP_RET_TRAP
if debugFilterDontPanic {
seccompOpts.DefaultAction = linux.SECCOMP_RET_USER_NOTIF
} else {
seccompOpts.DefaultAction = linux.SECCOMP_RET_TRAP
}
} else {
log.Infof("No precompiled program found for config options %v, building seccomp program from scratch. This may slow down container startup.", key)
if log.IsLogging(log.Debug) {
Expand Down

0 comments on commit 035059b

Please sign in to comment.