From 4e11daccf0be7328128e2c97b6cc77cfdd989020 Mon Sep 17 00:00:00 2001 From: Shambhavi Srivastava Date: Thu, 1 Dec 2022 14:17:12 -0800 Subject: [PATCH] Adding more trace point integration tests for the following syscalls: - signalfd - signalfd4 - fcntl - pipe - pipe2 - timerfd_create - timerfd_settime - timerfd_gettime - fork - vfork - inotify_init - inotify_init1 - inotify_add_watch - inotify_rm_watch - clone Updates #4805 PiperOrigin-RevId: 492297673 --- test/trace/BUILD | 7 +- test/trace/trace_amd64_test.go | 51 +++++++ test/trace/trace_arm64_test.go | 39 +++++ test/trace/trace_test.go | 258 ++++++++++++++++++++++++++++---- test/trace/workload/BUILD | 2 + test/trace/workload/workload.cc | 225 +++++++++++++++++++++++++++- 6 files changed, 553 insertions(+), 29 deletions(-) create mode 100644 test/trace/trace_amd64_test.go create mode 100644 test/trace/trace_arm64_test.go diff --git a/test/trace/BUILD b/test/trace/BUILD index 59d3f6141..7efe61d3e 100644 --- a/test/trace/BUILD +++ b/test/trace/BUILD @@ -4,8 +4,11 @@ package(licenses = ["notice"]) go_test( name = "trace_test", - size = "small", - srcs = ["trace_test.go"], + srcs = [ + "trace_amd64_test.go", + "trace_arm64_test.go", + "trace_test.go", + ], data = [ "//runsc", "//test/trace/workload", diff --git a/test/trace/trace_amd64_test.go b/test/trace/trace_amd64_test.go new file mode 100644 index 000000000..5a21f0c6d --- /dev/null +++ b/test/trace/trace_amd64_test.go @@ -0,0 +1,51 @@ +// 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 trace + +import ( + "fmt" + "testing" + + "golang.org/x/sys/unix" + "google.golang.org/protobuf/proto" + pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto" + "gvisor.dev/gvisor/pkg/sentry/seccheck/sinks/remote/test" +) + +func extraMatchers(t *testing.T, msgs []test.Message, matchers map[pb.MessageType]*checkers) { + // Register functions that verify each available point specific to amd64 architecture. + matchers[pb.MessageType_MESSAGE_SYSCALL_FORK] = &checkers{checker: checkSyscallFork} +} + +func checkSyscallSignalfdFlags(flags int32) error { + if flags != 0 && flags != (unix.SFD_CLOEXEC|unix.SFD_NONBLOCK) { + return fmt.Errorf("invalid flag got: %v", flags) + } + return nil +} + +func checkSyscallFork(msg test.Message) error { + p := pb.Fork{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + return nil +} diff --git a/test/trace/trace_arm64_test.go b/test/trace/trace_arm64_test.go new file mode 100644 index 000000000..0dced80af --- /dev/null +++ b/test/trace/trace_arm64_test.go @@ -0,0 +1,39 @@ +// 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 trace + +import ( + "fmt" + "testing" + + "golang.org/x/sys/unix" + pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto" + "gvisor.dev/gvisor/pkg/sentry/seccheck/sinks/remote/test" +) + +func extraMatchers(t *testing.T, msgs []test.Message, matchers map[pb.MessageType]*checkers) { + // Register functions that verify each available point specific to arm64 architecture. + return +} + +func checkSyscallSignalfdFlags(flags int32) error { + if flags != (unix.SFD_CLOEXEC | unix.SFD_NONBLOCK) { + return fmt.Errorf("invalid flag got: %v", flags) + } + return nil +} diff --git a/test/trace/trace_test.go b/test/trace/trace_test.go index 54ba184a7..5a7f4494c 100644 --- a/test/trace/trace_test.go +++ b/test/trace/trace_test.go @@ -34,6 +34,11 @@ import ( var cutoffTime time.Time +type checkers struct { + checker func(test.Message) error + count int +} + // TestAll enabled all trace points in the system with all optional and context // fields enabled. Then it runs a workload that will trigger those points and // run some basic validation over the points generated. @@ -87,15 +92,15 @@ func TestAll(t *testing.T) { // Wait until the sandbox disconnects to ensure all points were gathered. server.WaitForNoClients() - matchPoints(t, server.GetPoints()) + + matchers := matchPoints(t, server.GetPoints()) + extraMatchers(t, server.GetPoints(), matchers) + validatePoints(t, server.GetPoints(), matchers) } -func matchPoints(t *testing.T, msgs []test.Message) { +func matchPoints(t *testing.T, msgs []test.Message) map[pb.MessageType]*checkers { // Register functions that verify each available point. - matchers := map[pb.MessageType]*struct { - checker func(test.Message) error - count int - }{ + matchers := map[pb.MessageType]*checkers{ pb.MessageType_MESSAGE_CONTAINER_START: {checker: checkContainerStart}, pb.MessageType_MESSAGE_SENTRY_CLONE: {checker: checkSentryClone}, pb.MessageType_MESSAGE_SENTRY_EXEC: {checker: checkSentryExec}, @@ -116,13 +121,23 @@ func matchPoints(t *testing.T, msgs []test.Message) { pb.MessageType_MESSAGE_SYSCALL_DUP: {checker: checkSyscallDup}, pb.MessageType_MESSAGE_SYSCALL_PRLIMIT64: {checker: checkSyscallPrlimit64}, pb.MessageType_MESSAGE_SYSCALL_EVENTFD: {checker: checkSyscallEventfd}, + pb.MessageType_MESSAGE_SYSCALL_SIGNALFD: {checker: checkSyscallSignalfd}, pb.MessageType_MESSAGE_SYSCALL_BIND: {checker: checkSyscallBind}, pb.MessageType_MESSAGE_SYSCALL_ACCEPT: {checker: checkSyscallAccept}, - - // TODO(gvisor.dev/issue/4805): Add validation for these messages. - pb.MessageType_MESSAGE_SYSCALL_CLONE: {checker: checkTODO}, - pb.MessageType_MESSAGE_SYSCALL_PIPE: {checker: checkTODO}, + pb.MessageType_MESSAGE_SYSCALL_FCNTL: {checker: checkSyscallFcntl}, + pb.MessageType_MESSAGE_SYSCALL_PIPE: {checker: checkSyscallPipe}, + pb.MessageType_MESSAGE_SYSCALL_TIMERFD_CREATE: {checker: checkSyscallTimerfdCreate}, + pb.MessageType_MESSAGE_SYSCALL_TIMERFD_SETTIME: {checker: checkSyscallTimerfdSettime}, + pb.MessageType_MESSAGE_SYSCALL_TIMERFD_GETTIME: {checker: checkSyscallTimerfdGettime}, + pb.MessageType_MESSAGE_SYSCALL_INOTIFY_INIT: {checker: checkSyscallInotifyInit}, + pb.MessageType_MESSAGE_SYSCALL_INOTIFY_ADD_WATCH: {checker: checkSyscallInotifyInitAddWatch}, + pb.MessageType_MESSAGE_SYSCALL_INOTIFY_RM_WATCH: {checker: checkSyscallInotifyInitRmWatch}, + pb.MessageType_MESSAGE_SYSCALL_CLONE: {checker: checkSyscallClone}, } + return matchers +} + +func validatePoints(t *testing.T, msgs []test.Message, matchers map[pb.MessageType]*checkers) { for _, msg := range msgs { t.Logf("Processing message type %v", msg.MsgType) if handler := matchers[msg.MsgType]; handler == nil { @@ -147,7 +162,7 @@ func matchPoints(t *testing.T, msgs []test.Message) { func checkTimeNs(ns int64) error { if ns <= int64(cutoffTime.Nanosecond()) { - return fmt.Errorf("time should not be less than %d (%v), got: %d (%v)", cutoffTime.Nanosecond(), cutoffTime, ns, time.Unix(0, ns)) + return fmt.Errorf("time: got: %d (%v), should not be less than %d (%v)", ns, time.Unix(0, ns), cutoffTime.Nanosecond(), cutoffTime) } return nil } @@ -497,7 +512,7 @@ func checkSyscallSetid(msg test.Message) error { return err } if p.Id != 0 { - return fmt.Errorf(" invalid id: %d", p.Id) + return fmt.Errorf("invalid id: %d", p.Id) } return nil @@ -512,13 +527,13 @@ func checkSyscallSetresid(msg test.Message) error { return err } if p.GetRid() != 0 { - return fmt.Errorf(" Invalid RID: %d", p.Rid) + return fmt.Errorf("invalid rid: %d", p.Rid) } if p.GetEid() != 0 { - return fmt.Errorf(" Invalid EID: %d", p.Eid) + return fmt.Errorf("invalid eid: %d", p.Eid) } if p.GetSid() != 0 { - return fmt.Errorf(" Invalid SID: %d", p.Sid) + return fmt.Errorf("invalid sid: %d", p.Sid) } return nil @@ -551,10 +566,10 @@ func checkSyscallDup(msg test.Message) error { return err } if p.OldFd < 0 { - return fmt.Errorf("invalid FD: %d", p.OldFd) + return fmt.Errorf("invalid fd: %d", p.OldFd) } if p.NewFd < 0 { - return fmt.Errorf("invalid FD: %d", p.NewFd) + return fmt.Errorf("invalid fd: %d", p.NewFd) } if p.Flags != unix.O_CLOEXEC && p.Flags != 0 { return fmt.Errorf("invalid flag got: %v", p.Flags) @@ -572,7 +587,7 @@ func checkSyscallPrlimit64(msg test.Message) error { return err } if p.Pid < 0 { - return fmt.Errorf("invalid PID: %d", p.Pid) + return fmt.Errorf("invalid pid: %d", p.Pid) } return nil } @@ -586,10 +601,10 @@ func checkSyscallEventfd(msg test.Message) error { return err } if p.Val < 0 { - return fmt.Errorf("invalid PID: %d", p.Val) + return fmt.Errorf("invalid pid: %d", p.Val) } if p.Flags != unix.EFD_NONBLOCK && p.Flags != 0 { - return fmt.Errorf("invalid Flag got: %d, ", p.Flags) + return fmt.Errorf("invalid flag got: %d, ", p.Flags) } return nil @@ -604,10 +619,10 @@ func checkSyscallBind(msg test.Message) error { return err } if p.Fd < 0 { - return fmt.Errorf("invalid FD: %d", p.Fd) + return fmt.Errorf("invalid fd: %d", p.Fd) } if p.FdPath == " " { - return fmt.Errorf("invalid Path: %v", p.FdPath) + return fmt.Errorf("invalid path: %v", p.FdPath) } if len(p.Address) == 0 { return fmt.Errorf("invalid address: %d", p.Address) @@ -624,10 +639,10 @@ func checkSyscallAccept(msg test.Message) error { return err } if p.Fd < 0 { - return fmt.Errorf("invalid FD: %d", p.Fd) + return fmt.Errorf("invalid fd: %d", p.Fd) } if p.FdPath == "" { - return fmt.Errorf("invalid Path: %v", p.FdPath) + return fmt.Errorf("invalid path: %v", p.FdPath) } if len(p.Address) != 0 { return fmt.Errorf("invalid address: %d, %v", p.Address, p.Sysno) @@ -647,12 +662,203 @@ func checkSyscallChroot(msg test.Message) error { return err } if want := "trace_test.abc"; !strings.Contains(p.Pathname, want) { - return fmt.Errorf("wrong Pathname, want: %q, got: %q", want, p.Pathname) + return fmt.Errorf("wrong pathname, want: %q, got: %q", want, p.Pathname) } return nil } -func checkTODO(_ test.Message) error { +func checkSyscallFcntl(msg test.Message) error { + p := pb.Fcntl{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.Fd < 0 { + return fmt.Errorf("invalid fd: %d", p.Fd) + } + if p.Cmd != unix.F_GETFL { + return fmt.Errorf("invalid cmd: got: %v, want: F_GETFL", p.Cmd) + } + return nil +} + +func checkSyscallPipe(msg test.Message) error { + p := pb.Pipe{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.Reader < 0 { + return fmt.Errorf("invalid reader fd: %d", p.Reader) + } + if p.Writer < 0 { + return fmt.Errorf("invalid writer fd: %d", p.Writer) + } + if p.Flags != unix.O_CLOEXEC && p.Flags != 0 { + return fmt.Errorf("invalid flag got: %v", p.Flags) + } + return nil +} + +func checkSyscallSignalfd(msg test.Message) error { + p := pb.Signalfd{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.Fd != -1 { + return fmt.Errorf("invalid fd: %d", p.Fd) + } + if p.Sigset != 0 && p.Sigset != uint64(unix.SIGILL) { + return fmt.Errorf("invalid signal got: %v", p.Sigset) + } + return checkSyscallSignalfdFlags(p.Flags) +} + +func checkSyscallTimerfdCreate(msg test.Message) error { + p := pb.TimerfdCreate{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.ClockId != unix.CLOCK_REALTIME { + return fmt.Errorf("invalid clockid: %d", p.ClockId) + } + if p.Flags != 0 { + return fmt.Errorf("invalid flag got: %v", p.Flags) + } + return nil +} + +func checkSyscallTimerfdSettime(msg test.Message) error { + p := pb.TimerfdSetTime{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.Fd < 0 { + return fmt.Errorf("invalid clockid: %d", p.Fd) + } + if p.FdPath == "" { + return fmt.Errorf("invalid path: %q", p.FdPath) + } + if p.Flags != unix.TFD_TIMER_ABSTIME { + return fmt.Errorf("invalid flag got: %v", p.Flags) + } + if p.OldValue != nil { + return fmt.Errorf("invalid oldvalue: %v", p.OldValue.String()) + } + if p.NewValue == nil { + return fmt.Errorf("invalid oldvalue: %v", p.OldValue.String()) + } + return nil +} + +func checkSyscallTimerfdGettime(msg test.Message) error { + p := pb.TimerfdGetTime{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.Fd < 0 { + return fmt.Errorf("invalid clockid: %d", p.Fd) + } + if p.FdPath == "" { + return fmt.Errorf("invalid path: %q", p.FdPath) + } + if p.CurValue == nil { + return fmt.Errorf("invalid oldvalue: %v", p.CurValue.String()) + } + return nil +} + +func checkSyscallClone(msg test.Message) error { + p := pb.Clone{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + // Flags used by default in system calls that use clone(2) in the underying. + rawFlags := unix.CLONE_CHILD_CLEARTID | unix.CLONE_CHILD_SETTID | uint64(unix.SIGCHLD) + // Flags used for clone(2) syscall in workload.cc + cloneFlags := uint64(unix.SIGCHLD) | unix.CLONE_VFORK | unix.CLONE_FILES + if p.Flags != uint64(rawFlags) && p.Flags != cloneFlags { + return fmt.Errorf("invalid flag got: %v", p.Flags) + } + if (p.Flags == uint64(rawFlags) && p.Stack != 0) || (p.Flags == cloneFlags && p.Stack == 0) { + return fmt.Errorf("invalid stack got: %v", p.Stack) + } + return nil +} + +func checkSyscallInotifyInit(msg test.Message) error { + p := pb.InotifyInit{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if !(p.Flags == 0 || p.Flags == unix.IN_NONBLOCK) { + return fmt.Errorf("invalid flag got: %v", p.Flags) + } + return nil +} + +func checkSyscallInotifyInitAddWatch(msg test.Message) error { + p := pb.InotifyAddWatch{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.Fd < 0 { + return fmt.Errorf("invalid fd: %d", p.Fd) + } + if p.FdPath == "" { + return fmt.Errorf("invalid path: %v", p.FdPath) + } + if want := "timer_trace_test.abc"; !strings.Contains(p.Pathname, want) { + return fmt.Errorf("wrong pathname, got: %q, want: %q", p.Pathname, want) + } + if want := unix.IN_NONBLOCK; want != int(p.Mask) { + return fmt.Errorf("invalid mask: want: %v, got:%v", want, p.Mask) + } + return nil +} + +func checkSyscallInotifyInitRmWatch(msg test.Message) error { + p := pb.InotifyRmWatch{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.Fd < 0 { + return fmt.Errorf("invalid fd: %d", p.Fd) + } + if p.FdPath == "" { + return fmt.Errorf("invalid path: %q", p.FdPath) + } + if p.Wd < 0 { + return fmt.Errorf("invalid wd: %d", p.Wd) + } return nil } diff --git a/test/trace/workload/BUILD b/test/trace/workload/BUILD index 2f0c6b28e..b69532b71 100644 --- a/test/trace/workload/BUILD +++ b/test/trace/workload/BUILD @@ -12,8 +12,10 @@ cc_binary( deps = [ "//test/util:eventfd_util", "//test/util:file_descriptor", + "//test/util:memory_util", "//test/util:multiprocess_util", "//test/util:posix_error", + "//test/util:signal_util", "//test/util:test_util", "@com_google_absl//absl/cleanup", "@com_google_absl//absl/strings", diff --git a/test/trace/workload/workload.cc b/test/trace/workload/workload.cc index 0fb294044..0c5c49a27 100644 --- a/test/trace/workload/workload.cc +++ b/test/trace/workload/workload.cc @@ -12,17 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include +#include +#include #include +#include +#include #include +#include #include #include +#include #include #include #include #include +#include #include #include @@ -31,6 +39,7 @@ #include "absl/time/clock.h" #include "test/util/eventfd_util.h" #include "test/util/file_descriptor.h" +#include "test/util/memory_util.h" #include "test/util/multiprocess_util.h" #include "test/util/posix_error.h" #include "test/util/test_util.h" @@ -82,7 +91,6 @@ void runSocket() { if (pid < 0) { // Fork error. err(1, "fork"); - } else if (pid == 0) { // Child. close(parent_sock); // ensure it's not mistakely used in child. @@ -458,6 +466,202 @@ void runAccept4() { close(fd); } +void runSignalfd4() { + sigset_t mask; + sigemptyset(&mask); + int res = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); + if (res < 0) { + err(1, "signalfd4"); + } +} + +void runFcntl() { + const auto pathname = "trace_test.abc"; + static constexpr mode_t kDefaultDirMode = 0755; + int path_or_error = mkdir(pathname, kDefaultDirMode); + if (path_or_error != 0) { + err(1, "mkdir"); + } + int fd = open(pathname, O_DIRECTORY | O_RDONLY); + if (fd < 0) { + err(1, "open"); + } + auto fd_closer = absl::MakeCleanup([fd] { close(fd); }); + + int res = fcntl(fd, F_GETFL); + if (res < 0) { + err(1, "fcntl"); + } + rmdir(pathname); +} + +void runPipe() { + int fd[2]; + int res = pipe(fd); + if (res < 0) { + err(1, "pipe"); + } + close(fd[0]); + close(fd[1]); +} + +void runPipe2() { + int fd[2]; + int res = pipe2(fd, O_CLOEXEC); + if (res < 0) { + err(1, "pipe2"); + } + close(fd[0]); + close(fd[1]); +} + +void runTimerfdCreate() { + int fd = timerfd_create(CLOCK_REALTIME, 0); + if (fd < 0) { + err(1, "timerfd_create"); + } + close(fd); +} + +void runTimerfdSettime() { + int fd = timerfd_create(CLOCK_REALTIME, 0); + if (fd < 0) { + err(1, "timerfd_create"); + } + auto fd_closer = absl::MakeCleanup([fd] { close(fd); }); + + constexpr auto kInitial = absl::Milliseconds(10); + constexpr auto kInterval = absl::Milliseconds(25); + const itimerspec val = {absl::ToTimespec(kInitial), + absl::ToTimespec(kInterval)}; + int res = timerfd_settime(fd, TFD_TIMER_ABSTIME, &val, 0); + if (res < 0) { + err(1, "timerfd_settime"); + } +} + +void runTimerfdGettime() { + int fd = timerfd_create(CLOCK_REALTIME, 0); + if (fd < 0) { + err(1, "timerfd_create"); + } + auto fd_closer = absl::MakeCleanup([fd] { close(fd); }); + + itimerspec val; + int res = timerfd_gettime(fd, &val); + if (res < 0) { + err(1, "timerfd_gettime"); + } +} +// signalfd(2), fork(2), and vfork(2) system calls are not supported in arm +// architecture. +#ifdef __x86_64__ +void runFork() { + pid_t pid = syscall(__NR_fork); + if (pid < 0) { + err(1, "fork"); + } else if (pid == 0) { + exit(0); + } + RetryEINTR(waitpid)(pid, nullptr, 0); +} + +void runVfork() { + pid_t pid = vfork(); + if (pid < 0) { + err(1, "vfork"); + } else if (pid == 0) { + _exit(0); + } + RetryEINTR(waitpid)(pid, nullptr, 0); +} + +void runSignalfd() { + sigset_t mask; + sigemptyset(&mask); + constexpr int kSizeofKernelSigset = 8; + int res = syscall(__NR_signalfd, -1, &mask, kSizeofKernelSigset); + if (res < 0) { + err(1, "signalfd"); + } +} +#endif + +void runClone() { + Mapping child_stack = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + int child_pid; + child_pid = clone( + +[](void*) { return 0; }, + reinterpret_cast(child_stack.addr() + kPageSize), + SIGCHLD | CLONE_VFORK | CLONE_FILES, nullptr); + + if (child_pid < 0) { + err(1, "clone"); + } + RetryEINTR(waitpid)(child_pid, nullptr, 0); +} + +void runInotifyInit() { + int fd = inotify_init(); + if (fd < 0) { + err(1, "inotify_init"); + } + close(fd); +} + +void runInotifyInit1() { + int fd = inotify_init1(IN_NONBLOCK); + if (fd < 0) { + err(1, "inotify_init1"); + } + close(fd); +} + +void runInotifyAddWatch() { + const auto pathname = "timer_trace_test.abc"; + static constexpr mode_t kDefaultDirMode = 0755; + int path_or_error = mkdir(pathname, kDefaultDirMode); + if (path_or_error != 0) { + err(1, "mkdir"); + } + int fd = inotify_init1(IN_NONBLOCK); + if (fd < 0) { + err(1, "inotify_init1"); + } + auto fd_closer = absl::MakeCleanup([fd] { close(fd); }); + + int res = inotify_add_watch(fd, pathname, IN_NONBLOCK); + if (res < 0) { + err(1, "inotify_add_watch"); + } + rmdir(pathname); +} + +void runInotifyRmWatch() { + const auto pathname = "timer_trace_test.abc"; + static constexpr mode_t kDefaultDirMode = 0755; + int path_or_error = mkdir(pathname, kDefaultDirMode); + if (path_or_error != 0) { + err(1, "mkdir"); + } + int fd = inotify_init1(IN_NONBLOCK); + if (fd < 0) { + err(1, "inotify_init1"); + } + auto fd_closer = absl::MakeCleanup([fd] { close(fd); }); + + int wd = inotify_add_watch(fd, pathname, IN_NONBLOCK); + if (wd < 0) { + err(1, "inotify_add_watch"); + } + int res = inotify_rm_watch(fd, wd); + if (res < 0) { + err(1, "inotify_rm_watch"); + } + rmdir(pathname); +} + } // namespace testing } // namespace gvisor @@ -481,6 +685,25 @@ int main(int argc, char** argv) { ::gvisor::testing::runBind(); ::gvisor::testing::runAccept(); ::gvisor::testing::runAccept4(); + ::gvisor::testing::runSignalfd4(); + ::gvisor::testing::runFcntl(); + ::gvisor::testing::runPipe(); + ::gvisor::testing::runPipe2(); + ::gvisor::testing::runTimerfdCreate(); + ::gvisor::testing::runTimerfdSettime(); + ::gvisor::testing::runTimerfdGettime(); + ::gvisor::testing::runClone(); + ::gvisor::testing::runInotifyInit(); + ::gvisor::testing::runInotifyInit1(); + ::gvisor::testing::runInotifyAddWatch(); + ::gvisor::testing::runInotifyRmWatch(); +// signalfd(2), fork(2), and vfork(2) system calls are not supported in arm +// architecture. +#ifdef __x86_64__ + ::gvisor::testing::runSignalfd(); + ::gvisor::testing::runFork(); + ::gvisor::testing::runVfork(); +#endif // Run chroot at the end since it changes the root for all other tests. ::gvisor::testing::runChroot(); return 0;