github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/platform/platform.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package platform provides a Platform abstraction.
    16  //
    17  // See Platform for more information.
    18  package platform
    19  
    20  import (
    21  	"fmt"
    22  	"os"
    23  
    24  	"golang.org/x/sys/unix"
    25  	"github.com/metacubex/gvisor/pkg/abi/linux"
    26  	"github.com/metacubex/gvisor/pkg/context"
    27  	"github.com/metacubex/gvisor/pkg/hostarch"
    28  	"github.com/metacubex/gvisor/pkg/seccomp"
    29  	"github.com/metacubex/gvisor/pkg/seccomp/precompiledseccomp"
    30  	"github.com/metacubex/gvisor/pkg/sentry/arch"
    31  	"github.com/metacubex/gvisor/pkg/sentry/hostmm"
    32  	"github.com/metacubex/gvisor/pkg/sentry/memmap"
    33  	"github.com/metacubex/gvisor/pkg/usermem"
    34  )
    35  
    36  // Platform provides abstractions for execution contexts (Context,
    37  // AddressSpace).
    38  type Platform interface {
    39  	// SupportsAddressSpaceIO returns true if AddressSpaces returned by this
    40  	// Platform support AddressSpaceIO methods.
    41  	//
    42  	// The value returned by SupportsAddressSpaceIO is guaranteed to remain
    43  	// unchanged over the lifetime of the Platform.
    44  	SupportsAddressSpaceIO() bool
    45  
    46  	// CooperativelySchedulesAddressSpace returns true if the Platform has a
    47  	// limited number of AddressSpaces, such that mm.MemoryManager.Deactivate
    48  	// should call AddressSpace.Release when there are no goroutines that
    49  	// require the mm.MemoryManager to have an active AddressSpace.
    50  	//
    51  	// The value returned by CooperativelySchedulesAddressSpace is guaranteed
    52  	// to remain unchanged over the lifetime of the Platform.
    53  	CooperativelySchedulesAddressSpace() bool
    54  
    55  	// DetectsCPUPreemption returns true if Contexts returned by the Platform
    56  	// can reliably return ErrContextCPUPreempted.
    57  	DetectsCPUPreemption() bool
    58  
    59  	// HaveGlobalMemoryBarrier returns true if the GlobalMemoryBarrier method
    60  	// is supported.
    61  	HaveGlobalMemoryBarrier() bool
    62  
    63  	// OwnsPageTables returns true if the Platform implementation manages any
    64  	// page tables directly (rather than via host mmap(2) etc.) As of this
    65  	// writing, this property is relevant because the AddressSpace interface
    66  	// does not support specification of memory type (cacheability), such that
    67  	// host FDs specifying memory types (e.g. device drivers) can only set them
    68  	// correctly in host-managed page tables.
    69  	OwnsPageTables() bool
    70  
    71  	// MapUnit returns the alignment used for optional mappings into this
    72  	// platform's AddressSpaces. Higher values indicate lower per-page costs
    73  	// for AddressSpace.MapFile. As a special case, a MapUnit of 0 indicates
    74  	// that the cost of AddressSpace.MapFile is effectively independent of the
    75  	// number of pages mapped. If MapUnit is non-zero, it must be a power-of-2
    76  	// multiple of hostarch.PageSize.
    77  	MapUnit() uint64
    78  
    79  	// MinUserAddress returns the minimum mappable address on this
    80  	// platform.
    81  	MinUserAddress() hostarch.Addr
    82  
    83  	// MaxUserAddress returns the maximum mappable address on this
    84  	// platform.
    85  	MaxUserAddress() hostarch.Addr
    86  
    87  	// NewAddressSpace returns a new memory context for this platform.
    88  	//
    89  	// If mappingsID is not nil, the platform may assume that (1) all calls
    90  	// to NewAddressSpace with the same mappingsID represent the same
    91  	// (mutable) set of mappings, and (2) the set of mappings has not
    92  	// changed since the last time AddressSpace.Release was called on an
    93  	// AddressSpace returned by a call to NewAddressSpace with the same
    94  	// mappingsID.
    95  	//
    96  	// If a new AddressSpace cannot be created immediately, a nil
    97  	// AddressSpace is returned, along with channel that is closed when
    98  	// the caller should retry a call to NewAddressSpace.
    99  	//
   100  	// In general, this blocking behavior only occurs when
   101  	// CooperativelySchedulesAddressSpace (above) returns false.
   102  	NewAddressSpace(mappingsID any) (AddressSpace, <-chan struct{}, error)
   103  
   104  	// NewContext returns a new execution context.
   105  	NewContext(context.Context) Context
   106  
   107  	// PreemptAllCPUs causes all concurrent calls to Context.Switch(), as well
   108  	// as the first following call to Context.Switch() for each Context, to
   109  	// return ErrContextCPUPreempted.
   110  	//
   111  	// PreemptAllCPUs is only supported if DetectsCPUPremption() == true.
   112  	// Platforms for which this does not hold may panic if PreemptAllCPUs is
   113  	// called.
   114  	PreemptAllCPUs() error
   115  
   116  	// GlobalMemoryBarrier blocks until all threads running application code
   117  	// (via Context.Switch) and all task goroutines "have passed through a
   118  	// state where all memory accesses to user-space addresses match program
   119  	// order between entry to and return from [GlobalMemoryBarrier]", as for
   120  	// membarrier(2).
   121  	//
   122  	// Preconditions: HaveGlobalMemoryBarrier() == true.
   123  	GlobalMemoryBarrier() error
   124  
   125  	// SeccompInfo returns seccomp-related information about this platform.
   126  	SeccompInfo() SeccompInfo
   127  }
   128  
   129  // NoCPUPreemptionDetection implements Platform.DetectsCPUPreemption and
   130  // dependent methods for Platforms that do not support this feature.
   131  type NoCPUPreemptionDetection struct{}
   132  
   133  // DetectsCPUPreemption implements Platform.DetectsCPUPreemption.
   134  func (NoCPUPreemptionDetection) DetectsCPUPreemption() bool {
   135  	return false
   136  }
   137  
   138  // PreemptAllCPUs implements Platform.PreemptAllCPUs.
   139  func (NoCPUPreemptionDetection) PreemptAllCPUs() error {
   140  	panic("This platform does not support CPU preemption detection")
   141  }
   142  
   143  // UseHostGlobalMemoryBarrier implements Platform.HaveGlobalMemoryBarrier and
   144  // Platform.GlobalMemoryBarrier by invoking equivalent functionality on the
   145  // host.
   146  type UseHostGlobalMemoryBarrier struct{}
   147  
   148  // HaveGlobalMemoryBarrier implements Platform.HaveGlobalMemoryBarrier.
   149  func (UseHostGlobalMemoryBarrier) HaveGlobalMemoryBarrier() bool {
   150  	return hostmm.HaveGlobalMemoryBarrier()
   151  }
   152  
   153  // GlobalMemoryBarrier implements Platform.GlobalMemoryBarrier.
   154  func (UseHostGlobalMemoryBarrier) GlobalMemoryBarrier() error {
   155  	return hostmm.GlobalMemoryBarrier()
   156  }
   157  
   158  // UseHostProcessMemoryBarrier implements Platform.HaveGlobalMemoryBarrier and
   159  // Platform.GlobalMemoryBarrier by invoking a process-local memory barrier.
   160  // This is faster than UseHostGlobalMemoryBarrier, but is only appropriate for
   161  // platforms for which application code executes while using the sentry's
   162  // mm_struct.
   163  type UseHostProcessMemoryBarrier struct{}
   164  
   165  // HaveGlobalMemoryBarrier implements Platform.HaveGlobalMemoryBarrier.
   166  func (UseHostProcessMemoryBarrier) HaveGlobalMemoryBarrier() bool {
   167  	// Fall back to a global memory barrier if a process-local one isn't
   168  	// available.
   169  	return hostmm.HaveProcessMemoryBarrier() || hostmm.HaveGlobalMemoryBarrier()
   170  }
   171  
   172  // GlobalMemoryBarrier implements Platform.GlobalMemoryBarrier.
   173  func (UseHostProcessMemoryBarrier) GlobalMemoryBarrier() error {
   174  	if hostmm.HaveProcessMemoryBarrier() {
   175  		return hostmm.ProcessMemoryBarrier()
   176  	}
   177  	return hostmm.GlobalMemoryBarrier()
   178  }
   179  
   180  // DoesOwnPageTables implements Platform.OwnsPageTables in the positive.
   181  type DoesOwnPageTables struct{}
   182  
   183  // OwnsPageTables implements Platform.OwnsPageTables.
   184  func (DoesOwnPageTables) OwnsPageTables() bool {
   185  	return true
   186  }
   187  
   188  // DoesNotOwnPageTables implements Platform.OwnsPageTables in the negative.
   189  type DoesNotOwnPageTables struct{}
   190  
   191  // OwnsPageTables implements Platform.OwnsPageTables.
   192  func (DoesNotOwnPageTables) OwnsPageTables() bool {
   193  	return false
   194  }
   195  
   196  // MemoryManager represents an abstraction above the platform address space
   197  // which manages memory mappings and their contents.
   198  type MemoryManager interface {
   199  	//usermem.IO provides access to the contents of a virtual memory space.
   200  	usermem.IO
   201  	// MMap establishes a memory mapping.
   202  	MMap(ctx context.Context, opts memmap.MMapOpts) (hostarch.Addr, error)
   203  	// AddressSpace returns the AddressSpace bound to mm.
   204  	AddressSpace() AddressSpace
   205  	// FindVMAByName finds a vma with the specified name.
   206  	FindVMAByName(ar hostarch.AddrRange, hint string) (hostarch.Addr, uint64, error)
   207  }
   208  
   209  // Context represents the execution context for a single thread.
   210  type Context interface {
   211  	// Switch resumes execution of the thread specified by the arch.Context64
   212  	// in the provided address space. This call will block while the thread
   213  	// is executing.
   214  	//
   215  	// If cpu is non-negative, and it is not the number of the CPU that the
   216  	// thread executes on, Context should return ErrContextCPUPreempted. cpu
   217  	// can only be non-negative if Platform.DetectsCPUPreemption() is true;
   218  	// Contexts from Platforms for which this does not hold may ignore cpu, or
   219  	// panic if cpu is non-negative.
   220  	//
   221  	// Switch may return one of the following special errors:
   222  	//
   223  	//	- nil: The Context invoked a system call.
   224  	//
   225  	//	- ErrContextSignal: The Context was interrupted by a signal. The
   226  	//		returned *linux.SignalInfo contains information about the signal. If
   227  	//		linux.SignalInfo.Signo == SIGSEGV, the returned hostarch.AccessType
   228  	//		contains the access type of the triggering fault. The caller owns
   229  	//		the returned SignalInfo.
   230  	//
   231  	//	- ErrContextInterrupt: The Context was interrupted by a call to
   232  	//		Interrupt(). Switch() may return ErrContextInterrupt spuriously. In
   233  	//		particular, most implementations of Interrupt() will cause the first
   234  	//		following call to Switch() to return ErrContextInterrupt if there is no
   235  	//		concurrent call to Switch().
   236  	//
   237  	//	- ErrContextCPUPreempted: See the definition of that error for details.
   238  	Switch(ctx context.Context, mm MemoryManager, ac *arch.Context64, cpu int32) (*linux.SignalInfo, hostarch.AccessType, error)
   239  
   240  	// PullFullState() pulls a full state of the application thread.
   241  	//
   242  	// A platform can support lazy loading/restoring of a thread state
   243  	// which includes registers and a floating point state.
   244  	//
   245  	// For example, when the Sentry handles a system call, it may have only
   246  	// syscall arguments without other registers and a floating point
   247  	// state. And in this case, if the Sentry will need to construct a
   248  	// signal frame to call a signal handler, it will need to call
   249  	// PullFullState() to load all registers and FPU state.
   250  	//
   251  	// Preconditions: The caller must be running on the task goroutine.
   252  	PullFullState(as AddressSpace, ac *arch.Context64) error
   253  
   254  	// FullStateChanged() indicates that a thread state has been changed by
   255  	// the Sentry. This happens in case of the rt_sigreturn, execve, etc.
   256  	//
   257  	// First, it indicates that the Sentry has the full state of the thread
   258  	// and PullFullState() has to do nothing if it is called after
   259  	// FullStateChanged().
   260  	//
   261  	// Second, it forces restoring the full state of the application
   262  	// thread. A platform can support lazy loading/restoring of a thread
   263  	// state. This means that if the Sentry has not changed a thread state,
   264  	// the platform may not restore it.
   265  	//
   266  	// Preconditions: The caller must be running on the task goroutine.
   267  	FullStateChanged()
   268  
   269  	// Interrupt interrupts a concurrent call to Switch(), causing it to return
   270  	// ErrContextInterrupt.
   271  	Interrupt()
   272  
   273  	// Release() releases any resources associated with this context.
   274  	Release()
   275  
   276  	// PrepareSleep() is called when the tread switches to the
   277  	// interruptible sleep state.
   278  	PrepareSleep()
   279  }
   280  
   281  // ContextError is one of the possible errors returned by Context.Switch().
   282  type ContextError struct {
   283  	// Err is the underlying error.
   284  	Err error
   285  	// Errno is an approximation of what type of error this is supposed to
   286  	// be as defined by the linux errnos.
   287  	Errno unix.Errno
   288  }
   289  
   290  func (e *ContextError) Error() string {
   291  	return e.Err.Error()
   292  }
   293  
   294  var (
   295  	// ErrContextSignal is returned by Context.Switch() to indicate that the
   296  	// Context was interrupted by a signal.
   297  	ErrContextSignal = fmt.Errorf("interrupted by signal")
   298  
   299  	// ErrContextInterrupt is returned by Context.Switch() to indicate that the
   300  	// Context was interrupted by a call to Context.Interrupt().
   301  	ErrContextInterrupt = fmt.Errorf("interrupted by platform.Context.Interrupt()")
   302  
   303  	// ErrContextCPUPreempted is returned by Context.Switch() to indicate that
   304  	// one of the following occurred:
   305  	//
   306  	//	- The CPU executing the Context is not the CPU passed to
   307  	//		Context.Switch().
   308  	//
   309  	//	- The CPU executing the Context may have executed another Context since
   310  	//		the last time it executed this one; or the CPU has previously executed
   311  	//		another Context, and has never executed this one.
   312  	//
   313  	//	- Platform.PreemptAllCPUs() was called since the last return from
   314  	//		Context.Switch().
   315  	ErrContextCPUPreempted = fmt.Errorf("interrupted by CPU preemption")
   316  )
   317  
   318  // SignalInterrupt is a signal reserved for use by implementations of
   319  // Context.Interrupt(). The sentry guarantees that it will ignore delivery of
   320  // this signal both to Contexts and to the sentry itself, under the assumption
   321  // that they originate from races with Context.Interrupt().
   322  //
   323  // NOTE(b/23420492): The Go runtime only guarantees that a small subset
   324  // of signals will be always be unblocked on all threads, one of which
   325  // is SIGCHLD.
   326  const SignalInterrupt = linux.SIGCHLD
   327  
   328  // AddressSpace represents a virtual address space in which a Context can
   329  // execute.
   330  type AddressSpace interface {
   331  	// MapFile creates a shared mapping of offsets fr from f at address addr.
   332  	// Any existing overlapping mappings are silently replaced.
   333  	//
   334  	// If precommit is true, the platform should eagerly commit resources (e.g.
   335  	// physical memory) to the mapping. The precommit flag is advisory and
   336  	// implementations may choose to ignore it.
   337  	//
   338  	// Preconditions:
   339  	//	* addr and fr must be page-aligned.
   340  	//	* fr.Length() > 0.
   341  	//	* at.Any() == true.
   342  	//	* At least one reference must be held on all pages in fr, and must
   343  	//		continue to be held as long as pages are mapped.
   344  	MapFile(addr hostarch.Addr, f memmap.File, fr memmap.FileRange, at hostarch.AccessType, precommit bool) error
   345  
   346  	// Unmap unmaps the given range.
   347  	//
   348  	// Preconditions:
   349  	//	* addr is page-aligned.
   350  	//	* length > 0.
   351  	Unmap(addr hostarch.Addr, length uint64)
   352  
   353  	// Release releases this address space. After releasing, a new AddressSpace
   354  	// must be acquired via platform.NewAddressSpace().
   355  	Release()
   356  
   357  	// PreFork() is called before creating a copy of AddressSpace. This
   358  	// guarantees that this address space will be in a consistent state.
   359  	PreFork()
   360  
   361  	// PostFork() is called after creating a copy of AddressSpace.
   362  	PostFork()
   363  
   364  	// AddressSpaceIO methods are supported iff the associated platform's
   365  	// Platform.SupportsAddressSpaceIO() == true. AddressSpaces for which this
   366  	// does not hold may panic if AddressSpaceIO methods are invoked.
   367  	AddressSpaceIO
   368  }
   369  
   370  // AddressSpaceIO supports IO through the memory mappings installed in an
   371  // AddressSpace.
   372  //
   373  // AddressSpaceIO implementors are responsible for ensuring that address ranges
   374  // are application-mappable.
   375  type AddressSpaceIO interface {
   376  	// CopyOut copies len(src) bytes from src to the memory mapped at addr. It
   377  	// returns the number of bytes copied. If the number of bytes copied is <
   378  	// len(src), it returns a non-nil error explaining why.
   379  	CopyOut(addr hostarch.Addr, src []byte) (int, error)
   380  
   381  	// CopyIn copies len(dst) bytes from the memory mapped at addr to dst.
   382  	// It returns the number of bytes copied. If the number of bytes copied is
   383  	// < len(dst), it returns a non-nil error explaining why.
   384  	CopyIn(addr hostarch.Addr, dst []byte) (int, error)
   385  
   386  	// ZeroOut sets toZero bytes to 0, starting at addr. It returns the number
   387  	// of bytes zeroed. If the number of bytes zeroed is < toZero, it returns a
   388  	// non-nil error explaining why.
   389  	ZeroOut(addr hostarch.Addr, toZero uintptr) (uintptr, error)
   390  
   391  	// SwapUint32 atomically sets the uint32 value at addr to new and returns
   392  	// the previous value.
   393  	//
   394  	// Preconditions: addr must be aligned to a 4-byte boundary.
   395  	SwapUint32(addr hostarch.Addr, new uint32) (uint32, error)
   396  
   397  	// CompareAndSwapUint32 atomically compares the uint32 value at addr to
   398  	// old; if they are equal, the value in memory is replaced by new. In
   399  	// either case, the previous value stored in memory is returned.
   400  	//
   401  	// Preconditions: addr must be aligned to a 4-byte boundary.
   402  	CompareAndSwapUint32(addr hostarch.Addr, old, new uint32) (uint32, error)
   403  
   404  	// LoadUint32 atomically loads the uint32 value at addr and returns it.
   405  	//
   406  	// Preconditions: addr must be aligned to a 4-byte boundary.
   407  	LoadUint32(addr hostarch.Addr) (uint32, error)
   408  }
   409  
   410  // NoAddressSpaceIO implements AddressSpaceIO methods by panicking.
   411  type NoAddressSpaceIO struct{}
   412  
   413  // CopyOut implements AddressSpaceIO.CopyOut.
   414  func (NoAddressSpaceIO) CopyOut(addr hostarch.Addr, src []byte) (int, error) {
   415  	panic("This platform does not support AddressSpaceIO")
   416  }
   417  
   418  // CopyIn implements AddressSpaceIO.CopyIn.
   419  func (NoAddressSpaceIO) CopyIn(addr hostarch.Addr, dst []byte) (int, error) {
   420  	panic("This platform does not support AddressSpaceIO")
   421  }
   422  
   423  // ZeroOut implements AddressSpaceIO.ZeroOut.
   424  func (NoAddressSpaceIO) ZeroOut(addr hostarch.Addr, toZero uintptr) (uintptr, error) {
   425  	panic("This platform does not support AddressSpaceIO")
   426  }
   427  
   428  // SwapUint32 implements AddressSpaceIO.SwapUint32.
   429  func (NoAddressSpaceIO) SwapUint32(addr hostarch.Addr, new uint32) (uint32, error) {
   430  	panic("This platform does not support AddressSpaceIO")
   431  }
   432  
   433  // CompareAndSwapUint32 implements AddressSpaceIO.CompareAndSwapUint32.
   434  func (NoAddressSpaceIO) CompareAndSwapUint32(addr hostarch.Addr, old, new uint32) (uint32, error) {
   435  	panic("This platform does not support AddressSpaceIO")
   436  }
   437  
   438  // LoadUint32 implements AddressSpaceIO.LoadUint32.
   439  func (NoAddressSpaceIO) LoadUint32(addr hostarch.Addr) (uint32, error) {
   440  	panic("This platform does not support AddressSpaceIO")
   441  }
   442  
   443  // SegmentationFault is an error returned by AddressSpaceIO methods when IO
   444  // fails due to access of an unmapped page, or a mapped page with insufficient
   445  // permissions.
   446  type SegmentationFault struct {
   447  	// Addr is the address at which the fault occurred.
   448  	Addr hostarch.Addr
   449  }
   450  
   451  // Error implements error.Error.
   452  func (f SegmentationFault) Error() string {
   453  	return fmt.Sprintf("segmentation fault at %#x", f.Addr)
   454  }
   455  
   456  // Requirements is used to specify platform specific requirements.
   457  type Requirements struct {
   458  	// RequiresCurrentPIDNS indicates that the sandbox has to be started in the
   459  	// current pid namespace.
   460  	RequiresCurrentPIDNS bool
   461  	// RequiresCapSysPtrace indicates that the sandbox has to be started with
   462  	// the CAP_SYS_PTRACE capability.
   463  	RequiresCapSysPtrace bool
   464  }
   465  
   466  // SeccompInfo represents seccomp-bpf data for a given platform.
   467  type SeccompInfo interface {
   468  	// Variables returns a map from named variables to the value they should
   469  	// have with the platform as currently initialized.
   470  	// Variables are known only at runtime, but are not part of a platform's
   471  	// configuration. For example, the KVM platform having an FD representing
   472  	// the KVM VM is a variable: it is only known at runtime, but does not
   473  	// change the structure of the syscall rules.
   474  	// The set of variable names must be static regardless of platform
   475  	// configuration.
   476  	Variables() precompiledseccomp.Values
   477  
   478  	// ConfigKey returns a string that uniquely represents the set of
   479  	// configuration information from which syscall rules are derived,
   480  	// other than variables or CPU architecture.
   481  	// This should at least contain the platform name.
   482  	// If syscall rules are dependent on the platform's configuration,
   483  	// this should return a string that encapsulates the values of these
   484  	// configuration options.
   485  	// For example, if some option of the platform causes it to require a
   486  	// new syscall to be allowed, this option should be part of this string.
   487  	ConfigKey() string
   488  
   489  	// SyscallFilters returns syscalls made exclusively by this platform.
   490  	// `vars` maps variable names (as returned by `Variables()`) to values,
   491  	// and **the rules should depend on `vars`**. These will not necessarily
   492  	// map to the result of calling `Variables()` on the current `SeccompInfo`;
   493  	// during seccomp rule precompilation, these will be set to placeholder
   494  	// values.
   495  	SyscallFilters(vars precompiledseccomp.Values) seccomp.SyscallRules
   496  
   497  	// HottestSyscalls returns the list of syscall numbers that this platform
   498  	// calls most often, most-frequently-called first. No more than a dozen
   499  	// syscalls. Returning an empty or a nil slice is OK.
   500  	// This is used to produce a more efficient seccomp-bpf program that can
   501  	// check for the most frequently called syscalls first.
   502  	// What matters here is only the frequency at which a syscall is called,
   503  	// not the total amount of CPU time that is used to process it in the host
   504  	// kernel.
   505  	HottestSyscalls() []uintptr
   506  }
   507  
   508  // StaticSeccompInfo implements `SeccompInfo` for platforms which don't have
   509  // any configuration or variables.
   510  type StaticSeccompInfo struct {
   511  	// PlatformName is the platform name.
   512  	PlatformName string
   513  
   514  	// Filters is the platform's syscall filters.
   515  	Filters seccomp.SyscallRules
   516  
   517  	// HotSyscalls is the list of syscalls numbers that this platform
   518  	// calls most often, most-frequently-called first.
   519  	// See `SeccompInfo.HottestSyscalls` for more.
   520  	HotSyscalls []uintptr
   521  }
   522  
   523  // Variables implements `SeccompInfo.Variables`.
   524  func (StaticSeccompInfo) Variables() precompiledseccomp.Values {
   525  	return nil
   526  }
   527  
   528  // ConfigKey implements `SeccompInfo.ConfigKey`.
   529  func (s StaticSeccompInfo) ConfigKey() string {
   530  	return s.PlatformName
   531  }
   532  
   533  // SyscallFilters implements `SeccompInfo.SyscallFilters`.
   534  func (s StaticSeccompInfo) SyscallFilters(precompiledseccomp.Values) seccomp.SyscallRules {
   535  	return s.Filters
   536  }
   537  
   538  // HottestSyscalls implements `SeccompInfo.HottestSyscalls`.
   539  func (s StaticSeccompInfo) HottestSyscalls() []uintptr {
   540  	return s.HotSyscalls
   541  }
   542  
   543  // Constructor represents a platform type.
   544  type Constructor interface {
   545  	// New returns a new platform instance.
   546  	//
   547  	// Arguments:
   548  	//
   549  	//	* deviceFile - the device file (e.g. /dev/kvm for the KVM platform).
   550  	New(deviceFile *os.File) (Platform, error)
   551  
   552  	// OpenDevice opens the path to the device used by the platform.
   553  	// Passing in an empty string will use the default path for the device,
   554  	// e.g. "/dev/kvm" for the KVM platform.
   555  	OpenDevice(devicePath string) (*os.File, error)
   556  
   557  	// Requirements returns platform specific requirements.
   558  	Requirements() Requirements
   559  
   560  	// PrecompiledSeccompInfo returns a list of `SeccompInfo`s that is
   561  	// useful to precompile into the Sentry.
   562  	PrecompiledSeccompInfo() []SeccompInfo
   563  }
   564  
   565  // platforms contains all available platform types.
   566  var platforms = map[string]Constructor{}
   567  
   568  // Register registers a new platform type.
   569  func Register(name string, platform Constructor) {
   570  	platforms[name] = platform
   571  }
   572  
   573  // List lists available platforms.
   574  func List() (available []string) {
   575  	for name := range platforms {
   576  		available = append(available, name)
   577  	}
   578  	return
   579  }
   580  
   581  // Lookup looks up the platform constructor by name.
   582  func Lookup(name string) (Constructor, error) {
   583  	p, ok := platforms[name]
   584  	if !ok {
   585  		return nil, fmt.Errorf("unknown platform: %v", name)
   586  	}
   587  	return p, nil
   588  }