Add read/write syscalls to trace points

Closes #8092

PiperOrigin-RevId: 488719448
This commit is contained in:
Fabricio Voznika
2022-11-15 11:54:23 -08:00
committed by gVisor bot
parent 9cf34dd35f
commit e6f019594e
9 changed files with 435 additions and 20 deletions
+1
View File
@@ -113,6 +113,7 @@ std::vector<Callback> dispatchers = {
unpackSyscall<::gvisor::syscall::InotifyAddWatch>,
unpackSyscall<::gvisor::syscall::InotifyRmWatch>,
unpackSyscall<::gvisor::syscall::SocketPair>,
unpackSyscall<::gvisor::syscall::Write>,
};
void unpack(absl::string_view buf) {
+54
View File
@@ -24,6 +24,12 @@ func init() {
Name: "fd_path",
},
})
addSyscallPoint(1, "write", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(2, "open", nil)
addSyscallPoint(3, "close", []FieldDesc{
{
@@ -31,6 +37,30 @@ func init() {
Name: "fd_path",
},
})
addSyscallPoint(17, "pread64", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(18, "pwrite64", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(19, "readv", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(20, "writev", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(41, "socket", nil)
addSyscallPoint(42, "connect", []FieldDesc{
{
@@ -169,6 +199,30 @@ func init() {
},
})
addSyscallPoint(53, "socketpair", nil)
addSyscallPoint(295, "preadv", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(296, "pwritev", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(327, "preadv2", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(328, "pwritev2", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
const lastSyscallInTable = 441
for i := 0; i <= lastSyscallInTable; i++ {
+54
View File
@@ -24,6 +24,48 @@ func init() {
Name: "fd_path",
},
})
addSyscallPoint(64, "write", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(65, "readv", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(66, "writev", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(67, "pread64", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(68, "pwrite64", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(69, "preadv", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(70, "pwritev", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(57, "close", []FieldDesc{
{
ID: FieldSyscallPath,
@@ -145,6 +187,18 @@ func init() {
},
})
addSyscallPoint(199, "socketpair", nil)
addSyscallPoint(286, "preadv2", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
addSyscallPoint(287, "pwritev2", []FieldDesc{
{
ID: FieldSyscallPath,
Name: "fd_path",
},
})
const lastSyscallInTable = 441
for i := 0; i <= lastSyscallInTable; i++ {
+1
View File
@@ -131,5 +131,6 @@ enum MessageType {
MESSAGE_SYSCALL_INOTIFY_ADD_WATCH = 31;
MESSAGE_SYSCALL_INOTIFY_RM_WATCH = 32;
MESSAGE_SYSCALL_SOCKETPAIR = 33;
MESSAGE_SYSCALL_WRITE = 34;
}
// LINT.ThenChange(../../../../examples/seccheck/server.cc)
+16 -1
View File
@@ -61,6 +61,21 @@ message Read {
int64 fd = 4;
string fd_path = 5;
uint64 count = 6;
bool has_offset = 7;
int64 offset = 8;
uint32 flags = 9;
}
message Write {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
int64 fd = 4;
string fd_path = 5;
uint64 count = 6;
bool has_offset = 7;
int64 offset = 8;
uint32 flags = 9;
}
message Connect {
@@ -267,7 +282,7 @@ message InotifyInit {
int32 flags = 4;
}
message InotifyAddWatch{
message InotifyAddWatch {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
+18 -18
View File
@@ -53,7 +53,7 @@ var AMD64 = &kernel.SyscallTable{
AuditNumber: linux.AUDIT_ARCH_X86_64,
Table: map[uintptr]kernel.Syscall{
0: syscalls.SupportedPoint("read", Read, PointRead),
1: syscalls.Supported("write", Write),
1: syscalls.SupportedPoint("write", Write, PointWrite),
2: syscalls.SupportedPoint("open", Open, PointOpen),
3: syscalls.SupportedPoint("close", Close, PointClose),
4: syscalls.Supported("stat", Stat),
@@ -69,10 +69,10 @@ var AMD64 = &kernel.SyscallTable{
14: syscalls.Supported("rt_sigprocmask", RtSigprocmask),
15: syscalls.Supported("rt_sigreturn", RtSigreturn),
16: syscalls.Supported("ioctl", Ioctl),
17: syscalls.Supported("pread64", Pread64),
18: syscalls.Supported("pwrite64", Pwrite64),
19: syscalls.Supported("readv", Readv),
20: syscalls.Supported("writev", Writev),
17: syscalls.SupportedPoint("pread64", Pread64, PointPread64),
18: syscalls.SupportedPoint("pwrite64", Pwrite64, PointPwrite64),
19: syscalls.SupportedPoint("readv", Readv, PointReadv),
20: syscalls.SupportedPoint("writev", Writev, PointWritev),
21: syscalls.Supported("access", Access),
22: syscalls.SupportedPoint("pipe", Pipe, PointPipe),
23: syscalls.Supported("select", Select),
@@ -347,8 +347,8 @@ var AMD64 = &kernel.SyscallTable{
292: syscalls.SupportedPoint("dup3", Dup3, PointDup3),
293: syscalls.SupportedPoint("pipe2", Pipe2, PointPipe2),
294: syscalls.PartiallySupportedPoint("inotify_init1", InotifyInit1, PointInotifyInit1, "inotify events are only available inside the sandbox.", nil),
295: syscalls.Supported("preadv", Preadv),
296: syscalls.Supported("pwritev", Pwritev),
295: syscalls.SupportedPoint("preadv", Preadv, PointPreadv),
296: syscalls.SupportedPoint("pwritev", Pwritev, PointPwritev),
297: syscalls.Supported("rt_tgsigqueueinfo", RtTgsigqueueinfo),
298: syscalls.ErrorWithEvent("perf_event_open", linuxerr.ENODEV, "No support for perf counters", nil),
299: syscalls.Supported("recvmmsg", RecvMMsg),
@@ -382,8 +382,8 @@ var AMD64 = &kernel.SyscallTable{
// Syscalls implemented after 325 are "backports" from versions
// of Linux after 4.4.
326: syscalls.ErrorWithEvent("copy_file_range", linuxerr.ENOSYS, "", nil),
327: syscalls.Supported("preadv2", Preadv2),
328: syscalls.Supported("pwritev2", Pwritev2),
327: syscalls.SupportedPoint("preadv2", Preadv2, PointPreadv2),
328: syscalls.SupportedPoint("pwritev2", Pwritev2, PointPwritev2),
329: syscalls.ErrorWithEvent("pkey_mprotect", linuxerr.ENOSYS, "", nil),
330: syscalls.ErrorWithEvent("pkey_alloc", linuxerr.ENOSYS, "", nil),
331: syscalls.ErrorWithEvent("pkey_free", linuxerr.ENOSYS, "", nil),
@@ -495,13 +495,13 @@ var ARM64 = &kernel.SyscallTable{
61: syscalls.Supported("getdents64", Getdents64),
62: syscalls.Supported("lseek", Lseek),
63: syscalls.SupportedPoint("read", Read, PointRead),
64: syscalls.Supported("write", Write),
65: syscalls.Supported("readv", Readv),
66: syscalls.Supported("writev", Writev),
67: syscalls.Supported("pread64", Pread64),
68: syscalls.Supported("pwrite64", Pwrite64),
69: syscalls.Supported("preadv", Preadv),
70: syscalls.Supported("pwritev", Pwritev),
64: syscalls.SupportedPoint("write", Write, PointWrite),
65: syscalls.SupportedPoint("readv", Readv, PointReadv),
66: syscalls.SupportedPoint("writev", Writev, PointWritev),
67: syscalls.SupportedPoint("pread64", Pread64, PointPread64),
68: syscalls.SupportedPoint("pwrite64", Pwrite64, PointPwrite64),
69: syscalls.SupportedPoint("preadv", Preadv, PointPreadv),
70: syscalls.SupportedPoint("pwritev", Pwritev, PointPwritev),
71: syscalls.Supported("sendfile", Sendfile),
72: syscalls.Supported("pselect", Pselect),
73: syscalls.Supported("ppoll", Ppoll),
@@ -703,8 +703,8 @@ var ARM64 = &kernel.SyscallTable{
// Syscalls after 284 are "backports" from versions of Linux after 4.4.
285: syscalls.ErrorWithEvent("copy_file_range", linuxerr.ENOSYS, "", nil),
286: syscalls.Supported("preadv2", Preadv2),
287: syscalls.Supported("pwritev2", Pwritev2),
286: syscalls.SupportedPoint("preadv2", Preadv2, PointPreadv2),
287: syscalls.SupportedPoint("pwritev2", Pwritev2, PointPwritev2),
288: syscalls.ErrorWithEvent("pkey_mprotect", linuxerr.ENOSYS, "", nil),
289: syscalls.ErrorWithEvent("pkey_alloc", linuxerr.ENOSYS, "", nil),
290: syscalls.ErrorWithEvent("pkey_free", linuxerr.ENOSYS, "", nil),
+176
View File
@@ -24,6 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/seccheck"
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
"gvisor.dev/gvisor/pkg/usermem"
)
func newExitMaybe(info kernel.SyscallInfo) *pb.Exit {
@@ -58,6 +59,14 @@ func getFilePath(t *kernel.Task, fd int32) string {
return path
}
func getIovecSize(t *kernel.Task, addr hostarch.Addr, iovcnt int) uint64 {
dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{AddressSpaceActive: true})
if err != nil {
return 0
}
return uint64(dst.NumBytes())
}
// PointOpen converts open(2) syscall to proto.
func PointOpen(t *kernel.Task, _ seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Open{
@@ -167,6 +176,173 @@ func PointRead(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData
return p, pb.MessageType_MESSAGE_SYSCALL_READ
}
// PointPread64 converts pread64(2) syscall to proto.
func PointPread64(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Read{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: uint64(info.Args[2].SizeT()),
HasOffset: true,
Offset: info.Args[3].Int64(),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_READ
}
// PointReadv converts readv(2) syscall to proto.
func PointReadv(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Read{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_READ
}
// PointPreadv converts preadv(2) syscall to proto.
func PointPreadv(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Read{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())),
HasOffset: true,
Offset: info.Args[3].Int64(),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_READ
}
// PointPreadv2 converts preadv2(2) syscall to proto.
func PointPreadv2(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Read{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())),
HasOffset: true,
Offset: info.Args[3].Int64(),
Flags: info.Args[5].Uint(),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_READ
}
// PointWrite converts write(2) syscall to proto.
func PointWrite(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Write{
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, pb.MessageType_MESSAGE_SYSCALL_WRITE
}
// PointPwrite64 converts pwrite64(2) syscall to proto.
func PointPwrite64(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Write{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: uint64(info.Args[2].SizeT()),
HasOffset: true,
Offset: info.Args[3].Int64(),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_WRITE
}
// PointWritev converts writev(2) syscall to proto.
func PointWritev(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Write{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_WRITE
}
// PointPwritev converts pwritev(2) syscall to proto.
func PointPwritev(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Write{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())),
HasOffset: true,
Offset: info.Args[3].Int64(),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_WRITE
}
// PointPwritev2 converts pwritev2(2) syscall to proto.
func PointPwritev2(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Write{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Fd: int64(info.Args[0].Int()),
Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())),
HasOffset: true,
Offset: info.Args[3].Int64(),
Flags: info.Args[5].Uint(),
}
if fields.Local.Contains(seccheck.FieldSyscallPath) {
p.FdPath = getFilePath(t, int32(p.Fd))
}
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_WRITE
}
// PointSocket converts socket(2) syscall to proto.
func PointSocket(_ *kernel.Task, _ seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
p := &pb.Socket{
+39 -1
View File
@@ -108,6 +108,7 @@ func matchPoints(t *testing.T, msgs []test.Message) {
pb.MessageType_MESSAGE_SYSCALL_RAW: {checker: checkSyscallRaw},
pb.MessageType_MESSAGE_SYSCALL_READ: {checker: checkSyscallRead},
pb.MessageType_MESSAGE_SYSCALL_SOCKET: {checker: checkSyscallSocket},
pb.MessageType_MESSAGE_SYSCALL_WRITE: {checker: checkSyscallWrite},
// TODO(gvisor.dev/issue/4805): Add validation for these messages.
pb.MessageType_MESSAGE_SYSCALL_ACCEPT: {checker: checkTODO},
@@ -291,7 +292,44 @@ func checkSyscallRead(msg test.Message) error {
}
if p.Fd < 0 {
// Although negative FD is possible, it doesn't happen in the test.
return fmt.Errorf("reading negative FD: %d", p.Fd)
return fmt.Errorf("read negative FD: %d", p.Fd)
}
if p.HasOffset {
// Workload always uses 20 for read offsets (account for partial reads).
if lower, upper := int64(20), int64(120); p.Offset < lower && p.Offset > upper {
return fmt.Errorf("invalid offset, want: [%d, %d], got: %d", lower, upper, p.Offset)
}
} else if p.Offset != 0 {
return fmt.Errorf("offset should be 0: %+v", &p)
}
if p.Flags != 0 && p.Flags != unix.RWF_HIPRI {
return fmt.Errorf("invalid flag value, want: 0 || RWF_HIPRI, got: %+x", p.Flags)
}
return nil
}
func checkSyscallWrite(msg test.Message) error {
p := pb.Write{}
if err := proto.Unmarshal(msg.Msg, &p); err != nil {
return err
}
if err := checkContextData(p.ContextData); err != nil {
return err
}
if p.Fd < 0 {
// Although negative FD is possible, it doesn't happen in the test.
return fmt.Errorf("write negative FD: %d", p.Fd)
}
if p.HasOffset {
// Workload always uses 10 for write offsets (account for partial writes).
if lower, upper := int64(10), int64(110); p.Offset < lower && p.Offset > upper {
return fmt.Errorf("invalid offset, want: [%d, %d], got: %d", lower, upper, p.Offset)
}
} else if p.Offset != 0 {
return fmt.Errorf("offset should be 0: %+v", &p)
}
if p.Flags != 0 && p.Flags != unix.RWF_HIPRI {
return fmt.Errorf("invalid flag value, want: 0 || RWF_HIPRI, got: %+x", p.Flags)
}
return nil
}
+76
View File
@@ -115,12 +115,88 @@ void runSocket() {
}
}
void runReadWrite() {
const std::string path = "read-write.txt";
auto fd_or = Open(path, O_RDWR | O_CREAT, 0644);
if (!fd_or.ok()) {
err(1, "open(O_CREAT): %s", fd_or.error().ToString().c_str());
}
auto cleaup = absl::MakeCleanup([path] { unlink(path.c_str()); });
auto fd = std::move(fd_or.ValueOrDie());
// Test different flavors of write.
char msg[] = "hello world";
if (WriteFd(fd.get(), msg, ABSL_ARRAYSIZE(msg)) < 0) {
err(1, "write");
}
if (PwriteFd(fd.get(), msg, ABSL_ARRAYSIZE(msg), 10) < 0) {
err(1, "pwrite");
}
struct iovec write_vecs[] = {
{
.iov_base = msg,
.iov_len = ABSL_ARRAYSIZE(msg),
},
{
.iov_base = msg,
.iov_len = ABSL_ARRAYSIZE(msg) / 2,
},
};
if (writev(fd.get(), write_vecs, ABSL_ARRAYSIZE(write_vecs)) < 0) {
err(1, "writev");
}
if (pwritev(fd.get(), write_vecs, ABSL_ARRAYSIZE(write_vecs), 10) < 0) {
err(1, "pwritev");
}
if (pwritev2(fd.get(), write_vecs, ABSL_ARRAYSIZE(write_vecs), 10,
RWF_HIPRI) < 0) {
err(1, "pwritev2");
}
// Rewind the file and test different flavors of read.
if (lseek(fd.get(), 0, SEEK_SET) < 0) {
err(1, "seek(0)");
}
char buf[1024];
if (ReadFd(fd.get(), buf, ABSL_ARRAYSIZE(buf)) < 0) {
err(1, "read");
}
if (PreadFd(fd.get(), buf, ABSL_ARRAYSIZE(buf), 20) < 0) {
err(1, "read");
}
// Reuse same buffer, since it's not using the result anyways.
struct iovec read_vecs[] = {
{
.iov_base = buf,
.iov_len = ABSL_ARRAYSIZE(msg),
},
{
.iov_base = buf,
.iov_len = ABSL_ARRAYSIZE(msg) / 2,
},
};
if (readv(fd.get(), read_vecs, ABSL_ARRAYSIZE(read_vecs)) < 0) {
err(1, "writev");
}
if (preadv(fd.get(), read_vecs, ABSL_ARRAYSIZE(read_vecs), 20) < 0) {
err(1, "pwritev");
}
if (preadv2(fd.get(), read_vecs, ABSL_ARRAYSIZE(read_vecs), 20, RWF_HIPRI) <
0) {
err(1, "pwritev2");
}
}
} // namespace testing
} // namespace gvisor
int main(int argc, char** argv) {
::gvisor::testing::runForkExecve();
::gvisor::testing::runSocket();
::gvisor::testing::runReadWrite();
return 0;
}