From 80ee2d3acca8861626a20f1ae9d44f2724500a7c Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Wed, 25 Oct 2023 15:20:20 -0700 Subject: [PATCH] Move max syscall number out of `kernel` package. Also add "num" at the end of it so make it clear that it's an *inclusive* limit. Fix the uses of it which didn't treat it as inclusive. No strong opinion on where it should go, but having it available in other places of the codebase is helpful. It's already in two places, and I'd like to have it available in two more places: - In the seccomp program compiler, so that it knows how many syscalls it should check for deterministic-ness and caching the results. - In the seccomp fuzzer, to build an initial syscall corpus. PiperOrigin-RevId: 576660986 --- pkg/abi/sentry/BUILD | 15 +++++++++++++++ pkg/abi/sentry/sentry.go | 16 ++++++++++++++++ pkg/abi/sentry/syscall.go | 20 ++++++++++++++++++++ pkg/sentry/kernel/BUILD | 1 + pkg/sentry/kernel/syscalls.go | 32 +++++++++++--------------------- pkg/sentry/seccheck/BUILD | 2 ++ pkg/sentry/seccheck/syscall.go | 10 ++++++---- 7 files changed, 71 insertions(+), 25 deletions(-) create mode 100644 pkg/abi/sentry/BUILD create mode 100644 pkg/abi/sentry/sentry.go create mode 100644 pkg/abi/sentry/syscall.go diff --git a/pkg/abi/sentry/BUILD b/pkg/abi/sentry/BUILD new file mode 100644 index 000000000..d0e79fa27 --- /dev/null +++ b/pkg/abi/sentry/BUILD @@ -0,0 +1,15 @@ +load("//tools:defs.bzl", "go_library") + +package( + default_applicable_licenses = ["//:license"], + licenses = ["notice"], +) + +go_library( + name = "sentry", + srcs = [ + "sentry.go", + "syscall.go", + ], + visibility = ["//:sandbox"], +) diff --git a/pkg/abi/sentry/sentry.go b/pkg/abi/sentry/sentry.go new file mode 100644 index 000000000..84f765ed3 --- /dev/null +++ b/pkg/abi/sentry/sentry.go @@ -0,0 +1,16 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package sentry contains ABI-related constants for the gVisor sentry. +package sentry diff --git a/pkg/abi/sentry/syscall.go b/pkg/abi/sentry/syscall.go new file mode 100644 index 000000000..71391b5d7 --- /dev/null +++ b/pkg/abi/sentry/syscall.go @@ -0,0 +1,20 @@ +// Copyright 2023 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sentry + +// MaxSyscallNum is the largest-numbered syscall that is supported. +// Having this as a constant allows allocating per-syscall data structures +// that are of fixed size throughout the codebase. +const MaxSyscallNum = 2000 diff --git a/pkg/sentry/kernel/BUILD b/pkg/sentry/kernel/BUILD index 5c627205a..5892f32c5 100644 --- a/pkg/sentry/kernel/BUILD +++ b/pkg/sentry/kernel/BUILD @@ -317,6 +317,7 @@ go_library( "//pkg/abi", "//pkg/abi/linux", "//pkg/abi/linux/errno", + "//pkg/abi/sentry", "//pkg/atomicbitops", "//pkg/bitmap", "//pkg/bits", diff --git a/pkg/sentry/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index eb3957c67..7211d7cde 100644 --- a/pkg/sentry/kernel/syscalls.go +++ b/pkg/sentry/kernel/syscalls.go @@ -20,6 +20,7 @@ import ( "google.golang.org/protobuf/proto" "gvisor.dev/gvisor/pkg/abi" + "gvisor.dev/gvisor/pkg/abi/sentry" "gvisor.dev/gvisor/pkg/atomicbitops" "gvisor.dev/gvisor/pkg/bits" "gvisor.dev/gvisor/pkg/hostarch" @@ -30,17 +31,6 @@ import ( "gvisor.dev/gvisor/pkg/sync" ) -const ( - // maxSyscallNum is the highest supported syscall number. - // - // The types below create fast lookup slices for all syscalls. This maximum - // serves as a sanity check that we don't allocate huge slices for a very large - // syscall. This is checked during registration. - // 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. var outOfRangeSyscallNumber = []*metric.FieldValue{&metric.FieldValue{"-1"}} @@ -149,7 +139,7 @@ type SyscallFlagsTable struct { // // missing syscalls have the same value in enable as missingEnable to // avoid an extra branch in Word. - enable [maxSyscallNum + 1]atomicbitops.Uint32 + enable [sentry.MaxSyscallNum + 1]atomicbitops.Uint32 // missingEnable contains the enable bits for missing syscalls. missingEnable atomicbitops.Uint32 @@ -173,7 +163,7 @@ func (e *SyscallFlagsTable) init(table map[uintptr]Syscall) { func (e *SyscallFlagsTable) UpdateSecCheck(state *seccheck.State) { e.mu.Lock() defer e.mu.Unlock() - for sysno := uintptr(0); sysno < maxSyscallNum; sysno++ { + for sysno := uintptr(0); sysno <= sentry.MaxSyscallNum; sysno++ { oldFlags := e.enable[sysno].Load() if !bits.IsOn32(oldFlags, syscallPresent) { continue @@ -207,7 +197,7 @@ func (e *SyscallFlagsTable) UpdateSecCheck(state *seccheck.State) { // Word returns the enable bitfield for sysno. func (e *SyscallFlagsTable) Word(sysno uintptr) uint32 { - if sysno <= maxSyscallNum { + if sysno <= sentry.MaxSyscallNum { return e.enable[sysno].Load() } return e.missingEnable.Load() @@ -308,12 +298,12 @@ type SyscallTable struct { // lookup is a fixed-size array that holds the syscalls (indexed by // their numbers). It is used for fast look ups. - lookup [maxSyscallNum + 1]SyscallFn + lookup [sentry.MaxSyscallNum + 1]SyscallFn // pointCallbacks is a fixed-size array that holds SyscallToProto callbacks // (indexed by syscall numbers). It is used for fast lookups when // seccheck.Point is enabled for the syscall. - pointCallbacks [maxSyscallNum + 1]SyscallToProto + pointCallbacks [sentry.MaxSyscallNum + 1]SyscallToProto // Emulate is a collection of instruction addresses to emulate. The // keys are addresses, and the values are system call numbers. @@ -386,7 +376,7 @@ func LookupSyscallTable(os abi.OS, a arch.Arch) (*SyscallTable, bool) { // RegisterSyscallTable registers a new syscall table for use by a Kernel. func RegisterSyscallTable(s *SyscallTable) { - if max := s.MaxSysno(); max > maxSyscallNum { + if max := s.MaxSysno(); max > sentry.MaxSyscallNum { panic(fmt.Sprintf("SyscallTable %+v contains too large syscall number %d", s, max)) } if _, ok := LookupSyscallTable(s.OS, s.Arch); ok { @@ -394,9 +384,9 @@ func RegisterSyscallTable(s *SyscallTable) { } allSyscallTables = append(allSyscallTables, s) unimplementedSyscallCounterInit.Do(func() { - allowedValues := make([]*metric.FieldValue, maxSyscallNum+2) + allowedValues := make([]*metric.FieldValue, sentry.MaxSyscallNum+2) unimplementedSyscallNumbers = make(map[uintptr][]*metric.FieldValue, len(allowedValues)) - for i := uintptr(0); i <= maxSyscallNum; i++ { + for i := uintptr(0); i <= sentry.MaxSyscallNum; i++ { s := &metric.FieldValue{strconv.Itoa(int(i))} allowedValues[i] = s unimplementedSyscallNumbers[i] = []*metric.FieldValue{s} @@ -434,7 +424,7 @@ func (s *SyscallTable) Init() { // Lookup returns the syscall implementation, if one exists. func (s *SyscallTable) Lookup(sysno uintptr) SyscallFn { - if sysno <= maxSyscallNum { + if sysno <= sentry.MaxSyscallNum { return s.lookup[sysno] } return nil @@ -476,7 +466,7 @@ func (s *SyscallTable) mapLookup(sysno uintptr) SyscallFn { // LookupSyscallToProto looks up the SyscallToProto callback for the given // syscall. It may return nil if none is registered. func (s *SyscallTable) LookupSyscallToProto(sysno uintptr) SyscallToProto { - if sysno > maxSyscallNum { + if sysno > sentry.MaxSyscallNum { return nil } return s.pointCallbacks[sysno] diff --git a/pkg/sentry/seccheck/BUILD b/pkg/sentry/seccheck/BUILD index c772c4b0c..e8faf0410 100644 --- a/pkg/sentry/seccheck/BUILD +++ b/pkg/sentry/seccheck/BUILD @@ -29,7 +29,9 @@ go_library( ], visibility = ["//:sandbox"], deps = [ + "//pkg/abi", "//pkg/abi/linux", + "//pkg/abi/sentry", "//pkg/atomicbitops", "//pkg/context", "//pkg/fd", diff --git a/pkg/sentry/seccheck/syscall.go b/pkg/sentry/seccheck/syscall.go index cdfec0b55..e0231df44 100644 --- a/pkg/sentry/seccheck/syscall.go +++ b/pkg/sentry/seccheck/syscall.go @@ -14,6 +14,10 @@ package seccheck +import ( + "gvisor.dev/gvisor/pkg/abi/sentry" +) + // SyscallType is an enum that denotes different types of syscall points. There // are 2 types of syscall point: fully-schematized and raw. Schematizes are // points that have syscall specific format, e.g. open => {path, flags, mode}. @@ -47,9 +51,7 @@ type SyscallFlagListener interface { } const ( - // Copied from kernel.maxSyscallNum to avoid reverse dependency. - syscallsMax = 2000 - syscallPoints = syscallsMax * int(syscallTypesCount) + syscallPoints = (sentry.MaxSyscallNum + 1) * int(syscallTypesCount) ) // Fields that are common for many syscalls. @@ -75,7 +77,7 @@ func GetPointForSyscall(typ SyscallType, sysno uintptr) Point { // SyscallEnabled checks if the corresponding point for the syscall is enabled. func (s *State) SyscallEnabled(typ SyscallType, sysno uintptr) bool { // Prevent overflow. - if sysno >= syscallsMax { + if sysno >= sentry.MaxSyscallNum { return false } return s.Enabled(GetPointForSyscall(typ, sysno))