Platform interface: Group seccomp-related information to a sub-interface.

With the introduction of precompiled seccomp filters, we need three more
concepts that go into building the set of seccomp filters for a `Platform`:

- "Precompiled configurations": A list of platform configurations for which
  the seccomp filters get precompiled into the Sentry.
- "Config key": A string that identifies each configuration of the syscall
  filters of that platform. This is necessary to identify *which* precompiled
  seccomp filter to use at runtime.
- "Variables": A set of named variables that are runtime inputs in syscall
  filters. For example, the FD of the KVM device in the KVM platform.

Systrap is the only serious user of this, due to its need for a variable.
The other platforms return a basic struct which simply carries what the
previous two methods did.
I think we should have the KVM VM FD as part of the KVM filters; if we did,
that would be another variable.

This CL is a preamble to actually precompile seccomp filters.

PiperOrigin-RevId: 583482945
This commit is contained in:
Etienne Perot
2023-11-17 14:12:59 -08:00
committed by gVisor bot
parent 2bc70b209b
commit 6878f88aa4
18 changed files with 215 additions and 90 deletions
+1
View File
@@ -21,6 +21,7 @@ go_library(
"//pkg/cpuid",
"//pkg/hostarch",
"//pkg/seccomp",
"//pkg/seccomp/precompiledseccomp",
"//pkg/sentry/arch",
"//pkg/sentry/hostmm",
"//pkg/sentry/memmap",
+39 -28
View File
@@ -19,36 +19,47 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/seccomp"
"gvisor.dev/gvisor/pkg/sentry/platform"
)
// SyscallFilters returns syscalls made exclusively by the KVM platform.
func (k *KVM) SyscallFilters() seccomp.SyscallRules {
return k.archSyscallFilters().Merge(seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
unix.SYS_IOCTL: seccomp.Or{
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(KVM_RUN),
// SeccompInfo returns seccomp information for the KVM platform.
func (k *KVM) SeccompInfo() platform.SeccompInfo {
return platform.StaticSeccompInfo{
PlatformName: "kvm",
Filters: k.archSyscallFilters().Merge(seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
unix.SYS_IOCTL: seccomp.Or{
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(KVM_RUN),
},
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(KVM_SET_USER_MEMORY_REGION),
},
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(KVM_GET_REGS),
},
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(KVM_SET_REGS),
},
},
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(KVM_SET_USER_MEMORY_REGION),
unix.SYS_MEMBARRIER: seccomp.PerArg{
seccomp.EqualTo(linux.MEMBARRIER_CMD_PRIVATE_EXPEDITED),
seccomp.EqualTo(0),
},
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(KVM_GET_REGS),
},
seccomp.PerArg{
seccomp.NonNegativeFD{},
seccomp.EqualTo(KVM_SET_REGS),
},
},
unix.SYS_MEMBARRIER: seccomp.PerArg{
seccomp.EqualTo(linux.MEMBARRIER_CMD_PRIVATE_EXPEDITED),
seccomp.EqualTo(0),
},
unix.SYS_MMAP: seccomp.MatchAll{},
unix.SYS_RT_SIGSUSPEND: seccomp.MatchAll{},
unix.SYS_RT_SIGTIMEDWAIT: seccomp.MatchAll{},
_SYS_KVM_RETURN_TO_HOST: seccomp.MatchAll{},
}))
unix.SYS_MMAP: seccomp.MatchAll{},
unix.SYS_RT_SIGSUSPEND: seccomp.MatchAll{},
unix.SYS_RT_SIGTIMEDWAIT: seccomp.MatchAll{},
_SYS_KVM_RETURN_TO_HOST: seccomp.MatchAll{},
})),
HotSyscalls: hottestSyscalls(),
}
}
// PrecompiledSeccompInfo implements
// platform.Constructor.PrecompiledSeccompInfo.
func (*constructor) PrecompiledSeccompInfo() []platform.SeccompInfo {
return []platform.SeccompInfo{(*KVM)(nil).SeccompInfo()}
}
+2 -2
View File
@@ -50,8 +50,8 @@ func (k *KVM) archSyscallFilters() seccomp.SyscallRules {
})
}
// HottestSyscalls implements Platform.HottestSyscalls.
func (*KVM) HottestSyscalls() []uintptr {
// hottestSyscalls returns the list of hot syscalls for the KVM platform.
func hottestSyscalls() []uintptr {
return []uintptr{
unix.SYS_FUTEX,
unix.SYS_IOCTL,
+2 -2
View File
@@ -34,7 +34,7 @@ func (*KVM) archSyscallFilters() seccomp.SyscallRules {
})
}
// HottestSyscalls implements Platform.HottestSyscalls.
func (*KVM) HottestSyscalls() []uintptr {
// hottestSyscalls returns the list of hot syscalls for the KVM platform.
func hottestSyscalls() []uintptr {
return nil
}
+84 -21
View File
@@ -25,6 +25,7 @@ import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/seccomp"
"gvisor.dev/gvisor/pkg/seccomp/precompiledseccomp"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/hostmm"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -120,18 +121,8 @@ type Platform interface {
// Preconditions: HaveGlobalMemoryBarrier() == true.
GlobalMemoryBarrier() error
// 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
// SeccompInfo returns seccomp-related information about this platform.
SeccompInfo() SeccompInfo
}
// NoCPUPreemptionDetection implements Platform.DetectsCPUPreemption and
@@ -201,15 +192,6 @@ 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 {
@@ -467,6 +449,83 @@ type Requirements struct {
RequiresCapSysPtrace bool
}
// SeccompInfo represents seccomp-bpf data for a given platform.
type SeccompInfo interface {
// Variables returns a map from named variables to the value they should
// have with the platform as currently initialized.
// Variables are known only at runtime, but are not part of a platform's
// configuration. For example, the KVM platform having an FD representing
// the KVM VM is a variable: it is only known at runtime, but does not
// change the structure of the syscall rules.
// The set of variable names must be static regardless of platform
// configuration.
Variables() precompiledseccomp.Values
// ConfigKey returns a string that uniquely represents the set of
// configuration information from which syscall rules are derived,
// other than variables or CPU architecture.
// This should at least contain the platform name.
// If syscall rules are dependent on the platform's configuration,
// this should return a string that encapsulates the values of these
// configuration options.
// For example, if some option of the platform causes it to require a
// new syscall to be allowed, this option should be part of this string.
ConfigKey() string
// SyscallFilters returns syscalls made exclusively by this platform.
// `vars` maps variable names (as returned by `Variables()`) to values,
// and **the rules should depend on `vars`**. These will not necessarily
// map to the result of calling `Variables()` on the current `SeccompInfo`;
// during seccomp rule precompilation, these will be set to placeholder
// values.
SyscallFilters(vars precompiledseccomp.Values) 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
}
// StaticSeccompInfo implements `SeccompInfo` for platforms which don't have
// any configuration or variables.
type StaticSeccompInfo struct {
// PlatformName is the platform name.
PlatformName string
// Filters is the platform's syscall filters.
Filters seccomp.SyscallRules
// HotSyscalls is the list of syscalls numbers that this platform
// calls most often, most-frequently-called first.
// See `SeccompInfo.HottestSyscalls` for more.
HotSyscalls []uintptr
}
// Variables implements `SeccompInfo.Variables`.
func (StaticSeccompInfo) Variables() precompiledseccomp.Values {
return nil
}
// ConfigKey implements `SeccompInfo.ConfigKey`.
func (s StaticSeccompInfo) ConfigKey() string {
return s.PlatformName
}
// SyscallFilters implements `SeccompInfo.SyscallFilters`.
func (s StaticSeccompInfo) SyscallFilters(precompiledseccomp.Values) seccomp.SyscallRules {
return s.Filters
}
// HottestSyscalls implements `SeccompInfo.HottestSyscalls`.
func (s StaticSeccompInfo) HottestSyscalls() []uintptr {
return s.HotSyscalls
}
// Constructor represents a platform type.
type Constructor interface {
// New returns a new platform instance.
@@ -483,6 +542,10 @@ type Constructor interface {
// Requirements returns platform specific requirements.
Requirements() Requirements
// PrecompiledSeccompInfo returns a list of `SeccompInfo`s that is
// useful to precompile into the Sentry.
PrecompiledSeccompInfo() []SeccompInfo
}
// platforms contains all available platform types.
+17 -7
View File
@@ -17,13 +17,23 @@ package ptrace
import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/seccomp"
"gvisor.dev/gvisor/pkg/sentry/platform"
)
// SyscallFilters returns syscalls made exclusively by the ptrace platform.
func (*PTrace) SyscallFilters() seccomp.SyscallRules {
return seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
unix.SYS_PTRACE: seccomp.MatchAll{},
unix.SYS_TGKILL: seccomp.MatchAll{},
unix.SYS_WAIT4: seccomp.MatchAll{},
})
// SeccompInfo returns seccomp information for the ptrace platform.
func (*PTrace) SeccompInfo() platform.SeccompInfo {
return platform.StaticSeccompInfo{
PlatformName: "ptrace",
Filters: seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
unix.SYS_PTRACE: seccomp.MatchAll{},
unix.SYS_TGKILL: seccomp.MatchAll{},
unix.SYS_WAIT4: seccomp.MatchAll{},
}),
}
}
// PrecompiledSeccompInfo implements
// platform.Constructor.PrecompiledSeccompInfo.
func (*constructor) PrecompiledSeccompInfo() []platform.SeccompInfo {
return []platform.SeccompInfo{(*PTrace)(nil).SeccompInfo()}
}
-1
View File
@@ -208,7 +208,6 @@ type PTrace struct {
platform.NoCPUPreemptionDetection
platform.UseHostGlobalMemoryBarrier
platform.DoesNotOwnPageTables
platform.HottestSyscallsNotSpecified
}
// New returns a new ptrace-based implementation of the platform interface.
+1
View File
@@ -88,6 +88,7 @@ go_library(
"//pkg/refs",
"//pkg/safecopy",
"//pkg/seccomp",
"//pkg/seccomp/precompiledseccomp",
"//pkg/sentry/arch",
"//pkg/sentry/memmap",
"//pkg/sentry/pgalloc",
+42 -4
View File
@@ -18,10 +18,32 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/seccomp"
"gvisor.dev/gvisor/pkg/seccomp/precompiledseccomp"
"gvisor.dev/gvisor/pkg/sentry/platform"
)
// SyscallFilters returns syscalls made exclusively by the systrap platform.
func (p *Systrap) SyscallFilters() seccomp.SyscallRules {
// sysmsgThreadPriorityVarName is the seccomp filter variable name used to
// encode the sysmsg thread priority.
const sysmsgThreadPriorityVarName = "systrap_sysmsg_thread_priority"
// systrapSeccomp implements platform.SeccompInfo.
type systrapSeccomp struct{}
// Variables implements `platform.SeccompInfo.Variables`.
func (systrapSeccomp) Variables() precompiledseccomp.Values {
initSysmsgThreadPriority()
vars := precompiledseccomp.Values{}
vars.SetUint64(sysmsgThreadPriorityVarName, uint64(sysmsgThreadPriority))
return vars
}
// ConfigKey implements `platform.SeccompInfo.ConfigKey`.
func (systrapSeccomp) ConfigKey() string {
return "systrap"
}
// SyscallFilters implements `platform.SeccompInfo.SyscallFilters`.
func (systrapSeccomp) SyscallFilters(vars precompiledseccomp.Values) seccomp.SyscallRules {
return seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
unix.SYS_PTRACE: seccomp.Or{
seccomp.PerArg{
@@ -75,7 +97,23 @@ func (p *Systrap) SyscallFilters() seccomp.SyscallRules {
unix.SYS_SETPRIORITY: seccomp.PerArg{
seccomp.EqualTo(unix.PRIO_PROCESS),
seccomp.AnyValue{},
seccomp.EqualTo(sysmsgThreadPriority),
seccomp.EqualTo(vars.GetUint64(sysmsgThreadPriorityVarName)),
},
}).Merge(p.archSyscallFilters())
}).Merge(archSyscallFilters())
}
// HottestSyscalls implements `platform.SeccompInfo.HottestSyscalls`.
func (systrapSeccomp) HottestSyscalls() []uintptr {
return hottestSyscalls()
}
// SeccompInfo returns seccomp filter info for the systrap platform.
func (p *Systrap) SeccompInfo() platform.SeccompInfo {
return systrapSeccomp{}
}
// PrecompiledSeccompInfo implements
// platform.Constructor.PrecompiledSeccompInfo.
func (*constructor) PrecompiledSeccompInfo() []platform.SeccompInfo {
return []platform.SeccompInfo{(*Systrap)(nil).SeccompInfo()}
}
+5 -4
View File
@@ -22,13 +22,14 @@ import (
"gvisor.dev/gvisor/pkg/seccomp"
)
// SyscallFilters returns syscalls made exclusively by the systrap platform.
func (*Systrap) archSyscallFilters() seccomp.SyscallRules {
// archSyscallFilters returns architecture-specific syscalls made exclusively
// by the systrap platform.
func archSyscallFilters() seccomp.SyscallRules {
return seccomp.SyscallRules{}
}
// HottestSyscalls implements Platform.HottestSyscalls.
func (*Systrap) HottestSyscalls() []uintptr {
// hottestSyscalls returns the hottest syscalls used by the Systrap platform.
func hottestSyscalls() []uintptr {
return []uintptr{
unix.SYS_FUTEX,
unix.SYS_NANOSLEEP,
+5 -4
View File
@@ -23,8 +23,9 @@ import (
"gvisor.dev/gvisor/pkg/seccomp"
)
// SyscallFilters returns syscalls made exclusively by the systrap platform.
func (*Systrap) archSyscallFilters() seccomp.SyscallRules {
// archSyscallFilters returns architecture-specific syscalls made exclusively
// by the systrap platform.
func archSyscallFilters() seccomp.SyscallRules {
return seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
unix.SYS_PTRACE: seccomp.Or{
seccomp.PerArg{
@@ -41,7 +42,7 @@ func (*Systrap) archSyscallFilters() seccomp.SyscallRules {
})
}
// HottestSyscalls implements Platform.HottestSyscalls.
func (*Systrap) HottestSyscalls() []uintptr {
// hottestSyscalls returns the hottest syscalls used by the Systrap platform.
func hottestSyscalls() []uintptr {
return nil
}
+2 -2
View File
@@ -26,7 +26,7 @@ import (
// Options are seccomp filter related options.
type Options struct {
Platform platform.Platform
Platform platform.SeccompInfo
HostNetwork bool
HostNetworkRawSockets bool
HostFilesystem bool
@@ -87,7 +87,7 @@ func Rules(opt Options) (seccomp.SyscallRules, seccomp.SyscallRules) {
s.Merge(accel.Filters())
}
s.Merge(opt.Platform.SyscallFilters())
s.Merge(opt.Platform.SyscallFilters(opt.Platform.Variables()))
return s, seccomp.DenyNewExecMappings
}
+8 -8
View File
@@ -27,34 +27,34 @@ import (
func TestIoctlFirstArgumentIsNonNegativeFD(t *testing.T) {
for name, options := range map[string]Options{
"default systrap": Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
},
"default kvm": Options{
Platform: &kvm.KVM{},
Platform: (&kvm.KVM{}).SeccompInfo(),
},
"nvproxy": Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
NVProxy: true,
},
"tpuproxy": Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
TPUProxy: true,
},
"host network": Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
HostNetwork: true,
},
"host network with raw sockets": Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
HostNetwork: true,
HostNetworkRawSockets: true,
},
"profiling": Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
ProfileEnable: true,
},
"host filesystem": Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
HostFilesystem: true,
},
} {
+1 -1
View File
@@ -63,7 +63,7 @@ func action(s string) linux.BPFAction {
func main() {
flag.Parse()
opt := config.Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
NVProxy: *nvproxy,
}
rules, denyRules := config.Rules(opt)
+3 -3
View File
@@ -38,7 +38,7 @@ type Options struct {
// using the Systrap platform.
func BenchmarkSentrySystrap(b *testing.B) {
opts := config.Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
}
rules, denyRules := config.Rules(opts)
secbench.Run(b, secbench.BenchFromSyscallRules(
@@ -72,7 +72,7 @@ func BenchmarkSentrySystrap(b *testing.B) {
// using the KVM platform.
func BenchmarkSentryKVM(b *testing.B) {
opts := config.Options{
Platform: &kvm.KVM{},
Platform: (&kvm.KVM{}).SeccompInfo(),
}
rules, denyRules := config.Rules(opts)
secbench.Run(b, secbench.BenchFromSyscallRules(
@@ -102,7 +102,7 @@ func BenchmarkSentryKVM(b *testing.B) {
func BenchmarkNVProxyIoctl(b *testing.B) {
opts := config.Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
NVProxy: true,
}
rules, denyRules := config.Rules(opts)
+1 -1
View File
@@ -63,7 +63,7 @@ func FuzzFilterAgainstGolden(f *testing.F) {
}
filterOpts := config.Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
}
rules, denyRules := config.Rules(filterOpts)
ruleSets := []seccomp.RuleSet{
+1 -1
View File
@@ -28,7 +28,7 @@ import (
// do not affect the behavior of the generated seccomp-bpf program.
func FuzzFilterOptimizationsResultInConsistentProgram(f *testing.F) {
filterOpts := config.Options{
Platform: &systrap.Systrap{},
Platform: (&systrap.Systrap{}).SeccompInfo(),
}
rules, denyRules := config.Rules(filterOpts)
ruleSets := []seccomp.RuleSet{
+1 -1
View File
@@ -682,7 +682,7 @@ func (l *Loader) installSeccompFilters() error {
} else {
hostnet := l.root.conf.Network == config.NetworkHost
opts := filter.Options{
Platform: l.k.Platform,
Platform: l.k.Platform.SeccompInfo(),
HostNetwork: hostnet,
HostNetworkRawSockets: hostnet && l.root.conf.EnableRaw,
HostFilesystem: l.root.conf.DirectFS,