summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/strace
diff options
context:
space:
mode:
authorgVisor bot <gvisor-bot@google.com>2019-06-02 06:44:55 +0000
committergVisor bot <gvisor-bot@google.com>2019-06-02 06:44:55 +0000
commitceb0d792f328d1fc0692197d8856a43c3936a571 (patch)
tree83155f302eff44a78bcc30a3a08f4efe59a79379 /pkg/sentry/strace
parentdeb7ecf1e46862d54f4b102f2d163cfbcfc37f3b (diff)
parent216da0b733dbed9aad9b2ab92ac75bcb906fd7ee (diff)
Merge 216da0b7 (automated)
Diffstat (limited to 'pkg/sentry/strace')
-rw-r--r--pkg/sentry/strace/capability.go176
-rw-r--r--pkg/sentry/strace/clone.go113
-rw-r--r--pkg/sentry/strace/futex.go52
-rw-r--r--pkg/sentry/strace/linux64.go338
-rw-r--r--pkg/sentry/strace/open.go96
-rw-r--r--pkg/sentry/strace/poll.go72
-rw-r--r--pkg/sentry/strace/ptrace.go62
-rw-r--r--pkg/sentry/strace/signal.go148
-rw-r--r--pkg/sentry/strace/socket.go412
-rw-r--r--pkg/sentry/strace/strace.go820
-rwxr-xr-xpkg/sentry/strace/strace_go_proto/strace.pb.go247
-rwxr-xr-xpkg/sentry/strace/strace_state_autogen.go4
-rw-r--r--pkg/sentry/strace/syscalls.go267
13 files changed, 2807 insertions, 0 deletions
diff --git a/pkg/sentry/strace/capability.go b/pkg/sentry/strace/capability.go
new file mode 100644
index 000000000..f85d6636e
--- /dev/null
+++ b/pkg/sentry/strace/capability.go
@@ -0,0 +1,176 @@
+// Copyright 2019 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 strace
+
+import (
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
+)
+
+// CapabilityBitset is the set of capabilties in a bitset.
+var CapabilityBitset = abi.FlagSet{
+ {
+ Flag: 1 << uint32(linux.CAP_CHOWN),
+ Name: "CAP_CHOWN",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_DAC_OVERRIDE),
+ Name: "CAP_DAC_OVERRIDE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_DAC_READ_SEARCH),
+ Name: "CAP_DAC_READ_SEARCH",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_FOWNER),
+ Name: "CAP_FOWNER",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_FSETID),
+ Name: "CAP_FSETID",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_KILL),
+ Name: "CAP_KILL",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SETGID),
+ Name: "CAP_SETGID",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SETUID),
+ Name: "CAP_SETUID",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SETPCAP),
+ Name: "CAP_SETPCAP",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_LINUX_IMMUTABLE),
+ Name: "CAP_LINUX_IMMUTABLE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_NET_BIND_SERVICE),
+ Name: "CAP_NET_BIND_SERVICE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_NET_BROADCAST),
+ Name: "CAP_NET_BROADCAST",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_NET_ADMIN),
+ Name: "CAP_NET_ADMIN",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_NET_RAW),
+ Name: "CAP_NET_RAW",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_IPC_LOCK),
+ Name: "CAP_IPC_LOCK",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_IPC_OWNER),
+ Name: "CAP_IPC_OWNER",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_MODULE),
+ Name: "CAP_SYS_MODULE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_RAWIO),
+ Name: "CAP_SYS_RAWIO",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_CHROOT),
+ Name: "CAP_SYS_CHROOT",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_PTRACE),
+ Name: "CAP_SYS_PTRACE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_PACCT),
+ Name: "CAP_SYS_PACCT",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_ADMIN),
+ Name: "CAP_SYS_ADMIN",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_BOOT),
+ Name: "CAP_SYS_BOOT",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_NICE),
+ Name: "CAP_SYS_NICE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_RESOURCE),
+ Name: "CAP_SYS_RESOURCE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_TIME),
+ Name: "CAP_SYS_TIME",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYS_TTY_CONFIG),
+ Name: "CAP_SYS_TTY_CONFIG",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_MKNOD),
+ Name: "CAP_MKNOD",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_LEASE),
+ Name: "CAP_LEASE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_AUDIT_WRITE),
+ Name: "CAP_AUDIT_WRITE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_AUDIT_CONTROL),
+ Name: "CAP_AUDIT_CONTROL",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SETFCAP),
+ Name: "CAP_SETFCAP",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_MAC_OVERRIDE),
+ Name: "CAP_MAC_OVERRIDE",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_MAC_ADMIN),
+ Name: "CAP_MAC_ADMIN",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_SYSLOG),
+ Name: "CAP_SYSLOG",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_WAKE_ALARM),
+ Name: "CAP_WAKE_ALARM",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_BLOCK_SUSPEND),
+ Name: "CAP_BLOCK_SUSPEND",
+ },
+ {
+ Flag: 1 << uint32(linux.CAP_AUDIT_READ),
+ Name: "CAP_AUDIT_READ",
+ },
+}
diff --git a/pkg/sentry/strace/clone.go b/pkg/sentry/strace/clone.go
new file mode 100644
index 000000000..ff6a432c6
--- /dev/null
+++ b/pkg/sentry/strace/clone.go
@@ -0,0 +1,113 @@
+// Copyright 2018 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 strace
+
+import (
+ "syscall"
+
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+)
+
+// CloneFlagSet is the set of clone(2) flags.
+var CloneFlagSet = abi.FlagSet{
+ {
+ Flag: syscall.CLONE_VM,
+ Name: "CLONE_VM",
+ },
+ {
+ Flag: syscall.CLONE_FS,
+ Name: "CLONE_FS",
+ },
+ {
+ Flag: syscall.CLONE_FILES,
+ Name: "CLONE_FILES",
+ },
+ {
+ Flag: syscall.CLONE_SIGHAND,
+ Name: "CLONE_SIGHAND",
+ },
+ {
+ Flag: syscall.CLONE_PTRACE,
+ Name: "CLONE_PTRACE",
+ },
+ {
+ Flag: syscall.CLONE_VFORK,
+ Name: "CLONE_VFORK",
+ },
+ {
+ Flag: syscall.CLONE_PARENT,
+ Name: "CLONE_PARENT",
+ },
+ {
+ Flag: syscall.CLONE_THREAD,
+ Name: "CLONE_THREAD",
+ },
+ {
+ Flag: syscall.CLONE_NEWNS,
+ Name: "CLONE_NEWNS",
+ },
+ {
+ Flag: syscall.CLONE_SYSVSEM,
+ Name: "CLONE_SYSVSEM",
+ },
+ {
+ Flag: syscall.CLONE_SETTLS,
+ Name: "CLONE_SETTLS",
+ },
+ {
+ Flag: syscall.CLONE_PARENT_SETTID,
+ Name: "CLONE_PARENT_SETTID",
+ },
+ {
+ Flag: syscall.CLONE_CHILD_CLEARTID,
+ Name: "CLONE_CHILD_CLEARTID",
+ },
+ {
+ Flag: syscall.CLONE_DETACHED,
+ Name: "CLONE_DETACHED",
+ },
+ {
+ Flag: syscall.CLONE_UNTRACED,
+ Name: "CLONE_UNTRACED",
+ },
+ {
+ Flag: syscall.CLONE_CHILD_SETTID,
+ Name: "CLONE_CHILD_SETTID",
+ },
+ {
+ Flag: syscall.CLONE_NEWUTS,
+ Name: "CLONE_NEWUTS",
+ },
+ {
+ Flag: syscall.CLONE_NEWIPC,
+ Name: "CLONE_NEWIPC",
+ },
+ {
+ Flag: syscall.CLONE_NEWUSER,
+ Name: "CLONE_NEWUSER",
+ },
+ {
+ Flag: syscall.CLONE_NEWPID,
+ Name: "CLONE_NEWPID",
+ },
+ {
+ Flag: syscall.CLONE_NEWNET,
+ Name: "CLONE_NEWNET",
+ },
+ {
+ Flag: syscall.CLONE_IO,
+ Name: "CLONE_IO",
+ },
+}
diff --git a/pkg/sentry/strace/futex.go b/pkg/sentry/strace/futex.go
new file mode 100644
index 000000000..24301bda6
--- /dev/null
+++ b/pkg/sentry/strace/futex.go
@@ -0,0 +1,52 @@
+// Copyright 2018 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 strace
+
+import (
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
+)
+
+// FutexCmd are the possible futex(2) commands.
+var FutexCmd = abi.ValueSet{
+ linux.FUTEX_WAIT: "FUTEX_WAIT",
+ linux.FUTEX_WAKE: "FUTEX_WAKE",
+ linux.FUTEX_FD: "FUTEX_FD",
+ linux.FUTEX_REQUEUE: "FUTEX_REQUEUE",
+ linux.FUTEX_CMP_REQUEUE: "FUTEX_CMP_REQUEUE",
+ linux.FUTEX_WAKE_OP: "FUTEX_WAKE_OP",
+ linux.FUTEX_LOCK_PI: "FUTEX_LOCK_PI",
+ linux.FUTEX_UNLOCK_PI: "FUTEX_UNLOCK_PI",
+ linux.FUTEX_TRYLOCK_PI: "FUTEX_TRYLOCK_PI",
+ linux.FUTEX_WAIT_BITSET: "FUTEX_WAIT_BITSET",
+ linux.FUTEX_WAKE_BITSET: "FUTEX_WAKE_BITSET",
+ linux.FUTEX_WAIT_REQUEUE_PI: "FUTEX_WAIT_REQUEUE_PI",
+ linux.FUTEX_CMP_REQUEUE_PI: "FUTEX_CMP_REQUEUE_PI",
+}
+
+func futex(op uint64) string {
+ cmd := op &^ (linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_CLOCK_REALTIME)
+ clockRealtime := (op & linux.FUTEX_CLOCK_REALTIME) == linux.FUTEX_CLOCK_REALTIME
+ private := (op & linux.FUTEX_PRIVATE_FLAG) == linux.FUTEX_PRIVATE_FLAG
+
+ s := FutexCmd.Parse(cmd)
+ if clockRealtime {
+ s += "|FUTEX_CLOCK_REALTIME"
+ }
+ if private {
+ s += "|FUTEX_PRIVATE_FLAG"
+ }
+ return s
+}
diff --git a/pkg/sentry/strace/linux64.go b/pkg/sentry/strace/linux64.go
new file mode 100644
index 000000000..3650fd6e1
--- /dev/null
+++ b/pkg/sentry/strace/linux64.go
@@ -0,0 +1,338 @@
+// Copyright 2018 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 strace
+
+// linuxAMD64 provides a mapping of the Linux amd64 syscalls and their argument
+// types for display / formatting.
+var linuxAMD64 = SyscallMap{
+ 0: makeSyscallInfo("read", FD, ReadBuffer, Hex),
+ 1: makeSyscallInfo("write", FD, WriteBuffer, Hex),
+ 2: makeSyscallInfo("open", Path, OpenFlags, Mode),
+ 3: makeSyscallInfo("close", FD),
+ 4: makeSyscallInfo("stat", Path, Stat),
+ 5: makeSyscallInfo("fstat", FD, Stat),
+ 6: makeSyscallInfo("lstat", Path, Stat),
+ 7: makeSyscallInfo("poll", PollFDs, Hex, Hex),
+ 8: makeSyscallInfo("lseek", Hex, Hex, Hex),
+ 9: makeSyscallInfo("mmap", Hex, Hex, Hex, Hex, FD, Hex),
+ 10: makeSyscallInfo("mprotect", Hex, Hex, Hex),
+ 11: makeSyscallInfo("munmap", Hex, Hex),
+ 12: makeSyscallInfo("brk", Hex),
+ 13: makeSyscallInfo("rt_sigaction", Signal, SigAction, PostSigAction),
+ 14: makeSyscallInfo("rt_sigprocmask", SignalMaskAction, SigSet, PostSigSet, Hex),
+ 15: makeSyscallInfo("rt_sigreturn"),
+ 16: makeSyscallInfo("ioctl", FD, Hex, Hex),
+ 17: makeSyscallInfo("pread64", FD, ReadBuffer, Hex, Hex),
+ 18: makeSyscallInfo("pwrite64", FD, WriteBuffer, Hex, Hex),
+ 19: makeSyscallInfo("readv", FD, ReadIOVec, Hex),
+ 20: makeSyscallInfo("writev", FD, WriteIOVec, Hex),
+ 21: makeSyscallInfo("access", Path, Oct),
+ 22: makeSyscallInfo("pipe", PipeFDs),
+ 23: makeSyscallInfo("select", Hex, Hex, Hex, Hex, Timeval),
+ 24: makeSyscallInfo("sched_yield"),
+ 25: makeSyscallInfo("mremap", Hex, Hex, Hex, Hex, Hex),
+ 26: makeSyscallInfo("msync", Hex, Hex, Hex),
+ 27: makeSyscallInfo("mincore", Hex, Hex, Hex),
+ 28: makeSyscallInfo("madvise", Hex, Hex, Hex),
+ 29: makeSyscallInfo("shmget", Hex, Hex, Hex),
+ 30: makeSyscallInfo("shmat", Hex, Hex, Hex),
+ 31: makeSyscallInfo("shmctl", Hex, Hex, Hex),
+ 32: makeSyscallInfo("dup", FD),
+ 33: makeSyscallInfo("dup2", FD, FD),
+ 34: makeSyscallInfo("pause"),
+ 35: makeSyscallInfo("nanosleep", Timespec, PostTimespec),
+ 36: makeSyscallInfo("getitimer", ItimerType, PostItimerVal),
+ 37: makeSyscallInfo("alarm", Hex),
+ 38: makeSyscallInfo("setitimer", ItimerType, ItimerVal, PostItimerVal),
+ 39: makeSyscallInfo("getpid"),
+ 40: makeSyscallInfo("sendfile", FD, FD, Hex, Hex),
+ 41: makeSyscallInfo("socket", SockFamily, SockType, SockProtocol),
+ 42: makeSyscallInfo("connect", FD, SockAddr, Hex),
+ 43: makeSyscallInfo("accept", FD, PostSockAddr, SockLen),
+ 44: makeSyscallInfo("sendto", FD, Hex, Hex, Hex, SockAddr, Hex),
+ 45: makeSyscallInfo("recvfrom", FD, Hex, Hex, Hex, PostSockAddr, SockLen),
+ 46: makeSyscallInfo("sendmsg", FD, SendMsgHdr, Hex),
+ 47: makeSyscallInfo("recvmsg", FD, RecvMsgHdr, Hex),
+ 48: makeSyscallInfo("shutdown", FD, Hex),
+ 49: makeSyscallInfo("bind", FD, SockAddr, Hex),
+ 50: makeSyscallInfo("listen", FD, Hex),
+ 51: makeSyscallInfo("getsockname", FD, PostSockAddr, SockLen),
+ 52: makeSyscallInfo("getpeername", FD, PostSockAddr, SockLen),
+ 53: makeSyscallInfo("socketpair", SockFamily, SockType, SockProtocol, Hex),
+ 54: makeSyscallInfo("setsockopt", FD, Hex, Hex, Hex, Hex),
+ 55: makeSyscallInfo("getsockopt", FD, Hex, Hex, Hex, Hex),
+ 56: makeSyscallInfo("clone", CloneFlags, Hex, Hex, Hex, Hex),
+ 57: makeSyscallInfo("fork"),
+ 58: makeSyscallInfo("vfork"),
+ 59: makeSyscallInfo("execve", Path, ExecveStringVector, ExecveStringVector),
+ 60: makeSyscallInfo("exit", Hex),
+ 61: makeSyscallInfo("wait4", Hex, Hex, Hex, Rusage),
+ 62: makeSyscallInfo("kill", Hex, Signal),
+ 63: makeSyscallInfo("uname", Uname),
+ 64: makeSyscallInfo("semget", Hex, Hex, Hex),
+ 65: makeSyscallInfo("semop", Hex, Hex, Hex),
+ 66: makeSyscallInfo("semctl", Hex, Hex, Hex, Hex),
+ 67: makeSyscallInfo("shmdt", Hex),
+ 68: makeSyscallInfo("msgget", Hex, Hex),
+ 69: makeSyscallInfo("msgsnd", Hex, Hex, Hex, Hex),
+ 70: makeSyscallInfo("msgrcv", Hex, Hex, Hex, Hex, Hex),
+ 71: makeSyscallInfo("msgctl", Hex, Hex, Hex),
+ 72: makeSyscallInfo("fcntl", FD, Hex, Hex),
+ 73: makeSyscallInfo("flock", FD, Hex),
+ 74: makeSyscallInfo("fsync", FD),
+ 75: makeSyscallInfo("fdatasync", FD),
+ 76: makeSyscallInfo("truncate", Path, Hex),
+ 77: makeSyscallInfo("ftruncate", FD, Hex),
+ 78: makeSyscallInfo("getdents", FD, Hex, Hex),
+ 79: makeSyscallInfo("getcwd", PostPath, Hex),
+ 80: makeSyscallInfo("chdir", Path),
+ 81: makeSyscallInfo("fchdir", FD),
+ 82: makeSyscallInfo("rename", Path, Path),
+ 83: makeSyscallInfo("mkdir", Path, Oct),
+ 84: makeSyscallInfo("rmdir", Path),
+ 85: makeSyscallInfo("creat", Path, Oct),
+ 86: makeSyscallInfo("link", Path, Path),
+ 87: makeSyscallInfo("unlink", Path),
+ 88: makeSyscallInfo("symlink", Path, Path),
+ 89: makeSyscallInfo("readlink", Path, ReadBuffer, Hex),
+ 90: makeSyscallInfo("chmod", Path, Mode),
+ 91: makeSyscallInfo("fchmod", FD, Mode),
+ 92: makeSyscallInfo("chown", Path, Hex, Hex),
+ 93: makeSyscallInfo("fchown", FD, Hex, Hex),
+ 94: makeSyscallInfo("lchown", Path, Hex, Hex),
+ 95: makeSyscallInfo("umask", Hex),
+ 96: makeSyscallInfo("gettimeofday", Timeval, Hex),
+ 97: makeSyscallInfo("getrlimit", Hex, Hex),
+ 98: makeSyscallInfo("getrusage", Hex, Rusage),
+ 99: makeSyscallInfo("sysinfo", Hex),
+ 100: makeSyscallInfo("times", Hex),
+ 101: makeSyscallInfo("ptrace", PtraceRequest, Hex, Hex, Hex),
+ 102: makeSyscallInfo("getuid"),
+ 103: makeSyscallInfo("syslog", Hex, Hex, Hex),
+ 104: makeSyscallInfo("getgid"),
+ 105: makeSyscallInfo("setuid", Hex),
+ 106: makeSyscallInfo("setgid", Hex),
+ 107: makeSyscallInfo("geteuid"),
+ 108: makeSyscallInfo("getegid"),
+ 109: makeSyscallInfo("setpgid", Hex, Hex),
+ 110: makeSyscallInfo("getppid"),
+ 111: makeSyscallInfo("getpgrp"),
+ 112: makeSyscallInfo("setsid"),
+ 113: makeSyscallInfo("setreuid", Hex, Hex),
+ 114: makeSyscallInfo("setregid", Hex, Hex),
+ 115: makeSyscallInfo("getgroups", Hex, Hex),
+ 116: makeSyscallInfo("setgroups", Hex, Hex),
+ 117: makeSyscallInfo("setresuid", Hex, Hex, Hex),
+ 118: makeSyscallInfo("getresuid", Hex, Hex, Hex),
+ 119: makeSyscallInfo("setresgid", Hex, Hex, Hex),
+ 120: makeSyscallInfo("getresgid", Hex, Hex, Hex),
+ 121: makeSyscallInfo("getpgid", Hex),
+ 122: makeSyscallInfo("setfsuid", Hex),
+ 123: makeSyscallInfo("setfsgid", Hex),
+ 124: makeSyscallInfo("getsid", Hex),
+ 125: makeSyscallInfo("capget", CapHeader, PostCapData),
+ 126: makeSyscallInfo("capset", CapHeader, CapData),
+ 127: makeSyscallInfo("rt_sigpending", Hex),
+ 128: makeSyscallInfo("rt_sigtimedwait", SigSet, Hex, Timespec, Hex),
+ 129: makeSyscallInfo("rt_sigqueueinfo", Hex, Signal, Hex),
+ 130: makeSyscallInfo("rt_sigsuspend", Hex),
+ 131: makeSyscallInfo("sigaltstack", Hex, Hex),
+ 132: makeSyscallInfo("utime", Path, Utimbuf),
+ 133: makeSyscallInfo("mknod", Path, Mode, Hex),
+ 134: makeSyscallInfo("uselib", Hex),
+ 135: makeSyscallInfo("personality", Hex),
+ 136: makeSyscallInfo("ustat", Hex, Hex),
+ 137: makeSyscallInfo("statfs", Path, Hex),
+ 138: makeSyscallInfo("fstatfs", FD, Hex),
+ 139: makeSyscallInfo("sysfs", Hex, Hex, Hex),
+ 140: makeSyscallInfo("getpriority", Hex, Hex),
+ 141: makeSyscallInfo("setpriority", Hex, Hex, Hex),
+ 142: makeSyscallInfo("sched_setparam", Hex, Hex),
+ 143: makeSyscallInfo("sched_getparam", Hex, Hex),
+ 144: makeSyscallInfo("sched_setscheduler", Hex, Hex, Hex),
+ 145: makeSyscallInfo("sched_getscheduler", Hex),
+ 146: makeSyscallInfo("sched_get_priority_max", Hex),
+ 147: makeSyscallInfo("sched_get_priority_min", Hex),
+ 148: makeSyscallInfo("sched_rr_get_interval", Hex, Hex),
+ 149: makeSyscallInfo("mlock", Hex, Hex),
+ 150: makeSyscallInfo("munlock", Hex, Hex),
+ 151: makeSyscallInfo("mlockall", Hex),
+ 152: makeSyscallInfo("munlockall"),
+ 153: makeSyscallInfo("vhangup"),
+ 154: makeSyscallInfo("modify_ldt", Hex, Hex, Hex),
+ 155: makeSyscallInfo("pivot_root", Path, Path),
+ 156: makeSyscallInfo("_sysctl", Hex),
+ 157: makeSyscallInfo("prctl", Hex, Hex, Hex, Hex, Hex),
+ 158: makeSyscallInfo("arch_prctl", Hex, Hex),
+ 159: makeSyscallInfo("adjtimex", Hex),
+ 160: makeSyscallInfo("setrlimit", Hex, Hex),
+ 161: makeSyscallInfo("chroot", Path),
+ 162: makeSyscallInfo("sync"),
+ 163: makeSyscallInfo("acct", Hex),
+ 164: makeSyscallInfo("settimeofday", Timeval, Hex),
+ 165: makeSyscallInfo("mount", Path, Path, Path, Hex, Path),
+ 166: makeSyscallInfo("umount2", Path, Hex),
+ 167: makeSyscallInfo("swapon", Hex, Hex),
+ 168: makeSyscallInfo("swapoff", Hex),
+ 169: makeSyscallInfo("reboot", Hex, Hex, Hex, Hex),
+ 170: makeSyscallInfo("sethostname", Hex, Hex),
+ 171: makeSyscallInfo("setdomainname", Hex, Hex),
+ 172: makeSyscallInfo("iopl", Hex),
+ 173: makeSyscallInfo("ioperm", Hex, Hex, Hex),
+ 174: makeSyscallInfo("create_module", Path, Hex),
+ 175: makeSyscallInfo("init_module", Hex, Hex, Hex),
+ 176: makeSyscallInfo("delete_module", Hex, Hex),
+ 177: makeSyscallInfo("get_kernel_syms", Hex),
+ // 178: query_module (only present in Linux < 2.6)
+ 179: makeSyscallInfo("quotactl", Hex, Hex, Hex, Hex),
+ 180: makeSyscallInfo("nfsservctl", Hex, Hex, Hex),
+ // 181: getpmsg (not implemented in the Linux kernel)
+ // 182: putpmsg (not implemented in the Linux kernel)
+ // 183: afs_syscall (not implemented in the Linux kernel)
+ // 184: tuxcall (not implemented in the Linux kernel)
+ // 185: security (not implemented in the Linux kernel)
+ 186: makeSyscallInfo("gettid"),
+ 187: makeSyscallInfo("readahead", Hex, Hex, Hex),
+ 188: makeSyscallInfo("setxattr", Path, Path, Hex, Hex, Hex),
+ 189: makeSyscallInfo("lsetxattr", Path, Path, Hex, Hex, Hex),
+ 190: makeSyscallInfo("fsetxattr", FD, Path, Hex, Hex, Hex),
+ 191: makeSyscallInfo("getxattr", Path, Path, Hex, Hex),
+ 192: makeSyscallInfo("lgetxattr", Path, Path, Hex, Hex),
+ 193: makeSyscallInfo("fgetxattr", FD, Path, Hex, Hex),
+ 194: makeSyscallInfo("listxattr", Path, Path, Hex),
+ 195: makeSyscallInfo("llistxattr", Path, Path, Hex),
+ 196: makeSyscallInfo("flistxattr", FD, Path, Hex),
+ 197: makeSyscallInfo("removexattr", Path, Path),
+ 198: makeSyscallInfo("lremovexattr", Path, Path),
+ 199: makeSyscallInfo("fremovexattr", FD, Path),
+ 200: makeSyscallInfo("tkill", Hex, Signal),
+ 201: makeSyscallInfo("time", Hex),
+ 202: makeSyscallInfo("futex", Hex, FutexOp, Hex, Timespec, Hex, Hex),
+ 203: makeSyscallInfo("sched_setaffinity", Hex, Hex, Hex),
+ 204: makeSyscallInfo("sched_getaffinity", Hex, Hex, Hex),
+ 205: makeSyscallInfo("set_thread_area", Hex),
+ 206: makeSyscallInfo("io_setup", Hex, Hex),
+ 207: makeSyscallInfo("io_destroy", Hex),
+ 208: makeSyscallInfo("io_getevents", Hex, Hex, Hex, Hex, Timespec),
+ 209: makeSyscallInfo("io_submit", Hex, Hex, Hex),
+ 210: makeSyscallInfo("io_cancel", Hex, Hex, Hex),
+ 211: makeSyscallInfo("get_thread_area", Hex),
+ 212: makeSyscallInfo("lookup_dcookie", Hex, Hex, Hex),
+ 213: makeSyscallInfo("epoll_create", Hex),
+ // 214: epoll_ctl_old (not implemented in the Linux kernel)
+ // 215: epoll_wait_old (not implemented in the Linux kernel)
+ 216: makeSyscallInfo("remap_file_pages", Hex, Hex, Hex, Hex, Hex),
+ 217: makeSyscallInfo("getdents64", FD, Hex, Hex),
+ 218: makeSyscallInfo("set_tid_address", Hex),
+ 219: makeSyscallInfo("restart_syscall"),
+ 220: makeSyscallInfo("semtimedop", Hex, Hex, Hex, Hex),
+ 221: makeSyscallInfo("fadvise64", FD, Hex, Hex, Hex),
+ 222: makeSyscallInfo("timer_create", Hex, Hex, Hex),
+ 223: makeSyscallInfo("timer_settime", Hex, Hex, ItimerSpec, PostItimerSpec),
+ 224: makeSyscallInfo("timer_gettime", Hex, PostItimerSpec),
+ 225: makeSyscallInfo("timer_getoverrun", Hex),
+ 226: makeSyscallInfo("timer_delete", Hex),
+ 227: makeSyscallInfo("clock_settime", Hex, Timespec),
+ 228: makeSyscallInfo("clock_gettime", Hex, PostTimespec),
+ 229: makeSyscallInfo("clock_getres", Hex, PostTimespec),
+ 230: makeSyscallInfo("clock_nanosleep", Hex, Hex, Timespec, PostTimespec),
+ 231: makeSyscallInfo("exit_group", Hex),
+ 232: makeSyscallInfo("epoll_wait", Hex, Hex, Hex, Hex),
+ 233: makeSyscallInfo("epoll_ctl", Hex, Hex, FD, Hex),
+ 234: makeSyscallInfo("tgkill", Hex, Hex, Signal),
+ 235: makeSyscallInfo("utimes", Path, Timeval),
+ // 236: vserver (not implemented in the Linux kernel)
+ 237: makeSyscallInfo("mbind", Hex, Hex, Hex, Hex, Hex, Hex),
+ 238: makeSyscallInfo("set_mempolicy", Hex, Hex, Hex),
+ 239: makeSyscallInfo("get_mempolicy", Hex, Hex, Hex, Hex, Hex),
+ 240: makeSyscallInfo("mq_open", Hex, Hex, Hex, Hex),
+ 241: makeSyscallInfo("mq_unlink", Hex),
+ 242: makeSyscallInfo("mq_timedsend", Hex, Hex, Hex, Hex, Hex),
+ 243: makeSyscallInfo("mq_timedreceive", Hex, Hex, Hex, Hex, Hex),
+ 244: makeSyscallInfo("mq_notify", Hex, Hex),
+ 245: makeSyscallInfo("mq_getsetattr", Hex, Hex, Hex),
+ 246: makeSyscallInfo("kexec_load", Hex, Hex, Hex, Hex),
+ 247: makeSyscallInfo("waitid", Hex, Hex, Hex, Hex, Rusage),
+ 248: makeSyscallInfo("add_key", Hex, Hex, Hex, Hex, Hex),
+ 249: makeSyscallInfo("request_key", Hex, Hex, Hex, Hex),
+ 250: makeSyscallInfo("keyctl", Hex, Hex, Hex, Hex, Hex),
+ 251: makeSyscallInfo("ioprio_set", Hex, Hex, Hex),
+ 252: makeSyscallInfo("ioprio_get", Hex, Hex),
+ 253: makeSyscallInfo("inotify_init"),
+ 254: makeSyscallInfo("inotify_add_watch", Hex, Path, Hex),
+ 255: makeSyscallInfo("inotify_rm_watch", Hex, Hex),
+ 256: makeSyscallInfo("migrate_pages", Hex, Hex, Hex, Hex),
+ 257: makeSyscallInfo("openat", FD, Path, OpenFlags, Mode),
+ 258: makeSyscallInfo("mkdirat", FD, Path, Hex),
+ 259: makeSyscallInfo("mknodat", FD, Path, Mode, Hex),
+ 260: makeSyscallInfo("fchownat", FD, Path, Hex, Hex, Hex),
+ 261: makeSyscallInfo("futimesat", FD, Path, Hex),
+ 262: makeSyscallInfo("newfstatat", FD, Path, Stat, Hex),
+ 263: makeSyscallInfo("unlinkat", FD, Path, Hex),
+ 264: makeSyscallInfo("renameat", FD, Path, Hex, Path),
+ 265: makeSyscallInfo("linkat", FD, Path, Hex, Path, Hex),
+ 266: makeSyscallInfo("symlinkat", Path, Hex, Path),
+ 267: makeSyscallInfo("readlinkat", FD, Path, ReadBuffer, Hex),
+ 268: makeSyscallInfo("fchmodat", FD, Path, Mode),
+ 269: makeSyscallInfo("faccessat", FD, Path, Oct, Hex),
+ 270: makeSyscallInfo("pselect6", Hex, Hex, Hex, Hex, Hex, Hex),
+ 271: makeSyscallInfo("ppoll", PollFDs, Hex, Timespec, SigSet, Hex),
+ 272: makeSyscallInfo("unshare", CloneFlags),
+ 273: makeSyscallInfo("set_robust_list", Hex, Hex),
+ 274: makeSyscallInfo("get_robust_list", Hex, Hex, Hex),
+ 275: makeSyscallInfo("splice", FD, Hex, FD, Hex, Hex, Hex),
+ 276: makeSyscallInfo("tee", FD, FD, Hex, Hex),
+ 277: makeSyscallInfo("sync_file_range", FD, Hex, Hex, Hex),
+ 278: makeSyscallInfo("vmsplice", FD, Hex, Hex, Hex),
+ 279: makeSyscallInfo("move_pages", Hex, Hex, Hex, Hex, Hex, Hex),
+ 280: makeSyscallInfo("utimensat", FD, Path, UTimeTimespec, Hex),
+ 281: makeSyscallInfo("epoll_pwait", Hex, Hex, Hex, Hex, SigSet, Hex),
+ 282: makeSyscallInfo("signalfd", Hex, Hex, Hex),
+ 283: makeSyscallInfo("timerfd_create", Hex, Hex),
+ 284: makeSyscallInfo("eventfd", Hex),
+ 285: makeSyscallInfo("fallocate", FD, Hex, Hex, Hex),
+ 286: makeSyscallInfo("timerfd_settime", FD, Hex, ItimerSpec, PostItimerSpec),
+ 287: makeSyscallInfo("timerfd_gettime", FD, PostItimerSpec),
+ 288: makeSyscallInfo("accept4", FD, PostSockAddr, SockLen, SockFlags),
+ 289: makeSyscallInfo("signalfd4", Hex, Hex, Hex, Hex),
+ 290: makeSyscallInfo("eventfd2", Hex, Hex),
+ 291: makeSyscallInfo("epoll_create1", Hex),
+ 292: makeSyscallInfo("dup3", FD, FD, Hex),
+ 293: makeSyscallInfo("pipe2", PipeFDs, Hex),
+ 294: makeSyscallInfo("inotify_init1", Hex),
+ 295: makeSyscallInfo("preadv", FD, ReadIOVec, Hex, Hex),
+ 296: makeSyscallInfo("pwritev", FD, WriteIOVec, Hex, Hex),
+ 297: makeSyscallInfo("rt_tgsigqueueinfo", Hex, Hex, Signal, Hex),
+ 298: makeSyscallInfo("perf_event_open", Hex, Hex, Hex, Hex, Hex),
+ 299: makeSyscallInfo("recvmmsg", FD, Hex, Hex, Hex, Hex),
+ 300: makeSyscallInfo("fanotify_init", Hex, Hex),
+ 301: makeSyscallInfo("fanotify_mark", Hex, Hex, Hex, Hex, Hex),
+ 302: makeSyscallInfo("prlimit64", Hex, Hex, Hex, Hex),
+ 303: makeSyscallInfo("name_to_handle_at", FD, Hex, Hex, Hex, Hex),
+ 304: makeSyscallInfo("open_by_handle_at", FD, Hex, Hex),
+ 305: makeSyscallInfo("clock_adjtime", Hex, Hex),
+ 306: makeSyscallInfo("syncfs", FD),
+ 307: makeSyscallInfo("sendmmsg", FD, Hex, Hex, Hex),
+ 308: makeSyscallInfo("setns", FD, Hex),
+ 309: makeSyscallInfo("getcpu", Hex, Hex, Hex),
+ 310: makeSyscallInfo("process_vm_readv", Hex, ReadIOVec, Hex, IOVec, Hex, Hex),
+ 311: makeSyscallInfo("process_vm_writev", Hex, IOVec, Hex, WriteIOVec, Hex, Hex),
+ 312: makeSyscallInfo("kcmp", Hex, Hex, Hex, Hex, Hex),
+ 313: makeSyscallInfo("finit_module", Hex, Hex, Hex),
+ 314: makeSyscallInfo("sched_setattr", Hex, Hex, Hex),
+ 315: makeSyscallInfo("sched_getattr", Hex, Hex, Hex),
+ 316: makeSyscallInfo("renameat2", FD, Path, Hex, Path, Hex),
+ 317: makeSyscallInfo("seccomp", Hex, Hex, Hex),
+}
diff --git a/pkg/sentry/strace/open.go b/pkg/sentry/strace/open.go
new file mode 100644
index 000000000..140727b02
--- /dev/null
+++ b/pkg/sentry/strace/open.go
@@ -0,0 +1,96 @@
+// Copyright 2018 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 strace
+
+import (
+ "syscall"
+
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+)
+
+// OpenMode represents the mode to open(2) a file.
+var OpenMode = abi.ValueSet{
+ syscall.O_RDWR: "O_RDWR",
+ syscall.O_WRONLY: "O_WRONLY",
+ syscall.O_RDONLY: "O_RDONLY",
+}
+
+// OpenFlagSet is the set of open(2) flags.
+var OpenFlagSet = abi.FlagSet{
+ {
+ Flag: syscall.O_APPEND,
+ Name: "O_APPEND",
+ },
+ {
+ Flag: syscall.O_ASYNC,
+ Name: "O_ASYNC",
+ },
+ {
+ Flag: syscall.O_CLOEXEC,
+ Name: "O_CLOEXEC",
+ },
+ {
+ Flag: syscall.O_CREAT,
+ Name: "O_CREAT",
+ },
+ {
+ Flag: syscall.O_DIRECT,
+ Name: "O_DIRECT",
+ },
+ {
+ Flag: syscall.O_DIRECTORY,
+ Name: "O_DIRECTORY",
+ },
+ {
+ Flag: syscall.O_EXCL,
+ Name: "O_EXCL",
+ },
+ {
+ Flag: syscall.O_NOATIME,
+ Name: "O_NOATIME",
+ },
+ {
+ Flag: syscall.O_NOCTTY,
+ Name: "O_NOCTTY",
+ },
+ {
+ Flag: syscall.O_NOFOLLOW,
+ Name: "O_NOFOLLOW",
+ },
+ {
+ Flag: syscall.O_NONBLOCK,
+ Name: "O_NONBLOCK",
+ },
+ {
+ Flag: 0x200000, // O_PATH
+ Name: "O_PATH",
+ },
+ {
+ Flag: syscall.O_SYNC,
+ Name: "O_SYNC",
+ },
+ {
+ Flag: syscall.O_TRUNC,
+ Name: "O_TRUNC",
+ },
+}
+
+func open(val uint64) string {
+ s := OpenMode.Parse(val & syscall.O_ACCMODE)
+ if flags := OpenFlagSet.Parse(val &^ syscall.O_ACCMODE); flags != "" {
+ s += "|" + flags
+ }
+ return s
+}
diff --git a/pkg/sentry/strace/poll.go b/pkg/sentry/strace/poll.go
new file mode 100644
index 000000000..15605187d
--- /dev/null
+++ b/pkg/sentry/strace/poll.go
@@ -0,0 +1,72 @@
+// Copyright 2019 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 strace
+
+import (
+ "fmt"
+ "strings"
+
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
+ slinux "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/linux"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
+)
+
+// PollEventSet is the set of poll(2) event flags.
+var PollEventSet = abi.FlagSet{
+ {Flag: linux.POLLIN, Name: "POLLIN"},
+ {Flag: linux.POLLPRI, Name: "POLLPRI"},
+ {Flag: linux.POLLOUT, Name: "POLLOUT"},
+ {Flag: linux.POLLERR, Name: "POLLERR"},
+ {Flag: linux.POLLHUP, Name: "POLLHUP"},
+ {Flag: linux.POLLNVAL, Name: "POLLNVAL"},
+ {Flag: linux.POLLRDNORM, Name: "POLLRDNORM"},
+ {Flag: linux.POLLRDBAND, Name: "POLLRDBAND"},
+ {Flag: linux.POLLWRNORM, Name: "POLLWRNORM"},
+ {Flag: linux.POLLWRBAND, Name: "POLLWRBAND"},
+ {Flag: linux.POLLMSG, Name: "POLLMSG"},
+ {Flag: linux.POLLREMOVE, Name: "POLLREMOVE"},
+ {Flag: linux.POLLRDHUP, Name: "POLLRDHUP"},
+ {Flag: linux.POLLFREE, Name: "POLLFREE"},
+ {Flag: linux.POLL_BUSY_LOOP, Name: "POLL_BUSY_LOOP"},
+}
+
+func pollFD(t *kernel.Task, pfd *linux.PollFD, post bool) string {
+ revents := "..."
+ if post {
+ revents = PollEventSet.Parse(uint64(pfd.REvents))
+ }
+ return fmt.Sprintf("{FD: %s, Events: %s, REvents: %s}", fd(t, kdefs.FD(pfd.FD)), PollEventSet.Parse(uint64(pfd.Events)), revents)
+}
+
+func pollFDs(t *kernel.Task, addr usermem.Addr, nfds uint, post bool) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ pfds, err := slinux.CopyInPollFDs(t, addr, nfds)
+ if err != nil {
+ return fmt.Sprintf("%#x (error decoding pollfds: %s)", addr, err)
+ }
+
+ s := make([]string, 0, len(pfds))
+ for i := range pfds {
+ s = append(s, pollFD(t, &pfds[i], post))
+ }
+
+ return fmt.Sprintf("%#x [%s]", addr, strings.Join(s, ", "))
+}
diff --git a/pkg/sentry/strace/ptrace.go b/pkg/sentry/strace/ptrace.go
new file mode 100644
index 000000000..485aacb8a
--- /dev/null
+++ b/pkg/sentry/strace/ptrace.go
@@ -0,0 +1,62 @@
+// Copyright 2018 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 strace
+
+import (
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
+)
+
+// PtraceRequestSet are the possible ptrace(2) requests.
+var PtraceRequestSet = abi.ValueSet{
+ linux.PTRACE_TRACEME: "PTRACE_TRACEME",
+ linux.PTRACE_PEEKTEXT: "PTRACE_PEEKTEXT",
+ linux.PTRACE_PEEKDATA: "PTRACE_PEEKDATA",
+ linux.PTRACE_PEEKUSR: "PTRACE_PEEKUSR",
+ linux.PTRACE_POKETEXT: "PTRACE_POKETEXT",
+ linux.PTRACE_POKEDATA: "PTRACE_POKEDATA",
+ linux.PTRACE_POKEUSR: "PTRACE_POKEUSR",
+ linux.PTRACE_CONT: "PTRACE_CONT",
+ linux.PTRACE_KILL: "PTRACE_KILL",
+ linux.PTRACE_SINGLESTEP: "PTRACE_SINGLESTEP",
+ linux.PTRACE_ATTACH: "PTRACE_ATTACH",
+ linux.PTRACE_DETACH: "PTRACE_DETACH",
+ linux.PTRACE_SYSCALL: "PTRACE_SYSCALL",
+ linux.PTRACE_SETOPTIONS: "PTRACE_SETOPTIONS",
+ linux.PTRACE_GETEVENTMSG: "PTRACE_GETEVENTMSG",
+ linux.PTRACE_GETSIGINFO: "PTRACE_GETSIGINFO",
+ linux.PTRACE_SETSIGINFO: "PTRACE_SETSIGINFO",
+ linux.PTRACE_GETREGSET: "PTRACE_GETREGSET",
+ linux.PTRACE_SETREGSET: "PTRACE_SETREGSET",
+ linux.PTRACE_SEIZE: "PTRACE_SEIZE",
+ linux.PTRACE_INTERRUPT: "PTRACE_INTERRUPT",
+ linux.PTRACE_LISTEN: "PTRACE_LISTEN",
+ linux.PTRACE_PEEKSIGINFO: "PTRACE_PEEKSIGINFO",
+ linux.PTRACE_GETSIGMASK: "PTRACE_GETSIGMASK",
+ linux.PTRACE_SETSIGMASK: "PTRACE_SETSIGMASK",
+ linux.PTRACE_GETREGS: "PTRACE_GETREGS",
+ linux.PTRACE_SETREGS: "PTRACE_SETREGS",
+ linux.PTRACE_GETFPREGS: "PTRACE_GETFPREGS",
+ linux.PTRACE_SETFPREGS: "PTRACE_SETFPREGS",
+ linux.PTRACE_GETFPXREGS: "PTRACE_GETFPXREGS",
+ linux.PTRACE_SETFPXREGS: "PTRACE_SETFPXREGS",
+ linux.PTRACE_OLDSETOPTIONS: "PTRACE_OLDSETOPTIONS",
+ linux.PTRACE_GET_THREAD_AREA: "PTRACE_GET_THREAD_AREA",
+ linux.PTRACE_SET_THREAD_AREA: "PTRACE_SET_THREAD_AREA",
+ linux.PTRACE_ARCH_PRCTL: "PTRACE_ARCH_PRCTL",
+ linux.PTRACE_SYSEMU: "PTRACE_SYSEMU",
+ linux.PTRACE_SYSEMU_SINGLESTEP: "PTRACE_SYSEMU_SINGLESTEP",
+ linux.PTRACE_SINGLEBLOCK: "PTRACE_SINGLEBLOCK",
+}
diff --git a/pkg/sentry/strace/signal.go b/pkg/sentry/strace/signal.go
new file mode 100644
index 000000000..f82460e1c
--- /dev/null
+++ b/pkg/sentry/strace/signal.go
@@ -0,0 +1,148 @@
+// Copyright 2018 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 strace
+
+import (
+ "fmt"
+ "strings"
+
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
+)
+
+// signalNames contains the names of all named signals.
+var signalNames = abi.ValueSet{
+ uint64(linux.SIGABRT): "SIGABRT",
+ uint64(linux.SIGALRM): "SIGALRM",
+ uint64(linux.SIGBUS): "SIGBUS",
+ uint64(linux.SIGCHLD): "SIGCHLD",
+ uint64(linux.SIGCONT): "SIGCONT",
+ uint64(linux.SIGFPE): "SIGFPE",
+ uint64(linux.SIGHUP): "SIGHUP",
+ uint64(linux.SIGILL): "SIGILL",
+ uint64(linux.SIGINT): "SIGINT",
+ uint64(linux.SIGIO): "SIGIO",
+ uint64(linux.SIGKILL): "SIGKILL",
+ uint64(linux.SIGPIPE): "SIGPIPE",
+ uint64(linux.SIGPROF): "SIGPROF",
+ uint64(linux.SIGPWR): "SIGPWR",
+ uint64(linux.SIGQUIT): "SIGQUIT",
+ uint64(linux.SIGSEGV): "SIGSEGV",
+ uint64(linux.SIGSTKFLT): "SIGSTKFLT",
+ uint64(linux.SIGSTOP): "SIGSTOP",
+ uint64(linux.SIGSYS): "SIGSYS",
+ uint64(linux.SIGTERM): "SIGTERM",
+ uint64(linux.SIGTRAP): "SIGTRAP",
+ uint64(linux.SIGTSTP): "SIGTSTP",
+ uint64(linux.SIGTTIN): "SIGTTIN",
+ uint64(linux.SIGTTOU): "SIGTTOU",
+ uint64(linux.SIGURG): "SIGURG",
+ uint64(linux.SIGUSR1): "SIGUSR1",
+ uint64(linux.SIGUSR2): "SIGUSR2",
+ uint64(linux.SIGVTALRM): "SIGVTALRM",
+ uint64(linux.SIGWINCH): "SIGWINCH",
+ uint64(linux.SIGXCPU): "SIGXCPU",
+ uint64(linux.SIGXFSZ): "SIGXFSZ",
+}
+
+var signalMaskActions = abi.ValueSet{
+ linux.SIG_BLOCK: "SIG_BLOCK",
+ linux.SIG_UNBLOCK: "SIG_UNBLOCK",
+ linux.SIG_SETMASK: "SIG_SETMASK",
+}
+
+var sigActionFlags = abi.FlagSet{
+ {
+ Flag: linux.SA_NOCLDSTOP,
+ Name: "SA_NOCLDSTOP",
+ },
+ {
+ Flag: linux.SA_NOCLDWAIT,
+ Name: "SA_NOCLDWAIT",
+ },
+ {
+ Flag: linux.SA_SIGINFO,
+ Name: "SA_SIGINFO",
+ },
+ {
+ Flag: linux.SA_RESTORER,
+ Name: "SA_RESTORER",
+ },
+ {
+ Flag: linux.SA_ONSTACK,
+ Name: "SA_ONSTACK",
+ },
+ {
+ Flag: linux.SA_RESTART,
+ Name: "SA_RESTART",
+ },
+ {
+ Flag: linux.SA_NODEFER,
+ Name: "SA_NODEFER",
+ },
+ {
+ Flag: linux.SA_RESETHAND,
+ Name: "SA_RESETHAND",
+ },
+}
+
+func sigSet(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ var b [linux.SignalSetSize]byte
+ if _, err := t.CopyInBytes(addr, b[:]); err != nil {
+ return fmt.Sprintf("%#x (error copying sigset: %v)", addr, err)
+ }
+
+ set := linux.SignalSet(usermem.ByteOrder.Uint64(b[:]))
+
+ return fmt.Sprintf("%#x %s", addr, formatSigSet(set))
+}
+
+func formatSigSet(set linux.SignalSet) string {
+ var signals []string
+ linux.ForEachSignal(set, func(sig linux.Signal) {
+ signals = append(signals, signalNames.ParseDecimal(uint64(sig)))
+ })
+
+ return fmt.Sprintf("[%v]", strings.Join(signals, " "))
+}
+
+func sigAction(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ sa, err := t.CopyInSignalAct(addr)
+ if err != nil {
+ return fmt.Sprintf("%#x (error copying sigaction: %v)", addr, err)
+ }
+
+ var handler string
+ switch sa.Handler {
+ case linux.SIG_IGN:
+ handler = "SIG_IGN"
+ case linux.SIG_DFL:
+ handler = "SIG_DFL"
+ default:
+ handler = fmt.Sprintf("%#x", sa.Handler)
+ }
+
+ return fmt.Sprintf("%#x {Handler: %s, Flags: %s, Restorer: %#x, Mask: %s}", addr, handler, sigActionFlags.Parse(sa.Flags), sa.Restorer, formatSigSet(sa.Mask))
+}
diff --git a/pkg/sentry/strace/socket.go b/pkg/sentry/strace/socket.go
new file mode 100644
index 000000000..dbe53b9a2
--- /dev/null
+++ b/pkg/sentry/strace/socket.go
@@ -0,0 +1,412 @@
+// Copyright 2018 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 strace
+
+import (
+ "fmt"
+ "strings"
+
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
+ "gvisor.googlesource.com/gvisor/pkg/binary"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/socket/control"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/socket/epsocket"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/socket/netlink"
+ slinux "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/linux"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
+)
+
+// SocketFamily are the possible socket(2) families.
+var SocketFamily = abi.ValueSet{
+ linux.AF_UNSPEC: "AF_UNSPEC",
+ linux.AF_UNIX: "AF_UNIX",
+ linux.AF_INET: "AF_INET",
+ linux.AF_AX25: "AF_AX25",
+ linux.AF_IPX: "AF_IPX",
+ linux.AF_APPLETALK: "AF_APPLETALK",
+ linux.AF_NETROM: "AF_NETROM",
+ linux.AF_BRIDGE: "AF_BRIDGE",
+ linux.AF_ATMPVC: "AF_ATMPVC",
+ linux.AF_X25: "AF_X25",
+ linux.AF_INET6: "AF_INET6",
+ linux.AF_ROSE: "AF_ROSE",
+ linux.AF_DECnet: "AF_DECnet",
+ linux.AF_NETBEUI: "AF_NETBEUI",
+ linux.AF_SECURITY: "AF_SECURITY",
+ linux.AF_KEY: "AF_KEY",
+ linux.AF_NETLINK: "AF_NETLINK",
+ linux.AF_PACKET: "AF_PACKET",
+ linux.AF_ASH: "AF_ASH",
+ linux.AF_ECONET: "AF_ECONET",
+ linux.AF_ATMSVC: "AF_ATMSVC",
+ linux.AF_RDS: "AF_RDS",
+ linux.AF_SNA: "AF_SNA",
+ linux.AF_IRDA: "AF_IRDA",
+ linux.AF_PPPOX: "AF_PPPOX",
+ linux.AF_WANPIPE: "AF_WANPIPE",
+ linux.AF_LLC: "AF_LLC",
+ linux.AF_IB: "AF_IB",
+ linux.AF_MPLS: "AF_MPLS",
+ linux.AF_CAN: "AF_CAN",
+ linux.AF_TIPC: "AF_TIPC",
+ linux.AF_BLUETOOTH: "AF_BLUETOOTH",
+ linux.AF_IUCV: "AF_IUCV",
+ linux.AF_RXRPC: "AF_RXRPC",
+ linux.AF_ISDN: "AF_ISDN",
+ linux.AF_PHONET: "AF_PHONET",
+ linux.AF_IEEE802154: "AF_IEEE802154",
+ linux.AF_CAIF: "AF_CAIF",
+ linux.AF_ALG: "AF_ALG",
+ linux.AF_NFC: "AF_NFC",
+ linux.AF_VSOCK: "AF_VSOCK",
+}
+
+// SocketType are the possible socket(2) types.
+var SocketType = abi.ValueSet{
+ linux.SOCK_STREAM: "SOCK_STREAM",
+ linux.SOCK_DGRAM: "SOCK_DGRAM",
+ linux.SOCK_RAW: "SOCK_RAW",
+ linux.SOCK_RDM: "SOCK_RDM",
+ linux.SOCK_SEQPACKET: "SOCK_SEQPACKET",
+ linux.SOCK_DCCP: "SOCK_DCCP",
+ linux.SOCK_PACKET: "SOCK_PACKET",
+}
+
+// SocketFlagSet are the possible socket(2) flags.
+var SocketFlagSet = abi.FlagSet{
+ {
+ Flag: linux.SOCK_CLOEXEC,
+ Name: "SOCK_CLOEXEC",
+ },
+ {
+ Flag: linux.SOCK_NONBLOCK,
+ Name: "SOCK_NONBLOCK",
+ },
+}
+
+// ipProtocol are the possible socket(2) types for INET and INET6 sockets.
+var ipProtocol = abi.ValueSet{
+ linux.IPPROTO_IP: "IPPROTO_IP",
+ linux.IPPROTO_ICMP: "IPPROTO_ICMP",
+ linux.IPPROTO_IGMP: "IPPROTO_IGMP",
+ linux.IPPROTO_IPIP: "IPPROTO_IPIP",
+ linux.IPPROTO_TCP: "IPPROTO_TCP",
+ linux.IPPROTO_EGP: "IPPROTO_EGP",
+ linux.IPPROTO_PUP: "IPPROTO_PUP",
+ linux.IPPROTO_UDP: "IPPROTO_UDP",
+ linux.IPPROTO_IDP: "IPPROTO_IDP",
+ linux.IPPROTO_TP: "IPPROTO_TP",
+ linux.IPPROTO_DCCP: "IPPROTO_DCCP",
+ linux.IPPROTO_IPV6: "IPPROTO_IPV6",
+ linux.IPPROTO_RSVP: "IPPROTO_RSVP",
+ linux.IPPROTO_GRE: "IPPROTO_GRE",
+ linux.IPPROTO_ESP: "IPPROTO_ESP",
+ linux.IPPROTO_AH: "IPPROTO_AH",
+ linux.IPPROTO_MTP: "IPPROTO_MTP",
+ linux.IPPROTO_BEETPH: "IPPROTO_BEETPH",
+ linux.IPPROTO_ENCAP: "IPPROTO_ENCAP",
+ linux.IPPROTO_PIM: "IPPROTO_PIM",
+ linux.IPPROTO_COMP: "IPPROTO_COMP",
+ linux.IPPROTO_SCTP: "IPPROTO_SCTP",
+ linux.IPPROTO_UDPLITE: "IPPROTO_UDPLITE",
+ linux.IPPROTO_MPLS: "IPPROTO_MPLS",
+ linux.IPPROTO_RAW: "IPPROTO_RAW",
+}
+
+// SocketProtocol are the possible socket(2) protocols for each protocol family.
+var SocketProtocol = map[int32]abi.ValueSet{
+ linux.AF_INET: ipProtocol,
+ linux.AF_INET6: ipProtocol,
+ linux.AF_NETLINK: {
+ linux.NETLINK_ROUTE: "NETLINK_ROUTE",
+ linux.NETLINK_UNUSED: "NETLINK_UNUSED",
+ linux.NETLINK_USERSOCK: "NETLINK_USERSOCK",
+ linux.NETLINK_FIREWALL: "NETLINK_FIREWALL",
+ linux.NETLINK_SOCK_DIAG: "NETLINK_SOCK_DIAG",
+ linux.NETLINK_NFLOG: "NETLINK_NFLOG",
+ linux.NETLINK_XFRM: "NETLINK_XFRM",
+ linux.NETLINK_SELINUX: "NETLINK_SELINUX",
+ linux.NETLINK_ISCSI: "NETLINK_ISCSI",
+ linux.NETLINK_AUDIT: "NETLINK_AUDIT",
+ linux.NETLINK_FIB_LOOKUP: "NETLINK_FIB_LOOKUP",
+ linux.NETLINK_CONNECTOR: "NETLINK_CONNECTOR",
+ linux.NETLINK_NETFILTER: "NETLINK_NETFILTER",
+ linux.NETLINK_IP6_FW: "NETLINK_IP6_FW",
+ linux.NETLINK_DNRTMSG: "NETLINK_DNRTMSG",
+ linux.NETLINK_KOBJECT_UEVENT: "NETLINK_KOBJECT_UEVENT",
+ linux.NETLINK_GENERIC: "NETLINK_GENERIC",
+ linux.NETLINK_SCSITRANSPORT: "NETLINK_SCSITRANSPORT",
+ linux.NETLINK_ECRYPTFS: "NETLINK_ECRYPTFS",
+ linux.NETLINK_RDMA: "NETLINK_RDMA",
+ linux.NETLINK_CRYPTO: "NETLINK_CRYPTO",
+ },
+}
+
+var controlMessageType = map[int32]string{
+ linux.SCM_RIGHTS: "SCM_RIGHTS",
+ linux.SCM_CREDENTIALS: "SCM_CREDENTIALS",
+ linux.SO_TIMESTAMP: "SO_TIMESTAMP",
+}
+
+func cmsghdr(t *kernel.Task, addr usermem.Addr, length uint64, maxBytes uint64) string {
+ if length > maxBytes {
+ return fmt.Sprintf("%#x (error decoding control: invalid length (%d))", addr, length)
+ }
+
+ buf := make([]byte, length)
+ if _, err := t.CopyIn(addr, &buf); err != nil {
+ return fmt.Sprintf("%#x (error decoding control: %v)", addr, err)
+ }
+
+ var strs []string
+
+ for i := 0; i < len(buf); {
+ if i+linux.SizeOfControlMessageHeader > len(buf) {
+ strs = append(strs, "{invalid control message (too short)}")
+ break
+ }
+
+ var h linux.ControlMessageHeader
+ binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageHeader], usermem.ByteOrder, &h)
+
+ var skipData bool
+ level := "SOL_SOCKET"
+ if h.Level != linux.SOL_SOCKET {
+ skipData = true
+ level = fmt.Sprint(h.Level)
+ }
+
+ typ, ok := controlMessageType[h.Type]
+ if !ok {
+ skipData = true
+ typ = fmt.Sprint(h.Type)
+ }
+
+ if h.Length > uint64(len(buf)-i) {
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, content extends beyond buffer}",
+ level,
+ typ,
+ h.Length,
+ ))
+ break
+ }
+
+ i += linux.SizeOfControlMessageHeader
+ width := t.Arch().Width()
+ length := int(h.Length) - linux.SizeOfControlMessageHeader
+
+ if skipData {
+ strs = append(strs, fmt.Sprintf("{level=%s, type=%s, length=%d}", level, typ, h.Length))
+ i += control.AlignUp(length, width)
+ continue
+ }
+
+ switch h.Type {
+ case linux.SCM_RIGHTS:
+ rightsSize := control.AlignDown(length, linux.SizeOfControlMessageRight)
+
+ numRights := rightsSize / linux.SizeOfControlMessageRight
+ fds := make(linux.ControlMessageRights, numRights)
+ binary.Unmarshal(buf[i:i+rightsSize], usermem.ByteOrder, &fds)
+
+ rights := make([]string, 0, len(fds))
+ for _, fd := range fds {
+ rights = append(rights, fmt.Sprint(fd))
+ }
+
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, content: %s}",
+ level,
+ typ,
+ h.Length,
+ strings.Join(rights, ","),
+ ))
+
+ case linux.SCM_CREDENTIALS:
+ if length < linux.SizeOfControlMessageCredentials {
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, content too short}",
+ level,
+ typ,
+ h.Length,
+ ))
+ break
+ }
+
+ var creds linux.ControlMessageCredentials
+ binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageCredentials], usermem.ByteOrder, &creds)
+
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, pid: %d, uid: %d, gid: %d}",
+ level,
+ typ,
+ h.Length,
+ creds.PID,
+ creds.UID,
+ creds.GID,
+ ))
+
+ case linux.SO_TIMESTAMP:
+ if length < linux.SizeOfTimeval {
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, content too short}",
+ level,
+ typ,
+ h.Length,
+ ))
+ break
+ }
+
+ var tv linux.Timeval
+ binary.Unmarshal(buf[i:i+linux.SizeOfTimeval], usermem.ByteOrder, &tv)
+
+ strs = append(strs, fmt.Sprintf(
+ "{level=%s, type=%s, length=%d, Sec: %d, Usec: %d}",
+ level,
+ typ,
+ h.Length,
+ tv.Sec,
+ tv.Usec,
+ ))
+
+ default:
+ panic("unreachable")
+ }
+ i += control.AlignUp(length, width)
+ }
+
+ return fmt.Sprintf("%#x %s", addr, strings.Join(strs, ", "))
+}
+
+func msghdr(t *kernel.Task, addr usermem.Addr, printContent bool, maxBytes uint64) string {
+ var msg slinux.MessageHeader64
+ if err := slinux.CopyInMessageHeader64(t, addr, &msg); err != nil {
+ return fmt.Sprintf("%#x (error decoding msghdr: %v)", addr, err)
+ }
+ s := fmt.Sprintf(
+ "%#x {name=%#x, namelen=%d, iovecs=%s",
+ addr,
+ msg.Name,
+ msg.NameLen,
+ iovecs(t, usermem.Addr(msg.Iov), int(msg.IovLen), printContent, maxBytes),
+ )
+ if printContent {
+ s = fmt.Sprintf("%s, control={%s}", s, cmsghdr(t, usermem.Addr(msg.Control), msg.ControlLen, maxBytes))
+ } else {
+ s = fmt.Sprintf("%s, control=%#x, control_len=%d", s, msg.Control, msg.ControlLen)
+ }
+ return fmt.Sprintf("%s, flags=%d}", s, msg.Flags)
+}
+
+func sockAddr(t *kernel.Task, addr usermem.Addr, length uint32) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ b, err := slinux.CaptureAddress(t, addr, length)
+ if err != nil {
+ return fmt.Sprintf("%#x {error reading address: %v}", addr, err)
+ }
+
+ // Extract address family.
+ if len(b) < 2 {
+ return fmt.Sprintf("%#x {address too short: %d bytes}", addr, len(b))
+ }
+ family := usermem.ByteOrder.Uint16(b)
+
+ familyStr := SocketFamily.Parse(uint64(family))
+
+ switch family {
+ case linux.AF_INET, linux.AF_INET6, linux.AF_UNIX:
+ fa, err := epsocket.GetAddress(int(family), b)
+ if err != nil {
+ return fmt.Sprintf("%#x {Family: %s, error extracting address: %v}", addr, familyStr, err)
+ }
+
+ if family == linux.AF_UNIX {
+ return fmt.Sprintf("%#x {Family: %s, Addr: %q}", addr, familyStr, string(fa.Addr))
+ }
+
+ return fmt.Sprintf("%#x {Family: %s, Addr: %v, Port: %d}", addr, familyStr, fa.Addr, fa.Port)
+ case linux.AF_NETLINK:
+ sa, err := netlink.ExtractSockAddr(b)
+ if err != nil {
+ return fmt.Sprintf("%#x {Family: %s, error extracting address: %v}", addr, familyStr, err)
+ }
+ return fmt.Sprintf("%#x {Family: %s, PortID: %d, Groups: %d}", addr, familyStr, sa.PortID, sa.Groups)
+ default:
+ return fmt.Sprintf("%#x {Family: %s, family addr format unknown}", addr, familyStr)
+ }
+}
+
+func postSockAddr(t *kernel.Task, addr usermem.Addr, lengthPtr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ if lengthPtr == 0 {
+ return fmt.Sprintf("%#x {length null}", addr)
+ }
+
+ l, err := copySockLen(t, lengthPtr)
+ if err != nil {
+ return fmt.Sprintf("%#x {error reading length: %v}", addr, err)
+ }
+
+ return sockAddr(t, addr, l)
+}
+
+func copySockLen(t *kernel.Task, addr usermem.Addr) (uint32, error) {
+ // socklen_t is 32-bits.
+ var l uint32
+ _, err := t.CopyIn(addr, &l)
+ return l, err
+}
+
+func sockLenPointer(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+ l, err := copySockLen(t, addr)
+ if err != nil {
+ return fmt.Sprintf("%#x {error reading length: %v}", addr, err)
+ }
+ return fmt.Sprintf("%#x {length=%v}", addr, l)
+}
+
+func sockType(stype int32) string {
+ s := SocketType.Parse(uint64(stype & linux.SOCK_TYPE_MASK))
+ if flags := SocketFlagSet.Parse(uint64(stype &^ linux.SOCK_TYPE_MASK)); flags != "" {
+ s += "|" + flags
+ }
+ return s
+}
+
+func sockProtocol(family, protocol int32) string {
+ protocols, ok := SocketProtocol[family]
+ if !ok {
+ return fmt.Sprintf("%#x", protocol)
+ }
+ return protocols.Parse(uint64(protocol))
+}
+
+func sockFlags(flags int32) string {
+ if flags == 0 {
+ return "0"
+ }
+ return SocketFlagSet.Parse(uint64(flags))
+}
diff --git a/pkg/sentry/strace/strace.go b/pkg/sentry/strace/strace.go
new file mode 100644
index 000000000..f4c1be4ce
--- /dev/null
+++ b/pkg/sentry/strace/strace.go
@@ -0,0 +1,820 @@
+// Copyright 2018 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 strace implements the logic to print out the input and the return value
+// of each traced syscall.
+package strace
+
+import (
+ "encoding/binary"
+ "fmt"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
+ "gvisor.googlesource.com/gvisor/pkg/bits"
+ "gvisor.googlesource.com/gvisor/pkg/eventchannel"
+ "gvisor.googlesource.com/gvisor/pkg/seccomp"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/arch"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
+ pb "gvisor.googlesource.com/gvisor/pkg/sentry/strace/strace_go_proto"
+ slinux "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/linux"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
+)
+
+// DefaultLogMaximumSize is the default LogMaximumSize.
+const DefaultLogMaximumSize = 1024
+
+// LogMaximumSize determines the maximum display size for data blobs (read,
+// write, etc.).
+var LogMaximumSize uint = DefaultLogMaximumSize
+
+// EventMaximumSize determines the maximum size for data blobs (read, write,
+// etc.) sent over the event channel. Default is 0 because most clients cannot
+// do anything useful with binary text dump of byte array arguments.
+var EventMaximumSize uint
+
+// ItimerTypes are the possible itimer types.
+var ItimerTypes = abi.ValueSet{
+ linux.ITIMER_REAL: "ITIMER_REAL",
+ linux.ITIMER_VIRTUAL: "ITIMER_VIRTUAL",
+ linux.ITIMER_PROF: "ITIMER_PROF",
+}
+
+func iovecs(t *kernel.Task, addr usermem.Addr, iovcnt int, printContent bool, maxBytes uint64) string {
+ if iovcnt < 0 || iovcnt > linux.UIO_MAXIOV {
+ return fmt.Sprintf("%#x (error decoding iovecs: invalid iovcnt)", addr)
+ }
+ ars, err := t.CopyInIovecs(addr, iovcnt)
+ if err != nil {
+ return fmt.Sprintf("%#x (error decoding iovecs: %v)", addr, err)
+ }
+
+ var totalBytes uint64
+ var truncated bool
+ iovs := make([]string, iovcnt)
+ for i := 0; !ars.IsEmpty(); i, ars = i+1, ars.Tail() {
+ ar := ars.Head()
+ if ar.Length() == 0 || !printContent {
+ iovs[i] = fmt.Sprintf("{base=%#x, len=%d}", ar.Start, ar.Length())
+ continue
+ }
+
+ size := uint64(ar.Length())
+ if truncated || totalBytes+size > maxBytes {
+ truncated = true
+ size = maxBytes - totalBytes
+ } else {
+ totalBytes += uint64(ar.Length())
+ }
+
+ b := make([]byte, size)
+ amt, err := t.CopyIn(ar.Start, b)
+ if err != nil {
+ iovs[i] = fmt.Sprintf("{base=%#x, len=%d, %q..., error decoding string: %v}", ar.Start, ar.Length(), b[:amt], err)
+ continue
+ }
+
+ dot := ""
+ if truncated {
+ // Indicate truncation.
+ dot = "..."
+ }
+ iovs[i] = fmt.Sprintf("{base=%#x, len=%d, %q%s}", ar.Start, ar.Length(), b[:amt], dot)
+ }
+
+ return fmt.Sprintf("%#x %s", addr, strings.Join(iovs, ", "))
+}
+
+func dump(t *kernel.Task, addr usermem.Addr, size uint, maximumBlobSize uint) string {
+ origSize := size
+ if size > maximumBlobSize {
+ size = maximumBlobSize
+ }
+ if size == 0 {
+ return ""
+ }
+
+ b := make([]byte, size)
+ amt, err := t.CopyIn(addr, b)
+ if err != nil {
+ return fmt.Sprintf("%#x (error decoding string: %s)", addr, err)
+ }
+
+ dot := ""
+ if uint(amt) < origSize {
+ // ... if we truncated the dump.
+ dot = "..."
+ }
+
+ return fmt.Sprintf("%#x %q%s", addr, b[:amt], dot)
+}
+
+func path(t *kernel.Task, addr usermem.Addr) string {
+ path, err := t.CopyInString(addr, linux.PATH_MAX)
+ if err != nil {
+ return fmt.Sprintf("%#x (error decoding path: %s)", addr, err)
+ }
+ return fmt.Sprintf("%#x %s", addr, path)
+}
+
+func fd(t *kernel.Task, fd kdefs.FD) string {
+ root := t.FSContext().RootDirectory()
+ if root != nil {
+ defer root.DecRef()
+ }
+
+ if fd == linux.AT_FDCWD {
+ wd := t.FSContext().WorkingDirectory()
+ var name string
+ if wd != nil {
+ defer wd.DecRef()
+ name, _ = wd.FullName(root)
+ } else {
+ name = "(unknown cwd)"
+ }
+ return fmt.Sprintf("AT_FDCWD %s", name)
+ }
+
+ file := t.FDMap().GetFile(fd)
+ if file == nil {
+ // Cast FD to uint64 to avoid printing negative hex.
+ return fmt.Sprintf("%#x (bad FD)", uint64(fd))
+ }
+ defer file.DecRef()
+
+ name, _ := file.Dirent.FullName(root)
+ return fmt.Sprintf("%#x %s", fd, name)
+}
+
+func fdpair(t *kernel.Task, addr usermem.Addr) string {
+ var fds [2]int32
+ _, err := t.CopyIn(addr, &fds)
+ if err != nil {
+ return fmt.Sprintf("%#x (error decoding fds: %s)", addr, err)
+ }
+
+ return fmt.Sprintf("%#x [%d %d]", addr, fds[0], fds[1])
+}
+
+func uname(t *kernel.Task, addr usermem.Addr) string {
+ var u linux.UtsName
+ if _, err := t.CopyIn(addr, &u); err != nil {
+ return fmt.Sprintf("%#x (error decoding utsname: %s)", addr, err)
+ }
+
+ return fmt.Sprintf("%#x %s", addr, u)
+}
+
+func utimensTimespec(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ var tim linux.Timespec
+ if _, err := t.CopyIn(addr, &tim); err != nil {
+ return fmt.Sprintf("%#x (error decoding timespec: %s)", addr, err)
+ }
+
+ var ns string
+ switch tim.Nsec {
+ case linux.UTIME_NOW:
+ ns = "UTIME_NOW"
+ case linux.UTIME_OMIT:
+ ns = "UTIME_OMIT"
+ default:
+ ns = fmt.Sprintf("%v", tim.Nsec)
+ }
+ return fmt.Sprintf("%#x {sec=%v nsec=%s}", addr, tim.Sec, ns)
+}
+
+func timespec(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ var tim linux.Timespec
+ if _, err := t.CopyIn(addr, &tim); err != nil {
+ return fmt.Sprintf("%#x (error decoding timespec: %s)", addr, err)
+ }
+ return fmt.Sprintf("%#x {sec=%v nsec=%v}", addr, tim.Sec, tim.Nsec)
+}
+
+func timeval(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ var tim linux.Timeval
+ if _, err := t.CopyIn(addr, &tim); err != nil {
+ return fmt.Sprintf("%#x (error decoding timeval: %s)", addr, err)
+ }
+
+ return fmt.Sprintf("%#x {sec=%v usec=%v}", addr, tim.Sec, tim.Usec)
+}
+
+func utimbuf(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ var utim syscall.Utimbuf
+ if _, err := t.CopyIn(addr, &utim); err != nil {
+ return fmt.Sprintf("%#x (error decoding utimbuf: %s)", addr, err)
+ }
+
+ return fmt.Sprintf("%#x {actime=%v, modtime=%v}", addr, utim.Actime, utim.Modtime)
+}
+
+func stat(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ var stat linux.Stat
+ if _, err := t.CopyIn(addr, &stat); err != nil {
+ return fmt.Sprintf("%#x (error decoding stat: %s)", addr, err)
+ }
+ return fmt.Sprintf("%#x {dev=%d, ino=%d, mode=%s, nlink=%d, uid=%d, gid=%d, rdev=%d, size=%d, blksize=%d, blocks=%d, atime=%s, mtime=%s, ctime=%s}", addr, stat.Dev, stat.Ino, linux.FileMode(stat.Mode), stat.Nlink, stat.UID, stat.GID, stat.Rdev, stat.Size, stat.Blksize, stat.Blocks, time.Unix(stat.ATime.Sec, stat.ATime.Nsec), time.Unix(stat.MTime.Sec, stat.MTime.Nsec), time.Unix(stat.CTime.Sec, stat.CTime.Nsec))
+}
+
+func itimerval(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ interval := timeval(t, addr)
+ value := timeval(t, addr+usermem.Addr(binary.Size(linux.Timeval{})))
+ return fmt.Sprintf("%#x {interval=%s, value=%s}", addr, interval, value)
+}
+
+func itimerspec(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ interval := timespec(t, addr)
+ value := timespec(t, addr+usermem.Addr(binary.Size(linux.Timespec{})))
+ return fmt.Sprintf("%#x {interval=%s, value=%s}", addr, interval, value)
+}
+
+func stringVector(t *kernel.Task, addr usermem.Addr) string {
+ vec, err := t.CopyInVector(addr, slinux.ExecMaxElemSize, slinux.ExecMaxTotalSize)
+ if err != nil {
+ return fmt.Sprintf("%#x {error copying vector: %v}", addr, err)
+ }
+ s := fmt.Sprintf("%#x [", addr)
+ for i, v := range vec {
+ if i != 0 {
+ s += ", "
+ }
+ s += fmt.Sprintf("%q", v)
+ }
+ s += "]"
+ return s
+}
+
+func rusage(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ var ru linux.Rusage
+ if _, err := t.CopyIn(addr, &ru); err != nil {
+ return fmt.Sprintf("%#x (error decoding rusage: %s)", addr, err)
+ }
+ return fmt.Sprintf("%#x %+v", addr, ru)
+}
+
+func capHeader(t *kernel.Task, addr usermem.Addr) string {
+ if addr == 0 {
+ return "null"
+ }
+
+ var hdr linux.CapUserHeader
+ if _, err := t.CopyIn(addr, &hdr); err != nil {
+ return fmt.Sprintf("%#x (error decoding header: %s)", addr, err)
+ }
+
+ var version string
+ switch hdr.Version {
+ case linux.LINUX_CAPABILITY_VERSION_1:
+ version = "1"
+ case linux.LINUX_CAPABILITY_VERSION_2:
+ version = "2"
+ case linux.LINUX_CAPABILITY_VERSION_3:
+ version = "3"
+ default:
+ version = strconv.FormatUint(uint64(hdr.Version), 16)
+ }
+
+ return fmt.Sprintf("%#x {Version: %s, Pid: %d}", addr, version, hdr.Pid)
+}
+
+func capData(t *kernel.Task, hdrAddr, dataAddr usermem.Addr) string {
+ if dataAddr == 0 {
+ return "null"
+ }
+
+ var hdr linux.CapUserHeader
+ if _, err := t.CopyIn(hdrAddr, &hdr); err != nil {
+ return fmt.Sprintf("%#x (error decoding header: %v)", dataAddr, err)
+ }
+
+ var p, i, e uint64
+
+ switch hdr.Version {
+ case linux.LINUX_CAPABILITY_VERSION_1:
+ var data linux.CapUserData
+ if _, err := t.CopyIn(dataAddr, &data); err != nil {
+ return fmt.Sprintf("%#x (error decoding data: %v)", dataAddr, err)
+ }
+ p = uint64(data.Permitted)
+ i = uint64(data.Inheritable)
+ e = uint64(data.Effective)
+ case linux.LINUX_CAPABILITY_VERSION_2, linux.LINUX_CAPABILITY_VERSION_3:
+ var data [2]linux.CapUserData
+ if _, err := t.CopyIn(dataAddr, &data); err != nil {
+ return fmt.Sprintf("%#x (error decoding data: %v)", dataAddr, err)
+ }
+ p = uint64(data[0].Permitted) | (uint64(data[1].Permitted) << 32)
+ i = uint64(data[0].Inheritable) | (uint64(data[1].Inheritable) << 32)
+ e = uint64(data[0].Effective) | (uint64(data[1].Effective) << 32)
+ default:
+ return fmt.Sprintf("%#x (unknown version %d)", dataAddr, hdr.Version)
+ }
+
+ return fmt.Sprintf("%#x {Permitted: %s, Inheritable: %s, Effective: %s}", dataAddr, CapabilityBitset.Parse(p), CapabilityBitset.Parse(i), CapabilityBitset.Parse(e))
+}
+
+// pre fills in the pre-execution arguments for a system call. If an argument
+// cannot be interpreted before the system call is executed, then a hex value
+// will be used. Note that a full output slice will always be provided, that is
+// len(return) == len(args).
+func (i *SyscallInfo) pre(t *kernel.Task, args arch.SyscallArguments, maximumBlobSize uint) []string {
+ var output []string
+
+ for arg := range args {
+ if arg >= len(i.format) {
+ break
+ }
+ switch i.format[arg] {
+ case FD:
+ output = append(output, fd(t, kdefs.FD(args[arg].Int())))
+ case WriteBuffer:
+ output = append(output, dump(t, args[arg].Pointer(), args[arg+1].SizeT(), maximumBlobSize))
+ case WriteIOVec:
+ output = append(output, iovecs(t, args[arg].Pointer(), int(args[arg+1].Int()), true /* content */, uint64(maximumBlobSize)))
+ case IOVec:
+ output = append(output, iovecs(t, args[arg].Pointer(), int(args[arg+1].Int()), false /* content */, uint64(maximumBlobSize)))
+ case SendMsgHdr:
+ output = append(output, msghdr(t, args[arg].Pointer(), true /* content */, uint64(maximumBlobSize)))
+ case RecvMsgHdr:
+ output = append(output, msghdr(t, args[arg].Pointer(), false /* content */, uint64(maximumBlobSize)))
+ case Path:
+ output = append(output, path(t, args[arg].Pointer()))
+ case ExecveStringVector:
+ output = append(output, stringVector(t, args[arg].Pointer()))
+ case SockAddr:
+ output = append(output, sockAddr(t, args[arg].Pointer(), uint32(args[arg+1].Uint64())))
+ case SockLen:
+ output = append(output, sockLenPointer(t, args[arg].Pointer()))
+ case SockFamily:
+ output = append(output, SocketFamily.Parse(uint64(args[arg].Int())))
+ case SockType:
+ output = append(output, sockType(args[arg].Int()))
+ case SockProtocol:
+ output = append(output, sockProtocol(args[arg-2].Int(), args[arg].Int()))
+ case SockFlags:
+ output = append(output, sockFlags(args[arg].Int()))
+ case Timespec:
+ output = append(output, timespec(t, args[arg].Pointer()))
+ case UTimeTimespec:
+ output = append(output, utimensTimespec(t, args[arg].Pointer()))
+ case ItimerVal:
+ output = append(output, itimerval(t, args[arg].Pointer()))
+ case ItimerSpec:
+ output = append(output, itimerspec(t, args[arg].Pointer()))
+ case Timeval:
+ output = append(output, timeval(t, args[arg].Pointer()))
+ case Utimbuf:
+ output = append(output, utimbuf(t, args[arg].Pointer()))
+ case CloneFlags:
+ output = append(output, CloneFlagSet.Parse(uint64(args[arg].Uint())))
+ case OpenFlags:
+ output = append(output, open(uint64(args[arg].Uint())))
+ case Mode:
+ output = append(output, linux.FileMode(args[arg].ModeT()).String())
+ case FutexOp:
+ output = append(output, futex(uint64(args[arg].Uint())))
+ case PtraceRequest:
+ output = append(output, PtraceRequestSet.Parse(args[arg].Uint64()))
+ case ItimerType:
+ output = append(output, ItimerTypes.Parse(uint64(args[arg].Int())))
+ case Signal:
+ output = append(output, signalNames.ParseDecimal(args[arg].Uint64()))
+ case SignalMaskAction:
+ output = append(output, signalMaskActions.Parse(uint64(args[arg].Int())))
+ case SigSet:
+ output = append(output, sigSet(t, args[arg].Pointer()))
+ case SigAction:
+ output = append(output, sigAction(t, args[arg].Pointer()))
+ case CapHeader:
+ output = append(output, capHeader(t, args[arg].Pointer()))
+ case CapData:
+ output = append(output, capData(t, args[arg-1].Pointer(), args[arg].Pointer()))
+ case PollFDs:
+ output = append(output, pollFDs(t, args[arg].Pointer(), uint(args[arg+1].Uint()), false))
+ case Oct:
+ output = append(output, "0o"+strconv.FormatUint(args[arg].Uint64(), 8))
+ case Hex:
+ fallthrough
+ default:
+ output = append(output, "0x"+strconv.FormatUint(args[arg].Uint64(), 16))
+ }
+ }
+
+ return output
+}
+
+// post fills in the post-execution arguments for a system call. This modifies
+// the given output slice in place with arguments that may only be interpreted
+// after the system call has been executed.
+func (i *SyscallInfo) post(t *kernel.Task, args arch.SyscallArguments, rval uintptr, output []string, maximumBlobSize uint) {
+ for arg := range output {
+ if arg >= len(i.format) {
+ break
+ }
+ switch i.format[arg] {
+ case ReadBuffer:
+ output[arg] = dump(t, args[arg].Pointer(), uint(rval), maximumBlobSize)
+ case ReadIOVec:
+ printLength := uint64(rval)
+ if printLength > uint64(maximumBlobSize) {
+ printLength = uint64(maximumBlobSize)
+ }
+ output[arg] = iovecs(t, args[arg].Pointer(), int(args[arg+1].Int()), true /* content */, printLength)
+ case WriteIOVec, IOVec, WriteBuffer:
+ // We already have a big blast from write.
+ output[arg] = "..."
+ case SendMsgHdr:
+ output[arg] = msghdr(t, args[arg].Pointer(), false /* content */, uint64(maximumBlobSize))
+ case RecvMsgHdr:
+ output[arg] = msghdr(t, args[arg].Pointer(), true /* content */, uint64(maximumBlobSize))
+ case PostPath:
+ output[arg] = path(t, args[arg].Pointer())
+ case PipeFDs:
+ output[arg] = fdpair(t, args[arg].Pointer())
+ case Uname:
+ output[arg] = uname(t, args[arg].Pointer())
+ case Stat:
+ output[arg] = stat(t, args[arg].Pointer())
+ case PostSockAddr:
+ output[arg] = postSockAddr(t, args[arg].Pointer(), args[arg+1].Pointer())
+ case SockLen:
+ output[arg] = sockLenPointer(t, args[arg].Pointer())
+ case PostTimespec:
+ output[arg] = timespec(t, args[arg].Pointer())
+ case PostItimerVal:
+ output[arg] = itimerval(t, args[arg].Pointer())
+ case PostItimerSpec:
+ output[arg] = itimerspec(t, args[arg].Pointer())
+ case Timeval:
+ output[arg] = timeval(t, args[arg].Pointer())
+ case Rusage:
+ output[arg] = rusage(t, args[arg].Pointer())
+ case PostSigSet:
+ output[arg] = sigSet(t, args[arg].Pointer())
+ case PostSigAction:
+ output[arg] = sigAction(t, args[arg].Pointer())
+ case PostCapData:
+ output[arg] = capData(t, args[arg-1].Pointer(), args[arg].Pointer())
+ case PollFDs:
+ output[arg] = pollFDs(t, args[arg].Pointer(), uint(args[arg+1].Uint()), true)
+ }
+ }
+}
+
+// printEntry prints the given system call entry.
+func (i *SyscallInfo) printEnter(t *kernel.Task, args arch.SyscallArguments) []string {
+ output := i.pre(t, args, LogMaximumSize)
+
+ switch len(output) {
+ case 0:
+ t.Infof("%s E %s()", t.Name(), i.name)
+ case 1:
+ t.Infof("%s E %s(%s)", t.Name(), i.name,
+ output[0])
+ case 2:
+ t.Infof("%s E %s(%s, %s)", t.Name(), i.name,
+ output[0], output[1])
+ case 3:
+ t.Infof("%s E %s(%s, %s, %s)", t.Name(), i.name,
+ output[0], output[1], output[2])
+ case 4:
+ t.Infof("%s E %s(%s, %s, %s, %s)", t.Name(), i.name,
+ output[0], output[1], output[2], output[3])
+ case 5:
+ t.Infof("%s E %s(%s, %s, %s, %s, %s)", t.Name(), i.name,
+ output[0], output[1], output[2], output[3], output[4])
+ case 6:
+ t.Infof("%s E %s(%s, %s, %s, %s, %s, %s)", t.Name(), i.name,
+ output[0], output[1], output[2], output[3], output[4], output[5])
+ }
+
+ return output
+}
+
+// printExit prints the given system call exit.
+func (i *SyscallInfo) printExit(t *kernel.Task, elapsed time.Duration, output []string, args arch.SyscallArguments, retval uintptr, err error, errno int) {
+ var rval string
+ if err == nil {
+ // Fill in the output after successful execution.
+ i.post(t, args, retval, output, LogMaximumSize)
+ rval = fmt.Sprintf("%#x (%v)", retval, elapsed)
+ } else {
+ rval = fmt.Sprintf("%#x errno=%d (%s) (%v)", retval, errno, err, elapsed)
+ }
+
+ switch len(output) {
+ case 0:
+ t.Infof("%s X %s() = %s", t.Name(), i.name,
+ rval)
+ case 1:
+ t.Infof("%s X %s(%s) = %s", t.Name(), i.name,
+ output[0], rval)
+ case 2:
+ t.Infof("%s X %s(%s, %s) = %s", t.Name(), i.name,
+ output[0], output[1], rval)
+ case 3:
+ t.Infof("%s X %s(%s, %s, %s) = %s", t.Name(), i.name,
+ output[0], output[1], output[2], rval)
+ case 4:
+ t.Infof("%s X %s(%s, %s, %s, %s) = %s", t.Name(), i.name,
+ output[0], output[1], output[2], output[3], rval)
+ case 5:
+ t.Infof("%s X %s(%s, %s, %s, %s, %s) = %s", t.Name(), i.name,
+ output[0], output[1], output[2], output[3], output[4], rval)
+ case 6:
+ t.Infof("%s X %s(%s, %s, %s, %s, %s, %s) = %s", t.Name(), i.name,
+ output[0], output[1], output[2], output[3], output[4], output[5], rval)
+ }
+}
+
+// sendEnter sends the syscall enter to event log.
+func (i *SyscallInfo) sendEnter(t *kernel.Task, args arch.SyscallArguments) []string {
+ output := i.pre(t, args, EventMaximumSize)
+
+ event := pb.Strace{
+ Process: t.Name(),
+ Function: i.name,
+ Info: &pb.Strace_Enter{
+ Enter: &pb.StraceEnter{},
+ },
+ }
+ for _, arg := range output {
+ event.Args = append(event.Args, arg)
+ }
+ eventchannel.Emit(&event)
+
+ return output
+}
+
+// sendExit sends the syscall exit to event log.
+func (i *SyscallInfo) sendExit(t *kernel.Task, elapsed time.Duration, output []string, args arch.SyscallArguments, rval uintptr, err error, errno int) {
+ if err == nil {
+ // Fill in the output after successful execution.
+ i.post(t, args, rval, output, EventMaximumSize)
+ }
+
+ exit := &pb.StraceExit{
+ Return: fmt.Sprintf("%#x", rval),
+ ElapsedNs: elapsed.Nanoseconds(),
+ }
+ if err != nil {
+ exit.Error = err.Error()
+ exit.ErrNo = int64(errno)
+ }
+ event := pb.Strace{
+ Process: t.Name(),
+ Function: i.name,
+ Info: &pb.Strace_Exit{Exit: exit},
+ }
+ for _, arg := range output {
+ event.Args = append(event.Args, arg)
+ }
+ eventchannel.Emit(&event)
+}
+
+type syscallContext struct {
+ info SyscallInfo
+ args arch.SyscallArguments
+ start time.Time
+ logOutput []string
+ eventOutput []string
+ flags uint32
+}
+
+// SyscallEnter implements kernel.Stracer.SyscallEnter. It logs the syscall
+// entry trace.
+func (s SyscallMap) SyscallEnter(t *kernel.Task, sysno uintptr, args arch.SyscallArguments, flags uint32) interface{} {
+ info, ok := s[sysno]
+ if !ok {
+ info = SyscallInfo{
+ name: fmt.Sprintf("sys_%d", sysno),
+ format: defaultFormat,
+ }
+ }
+
+ var output, eventOutput []string
+ if bits.IsOn32(flags, kernel.StraceEnableLog) {
+ output = info.printEnter(t, args)
+ }
+ if bits.IsOn32(flags, kernel.StraceEnableEvent) {
+ eventOutput = info.sendEnter(t, args)
+ }
+
+ return &syscallContext{
+ info: info,
+ args: args,
+ start: time.Now(),
+ logOutput: output,
+ eventOutput: eventOutput,
+ flags: flags,
+ }
+}
+
+// SyscallExit implements kernel.Stracer.SyscallExit. It logs the syscall
+// exit trace.
+func (s SyscallMap) SyscallExit(context interface{}, t *kernel.Task, sysno, rval uintptr, err error) {
+ errno := t.ExtractErrno(err, int(sysno))
+ c := context.(*syscallContext)
+
+ elapsed := time.Since(c.start)
+ if bits.IsOn32(c.flags, kernel.StraceEnableLog) {
+ c.info.printExit(t, elapsed, c.logOutput, c.args, rval, err, errno)
+ }
+ if bits.IsOn32(c.flags, kernel.StraceEnableEvent) {
+ c.info.sendExit(t, elapsed, c.eventOutput, c.args, rval, err, errno)
+ }
+}
+
+// ConvertToSysnoMap converts the names to a map keyed on the syscall number
+// and value set to true.
+//
+// The map is in a convenient format to pass to SyscallFlagsTable.Enable().
+func (s SyscallMap) ConvertToSysnoMap(syscalls []string) (map[uintptr]bool, error) {
+ if syscalls == nil {
+ // Sentinel: no list.
+ return nil, nil
+ }
+
+ l := make(map[uintptr]bool)
+ for _, sc := range syscalls {
+ // Try to match this system call.
+ sysno, ok := s.ConvertToSysno(sc)
+ if !ok {
+ return nil, fmt.Errorf("syscall %q not found", sc)
+ }
+ l[sysno] = true
+ }
+
+ // Success.
+ return l, nil
+}
+
+// ConvertToSysno converts the name to system call number. Returns false
+// if syscall with same name is not found.
+func (s SyscallMap) ConvertToSysno(syscall string) (uintptr, bool) {
+ for sysno, info := range s {
+ if info.name != "" && info.name == syscall {
+ return sysno, true
+ }
+ }
+ return 0, false
+}
+
+// Name returns the syscall name.
+func (s SyscallMap) Name(sysno uintptr) string {
+ if info, ok := s[sysno]; ok {
+ return info.name
+ }
+ return fmt.Sprintf("sys_%d", sysno)
+}
+
+// Initialize prepares all syscall tables for use by this package.
+//
+// N.B. This is not in an init function because we can't be sure all syscall
+// tables are registered with the kernel when init runs.
+//
+// TODO(gvisor.dev/issue/155): remove kernel package dependencies from this
+// package and have the kernel package self-initialize all syscall tables.
+func Initialize() {
+ for _, table := range kernel.SyscallTables() {
+ // Is this known?
+ sys, ok := Lookup(table.OS, table.Arch)
+ if !ok {
+ continue
+ }
+
+ table.Stracer = sys
+ }
+}
+
+// SinkType defines where to send straces to.
+type SinkType uint32
+
+const (
+ // SinkTypeLog sends straces to text log
+ SinkTypeLog SinkType = 1 << iota
+
+ // SinkTypeEvent sends strace to event log
+ SinkTypeEvent
+)
+
+func convertToSyscallFlag(sinks SinkType) uint32 {
+ ret := uint32(0)
+ if bits.IsOn32(uint32(sinks), uint32(SinkTypeLog)) {
+ ret |= kernel.StraceEnableLog
+ }
+ if bits.IsOn32(uint32(sinks), uint32(SinkTypeEvent)) {
+ ret |= kernel.StraceEnableEvent
+ }
+ return ret
+}
+
+// Enable enables the syscalls in whitelist in all syscall tables.
+//
+// Preconditions: Initialize has been called.
+func Enable(whitelist []string, sinks SinkType) error {
+ flags := convertToSyscallFlag(sinks)
+ for _, table := range kernel.SyscallTables() {
+ // Is this known?
+ sys, ok := Lookup(table.OS, table.Arch)
+ if !ok {
+ continue
+ }
+
+ // Convert to a set of system calls numbers.
+ wl, err := sys.ConvertToSysnoMap(whitelist)
+ if err != nil {
+ return err
+ }
+
+ table.FeatureEnable.Enable(flags, wl, true)
+ }
+
+ // Done.
+ return nil
+}
+
+// Disable will disable Strace for all system calls and missing syscalls.
+//
+// Preconditions: Initialize has been called.
+func Disable(sinks SinkType) {
+ flags := convertToSyscallFlag(sinks)
+ for _, table := range kernel.SyscallTables() {
+ // Strace will be disabled for all syscalls including missing.
+ table.FeatureEnable.Enable(flags, nil, false)
+ }
+}
+
+// EnableAll enables all syscalls in all syscall tables.
+//
+// Preconditions: Initialize has been called.
+func EnableAll(sinks SinkType) {
+ flags := convertToSyscallFlag(sinks)
+ for _, table := range kernel.SyscallTables() {
+ // Is this known?
+ if _, ok := Lookup(table.OS, table.Arch); !ok {
+ continue
+ }
+
+ table.FeatureEnable.EnableAll(flags)
+ }
+}
+
+func init() {
+ t, ok := Lookup(abi.Host, arch.Host)
+ if ok {
+ // Provide the native table as the lookup for seccomp
+ // debugging. This is best-effort. This is provided this way to
+ // avoid dependencies from seccomp to this package.
+ seccomp.SyscallName = t.Name
+ }
+}
diff --git a/pkg/sentry/strace/strace_go_proto/strace.pb.go b/pkg/sentry/strace/strace_go_proto/strace.pb.go
new file mode 100755
index 000000000..ef45661bc
--- /dev/null
+++ b/pkg/sentry/strace/strace_go_proto/strace.pb.go
@@ -0,0 +1,247 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: pkg/sentry/strace/strace.proto
+
+package gvisor
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Strace struct {
+ Process string `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"`
+ Function string `protobuf:"bytes,2,opt,name=function,proto3" json:"function,omitempty"`
+ Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"`
+ // Types that are valid to be assigned to Info:
+ // *Strace_Enter
+ // *Strace_Exit
+ Info isStrace_Info `protobuf_oneof:"info"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Strace) Reset() { *m = Strace{} }
+func (m *Strace) String() string { return proto.CompactTextString(m) }
+func (*Strace) ProtoMessage() {}
+func (*Strace) Descriptor() ([]byte, []int) {
+ return fileDescriptor_50c4b43677c82b5f, []int{0}
+}
+
+func (m *Strace) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Strace.Unmarshal(m, b)
+}
+func (m *Strace) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Strace.Marshal(b, m, deterministic)
+}
+func (m *Strace) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Strace.Merge(m, src)
+}
+func (m *Strace) XXX_Size() int {
+ return xxx_messageInfo_Strace.Size(m)
+}
+func (m *Strace) XXX_DiscardUnknown() {
+ xxx_messageInfo_Strace.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Strace proto.InternalMessageInfo
+
+func (m *Strace) GetProcess() string {
+ if m != nil {
+ return m.Process
+ }
+ return ""
+}
+
+func (m *Strace) GetFunction() string {
+ if m != nil {
+ return m.Function
+ }
+ return ""
+}
+
+func (m *Strace) GetArgs() []string {
+ if m != nil {
+ return m.Args
+ }
+ return nil
+}
+
+type isStrace_Info interface {
+ isStrace_Info()
+}
+
+type Strace_Enter struct {
+ Enter *StraceEnter `protobuf:"bytes,4,opt,name=enter,proto3,oneof"`
+}
+
+type Strace_Exit struct {
+ Exit *StraceExit `protobuf:"bytes,5,opt,name=exit,proto3,oneof"`
+}
+
+func (*Strace_Enter) isStrace_Info() {}
+
+func (*Strace_Exit) isStrace_Info() {}
+
+func (m *Strace) GetInfo() isStrace_Info {
+ if m != nil {
+ return m.Info
+ }
+ return nil
+}
+
+func (m *Strace) GetEnter() *StraceEnter {
+ if x, ok := m.GetInfo().(*Strace_Enter); ok {
+ return x.Enter
+ }
+ return nil
+}
+
+func (m *Strace) GetExit() *StraceExit {
+ if x, ok := m.GetInfo().(*Strace_Exit); ok {
+ return x.Exit
+ }
+ return nil
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*Strace) XXX_OneofWrappers() []interface{} {
+ return []interface{}{
+ (*Strace_Enter)(nil),
+ (*Strace_Exit)(nil),
+ }
+}
+
+type StraceEnter struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *StraceEnter) Reset() { *m = StraceEnter{} }
+func (m *StraceEnter) String() string { return proto.CompactTextString(m) }
+func (*StraceEnter) ProtoMessage() {}
+func (*StraceEnter) Descriptor() ([]byte, []int) {
+ return fileDescriptor_50c4b43677c82b5f, []int{1}
+}
+
+func (m *StraceEnter) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_StraceEnter.Unmarshal(m, b)
+}
+func (m *StraceEnter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_StraceEnter.Marshal(b, m, deterministic)
+}
+func (m *StraceEnter) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_StraceEnter.Merge(m, src)
+}
+func (m *StraceEnter) XXX_Size() int {
+ return xxx_messageInfo_StraceEnter.Size(m)
+}
+func (m *StraceEnter) XXX_DiscardUnknown() {
+ xxx_messageInfo_StraceEnter.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_StraceEnter proto.InternalMessageInfo
+
+type StraceExit struct {
+ Return string `protobuf:"bytes,1,opt,name=return,proto3" json:"return,omitempty"`
+ Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
+ ErrNo int64 `protobuf:"varint,3,opt,name=err_no,json=errNo,proto3" json:"err_no,omitempty"`
+ ElapsedNs int64 `protobuf:"varint,4,opt,name=elapsed_ns,json=elapsedNs,proto3" json:"elapsed_ns,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *StraceExit) Reset() { *m = StraceExit{} }
+func (m *StraceExit) String() string { return proto.CompactTextString(m) }
+func (*StraceExit) ProtoMessage() {}
+func (*StraceExit) Descriptor() ([]byte, []int) {
+ return fileDescriptor_50c4b43677c82b5f, []int{2}
+}
+
+func (m *StraceExit) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_StraceExit.Unmarshal(m, b)
+}
+func (m *StraceExit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_StraceExit.Marshal(b, m, deterministic)
+}
+func (m *StraceExit) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_StraceExit.Merge(m, src)
+}
+func (m *StraceExit) XXX_Size() int {
+ return xxx_messageInfo_StraceExit.Size(m)
+}
+func (m *StraceExit) XXX_DiscardUnknown() {
+ xxx_messageInfo_StraceExit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_StraceExit proto.InternalMessageInfo
+
+func (m *StraceExit) GetReturn() string {
+ if m != nil {
+ return m.Return
+ }
+ return ""
+}
+
+func (m *StraceExit) GetError() string {
+ if m != nil {
+ return m.Error
+ }
+ return ""
+}
+
+func (m *StraceExit) GetErrNo() int64 {
+ if m != nil {
+ return m.ErrNo
+ }
+ return 0
+}
+
+func (m *StraceExit) GetElapsedNs() int64 {
+ if m != nil {
+ return m.ElapsedNs
+ }
+ return 0
+}
+
+func init() {
+ proto.RegisterType((*Strace)(nil), "gvisor.Strace")
+ proto.RegisterType((*StraceEnter)(nil), "gvisor.StraceEnter")
+ proto.RegisterType((*StraceExit)(nil), "gvisor.StraceExit")
+}
+
+func init() { proto.RegisterFile("pkg/sentry/strace/strace.proto", fileDescriptor_50c4b43677c82b5f) }
+
+var fileDescriptor_50c4b43677c82b5f = []byte{
+ // 255 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x90, 0xdd, 0x4a, 0xf4, 0x30,
+ 0x10, 0x86, 0xb7, 0x5f, 0xdb, 0x7c, 0x76, 0x16, 0x4f, 0xc6, 0x1f, 0x82, 0xa0, 0x94, 0x1e, 0x05,
+ 0x84, 0x2e, 0xe8, 0x1d, 0x08, 0xc2, 0x1e, 0xed, 0x41, 0xbc, 0x80, 0xa5, 0xd6, 0xd9, 0x12, 0x94,
+ 0x24, 0x4c, 0xb2, 0xb2, 0x5e, 0x96, 0x77, 0x28, 0xa6, 0xf1, 0x07, 0x8f, 0x92, 0x67, 0xde, 0x87,
+ 0x0c, 0x6f, 0xe0, 0xca, 0x3f, 0x4f, 0xab, 0x40, 0x36, 0xf2, 0xdb, 0x2a, 0x44, 0x1e, 0x46, 0xca,
+ 0x47, 0xef, 0xd9, 0x45, 0x87, 0x62, 0x7a, 0x35, 0xc1, 0x71, 0xf7, 0x5e, 0x80, 0x78, 0x48, 0x01,
+ 0x4a, 0xf8, 0xef, 0xd9, 0x8d, 0x14, 0x82, 0x2c, 0xda, 0x42, 0x35, 0xfa, 0x0b, 0xf1, 0x02, 0x8e,
+ 0x76, 0x7b, 0x3b, 0x46, 0xe3, 0xac, 0xfc, 0x97, 0xa2, 0x6f, 0x46, 0x84, 0x6a, 0xe0, 0x29, 0xc8,
+ 0xb2, 0x2d, 0x55, 0xa3, 0xd3, 0x1d, 0xaf, 0xa1, 0x26, 0x1b, 0x89, 0x65, 0xd5, 0x16, 0x6a, 0x79,
+ 0x73, 0xd2, 0xcf, 0xcb, 0xfa, 0x79, 0xd1, 0xfd, 0x67, 0xb4, 0x5e, 0xe8, 0xd9, 0x41, 0x05, 0x15,
+ 0x1d, 0x4c, 0x94, 0x75, 0x72, 0xf1, 0x8f, 0x7b, 0x30, 0x71, 0xbd, 0xd0, 0xc9, 0xb8, 0x13, 0x50,
+ 0x19, 0xbb, 0x73, 0xdd, 0x31, 0x2c, 0x7f, 0xbd, 0xd4, 0x79, 0x80, 0x1f, 0x19, 0xcf, 0x41, 0x30,
+ 0xc5, 0x3d, 0xdb, 0x5c, 0x22, 0x13, 0x9e, 0x42, 0x4d, 0xcc, 0x8e, 0x73, 0x81, 0x19, 0xf0, 0x0c,
+ 0x04, 0x31, 0x6f, 0xad, 0x93, 0x65, 0x5b, 0xa8, 0x32, 0x8d, 0x37, 0x0e, 0x2f, 0x01, 0xe8, 0x65,
+ 0xf0, 0x81, 0x9e, 0xb6, 0x36, 0xa4, 0x16, 0xa5, 0x6e, 0xf2, 0x64, 0x13, 0x1e, 0x45, 0xfa, 0xc3,
+ 0xdb, 0x8f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x42, 0x9a, 0xbc, 0x81, 0x65, 0x01, 0x00, 0x00,
+}
diff --git a/pkg/sentry/strace/strace_state_autogen.go b/pkg/sentry/strace/strace_state_autogen.go
new file mode 100755
index 000000000..9dc697ed6
--- /dev/null
+++ b/pkg/sentry/strace/strace_state_autogen.go
@@ -0,0 +1,4 @@
+// automatically generated by stateify.
+
+package strace
+
diff --git a/pkg/sentry/strace/syscalls.go b/pkg/sentry/strace/syscalls.go
new file mode 100644
index 000000000..eae2d6c12
--- /dev/null
+++ b/pkg/sentry/strace/syscalls.go
@@ -0,0 +1,267 @@
+// Copyright 2018 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 strace
+
+import (
+ "gvisor.googlesource.com/gvisor/pkg/abi"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/arch"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
+)
+
+// FormatSpecifier values describe how an individual syscall argument should be
+// formatted.
+type FormatSpecifier int
+
+// Valid FormatSpecifiers.
+//
+// Unless otherwise specified, values are formatted before syscall execution
+// and not updated after syscall execution (the same value is output).
+const (
+ // Hex is just a hexadecimal number.
+ Hex FormatSpecifier = iota
+
+ // Oct is just an octal number.
+ Oct
+
+ // FD is a file descriptor.
+ FD
+
+ // ReadBuffer is a buffer for a read-style call. The syscall return
+ // value is used for the length.
+ //
+ // Formatted after syscall execution.
+ ReadBuffer
+
+ // WriteBuffer is a buffer for a write-style call. The following arg is
+ // used for the length.
+ //
+ // Contents omitted after syscall execution.
+ WriteBuffer
+
+ // ReadIOVec is a pointer to a struct iovec for a writev-style call.
+ // The following arg is used for the length. The return value is used
+ // for the total length.
+ //
+ // Complete contents only formatted after syscall execution.
+ ReadIOVec
+
+ // WriteIOVec is a pointer to a struct iovec for a writev-style call.
+ // The following arg is used for the length.
+ //
+ // Complete contents only formatted before syscall execution, omitted
+ // after.
+ WriteIOVec
+
+ // IOVec is a generic pointer to a struct iovec. Contents are not dumped.
+ IOVec
+
+ // SendMsgHdr is a pointer to a struct msghdr for a sendmsg-style call.
+ // Contents formatted only before syscall execution, omitted after.
+ SendMsgHdr
+
+ // RecvMsgHdr is a pointer to a struct msghdr for a recvmsg-style call.
+ // Contents formatted only after syscall execution.
+ RecvMsgHdr
+
+ // Path is a pointer to a char* path.
+ Path
+
+ // PostPath is a pointer to a char* path, formatted after syscall
+ // execution.
+ PostPath
+
+ // ExecveStringVector is a NULL-terminated array of strings. Enforces
+ // the maximum execve array length.
+ ExecveStringVector
+
+ // PipeFDs is an array of two FDs, formatted after syscall execution.
+ PipeFDs
+
+ // Uname is a pointer to a struct uname, formatted after syscall execution.
+ Uname
+
+ // Stat is a pointer to a struct stat, formatted after syscall execution.
+ Stat
+
+ // SockAddr is a pointer to a struct sockaddr. The following arg is
+ // used for length.
+ SockAddr
+
+ // PostSockAddr is a pointer to a struct sockaddr, formatted after
+ // syscall execution. The following arg is a pointer to the socklen_t
+ // length.
+ PostSockAddr
+
+ // SockLen is a pointer to a socklen_t, formatted before and after
+ // syscall execution.
+ SockLen
+
+ // SockFamily is a socket protocol family value.
+ SockFamily
+
+ // SockType is a socket type and flags value.
+ SockType
+
+ // SockProtocol is a socket protocol value. Argument n-2 is the socket
+ // protocol family.
+ SockProtocol
+
+ // SockFlags are socket flags.
+ SockFlags
+
+ // Timespec is a pointer to a struct timespec.
+ Timespec
+
+ // PostTimespec is a pointer to a struct timespec, formatted after
+ // syscall execution.
+ PostTimespec
+
+ // UTimeTimespec is a pointer to a struct timespec. Formatting includes
+ // UTIME_NOW and UTIME_OMIT.
+ UTimeTimespec
+
+ // ItimerVal is a pointer to a struct itimerval.
+ ItimerVal
+
+ // PostItimerVal is a pointer to a struct itimerval, formatted after
+ // syscall execution.
+ PostItimerVal
+
+ // ItimerSpec is a pointer to a struct itimerspec.
+ ItimerSpec
+
+ // PostItimerSpec is a pointer to a struct itimerspec, formatted after
+ // syscall execution.
+ PostItimerSpec
+
+ // Timeval is a pointer to a struct timeval, formatted before and after
+ // syscall execution.
+ Timeval
+
+ // Utimbuf is a pointer to a struct utimbuf.
+ Utimbuf
+
+ // Rusage is a struct rusage, formatted after syscall execution.
+ Rusage
+
+ // CloneFlags are clone(2) flags.
+ CloneFlags
+
+ // OpenFlags are open(2) flags.
+ OpenFlags
+
+ // Mode is a mode_t.
+ Mode
+
+ // FutexOp is the futex(2) operation.
+ FutexOp
+
+ // PtraceRequest is the ptrace(2) request.
+ PtraceRequest
+
+ // ItimerType is an itimer type (ITIMER_REAL, etc).
+ ItimerType
+
+ // Signal is a signal number.
+ Signal
+
+ // SignalMaskAction is a signal mask action passed to rt_sigprocmask(2).
+ SignalMaskAction
+
+ // SigSet is a signal set.
+ SigSet
+
+ // PostSigSet is a signal set, formatted after syscall execution.
+ PostSigSet
+
+ // SigAction is a struct sigaction.
+ SigAction
+
+ // PostSigAction is a struct sigaction, formatted after syscall execution.
+ PostSigAction
+
+ // CapHeader is a cap_user_header_t.
+ CapHeader
+
+ // CapData is the data argument to capget(2)/capset(2). The previous
+ // argument must be CapHeader.
+ CapData
+
+ // PostCapData is the data argument to capget(2)/capset(2), formatted
+ // after syscall execution. The previous argument must be CapHeader.
+ PostCapData
+
+ // PollFDs is an array of struct pollfd. The number of entries in the
+ // array is in the next argument.
+ PollFDs
+)
+
+// defaultFormat is the syscall argument format to use if the actual format is
+// not known. It formats all six arguments as hex.
+var defaultFormat = []FormatSpecifier{Hex, Hex, Hex, Hex, Hex, Hex}
+
+// SyscallInfo captures the name and printing format of a syscall.
+type SyscallInfo struct {
+ // name is the name of the syscall.
+ name string
+
+ // format contains the format specifiers for each argument.
+ //
+ // Syscall calls can have up to six arguments. Arguments without a
+ // corresponding entry in format will not be printed.
+ format []FormatSpecifier
+}
+
+// makeSyscallInfo returns a SyscallInfo for a syscall.
+func makeSyscallInfo(name string, f ...FormatSpecifier) SyscallInfo {
+ return SyscallInfo{name: name, format: f}
+}
+
+// SyscallMap maps syscalls into names and printing formats.
+type SyscallMap map[uintptr]SyscallInfo
+
+var _ kernel.Stracer = (SyscallMap)(nil)
+
+// syscallTable contains the syscalls for a specific OS/Arch.
+type syscallTable struct {
+ // os is the operating system this table targets.
+ os abi.OS
+
+ // arch is the architecture this table targets.
+ arch arch.Arch
+
+ // syscalls contains the syscall mappings.
+ syscalls SyscallMap
+}
+
+// syscallTables contains all syscall tables.
+var syscallTables = []syscallTable{
+ {
+ os: abi.Linux,
+ arch: arch.AMD64,
+ syscalls: linuxAMD64,
+ },
+}
+
+// Lookup returns the SyscallMap for the OS/Arch combination. The returned map
+// must not be changed.
+func Lookup(os abi.OS, a arch.Arch) (SyscallMap, bool) {
+ for _, s := range syscallTables {
+ if s.os == os && s.arch == a {
+ return s.syscalls, true
+ }
+ }
+ return nil, false
+}