mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Plumb seccomp program options through the Sentry filter and Platform.
This adds a `HottestSyscalls` function to the `Platform` interface, as the platform determines which syscalls are most often called by the Sentry. This is used by the Sentry seccomp filter code to build the final list of hot syscalls. PiperOrigin-RevId: 582833363
This commit is contained in:
committed by
gVisor bot
parent
8d87a534c0
commit
9fd832da02
@@ -69,6 +69,8 @@ type KVM struct {
|
||||
|
||||
platform.DoesOwnPageTables
|
||||
|
||||
platform.HottestSyscallsNotSpecified
|
||||
|
||||
// machine is the backing VM.
|
||||
machine *machine
|
||||
}
|
||||
|
||||
@@ -122,6 +122,16 @@ type Platform interface {
|
||||
|
||||
// SyscallFilters returns syscalls made exclusively by this platform.
|
||||
SyscallFilters() seccomp.SyscallRules
|
||||
|
||||
// HottestSyscalls returns the list of syscall numbers that this platform
|
||||
// calls most often, most-frequently-called first. No more than a dozen
|
||||
// syscalls. Returning an empty or a nil slice is OK.
|
||||
// This is used to produce a more efficient seccomp-bpf program that can
|
||||
// check for the most frequently called syscalls first.
|
||||
// What matters here is only the frequency at which a syscall is called,
|
||||
// not the total amount of CPU time that is used to process it in the host
|
||||
// kernel.
|
||||
HottestSyscalls() []uintptr
|
||||
}
|
||||
|
||||
// NoCPUPreemptionDetection implements Platform.DetectsCPUPreemption and
|
||||
@@ -191,6 +201,15 @@ func (DoesNotOwnPageTables) OwnsPageTables() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HottestSyscallsNotSpecified implements Platform.HottestSyscalls and does
|
||||
// not return any syscall as being hot.
|
||||
type HottestSyscallsNotSpecified struct{}
|
||||
|
||||
// HottestSyscalls implements Platform.HottestSyscalls.
|
||||
func (HottestSyscallsNotSpecified) HottestSyscalls() []uintptr {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MemoryManager represents an abstraction above the platform address space
|
||||
// which manages memory mappings and their contents.
|
||||
type MemoryManager interface {
|
||||
|
||||
@@ -208,6 +208,7 @@ type PTrace struct {
|
||||
platform.NoCPUPreemptionDetection
|
||||
platform.UseHostGlobalMemoryBarrier
|
||||
platform.DoesNotOwnPageTables
|
||||
platform.HottestSyscallsNotSpecified
|
||||
}
|
||||
|
||||
// New returns a new ptrace-based implementation of the platform interface.
|
||||
|
||||
@@ -301,6 +301,7 @@ type Systrap struct {
|
||||
platform.NoCPUPreemptionDetection
|
||||
platform.UseHostGlobalMemoryBarrier
|
||||
platform.DoesNotOwnPageTables
|
||||
platform.HottestSyscallsNotSpecified
|
||||
|
||||
// memoryFile is used to create a stub sysmsg stack
|
||||
// which is shared with the Sentry.
|
||||
|
||||
@@ -44,3 +44,7 @@ func init() {
|
||||
func archFstatAtSysNo() uintptr {
|
||||
return unix.SYS_NEWFSTATAT
|
||||
}
|
||||
|
||||
func archSpecificHotSyscalls() []uintptr {
|
||||
return nil // TODO(b/298726675): Populate.
|
||||
}
|
||||
|
||||
@@ -44,3 +44,7 @@ func init() {
|
||||
func archFstatAtSysNo() uintptr {
|
||||
return unix.SYS_FSTATAT
|
||||
}
|
||||
|
||||
func archSpecificHotSyscalls() []uintptr {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -36,10 +36,11 @@ var (
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
rules, denyRules := filter.Rules(filter.Options{
|
||||
opt := filter.Options{
|
||||
Platform: &systrap.Systrap{},
|
||||
NVProxy: *nvproxy,
|
||||
})
|
||||
}
|
||||
rules, denyRules := filter.Rules(opt)
|
||||
insns, stats, err := seccomp.BuildProgram([]seccomp.RuleSet{
|
||||
{
|
||||
Rules: denyRules,
|
||||
@@ -49,10 +50,7 @@ func main() {
|
||||
Rules: rules,
|
||||
Action: linux.SECCOMP_RET_ALLOW,
|
||||
},
|
||||
}, seccomp.ProgramOptions{
|
||||
DefaultAction: linux.SECCOMP_RET_ERRNO,
|
||||
BadArchAction: linux.SECCOMP_RET_ERRNO,
|
||||
})
|
||||
}, filter.SeccompOptions(opt))
|
||||
if err != nil {
|
||||
log.Warningf("%v", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package filter
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/seccomp"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/accel"
|
||||
@@ -37,7 +38,7 @@ type Options struct {
|
||||
ControllerFD int
|
||||
}
|
||||
|
||||
// Rules returns the seccomp (rules, denyRules) to use for the Sentry.
|
||||
// Rules returns the seccomp rules and denyRules to use for the Sentry.
|
||||
func Rules(opt Options) (seccomp.SyscallRules, seccomp.SyscallRules) {
|
||||
s := allowedSyscalls
|
||||
s.Merge(controlServerFilters(opt.ControllerFD))
|
||||
@@ -72,14 +73,40 @@ func Rules(opt Options) (seccomp.SyscallRules, seccomp.SyscallRules) {
|
||||
}
|
||||
|
||||
s.Merge(opt.Platform.SyscallFilters())
|
||||
|
||||
return s, seccomp.DenyNewExecMappings
|
||||
}
|
||||
|
||||
// SeccompOptions returns the seccomp program options to use for the filter.
|
||||
func SeccompOptions(opt Options) seccomp.ProgramOptions {
|
||||
// futex(2) is unequivocally the most-frequently-used syscall by the
|
||||
// Sentry across all platforms.
|
||||
hotSyscalls := []uintptr{unix.SYS_FUTEX}
|
||||
// ... Then comes the platform-specific hot syscalls which are typically
|
||||
// part of the syscall interception hot path.
|
||||
hotSyscalls = append(hotSyscalls, opt.Platform.HottestSyscalls()...)
|
||||
// ... Then come a few syscalls that are frequent just from workloads in
|
||||
// general.
|
||||
hotSyscalls = append(hotSyscalls, archSpecificHotSyscalls()...)
|
||||
|
||||
// Now deduplicate them.
|
||||
sysnoMap := make(map[uintptr]struct{}, len(hotSyscalls))
|
||||
uniqueHotSyscalls := make([]uintptr, 0, len(hotSyscalls))
|
||||
for _, sysno := range hotSyscalls {
|
||||
if _, alreadyAdded := sysnoMap[sysno]; !alreadyAdded {
|
||||
sysnoMap[sysno] = struct{}{}
|
||||
uniqueHotSyscalls = append(uniqueHotSyscalls, sysno)
|
||||
}
|
||||
}
|
||||
|
||||
opts := seccomp.DefaultProgramOptions()
|
||||
opts.HotSyscalls = uniqueHotSyscalls
|
||||
return opts
|
||||
}
|
||||
|
||||
// Install seccomp filters based on the given platform.
|
||||
func Install(opt Options) error {
|
||||
rules, denyRules := Rules(opt)
|
||||
return seccomp.Install(rules, denyRules, seccomp.DefaultProgramOptions())
|
||||
return seccomp.Install(rules, denyRules, SeccompOptions(opt))
|
||||
}
|
||||
|
||||
// Report writes a warning message to the log.
|
||||
|
||||
@@ -37,9 +37,10 @@ type Options struct {
|
||||
// BenchmarkSentrySystrap benchmarks the seccomp filters used by the Sentry
|
||||
// using the Systrap platform.
|
||||
func BenchmarkSentrySystrap(b *testing.B) {
|
||||
rules, denyRules := filter.Rules(filter.Options{
|
||||
opts := filter.Options{
|
||||
Platform: &systrap.Systrap{},
|
||||
})
|
||||
}
|
||||
rules, denyRules := filter.Rules(opts)
|
||||
secbench.Run(b, secbench.BenchFromSyscallRules(
|
||||
b,
|
||||
"Postgres",
|
||||
@@ -63,15 +64,17 @@ func BenchmarkSentrySystrap(b *testing.B) {
|
||||
},
|
||||
rules,
|
||||
denyRules,
|
||||
filter.SeccompOptions(opts),
|
||||
))
|
||||
}
|
||||
|
||||
// BenchmarkSentryKVM benchmarks the seccomp filters used by the Sentry
|
||||
// using the KVM platform.
|
||||
func BenchmarkSentryKVM(b *testing.B) {
|
||||
rules, denyRules := filter.Rules(filter.Options{
|
||||
opts := filter.Options{
|
||||
Platform: &kvm.KVM{},
|
||||
})
|
||||
}
|
||||
rules, denyRules := filter.Rules(opts)
|
||||
secbench.Run(b, secbench.BenchFromSyscallRules(
|
||||
b,
|
||||
"Postgres",
|
||||
@@ -93,14 +96,16 @@ func BenchmarkSentryKVM(b *testing.B) {
|
||||
},
|
||||
rules,
|
||||
denyRules,
|
||||
filter.SeccompOptions(opts),
|
||||
))
|
||||
}
|
||||
|
||||
func BenchmarkNVProxyIoctl(b *testing.B) {
|
||||
rules, denyRules := filter.Rules(filter.Options{
|
||||
opts := filter.Options{
|
||||
Platform: &systrap.Systrap{},
|
||||
NVProxy: true,
|
||||
})
|
||||
}
|
||||
rules, denyRules := filter.Rules(opts)
|
||||
var sequences []secbenchdef.Sequence
|
||||
if err := rules.ForSingleArgument(unix.SYS_IOCTL, 1, func(v seccomp.ValueMatcher) error {
|
||||
if arg1Equal, isArg1Equal := v.(seccomp.EqualTo); isArg1Equal {
|
||||
@@ -123,5 +128,6 @@ func BenchmarkNVProxyIoctl(b *testing.B) {
|
||||
},
|
||||
rules,
|
||||
denyRules,
|
||||
filter.SeccompOptions(opts),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -33,13 +33,22 @@ import (
|
||||
)
|
||||
|
||||
// BenchFromSyscallRules returns a new Bench created from SyscallRules.
|
||||
func BenchFromSyscallRules(b *testing.B, name string, profile secbenchdef.Profile, rules seccomp.SyscallRules, denyRules seccomp.SyscallRules) secbenchdef.Bench {
|
||||
func BenchFromSyscallRules(b *testing.B, name string, profile secbenchdef.Profile, rules seccomp.SyscallRules, denyRules seccomp.SyscallRules, options seccomp.ProgramOptions) secbenchdef.Bench {
|
||||
// If there is a rule allowing rt_sigreturn to be called,
|
||||
// also add a rule for the stand-in syscall number instead.
|
||||
if rules.Has(unix.SYS_RT_SIGRETURN) {
|
||||
rules = rules.Copy()
|
||||
rules.Set(uintptr(secbenchdef.RTSigreturn.Data(profile.Arch).Nr), rules.Get(unix.SYS_RT_SIGRETURN))
|
||||
}
|
||||
// Also replace it in the list of hottest syscalls.
|
||||
for i, sysno := range options.HotSyscalls {
|
||||
if sysno == unix.SYS_RT_SIGRETURN {
|
||||
options.HotSyscalls[i] = uintptr(secbenchdef.RTSigreturn.Data(profile.Arch).Nr)
|
||||
}
|
||||
}
|
||||
|
||||
options.DefaultAction = linux.SECCOMP_RET_ERRNO
|
||||
options.BadArchAction = linux.SECCOMP_RET_ERRNO
|
||||
insns, buildStats, err := seccomp.BuildProgram([]seccomp.RuleSet{
|
||||
{
|
||||
Rules: denyRules,
|
||||
@@ -49,10 +58,7 @@ func BenchFromSyscallRules(b *testing.B, name string, profile secbenchdef.Profil
|
||||
Rules: rules,
|
||||
Action: linux.SECCOMP_RET_ALLOW,
|
||||
},
|
||||
}, seccomp.ProgramOptions{
|
||||
DefaultAction: linux.SECCOMP_RET_ERRNO,
|
||||
BadArchAction: linux.SECCOMP_RET_ERRNO,
|
||||
})
|
||||
}, options)
|
||||
if err != nil {
|
||||
b.Fatalf("BuildProgram() failed: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user