mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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
This commit is contained in:
committed by
gVisor bot
parent
9c444c44e0
commit
4e11daccf0
+5
-2
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+232
-26
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -12,17 +12,25 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <bits/types/struct_itimerspec.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
@@ -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<void*>(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;
|
||||
|
||||
Reference in New Issue
Block a user