From 68267dccc8f057e29624dcfd80274c804e0a372c Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Thu, 23 Mar 2023 14:21:44 -0700 Subject: [PATCH] `runsc`: Add metric counting calls of unimplemented system calls. This is useful to determine which syscalls users want but are unimplemented. PiperOrigin-RevId: 518962607 --- pkg/sentry/kernel/kernel.go | 1 + pkg/sentry/kernel/syscalls.go | 44 +++++++++++++++++++++++++++++++++ pkg/sentry/syscalls/syscalls.go | 1 + 3 files changed, 46 insertions(+) diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index be17dc39a..a8eac65fc 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -1540,6 +1540,7 @@ func (k *Kernel) EmitUnimplementedEvent(ctx context.Context, sysno uintptr) { }) t := TaskFromContext(ctx) + IncrementUnimplementedSyscallCounter(sysno) _, _ = k.unimplementedSyscallEmitter.Emit(&uspb.UnimplementedSyscall{ Tid: int32(t.ThreadID()), Registers: t.Arch().StateData().Proto(), diff --git a/pkg/sentry/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index 55fe2cf4c..fdf8b1c67 100644 --- a/pkg/sentry/kernel/syscalls.go +++ b/pkg/sentry/kernel/syscalls.go @@ -16,12 +16,14 @@ package kernel import ( "fmt" + "strconv" "google.golang.org/protobuf/proto" "gvisor.dev/gvisor/pkg/abi" "gvisor.dev/gvisor/pkg/atomicbitops" "gvisor.dev/gvisor/pkg/bits" "gvisor.dev/gvisor/pkg/hostarch" + "gvisor.dev/gvisor/pkg/metric" "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/seccheck" pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto" @@ -37,6 +39,10 @@ const ( // LINT.IfChange maxSyscallNum = 2000 // LINT.ThenChange(../seccheck/syscall.go) + + // outOfRangeSyscallNumber is used to represent a syscall number that is out of the + // range [0, maxSyscallNum] in monitoring. + outOfRangeSyscallNumber = "-1" ) // SyscallSupportLevel is a syscall support levels. @@ -347,6 +353,19 @@ func (s *SyscallTable) MaxSysno() (max uintptr) { // allSyscallTables contains all known tables. var allSyscallTables []*SyscallTable +var ( + // unimplementedSyscallCounterInit ensures the following fields are only initialized once. + unimplementedSyscallCounterInit sync.Once + + // unimplementedSyscallNumbers maps syscall numbers to their string representation. + // Used such that incrementing unimplementedSyscallCounter does not require allocating memory. + unimplementedSyscallNumbers map[uintptr]string + + // unimplementedSyscallCounter tracks the number of times each unimplemented syscall has been + // called by the sandboxed application. + unimplementedSyscallCounter *metric.Uint64Metric +) + // SyscallTables returns a read-only slice of registered SyscallTables. func SyscallTables() []*SyscallTable { return allSyscallTables @@ -371,6 +390,17 @@ func RegisterSyscallTable(s *SyscallTable) { panic(fmt.Sprintf("Duplicate SyscallTable registered for OS %v Arch %v", s.OS, s.Arch)) } allSyscallTables = append(allSyscallTables, s) + unimplementedSyscallCounterInit.Do(func() { + allowedValues := make([]string, maxSyscallNum+2) + unimplementedSyscallNumbers = make(map[uintptr]string, len(allowedValues)) + for i := uintptr(0); i <= maxSyscallNum; i++ { + s := strconv.Itoa(int(i)) + allowedValues[i] = s + unimplementedSyscallNumbers[i] = s + } + allowedValues[len(allowedValues)-1] = outOfRangeSyscallNumber + unimplementedSyscallCounter = metric.MustCreateNewUint64Metric("unimplemented_syscalls", true, "Number of times the application tried to call an unimplemented syscall, broken down by syscall number", metric.NewField("sysno", allowedValues)) + }) s.Init() } @@ -461,3 +491,17 @@ type SyscallInfo struct { Rval uintptr Errno int } + +// IncrementUnimplementedSyscallCounter increments the "unimplemented syscall" metric for the given +// syscall number. +// A syscall table must have been initialized prior to calling this function. +// +checkescape:all +// +//go:nosplit +func IncrementUnimplementedSyscallCounter(sysno uintptr) { + s, found := unimplementedSyscallNumbers[sysno] + if !found { + s = outOfRangeSyscallNumber + } + unimplementedSyscallCounter.Increment(s) +} diff --git a/pkg/sentry/syscalls/syscalls.go b/pkg/sentry/syscalls/syscalls.go index 7d5c9d8b3..a18eb0c9a 100644 --- a/pkg/sentry/syscalls/syscalls.go +++ b/pkg/sentry/syscalls/syscalls.go @@ -78,6 +78,7 @@ func Error(name string, err error, note string, urls []string) kernel.Syscall { return kernel.Syscall{ Name: name, Fn: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + kernel.IncrementUnimplementedSyscallCounter(sysno) return 0, nil, err }, SupportLevel: kernel.SupportUnimplemented,