Add Points to some syscalls

Added a raw syscall points to all syscalls. Added schematized syscall
points to the following syscalls:

  - read
  - close
  - socket
  - connect
  - execve
  - creat
  - openat
  - execveat

Updates #4805

PiperOrigin-RevId: 446008358
This commit is contained in:
Fabricio Voznika
2022-05-02 13:03:04 -07:00
committed by gVisor bot
parent 87180c225b
commit f2b6fbb47e
21 changed files with 784 additions and 48 deletions
+30
View File
@@ -10,6 +10,36 @@
},
{
"name": "sentry/task_exit"
},
{
"name": "syscall/openat/enter"
},
{
"name": "syscall/openat/exit"
},
{
"name": "syscall/read/enter",
"optional_fields": [
"fd_path"
],
"context_fields": [
"time",
"container_id",
"thread_id"
]
},
{
"name": "syscall/read/exit"
},
{
"name": "syscall/sysno/1/enter",
"context_fields": [
"time",
"container_id"
]
},
{
"name": "syscall/sysno/1/exit"
}
],
"sinks": [
+24
View File
@@ -31,6 +31,7 @@
#include "absl/strings/string_view.h"
#include "pkg/sentry/seccheck/points/container.pb.h"
#include "pkg/sentry/seccheck/points/sentry.pb.h"
#include "pkg/sentry/seccheck/points/syscall.pb.h"
typedef std::function<void(const google::protobuf::Any& any)> Callback;
@@ -55,6 +56,22 @@ void log(const char* fmt, ...) {
}
}
template <class T>
void unpackSyscall(const google::protobuf::Any& any) {
T evt;
if (!any.UnpackTo(&evt)) {
err(1, "UnpackTo(): %s", any.DebugString().c_str());
}
auto last_dot = any.type_url().find_last_of('.');
if (last_dot == std::string::npos) {
err(1, "invalid name: %.*s", static_cast<int>(any.type_url().size()),
any.type_url().data());
}
auto name = any.type_url().substr(last_dot + 1);
log("%s %.*s %s\n", evt.has_exit() ? "X" : "E", static_cast<int>(name.size()),
name.data(), evt.ShortDebugString().c_str());
}
template <class T>
void unpack(const google::protobuf::Any& any) {
T evt;
@@ -67,6 +84,13 @@ void unpack(const google::protobuf::Any& any) {
}
std::map<std::string, Callback> dispatchers = {
{"gvisor.syscall.Syscall", unpackSyscall<::gvisor::syscall::Syscall>},
{"gvisor.syscall.Read", unpackSyscall<::gvisor::syscall::Read>},
{"gvisor.syscall.Open", unpackSyscall<::gvisor::syscall::Open>},
{"gvisor.syscall.Connect", unpackSyscall<::gvisor::syscall::Connect>},
{"gvisor.syscall.Execve", unpackSyscall<::gvisor::syscall::Execve>},
{"gvisor.syscall.Close", unpackSyscall<::gvisor::syscall::Close>},
{"gvisor.syscall.Socket", unpackSyscall<::gvisor::syscall::Socket>},
{"gvisor.container.Start", unpack<::gvisor::container::Start>},
{"gvisor.sentry.CloneInfo", unpack<::gvisor::sentry::CloneInfo>},
{"gvisor.sentry.ExecveInfo", unpack<::gvisor::sentry::ExecveInfo>},
+3 -2
View File
@@ -21,7 +21,6 @@ import (
"gvisor.dev/gvisor/pkg/abi"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/bits"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/seccheck"
@@ -85,6 +84,8 @@ type Syscall struct {
URLs []string
// PointCallback is an optional callback that converts syscall arguments
// to a proto that can be used with seccheck.Checker.
// Callback functions must follow this naming convention:
// PointSyscallNameInCamelCase, e.g. PointReadat, PointRtSigaction.
PointCallback SyscallToProto
}
@@ -398,7 +399,7 @@ func (s *SyscallTable) LookupSyscallToProto(sysno uintptr) SyscallToProto {
// SyscallToProto is a callback function that converts generic syscall data to
// schematized protobuf for the corresponding syscall.
type SyscallToProto func(context.Context, seccheck.FieldSet, *pb.ContextData, SyscallInfo) proto.Message
type SyscallToProto func(*Task, seccheck.FieldSet, *pb.ContextData, SyscallInfo) proto.Message
// SyscallInfo provides generic information about the syscall.
type SyscallInfo struct {
+1 -1
View File
@@ -313,7 +313,7 @@ func getExecveSeccheckInfo(t *Task, argv, env []string, executable fsbridge.File
if executable != nil {
info.BinaryPath = pathname
if vfs2bridgeFile, ok := executable.(*fsbridge.VFSFile); ok {
if fields.Local.Contains(seccheck.ExecveFieldBinaryInfo) {
if fields.Local.Contains(seccheck.FieldSentryExecveBinaryInfo) {
statOpts := vfs.StatOptions{
Mask: linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_UID | linux.STATX_GID,
}
+2
View File
@@ -19,6 +19,8 @@ go_library(
srcs = [
"config.go",
"metadata.go",
"metadata_amd64.go",
"metadata_arm64.go",
"seccheck.go",
"seqatomic_checkerslice_unsafe.go",
"syscall.go",
+1 -1
View File
@@ -123,7 +123,7 @@ func findSession(name string) (*State, error) {
}
func findPointDesc(name string) (PointDesc, error) {
if desc, ok := points[name]; ok {
if desc, ok := Points[name]; ok {
return desc, nil
}
return PointDesc{}, fmt.Errorf("point %q not found", name)
+40 -13
View File
@@ -17,6 +17,7 @@ package seccheck
import (
"fmt"
"os"
"path"
"gvisor.dev/gvisor/pkg/fd"
)
@@ -46,22 +47,25 @@ const (
FieldCtxtTime
)
// Fields for container/start point.
const (
// ContainerStartFieldEnv is an optional field to collect list of environment
// FieldContainerStartEnv is an optional field to collect list of environment
// variables set for the container start process.
ContainerStartFieldEnv Field = iota
FieldContainerStartEnv Field = iota
)
// Fields for sentry/execve point.
const (
// ExecveFieldBinaryInfo is an optional field to collect information about the
// binary being executed.
ExecveFieldBinaryInfo Field = iota
// FieldSentryExecveBinaryInfo is an optional field to collect information
// about the binary being executed.
FieldSentryExecveBinaryInfo Field = iota
)
var points = map[string]PointDesc{}
// Points is a map with all the Points registered in the system.
var Points = map[string]PointDesc{}
var sinks = map[string]SinkDesc{}
// defaultContextFields are the fields present in most points.
// defaultContextFields are the fields present in most Points.
var defaultContextFields = []FieldDesc{
{
ID: FieldCtxtTime,
@@ -127,7 +131,7 @@ func RegisterSink(sink SinkDesc) {
// PointDesc describes a Point that is available to be configured.
// Schema for these points are defined in pkg/sentry/seccheck/points/.
type PointDesc struct {
// ID is the point unique indentifier.
// ID is the point unique identifier.
ID Point
// Name is the point unique name. Convention is to use the following format:
// namespace/name
@@ -153,7 +157,7 @@ type FieldDesc struct {
}
func registerPoint(pt PointDesc) {
if _, ok := points[pt.Name]; ok {
if _, ok := Points[pt.Name]; ok {
panic(fmt.Sprintf("Point %q already registered", pt.Name))
}
if err := validateFields(pt.OptionalFields); err != nil {
@@ -162,7 +166,7 @@ func registerPoint(pt PointDesc) {
if err := validateFields(pt.ContextFields); err != nil {
panic(err)
}
points[pt.Name] = pt
Points[pt.Name] = pt
}
func validateFields(fields []FieldDesc) error {
@@ -181,7 +185,30 @@ func validateFields(fields []FieldDesc) error {
return nil
}
// These are all the points available in the system.
func addRawSyscallPoint(sysno uintptr) {
addSyscallPointHelper(SyscallRawEnter, sysno, fmt.Sprintf("sysno/%d", sysno), nil)
}
func addSyscallPoint(sysno uintptr, name string, optionalFields []FieldDesc) {
addSyscallPointHelper(SyscallEnter, sysno, name, optionalFields)
}
func addSyscallPointHelper(typ SyscallType, sysno uintptr, name string, optionalFields []FieldDesc) {
registerPoint(PointDesc{
ID: GetPointForSyscall(typ, sysno),
Name: path.Join("syscall", name, "enter"),
OptionalFields: optionalFields,
ContextFields: defaultContextFields,
})
registerPoint(PointDesc{
ID: GetPointForSyscall(typ+1, sysno),
Name: path.Join("syscall", name, "exit"),
OptionalFields: optionalFields,
ContextFields: defaultContextFields,
})
}
// These are all the Points available in the system.
func init() {
// Points from the container namespace.
registerPoint(PointDesc{
@@ -189,7 +216,7 @@ func init() {
Name: "container/start",
OptionalFields: []FieldDesc{
{
ID: ContainerStartFieldEnv,
ID: FieldContainerStartEnv,
Name: "env",
},
},
@@ -207,7 +234,7 @@ func init() {
Name: "sentry/execve",
OptionalFields: []FieldDesc{
{
ID: ExecveFieldBinaryInfo,
ID: FieldSentryExecveBinaryInfo,
Name: "binary_info",
},
},
+74
View File
@@ -0,0 +1,74 @@
// Copyright 2022 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.
//go:build amd64
// +build amd64
package seccheck
func init() {
addSyscallPoint(0, "read", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(2, "open", nil)
addSyscallPoint(3, "close", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(41, "socket", nil)
addSyscallPoint(42, "connect", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(59, "execve", []FieldDesc{
{
ID: FieldSyscallExecveEnvv,
Name: "envv",
},
})
addSyscallPoint(85, "creat", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(257, "openat", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(322, "execveat", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
{
ID: FieldSyscallExecveEnvv,
Name: "envv",
},
})
const lastSyscallInTable = 441
for i := 0; i <= lastSyscallInTable; i++ {
addRawSyscallPoint(uintptr(i))
}
}
+67
View File
@@ -0,0 +1,67 @@
// Copyright 2022 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.
//go:build arm64
// +build arm64
package seccheck
func init() {
addSyscallPoint(63, "read", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(57, "close", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(198, "socket", nil)
addSyscallPoint(203, "connect", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(221, "execve", []FieldDesc{
{
ID: FieldSyscallExecveEnvv,
Name: "envv",
},
})
addSyscallPoint(56, "openat", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(281, "execveat", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
{
ID: FieldSyscallExecveEnvv,
Name: "envv",
},
})
const lastSyscallInTable = 441
for i := 0; i <= lastSyscallInTable; i++ {
addRawSyscallPoint(uintptr(i))
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ func TestSinkRegistration(t *testing.T) {
func TestPointRegistration(t *testing.T) {
point := PointDesc{Name: "test"}
registerPoint(point)
if _, ok := points["test"]; !ok {
if _, ok := Points["test"]; !ok {
t.Errorf("point registration failed")
}
+59
View File
@@ -34,3 +34,62 @@ message Syscall {
uint64 arg5 = 9;
uint64 arg6 = 10;
}
message Open {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
int64 fd = 4;
string fd_path = 5;
string pathname = 6;
uint32 flags = 7;
uint32 mode = 8;
}
message Close {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
int64 fd = 4;
string fd_path = 5;
}
message Read {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
int64 fd = 4;
string fd_path = 5;
uint64 count = 6;
bytes data = 7;
}
message Connect {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
int64 fd = 4;
string fd_path = 5;
bytes address = 6;
}
message Execve {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
int64 fd = 4;
string fd_path = 5;
string pathname = 6;
repeated string argv = 7;
repeated string envv = 8;
uint32 flags = 9;
}
message Socket {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
int32 domain = 4;
int32 type = 5;
int32 protocol = 6;
}
+15
View File
@@ -41,6 +41,21 @@ const (
syscallPoints = syscallsMax * int(syscallTypesCount)
)
// Fields that are common for many syscalls.
const (
// FieldSyscallPath is an optional field to collect path from an FD. Given
// that many syscalls operate on FDs, this const is used across syscalls.
FieldSyscallPath Field = iota
)
// Fields for execve*(2) syscalls.
const (
// FieldSyscallExecveEnvv is an optional field to collect list of environment
// variables. Start after FieldSyscallPath because execveat(2) can collect
// path from FD.
FieldSyscallExecveEnvv = FieldSyscallPath + 1
)
// GetPointForSyscall translates the syscall number to the corresponding Point.
func GetPointForSyscall(typ SyscallType, sysno uintptr) Point {
return Point(sysno)*Point(syscallTypesCount) + Point(typ) + pointLengthBeforeSyscalls
+17 -1
View File
@@ -1,4 +1,4 @@
load("//tools:defs.bzl", "go_library")
load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
@@ -8,6 +8,7 @@ go_library(
"error.go",
"flags.go",
"linux64.go",
"points.go",
"sigset.go",
"sys_aio.go",
"sys_capability.go",
@@ -96,6 +97,8 @@ go_library(
"//pkg/sentry/loader",
"//pkg/sentry/memmap",
"//pkg/sentry/mm",
"//pkg/sentry/seccheck",
"//pkg/sentry/seccheck/points:points_go_proto",
"//pkg/sentry/socket",
"//pkg/sentry/socket/control",
"//pkg/sentry/socket/unix/transport",
@@ -106,6 +109,19 @@ go_library(
"//pkg/syserr",
"//pkg/usermem",
"//pkg/waiter",
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
go_test(
name = "linux_test",
size = "small",
srcs = [
"linux64_amd64_test.go",
"linux64_arm64_test.go",
"linux64_test.go",
],
library = ":linux",
deps = ["//pkg/sentry/seccheck"],
)
+11 -11
View File
@@ -52,9 +52,9 @@ var AMD64 = &kernel.SyscallTable{
},
AuditNumber: linux.AUDIT_ARCH_X86_64,
Table: map[uintptr]kernel.Syscall{
0: syscalls.Supported("read", Read),
0: syscalls.SupportedPoint("read", Read, PointRead),
1: syscalls.Supported("write", Write),
2: syscalls.PartiallySupported("open", Open, "Options O_DIRECT, O_NOATIME, O_PATH, O_TMPFILE, O_SYNC are not supported.", nil),
2: syscalls.PartiallySupportedPoint("open", Open, PointOpen, "Options O_DIRECT, O_NOATIME, O_PATH, O_TMPFILE, O_SYNC are not supported.", nil),
3: syscalls.Supported("close", Close),
4: syscalls.Supported("stat", Stat),
5: syscalls.Supported("fstat", Fstat),
@@ -94,7 +94,7 @@ var AMD64 = &kernel.SyscallTable{
39: syscalls.Supported("getpid", Getpid),
40: syscalls.Supported("sendfile", Sendfile),
41: syscalls.PartiallySupported("socket", Socket, "Limited support for AF_NETLINK, NETLINK_ROUTE sockets. Limited support for SOCK_RAW.", nil),
42: syscalls.Supported("connect", Connect),
42: syscalls.SupportedPoint("connect", Connect, PointConnect),
43: syscalls.Supported("accept", Accept),
44: syscalls.Supported("sendto", SendTo),
45: syscalls.Supported("recvfrom", RecvFrom),
@@ -111,7 +111,7 @@ var AMD64 = &kernel.SyscallTable{
56: syscalls.PartiallySupported("clone", Clone, "Mount namespace (CLONE_NEWNS) not supported. Options CLONE_PARENT, CLONE_SYSVSEM not supported.", nil),
57: syscalls.Supported("fork", Fork),
58: syscalls.Supported("vfork", Vfork),
59: syscalls.Supported("execve", Execve),
59: syscalls.SupportedPoint("execve", Execve, PointExecve),
60: syscalls.Supported("exit", Exit),
61: syscalls.Supported("wait4", Wait4),
62: syscalls.Supported("kill", Kill),
@@ -309,7 +309,7 @@ var AMD64 = &kernel.SyscallTable{
254: syscalls.PartiallySupported("inotify_add_watch", InotifyAddWatch, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
255: syscalls.PartiallySupported("inotify_rm_watch", InotifyRmWatch, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
256: syscalls.CapError("migrate_pages", linux.CAP_SYS_NICE, "", nil),
257: syscalls.Supported("openat", Openat),
257: syscalls.SupportedPoint("openat", Openat, PointOpenat),
258: syscalls.Supported("mkdirat", Mkdirat),
259: syscalls.Supported("mknodat", Mknodat),
260: syscalls.Supported("fchownat", Fchownat),
@@ -374,7 +374,7 @@ var AMD64 = &kernel.SyscallTable{
319: syscalls.Supported("memfd_create", MemfdCreate),
320: syscalls.CapError("kexec_file_load", linux.CAP_SYS_BOOT, "", nil),
321: syscalls.CapError("bpf", linux.CAP_SYS_ADMIN, "", nil),
322: syscalls.Supported("execveat", Execveat),
322: syscalls.SupportedPoint("execveat", Execveat, PointExecveat),
323: syscalls.ErrorWithEvent("userfaultfd", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/266"}), // TODO(b/118906345)
324: syscalls.PartiallySupported("membarrier", Membarrier, "Not supported on all platforms.", nil),
325: syscalls.PartiallySupported("mlock2", Mlock2, "Stub implementation. The sandbox lacks appropriate permissions.", nil),
@@ -486,14 +486,14 @@ var ARM64 = &kernel.SyscallTable{
53: syscalls.Supported("fchmodat", Fchmodat),
54: syscalls.Supported("fchownat", Fchownat),
55: syscalls.Supported("fchown", Fchown),
56: syscalls.Supported("openat", Openat),
56: syscalls.SupportedPoint("openat", Openat, PointOpenat),
57: syscalls.Supported("close", Close),
58: syscalls.CapError("vhangup", linux.CAP_SYS_TTY_CONFIG, "", nil),
59: syscalls.Supported("pipe2", Pipe2),
60: syscalls.CapError("quotactl", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_admin for most operations
61: syscalls.Supported("getdents64", Getdents64),
62: syscalls.Supported("lseek", Lseek),
63: syscalls.Supported("read", Read),
63: syscalls.SupportedPoint("read", Read, PointRead),
64: syscalls.Supported("write", Write),
65: syscalls.Supported("readv", Readv),
66: syscalls.Supported("writev", Writev),
@@ -633,7 +633,7 @@ var ARM64 = &kernel.SyscallTable{
200: syscalls.PartiallySupported("bind", Bind, "Autobind for abstract Unix sockets is not supported.", nil),
201: syscalls.Supported("listen", Listen),
202: syscalls.Supported("accept", Accept),
203: syscalls.Supported("connect", Connect),
203: syscalls.SupportedPoint("connect", Connect, PointConnect),
204: syscalls.Supported("getsockname", GetSockName),
205: syscalls.Supported("getpeername", GetPeerName),
206: syscalls.Supported("sendto", SendTo),
@@ -651,7 +651,7 @@ var ARM64 = &kernel.SyscallTable{
218: syscalls.Error("request_key", linuxerr.EACCES, "Not available to user.", nil),
219: syscalls.Error("keyctl", linuxerr.EACCES, "Not available to user.", nil),
220: syscalls.PartiallySupported("clone", Clone, "Mount namespace (CLONE_NEWNS) not supported. Options CLONE_PARENT, CLONE_SYSVSEM not supported.", nil),
221: syscalls.Supported("execve", Execve),
221: syscalls.SupportedPoint("execve", Execve, PointExecve),
222: syscalls.PartiallySupported("mmap", Mmap, "Generally supported with exceptions. Options MAP_FIXED_NOREPLACE, MAP_SHARED_VALIDATE, MAP_SYNC MAP_GROWSDOWN, MAP_HUGETLB are not supported.", nil),
223: syscalls.PartiallySupported("fadvise64", Fadvise64, "Not all options are supported.", nil),
224: syscalls.CapError("swapon", linux.CAP_SYS_ADMIN, "", nil),
@@ -695,7 +695,7 @@ var ARM64 = &kernel.SyscallTable{
278: syscalls.Supported("getrandom", GetRandom),
279: syscalls.Supported("memfd_create", MemfdCreate),
280: syscalls.CapError("bpf", linux.CAP_SYS_ADMIN, "", nil),
281: syscalls.Supported("execveat", Execveat),
281: syscalls.SupportedPoint("execveat", Execveat, PointExecveat),
282: syscalls.ErrorWithEvent("userfaultfd", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/266"}), // TODO(b/118906345)
283: syscalls.PartiallySupported("membarrier", Membarrier, "Not supported on all platforms.", nil),
284: syscalls.PartiallySupported("mlock2", Mlock2, "Stub implementation. The sandbox lacks appropriate permissions.", nil),
@@ -0,0 +1,20 @@
// Copyright 2022 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.
//go:build amd64
// +build amd64
package linux
var archToTest = AMD64
@@ -0,0 +1,20 @@
// Copyright 2022 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.
//go:build arm64
// +build arm64
package linux
var archToTest = ARM64
+102
View File
@@ -0,0 +1,102 @@
// Copyright 2022 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 linux
import (
"fmt"
"reflect"
"runtime"
"strings"
"testing"
"unicode"
"gvisor.dev/gvisor/pkg/sentry/seccheck"
)
func findPoint(name string) (seccheck.PointDesc, bool) {
for _, pt := range seccheck.Points {
if pt.Name == name {
return pt, true
}
}
return seccheck.PointDesc{}, false
}
// TestSeccheckMax catches cases that a new syscall was added but seccheck raw
// syscall numbers (e.g. syscall/sysno/123) have not been updated.
func TestSeccheckMax(t *testing.T) {
max := uintptr(0)
for sysno := range archToTest.Table {
if sysno > max {
max = sysno
}
}
want := fmt.Sprintf("syscall/sysno/%d/enter", max)
if _, ok := findPoint(want); !ok {
t.Errorf("seccheck.PointDesc for syscall %d not found. Update pkg/sentry/seccheck/metadata_amd64.go", max)
}
}
// TestSeccheckSyscalls verifies that all syscalls registered with a point
// callback have the corresponding seccheck metadata created.
func TestSeccheckSyscalls(t *testing.T) {
for sysno, syscall := range archToTest.Table {
if syscall.PointCallback == nil {
continue
}
// For every syscall with a PointCallback, there must be a corresponding
// seccheck.PointDesc created.
funcName := runtime.FuncForPC(reflect.ValueOf(syscall.PointCallback).Pointer()).Name()
if idx := strings.LastIndex(funcName, "."); idx > -1 {
funcName = funcName[idx+1:]
}
t.Run(funcName, func(t *testing.T) {
if !strings.HasPrefix(funcName, "Point") {
t.Errorf("PointCallback function name must start with Point: %q", funcName)
}
funcName = strings.TrimPrefix(funcName, "Point")
if len(funcName) == 0 {
t.Errorf("PointCallback function name invalid: %q", funcName)
}
pointName := strings.ToLower(string(funcName[0]))
for _, c := range funcName[1:] {
if unicode.IsUpper(c) {
pointName += "_"
}
pointName += string(unicode.ToLower(c))
}
for _, flavor := range []struct {
suffix string
typ seccheck.SyscallType
}{
{suffix: "enter", typ: seccheck.SyscallEnter},
{suffix: "exit", typ: seccheck.SyscallExit},
} {
fullName := fmt.Sprintf("syscall/%s/%s", pointName, flavor.suffix)
pt, ok := findPoint(fullName)
if !ok {
t.Fatalf("seccheck.PointDesc %q not found.", fullName)
}
if want := seccheck.GetPointForSyscall(flavor.typ, sysno); want != pt.ID {
t.Errorf("seccheck.Point for syscall %q is wrong, want: %v, got: %v", pointName, want, pt.ID)
}
}
})
}
}
+263
View File
@@ -0,0 +1,263 @@
// Copyright 2022 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 linux
import (
"fmt"
"google.golang.org/protobuf/proto"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/seccheck"
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
)
func newExitMaybe(info kernel.SyscallInfo) *pb.Exit {
if !info.Exit {
return nil
}
return &pb.Exit{
Result: int64(info.Rval),
Errorno: int64(info.Errno),
}
}
func getFilePath(t *kernel.Task, fd int32) string {
fdt := t.FDTable()
if fdt == nil {
return "[err: no FD table]"
}
file, _ := fdt.GetVFS2(fd)
if file == nil {
return "[err: requires VFS2]"
}
defer file.DecRef(t)
root := t.MountNamespaceVFS2().Root()
path, err := t.Kernel().VFS().PathnameWithDeleted(t, root, file.VirtualDentry())
if err != nil {
return fmt.Sprintf("[err: %v]", err)
}
return path
}
// PointOpen converts open(2) syscall to proto.
func PointOpen(t *kernel.Task, _ seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Open{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: linux.AT_FDCWD,
Flags: info.Args[1].Uint(),
Mode: uint32(info.Args[2].ModeT()),
}
addr := info.Args[0].Pointer()
if addr > 0 {
path, err := t.CopyInString(addr, linux.PATH_MAX)
if err == nil {
p.Pathname = path
}
}
p.Exit = newExitMaybe(info)
return p
}
// PointOpenat converts openat(2) syscall to proto.
func PointOpenat(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Open{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Flags: info.Args[2].Uint(),
}
addr := info.Args[1].Pointer()
if addr > 0 {
path, err := t.CopyInString(addr, linux.PATH_MAX)
if err == nil {
p.Pathname = path
}
}
if p.Flags&linux.O_CREAT != 0 {
p.Mode = uint32(info.Args[3].ModeT())
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p
}
// PointCreat converts creat(2) syscall to proto.
func PointCreat(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Open{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: linux.AT_FDCWD,
Flags: linux.O_WRONLY | linux.O_CREAT | linux.O_TRUNC,
Mode: uint32(info.Args[1].ModeT()),
}
addr := info.Args[0].Pointer()
if addr > 0 {
path, err := t.CopyInString(addr, linux.PATH_MAX)
if err == nil {
p.Pathname = path
}
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p
}
// PointClose converts close(2) syscall to proto.
func PointClose(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Close{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p
}
// PointRead converts read(2) syscall to proto.
func PointRead(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Read{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: uint64(info.Args[2].SizeT()),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p
}
// PointSocket converts socket(2) syscall to proto.
func PointSocket(_ *kernel.Task, _ seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Socket{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Domain: info.Args[0].Int(),
Type: info.Args[1].Int(),
Protocol: info.Args[2].Int(),
}
p.Exit = newExitMaybe(info)
return p
}
// PointConnect converts connect(2) syscall to proto.
func PointConnect(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Connect{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
}
addr := info.Args[1].Pointer()
addrlen := info.Args[2].Uint()
if addr > 0 {
p.Address = make([]byte, addrlen)
_, _ = t.CopyInBytes(addr, p.Address)
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p
}
// PointExecve converts execve(2) syscall to proto.
func PointExecve(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Execve{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
}
if pathname, err := t.CopyInString(info.Args[0].Pointer(), linux.PATH_MAX); err == nil {
p.Pathname = pathname
}
if argvAddr := info.Args[1].Pointer(); argvAddr != 0 {
if argv, err := t.CopyInVector(argvAddr, ExecMaxElemSize, ExecMaxTotalSize); err == nil {
p.Argv = argv
}
}
if fields.Local.Contains(seccheck.FieldSyscallExecveEnvv) {
if envvAddr := info.Args[2].Pointer(); envvAddr != 0 {
if envv, err := t.CopyInVector(envvAddr, ExecMaxElemSize, ExecMaxTotalSize); err == nil {
p.Envv = envv
}
}
}
p.Exit = newExitMaybe(info)
return p
}
// PointExecveat converts execveat(2) syscall to proto.
func PointExecveat(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) proto.Message {
p := &pb.Execve{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Flags: info.Args[4].Uint(),
}
if pathname, err := t.CopyInString(info.Args[1].Pointer(), linux.PATH_MAX); err == nil {
p.Pathname = pathname
}
if argvAddr := info.Args[2].Pointer(); argvAddr != 0 {
if argv, err := t.CopyInVector(argvAddr, ExecMaxElemSize, ExecMaxTotalSize); err == nil {
p.Argv = argv
}
}
if fields.Local.Contains(seccheck.FieldSyscallExecveEnvv) {
if envvAddr := info.Args[3].Pointer(); envvAddr != 0 {
if envv, err := t.CopyInVector(envvAddr, ExecMaxElemSize, ExecMaxTotalSize); err == nil {
p.Envv = envv
}
}
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p
}
+16 -16
View File
@@ -24,10 +24,10 @@ import (
func Override() {
// Override AMD64.
s := linux.AMD64
s.Table[0] = syscalls.Supported("read", Read)
s.Table[0] = syscalls.SupportedPoint("read", Read, linux.PointRead)
s.Table[1] = syscalls.Supported("write", Write)
s.Table[2] = syscalls.Supported("open", Open)
s.Table[3] = syscalls.Supported("close", Close)
s.Table[2] = syscalls.SupportedPoint("open", Open, linux.PointOpen)
s.Table[3] = syscalls.SupportedPoint("close", Close, linux.PointClose)
s.Table[4] = syscalls.Supported("stat", Stat)
s.Table[5] = syscalls.Supported("fstat", Fstat)
s.Table[6] = syscalls.Supported("lstat", Lstat)
@@ -45,8 +45,8 @@ func Override() {
s.Table[32] = syscalls.Supported("dup", Dup)
s.Table[33] = syscalls.Supported("dup2", Dup2)
s.Table[40] = syscalls.Supported("sendfile", Sendfile)
s.Table[41] = syscalls.Supported("socket", Socket)
s.Table[42] = syscalls.Supported("connect", Connect)
s.Table[41] = syscalls.SupportedPoint("socket", Socket, linux.PointSocket)
s.Table[42] = syscalls.SupportedPoint("connect", Connect, linux.PointConnect)
s.Table[43] = syscalls.Supported("accept", Accept)
s.Table[44] = syscalls.Supported("sendto", SendTo)
s.Table[45] = syscalls.Supported("recvfrom", RecvFrom)
@@ -60,7 +60,7 @@ func Override() {
s.Table[53] = syscalls.Supported("socketpair", SocketPair)
s.Table[54] = syscalls.Supported("setsockopt", SetSockOpt)
s.Table[55] = syscalls.Supported("getsockopt", GetSockOpt)
s.Table[59] = syscalls.Supported("execve", Execve)
s.Table[59] = syscalls.SupportedPoint("execve", Execve, linux.PointExecve)
s.Table[72] = syscalls.Supported("fcntl", Fcntl)
s.Table[73] = syscalls.Supported("flock", Flock)
s.Table[74] = syscalls.Supported("fsync", Fsync)
@@ -74,7 +74,7 @@ func Override() {
s.Table[82] = syscalls.Supported("rename", Rename)
s.Table[83] = syscalls.Supported("mkdir", Mkdir)
s.Table[84] = syscalls.Supported("rmdir", Rmdir)
s.Table[85] = syscalls.Supported("creat", Creat)
s.Table[85] = syscalls.SupportedPoint("creat", Creat, linux.PointCreat)
s.Table[86] = syscalls.Supported("link", Link)
s.Table[87] = syscalls.Supported("unlink", Unlink)
s.Table[88] = syscalls.Supported("symlink", Symlink)
@@ -118,7 +118,7 @@ func Override() {
s.Table[253] = syscalls.PartiallySupported("inotify_init", InotifyInit, "inotify events are only available inside the sandbox.", nil)
s.Table[254] = syscalls.PartiallySupported("inotify_add_watch", InotifyAddWatch, "inotify events are only available inside the sandbox.", nil)
s.Table[255] = syscalls.PartiallySupported("inotify_rm_watch", InotifyRmWatch, "inotify events are only available inside the sandbox.", nil)
s.Table[257] = syscalls.Supported("openat", Openat)
s.Table[257] = syscalls.SupportedPoint("openat", Openat, linux.PointOpenat)
s.Table[258] = syscalls.Supported("mkdirat", Mkdirat)
s.Table[259] = syscalls.Supported("mknodat", Mknodat)
s.Table[260] = syscalls.Supported("fchownat", Fchownat)
@@ -158,7 +158,7 @@ func Override() {
s.Table[307] = syscalls.Supported("sendmmsg", SendMMsg)
s.Table[316] = syscalls.Supported("renameat2", Renameat2)
s.Table[319] = syscalls.Supported("memfd_create", MemfdCreate)
s.Table[322] = syscalls.Supported("execveat", Execveat)
s.Table[322] = syscalls.SupportedPoint("execveat", Execveat, linux.PointExecveat)
s.Table[327] = syscalls.Supported("preadv2", Preadv2)
s.Table[328] = syscalls.Supported("pwritev2", Pwritev2)
s.Table[332] = syscalls.Supported("statx", Statx)
@@ -217,12 +217,12 @@ func Override() {
s.Table[53] = syscalls.Supported("fchmodat", Fchmodat)
s.Table[54] = syscalls.Supported("fchownat", Fchownat)
s.Table[55] = syscalls.Supported("fchown", Fchown)
s.Table[56] = syscalls.Supported("openat", Openat)
s.Table[57] = syscalls.Supported("close", Close)
s.Table[56] = syscalls.SupportedPoint("openat", Openat, linux.PointOpenat)
s.Table[57] = syscalls.SupportedPoint("close", Close, linux.PointClose)
s.Table[59] = syscalls.Supported("pipe2", Pipe2)
s.Table[61] = syscalls.Supported("getdents64", Getdents64)
s.Table[62] = syscalls.Supported("lseek", Lseek)
s.Table[63] = syscalls.Supported("read", Read)
s.Table[63] = syscalls.SupportedPoint("read", Read, linux.PointRead)
s.Table[64] = syscalls.Supported("write", Write)
s.Table[65] = syscalls.Supported("readv", Readv)
s.Table[66] = syscalls.Supported("writev", Writev)
@@ -249,12 +249,12 @@ func Override() {
s.Table[88] = syscalls.Supported("utimensat", Utimensat)
s.Table[180] = syscalls.Supported("mq_open", MqOpen)
s.Table[181] = syscalls.Supported("mq_unlink", MqUnlink)
s.Table[198] = syscalls.Supported("socket", Socket)
s.Table[198] = syscalls.SupportedPoint("socket", Socket, linux.PointSocket)
s.Table[199] = syscalls.Supported("socketpair", SocketPair)
s.Table[200] = syscalls.Supported("bind", Bind)
s.Table[201] = syscalls.Supported("listen", Listen)
s.Table[202] = syscalls.Supported("accept", Accept)
s.Table[203] = syscalls.Supported("connect", Connect)
s.Table[203] = syscalls.SupportedPoint("connect", Connect, linux.PointConnect)
s.Table[204] = syscalls.Supported("getsockname", GetSockName)
s.Table[205] = syscalls.Supported("getpeername", GetPeerName)
s.Table[206] = syscalls.Supported("sendto", SendTo)
@@ -265,7 +265,7 @@ func Override() {
s.Table[211] = syscalls.Supported("sendmsg", SendMsg)
s.Table[212] = syscalls.Supported("recvmsg", RecvMsg)
s.Table[213] = syscalls.Supported("readahead", Readahead)
s.Table[221] = syscalls.Supported("execve", Execve)
s.Table[221] = syscalls.SupportedPoint("execve", Execve, linux.PointExecve)
s.Table[222] = syscalls.Supported("mmap", Mmap)
s.Table[223] = syscalls.PartiallySupported("fadvise64", Fadvise64, "Not all options are supported.", nil)
s.Table[242] = syscalls.Supported("accept4", Accept4)
@@ -274,7 +274,7 @@ func Override() {
s.Table[269] = syscalls.Supported("sendmmsg", SendMMsg)
s.Table[276] = syscalls.Supported("renameat2", Renameat2)
s.Table[279] = syscalls.Supported("memfd_create", MemfdCreate)
s.Table[281] = syscalls.Supported("execveat", Execveat)
s.Table[281] = syscalls.SupportedPoint("execveat", Execveat, linux.PointExecveat)
s.Table[286] = syscalls.Supported("preadv2", Preadv2)
s.Table[287] = syscalls.Supported("pwritev2", Pwritev2)
s.Table[291] = syscalls.Supported("statx", Statx)
+16
View File
@@ -43,6 +43,14 @@ func Supported(name string, fn kernel.SyscallFn) kernel.Syscall {
}
}
// SupportedPoint returns a syscall that is fully supported with a correspoding
// seccheck.Point.
func SupportedPoint(name string, fn kernel.SyscallFn, cb kernel.SyscallToProto) kernel.Syscall {
sys := Supported(name, fn)
sys.PointCallback = cb
return sys
}
// PartiallySupported returns a syscall that has a partial implementation.
func PartiallySupported(name string, fn kernel.SyscallFn, note string, urls []string) kernel.Syscall {
return kernel.Syscall{
@@ -54,6 +62,14 @@ func PartiallySupported(name string, fn kernel.SyscallFn, note string, urls []st
}
}
// PartiallySupportedPoint returns a syscall that has a partial implementation
// with a correspoding seccheck.Point.
func PartiallySupportedPoint(name string, fn kernel.SyscallFn, cb kernel.SyscallToProto, note string, urls []string) kernel.Syscall {
sys := PartiallySupported(name, fn, note, urls)
sys.PointCallback = cb
return sys
}
// Error returns a syscall handler that will always give the passed error.
func Error(name string, err error, note string, urls []string) kernel.Syscall {
if note != "" {

Some files were not shown because too many files have changed in this diff Show More