From d4510e760bb08d1ab5f995e838b9a600e400a405 Mon Sep 17 00:00:00 2001 From: Lucas Manning Date: Thu, 20 Jul 2023 13:13:05 -0700 Subject: [PATCH] Add seccomp filters for TPU proxying and stub out accel fd methods. PiperOrigin-RevId: 549718797 --- pkg/seccomp/seccomp.go | 7 ++ pkg/sentry/devices/accel/BUILD | 24 ++++ pkg/sentry/devices/accel/accel.go | 64 ++++++++++ pkg/sentry/devices/accel/seccomp_filters.go | 116 ++++++++++++++++++ pkg/sentry/devices/nvproxy/seccomp_filters.go | 2 +- runsc/boot/filter/BUILD | 1 + runsc/boot/filter/config.go | 8 +- runsc/boot/filter/filter.go | 6 + runsc/boot/loader.go | 1 + 9 files changed, 221 insertions(+), 8 deletions(-) create mode 100644 pkg/sentry/devices/accel/BUILD create mode 100644 pkg/sentry/devices/accel/accel.go create mode 100644 pkg/sentry/devices/accel/seccomp_filters.go diff --git a/pkg/seccomp/seccomp.go b/pkg/seccomp/seccomp.go index f0e9ad60b..c2e860c55 100644 --- a/pkg/seccomp/seccomp.go +++ b/pkg/seccomp/seccomp.go @@ -34,6 +34,13 @@ const ( defaultLabel = "default_action" ) +// NonNegativeFDCheck ensures an FD argument is a non-negative int. +func NonNegativeFDCheck() LessThanOrEqual { + // Negative int32 has the MSB (31st bit) set. So the raw uint FD value must + // be less than or equal to 0x7fffffff. + return LessThanOrEqual(0x7fffffff) +} + // Install generates BPF code based on the set of syscalls provided. It only // allows syscalls that conform to the specification. Syscalls that violate the // specification will trigger RET_KILL_PROCESS. If RET_KILL_PROCESS is not diff --git a/pkg/sentry/devices/accel/BUILD b/pkg/sentry/devices/accel/BUILD new file mode 100644 index 000000000..89c006d06 --- /dev/null +++ b/pkg/sentry/devices/accel/BUILD @@ -0,0 +1,24 @@ +load("//tools:defs.bzl", "go_library") + +licenses(["notice"]) + +go_library( + name = "accel", + srcs = [ + "accel.go", + "seccomp_filters.go", + ], + visibility = ["//pkg/sentry:internal"], + deps = [ + "//pkg/abi/gasket", + "//pkg/abi/linux", + "//pkg/context", + "//pkg/errors/linuxerr", + "//pkg/seccomp", + "//pkg/sentry/arch", + "//pkg/sentry/vfs", + "//pkg/usermem", + "//pkg/waiter", + "@org_golang_x_sys//unix:go_default_library", + ], +) diff --git a/pkg/sentry/devices/accel/accel.go b/pkg/sentry/devices/accel/accel.go new file mode 100644 index 000000000..fef24a23f --- /dev/null +++ b/pkg/sentry/devices/accel/accel.go @@ -0,0 +1,64 @@ +// 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 accel implements proxying for hardware accelerators. +package accel + +import ( + "gvisor.dev/gvisor/pkg/context" + "gvisor.dev/gvisor/pkg/errors/linuxerr" + "gvisor.dev/gvisor/pkg/sentry/arch" + "gvisor.dev/gvisor/pkg/sentry/vfs" + "gvisor.dev/gvisor/pkg/usermem" + "gvisor.dev/gvisor/pkg/waiter" +) + +// accelFD implements vfs.FileDescriptionImpl for /dev/accel[0-9]+. +// +// accelFD is not savable; we do not implement save/restore of accelerator +// state. +type accelFD struct { + vfsfd vfs.FileDescription + vfs.FileDescriptionDefaultImpl + vfs.DentryMetadataFileDescriptionImpl + vfs.NoLockFD +} + +// Release implements vfs.FileDescriptionImpl.Release. +func (fd *accelFD) Release(context.Context) { +} + +// EventRegister implements waiter.Waitable.EventRegister. +func (fd *accelFD) EventRegister(e *waiter.Entry) error { + return nil +} + +// EventUnregister implements waiter.Waitable.EventUnregister. +func (fd *accelFD) EventUnregister(e *waiter.Entry) { +} + +// Readiness implements waiter.Waitable.Readiness. +func (fd *accelFD) Readiness(mask waiter.EventMask) waiter.EventMask { + return waiter.EventErr +} + +// Epollable implements vfs.FileDescriptionImpl.Epollable. +func (fd *accelFD) Epollable() bool { + return true +} + +// Ioctl implements vfs.FileDescriptionImpl.Ioctl. +func (fd *accelFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { + return 0, linuxerr.ENOSYS +} diff --git a/pkg/sentry/devices/accel/seccomp_filters.go b/pkg/sentry/devices/accel/seccomp_filters.go new file mode 100644 index 000000000..8d4470c7e --- /dev/null +++ b/pkg/sentry/devices/accel/seccomp_filters.go @@ -0,0 +1,116 @@ +// 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 accel + +import ( + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/abi/gasket" + "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/seccomp" +) + +// Filters returns seccomp-bpf filters for this package. +func Filters() seccomp.SyscallRules { + nonNegativeFD := seccomp.NonNegativeFDCheck() + return seccomp.SyscallRules{ + unix.SYS_OPENAT: []seccomp.Rule{ + { + // All paths that we openat() are absolute, so we pass a dirfd + // of -1 (which is invalid for relative paths, but ignored for + // absolute paths) to hedge against bugs involving AT_FDCWD or + // real dirfds. + seccomp.EqualTo(^uintptr(0)), + seccomp.MatchAny{}, + seccomp.MaskedEqual(unix.O_CREAT|unix.O_NOFOLLOW, unix.O_NOFOLLOW), + seccomp.MatchAny{}, + }, + }, + unix.SYS_GETDENTS64: {}, + unix.SYS_IOCTL: []seccomp.Rule{ + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_RESET), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_SET_EVENTFD), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_CLEAR_EVENTFD), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_NUMBER_PAGE_TABLES), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_PAGE_TABLE_SIZE), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_PARTITION_PAGE_TABLE), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_MAP_BUFFER), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_UNMAP_BUFFER), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_REGISTER_INTERRUPT), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_UNREGISTER_INTERRUPT), + }, + { + nonNegativeFD, + seccomp.EqualTo(gasket.GASKET_IOCTL_MAP_DMA_BUF), + }, + }, + unix.SYS_EVENTFD2: []seccomp.Rule{ + { + seccomp.MatchAny{}, + seccomp.EqualTo(linux.EFD_NONBLOCK), + }, + { + seccomp.MatchAny{}, + seccomp.EqualTo(linux.EFD_NONBLOCK | linux.EFD_SEMAPHORE), + }, + }, + unix.SYS_MREMAP: []seccomp.Rule{ + { + seccomp.MatchAny{}, + seccomp.EqualTo(0), /* old_size */ + seccomp.MatchAny{}, + seccomp.EqualTo(linux.MREMAP_MAYMOVE | linux.MREMAP_FIXED), + seccomp.MatchAny{}, + seccomp.EqualTo(0), + }, + }, + } +} diff --git a/pkg/sentry/devices/nvproxy/seccomp_filters.go b/pkg/sentry/devices/nvproxy/seccomp_filters.go index fe440350e..6988dab54 100644 --- a/pkg/sentry/devices/nvproxy/seccomp_filters.go +++ b/pkg/sentry/devices/nvproxy/seccomp_filters.go @@ -23,7 +23,7 @@ import ( // Filters returns seccomp-bpf filters for this package. func Filters() seccomp.SyscallRules { - nonNegativeFD := seccomp.LessThanOrEqual(0x7fff_ffff /* max int32 */) + nonNegativeFD := seccomp.NonNegativeFDCheck() notIocSizeMask := ^(((uintptr(1) << linux.IOC_SIZEBITS) - 1) << linux.IOC_SIZESHIFT) // for ioctls taking arbitrary size return seccomp.SyscallRules{ unix.SYS_OPENAT: []seccomp.Rule{ diff --git a/runsc/boot/filter/BUILD b/runsc/boot/filter/BUILD index b4fa0dd48..bd99ceb35 100644 --- a/runsc/boot/filter/BUILD +++ b/runsc/boot/filter/BUILD @@ -28,6 +28,7 @@ go_library( "//pkg/abi/linux", "//pkg/log", "//pkg/seccomp", + "//pkg/sentry/devices/accel", "//pkg/sentry/devices/nvproxy", "//pkg/sentry/platform", "//pkg/sentry/socket/hostinet", diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go index 1e97f20e4..9fb0f11d7 100644 --- a/runsc/boot/filter/config.go +++ b/runsc/boot/filter/config.go @@ -368,19 +368,13 @@ func controlServerFilters(fd int) seccomp.SyscallRules { } } -func nonNegativeFDCheck() seccomp.LessThanOrEqual { - // Negative int32 has the MSB (31st bit) set. So the raw uint FD value must - // be less than or equal to 0x7fffffff. - return seccomp.LessThanOrEqual(0x7fffffff) -} - // hostFilesystemFilters contains syscalls that are needed by directfs. func hostFilesystemFilters() seccomp.SyscallRules { // Directfs allows FD-based filesystem syscalls. We deny these syscalls with // negative FD values (like AT_FDCWD or invalid FD numbers). We try to be as // restrictive as possible because any restriction here improves security. We // don't know what set of arguments will trigger a future vulnerability. - validFDCheck := nonNegativeFDCheck() + validFDCheck := seccomp.NonNegativeFDCheck() return seccomp.SyscallRules{ unix.SYS_FCHOWNAT: []seccomp.Rule{ { diff --git a/runsc/boot/filter/filter.go b/runsc/boot/filter/filter.go index dec0f8644..1a10fd811 100644 --- a/runsc/boot/filter/filter.go +++ b/runsc/boot/filter/filter.go @@ -20,6 +20,7 @@ package filter import ( "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/seccomp" + "gvisor.dev/gvisor/pkg/sentry/devices/accel" "gvisor.dev/gvisor/pkg/sentry/devices/nvproxy" "gvisor.dev/gvisor/pkg/sentry/platform" ) @@ -32,6 +33,7 @@ type Options struct { HostFilesystem bool ProfileEnable bool NVProxy bool + TPUProxy bool ControllerFD int } @@ -64,6 +66,10 @@ func Install(opt Options) error { Report("Nvidia GPU driver proxy enabled: syscall filters less restrictive!") s.Merge(nvproxy.Filters()) } + if opt.TPUProxy { + Report("TPU device proxy enabled: syscall filters less restrictive!") + s.Merge(accel.Filters()) + } s.Merge(opt.Platform.SyscallFilters()) diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 8a0063153..9949fb09a 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -649,6 +649,7 @@ func (l *Loader) installSeccompFilters() error { HostFilesystem: l.root.conf.DirectFS, ProfileEnable: l.root.conf.ProfileEnable, NVProxy: l.root.conf.NVProxy, + TPUProxy: l.root.conf.TPUProxy, ControllerFD: l.ctrl.srv.FD(), } if err := filter.Install(opts); err != nil {