diff options
author | Googler <noreply@google.com> | 2018-04-27 10:37:02 -0700 |
---|---|---|
committer | Adin Scannell <ascannell@google.com> | 2018-04-28 01:44:26 -0400 |
commit | d02b74a5dcfed4bfc8f2f8e545bca4d2afabb296 (patch) | |
tree | 54f95eef73aee6bacbfc736fffc631be2605ed53 /pkg/sentry/strace | |
parent | f70210e742919f40aa2f0934a22f1c9ba6dada62 (diff) |
Check in gVisor.
PiperOrigin-RevId: 194583126
Change-Id: Ica1d8821a90f74e7e745962d71801c598c652463
Diffstat (limited to 'pkg/sentry/strace')
-rw-r--r-- | pkg/sentry/strace/BUILD | 48 | ||||
-rw-r--r-- | pkg/sentry/strace/clone.go | 113 | ||||
-rw-r--r-- | pkg/sentry/strace/futex.go | 91 | ||||
-rw-r--r-- | pkg/sentry/strace/linux64.go | 338 | ||||
-rw-r--r-- | pkg/sentry/strace/open.go | 105 | ||||
-rw-r--r-- | pkg/sentry/strace/ptrace.go | 178 | ||||
-rw-r--r-- | pkg/sentry/strace/socket.go | 674 | ||||
-rw-r--r-- | pkg/sentry/strace/strace.go | 666 | ||||
-rw-r--r-- | pkg/sentry/strace/strace.proto | 50 | ||||
-rw-r--r-- | pkg/sentry/strace/syscalls.go | 217 |
10 files changed, 2480 insertions, 0 deletions
diff --git a/pkg/sentry/strace/BUILD b/pkg/sentry/strace/BUILD new file mode 100644 index 000000000..c5946a564 --- /dev/null +++ b/pkg/sentry/strace/BUILD @@ -0,0 +1,48 @@ +package(licenses = ["notice"]) # Apache 2.0 + +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "strace", + srcs = [ + "clone.go", + "futex.go", + "linux64.go", + "open.go", + "ptrace.go", + "socket.go", + "strace.go", + "syscalls.go", + ], + importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/strace", + visibility = ["//:sandbox"], + deps = [ + ":strace_go_proto", + "//pkg/abi", + "//pkg/abi/linux", + "//pkg/binary", + "//pkg/bits", + "//pkg/eventchannel", + "//pkg/sentry/arch", + "//pkg/sentry/kernel", + "//pkg/sentry/socket/control", + "//pkg/sentry/socket/epsocket", + "//pkg/sentry/socket/netlink", + "//pkg/sentry/syscalls/linux", + "//pkg/sentry/usermem", + ], +) + +proto_library( + name = "strace_proto", + srcs = ["strace.proto"], + visibility = ["//visibility:public"], +) + +go_proto_library( + name = "strace_go_proto", + importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/strace/strace_go_proto", + proto = ":strace_proto", + visibility = ["//visibility:public"], +) diff --git a/pkg/sentry/strace/clone.go b/pkg/sentry/strace/clone.go new file mode 100644 index 000000000..b82ca1ad1 --- /dev/null +++ b/pkg/sentry/strace/clone.go @@ -0,0 +1,113 @@ +// Copyright 2018 Google Inc. +// +// 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..3da108cb7 --- /dev/null +++ b/pkg/sentry/strace/futex.go @@ -0,0 +1,91 @@ +// Copyright 2018 Google Inc. +// +// 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{ + { + Value: linux.FUTEX_WAIT, + Name: "FUTEX_WAIT", + }, + { + Value: linux.FUTEX_WAKE, + Name: "FUTEX_WAKE", + }, + { + Value: linux.FUTEX_FD, + Name: "FUTEX_FD", + }, + { + Value: linux.FUTEX_REQUEUE, + Name: "FUTEX_REQUEUE", + }, + { + Value: linux.FUTEX_CMP_REQUEUE, + Name: "FUTEX_CMP_REQUEUE", + }, + { + Value: linux.FUTEX_WAKE_OP, + Name: "FUTEX_WAKE_OP", + }, + { + Value: linux.FUTEX_LOCK_PI, + Name: "FUTEX_LOCK_PI", + }, + { + Value: linux.FUTEX_UNLOCK_PI, + Name: "FUTEX_UNLOCK_PI", + }, + { + Value: linux.FUTEX_TRYLOCK_PI, + Name: "FUTEX_TRYLOCK_PI", + }, + { + Value: linux.FUTEX_WAIT_BITSET, + Name: "FUTEX_WAIT_BITSET", + }, + { + Value: linux.FUTEX_WAKE_BITSET, + Name: "FUTEX_WAKE_BITSET", + }, + { + Value: linux.FUTEX_WAIT_REQUEUE_PI, + Name: "FUTEX_WAIT_REQUEUE_PI", + }, + { + Value: linux.FUTEX_CMP_REQUEUE_PI, + Name: "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..90ea8c36f --- /dev/null +++ b/pkg/sentry/strace/linux64.go @@ -0,0 +1,338 @@ +// Copyright 2018 Google Inc. +// +// 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", Hex, ReadBuffer, Hex), + 1: makeSyscallInfo("write", Hex, WriteBuffer, Hex), + 2: makeSyscallInfo("open", Path, OpenFlags, Mode), + 3: makeSyscallInfo("close", Hex), + 4: makeSyscallInfo("stat", Path, Stat), + 5: makeSyscallInfo("fstat", Hex, Stat), + 6: makeSyscallInfo("lstat", Path, Stat), + 7: makeSyscallInfo("poll", Hex, Hex, Hex), + 8: makeSyscallInfo("lseek", Hex, Hex, Hex), + 9: makeSyscallInfo("mmap", Hex, Hex, Hex, Hex, Hex, Hex), + 10: makeSyscallInfo("mprotect", Hex, Hex, Hex), + 11: makeSyscallInfo("munmap", Hex, Hex), + 12: makeSyscallInfo("brk", Hex), + 13: makeSyscallInfo("rt_sigaction", Hex, Hex, Hex), + 14: makeSyscallInfo("rt_sigprocmask", Hex, Hex, Hex, Hex), + 15: makeSyscallInfo("rt_sigreturn"), + 16: makeSyscallInfo("ioctl", Hex, Hex, Hex), + 17: makeSyscallInfo("pread64", Hex, ReadBuffer, Hex, Hex), + 18: makeSyscallInfo("pwrite64", Hex, WriteBuffer, Hex, Hex), + 19: makeSyscallInfo("readv", Hex, ReadIOVec, Hex), + 20: makeSyscallInfo("writev", Hex, 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", Hex), + 33: makeSyscallInfo("dup2", Hex, Hex), + 34: makeSyscallInfo("pause"), + 35: makeSyscallInfo("nanosleep", Timespec, PostTimespec), + 36: makeSyscallInfo("getitimer", Hex, PostItimerVal), + 37: makeSyscallInfo("alarm", Hex), + 38: makeSyscallInfo("setitimer", Hex, ItimerVal, PostItimerVal), + 39: makeSyscallInfo("getpid"), + 40: makeSyscallInfo("sendfile", Hex, Hex, Hex, Hex), + 41: makeSyscallInfo("socket", SockFamily, SockType, SockProtocol), + 42: makeSyscallInfo("connect", Hex, SockAddr, Hex), + 43: makeSyscallInfo("accept", Hex, PostSockAddr, SockLen), + 44: makeSyscallInfo("sendto", Hex, Hex, Hex, Hex, SockAddr, Hex), + 45: makeSyscallInfo("recvfrom", Hex, Hex, Hex, Hex, PostSockAddr, SockLen), + 46: makeSyscallInfo("sendmsg", Hex, SendMsgHdr, Hex), + 47: makeSyscallInfo("recvmsg", Hex, RecvMsgHdr, Hex), + 48: makeSyscallInfo("shutdown", Hex, Hex), + 49: makeSyscallInfo("bind", Hex, SockAddr, Hex), + 50: makeSyscallInfo("listen", Hex, Hex), + 51: makeSyscallInfo("getsockname", Hex, PostSockAddr, SockLen), + 52: makeSyscallInfo("getpeername", Hex, PostSockAddr, SockLen), + 53: makeSyscallInfo("socketpair", SockFamily, SockType, SockProtocol, Hex), + 54: makeSyscallInfo("setsockopt", Hex, Hex, Hex, Hex, Hex), + 55: makeSyscallInfo("getsockopt", Hex, 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, Hex), + 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", Hex, Hex, Hex), + 73: makeSyscallInfo("flock", Hex, Hex), + 74: makeSyscallInfo("fsync", Hex), + 75: makeSyscallInfo("fdatasync", Hex), + 76: makeSyscallInfo("truncate", Path, Hex), + 77: makeSyscallInfo("ftruncate", Hex, Hex), + 78: makeSyscallInfo("getdents", Hex, Hex, Hex), + 79: makeSyscallInfo("getcwd", Hex, Hex), + 80: makeSyscallInfo("chdir", Path), + 81: makeSyscallInfo("fchdir", Hex), + 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", Hex, Mode), + 92: makeSyscallInfo("chown", Path, Hex, Hex), + 93: makeSyscallInfo("fchown", Hex, Hex, Hex), + 94: makeSyscallInfo("lchown", Hex, 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", Hex, Hex), + 126: makeSyscallInfo("capset", Hex, Hex), + 127: makeSyscallInfo("rt_sigpending", Hex), + 128: makeSyscallInfo("rt_sigtimedwait", Hex, Hex, Timespec, Hex), + 129: makeSyscallInfo("rt_sigqueueinfo", Hex, Hex, 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", Hex, 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", Hex, Hex), + 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", Hex, Path, Hex, Hex, Hex), + 191: makeSyscallInfo("getxattr", Path, Path, Hex, Hex), + 192: makeSyscallInfo("lgetxattr", Path, Path, Hex, Hex), + 193: makeSyscallInfo("fgetxattr", Hex, Path, Hex, Hex), + 194: makeSyscallInfo("listxattr", Path, Path, Hex), + 195: makeSyscallInfo("llistxattr", Path, Path, Hex), + 196: makeSyscallInfo("flistxattr", Hex, Path, Hex), + 197: makeSyscallInfo("removexattr", Path, Path), + 198: makeSyscallInfo("lremovexattr", Path, Path), + 199: makeSyscallInfo("fremovexattr", Hex, Path), + 200: makeSyscallInfo("tkill", Hex, Hex), + 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", Hex, Hex, Hex), + 218: makeSyscallInfo("set_tid_address", Hex), + 219: makeSyscallInfo("restart_syscall"), + 220: makeSyscallInfo("semtimedop", Hex, Hex, Hex, Hex), + 221: makeSyscallInfo("fadvise64", Hex, Hex, Hex, Hex), + 222: makeSyscallInfo("timer_create", Hex, Hex, Hex), + 223: makeSyscallInfo("timer_settime", Hex, Hex, Hex, Hex), + 224: makeSyscallInfo("timer_gettime", Hex, Hex), + 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, Hex, Hex), + 234: makeSyscallInfo("tgkill", Hex, Hex, Hex), + 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, Hex, Hex), + 255: makeSyscallInfo("inotify_rm_watch", Hex, Hex), + 256: makeSyscallInfo("migrate_pages", Hex, Hex, Hex, Hex), + 257: makeSyscallInfo("openat", Hex, Path, Hex, Mode), + 258: makeSyscallInfo("mkdirat", Hex, Path, Hex), + 259: makeSyscallInfo("mknodat", Hex, Path, Mode, Hex), + 260: makeSyscallInfo("fchownat", Hex, Path, Hex, Hex, Hex), + 261: makeSyscallInfo("futimesat", Hex, Path, Hex), + 262: makeSyscallInfo("newfstatat", Hex, Path, Stat, Hex), + 263: makeSyscallInfo("unlinkat", Hex, Path, Hex), + 264: makeSyscallInfo("renameat", Hex, Path, Hex, Path), + 265: makeSyscallInfo("linkat", Hex, Path, Hex, Path, Hex), + 266: makeSyscallInfo("symlinkat", Path, Hex, Path), + 267: makeSyscallInfo("readlinkat", Hex, Path, ReadBuffer, Hex), + 268: makeSyscallInfo("fchmodat", Hex, Path, Mode), + 269: makeSyscallInfo("faccessat", Hex, Path, Oct, Hex), + 270: makeSyscallInfo("pselect6", Hex, Hex, Hex, Hex, Hex, Hex), + 271: makeSyscallInfo("ppoll", Hex, Hex, Timespec, Hex, Hex), + 272: makeSyscallInfo("unshare", Hex), + 273: makeSyscallInfo("set_robust_list", Hex, Hex), + 274: makeSyscallInfo("get_robust_list", Hex, Hex, Hex), + 275: makeSyscallInfo("splice", Hex, Hex, Hex, Hex, Hex, Hex), + 276: makeSyscallInfo("tee", Hex, Hex, Hex, Hex), + 277: makeSyscallInfo("sync_file_range", Hex, Hex, Hex, Hex), + 278: makeSyscallInfo("vmsplice", Hex, Hex, Hex, Hex), + 279: makeSyscallInfo("move_pages", Hex, Hex, Hex, Hex, Hex, Hex), + 280: makeSyscallInfo("utimensat", Hex, Path, UTimeTimespec, Hex), + 281: makeSyscallInfo("epoll_pwait", Hex, Hex, Hex, Hex, Hex, Hex), + 282: makeSyscallInfo("signalfd", Hex, Hex, Hex), + 283: makeSyscallInfo("timerfd_create", Hex, Hex), + 284: makeSyscallInfo("eventfd", Hex), + 285: makeSyscallInfo("fallocate", Hex, Hex, Hex, Hex), + 286: makeSyscallInfo("timerfd_settime", Hex, Hex, Hex, Hex), + 287: makeSyscallInfo("timerfd_gettime", Hex, Hex), + 288: makeSyscallInfo("accept4", Hex, PostSockAddr, SockLen, SockFlags), + 289: makeSyscallInfo("signalfd4", Hex, Hex, Hex, Hex), + 290: makeSyscallInfo("eventfd2", Hex, Hex), + 291: makeSyscallInfo("epoll_create1", Hex), + 292: makeSyscallInfo("dup3", Hex, Hex, Hex), + 293: makeSyscallInfo("pipe2", PipeFDs, Hex), + 294: makeSyscallInfo("inotify_init1", Hex), + 295: makeSyscallInfo("preadv", Hex, ReadIOVec, Hex, Hex), + 296: makeSyscallInfo("pwritev", Hex, WriteIOVec, Hex, Hex), + 297: makeSyscallInfo("rt_tgsigqueueinfo", Hex, Hex, Hex, Hex), + 298: makeSyscallInfo("perf_event_open", Hex, Hex, Hex, Hex, Hex), + 299: makeSyscallInfo("recvmmsg", Hex, 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", Hex, Hex, Hex, Hex, Hex), + 304: makeSyscallInfo("open_by_handle_at", Hex, Hex, Hex), + 305: makeSyscallInfo("clock_adjtime", Hex, Hex), + 306: makeSyscallInfo("syncfs", Hex), + 307: makeSyscallInfo("sendmmsg", Hex, Hex, Hex, Hex), + 308: makeSyscallInfo("setns", Hex, 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", Hex, 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..839d5eda7 --- /dev/null +++ b/pkg/sentry/strace/open.go @@ -0,0 +1,105 @@ +// Copyright 2018 Google Inc. +// +// 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{ + { + Value: syscall.O_RDWR, + Name: "O_RDWR", + }, + { + Value: syscall.O_WRONLY, + Name: "O_WRONLY", + }, + { + Value: syscall.O_RDONLY, + Name: "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/ptrace.go b/pkg/sentry/strace/ptrace.go new file mode 100644 index 000000000..a0dabb27a --- /dev/null +++ b/pkg/sentry/strace/ptrace.go @@ -0,0 +1,178 @@ +// Copyright 2018 Google Inc. +// +// 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" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" +) + +// PtraceRequestSet are the possible ptrace(2) requests. +var PtraceRequestSet = abi.ValueSet{ + { + Value: syscall.PTRACE_TRACEME, + Name: "PTRACE_TRACEME", + }, + { + Value: syscall.PTRACE_PEEKTEXT, + Name: "PTRACE_PEEKTEXT", + }, + { + Value: syscall.PTRACE_PEEKDATA, + Name: "PTRACE_PEEKDATA", + }, + { + Value: syscall.PTRACE_PEEKUSR, + Name: "PTRACE_PEEKUSR", + }, + { + Value: syscall.PTRACE_POKETEXT, + Name: "PTRACE_POKETEXT", + }, + { + Value: syscall.PTRACE_POKEDATA, + Name: "PTRACE_POKEDATA", + }, + { + Value: syscall.PTRACE_POKEUSR, + Name: "PTRACE_POKEUSR", + }, + { + Value: syscall.PTRACE_CONT, + Name: "PTRACE_CONT", + }, + { + Value: syscall.PTRACE_KILL, + Name: "PTRACE_KILL", + }, + { + Value: syscall.PTRACE_SINGLESTEP, + Name: "PTRACE_SINGLESTEP", + }, + { + Value: syscall.PTRACE_ATTACH, + Name: "PTRACE_ATTACH", + }, + { + Value: syscall.PTRACE_DETACH, + Name: "PTRACE_DETACH", + }, + { + Value: syscall.PTRACE_SYSCALL, + Name: "PTRACE_SYSCALL", + }, + { + Value: syscall.PTRACE_SETOPTIONS, + Name: "PTRACE_SETOPTIONS", + }, + { + Value: syscall.PTRACE_GETEVENTMSG, + Name: "PTRACE_GETEVENTMSG", + }, + { + Value: syscall.PTRACE_GETSIGINFO, + Name: "PTRACE_GETSIGINFO", + }, + { + Value: syscall.PTRACE_SETSIGINFO, + Name: "PTRACE_SETSIGINFO", + }, + { + Value: syscall.PTRACE_GETREGSET, + Name: "PTRACE_GETREGSET", + }, + { + Value: syscall.PTRACE_SETREGSET, + Name: "PTRACE_SETREGSET", + }, + { + Value: kernel.PTRACE_SEIZE, + Name: "PTRACE_SEIZE", + }, + { + Value: kernel.PTRACE_INTERRUPT, + Name: "PTRACE_INTERRUPT", + }, + { + Value: kernel.PTRACE_LISTEN, + Name: "PTRACE_LISTEN", + }, + { + Value: kernel.PTRACE_PEEKSIGINFO, + Name: "PTRACE_PEEKSIGINFO", + }, + { + Value: kernel.PTRACE_GETSIGMASK, + Name: "PTRACE_GETSIGMASK", + }, + { + Value: kernel.PTRACE_SETSIGMASK, + Name: "PTRACE_SETSIGMASK", + }, + { + Value: syscall.PTRACE_GETREGS, + Name: "PTRACE_GETREGS", + }, + { + Value: syscall.PTRACE_SETREGS, + Name: "PTRACE_SETREGS", + }, + { + Value: syscall.PTRACE_GETFPREGS, + Name: "PTRACE_GETFPREGS", + }, + { + Value: syscall.PTRACE_SETFPREGS, + Name: "PTRACE_SETFPREGS", + }, + { + Value: syscall.PTRACE_GETFPXREGS, + Name: "PTRACE_GETFPXREGS", + }, + { + Value: syscall.PTRACE_SETFPXREGS, + Name: "PTRACE_SETFPXREGS", + }, + { + Value: syscall.PTRACE_OLDSETOPTIONS, + Name: "PTRACE_OLDSETOPTIONS", + }, + { + Value: syscall.PTRACE_GET_THREAD_AREA, + Name: "PTRACE_GET_THREAD_AREA", + }, + { + Value: syscall.PTRACE_SET_THREAD_AREA, + Name: "PTRACE_SET_THREAD_AREA", + }, + { + Value: syscall.PTRACE_ARCH_PRCTL, + Name: "PTRACE_ARCH_PRCTL", + }, + { + Value: syscall.PTRACE_SYSEMU, + Name: "PTRACE_SYSEMU", + }, + { + Value: syscall.PTRACE_SYSEMU_SINGLESTEP, + Name: "PTRACE_SYSEMU_SINGLESTEP", + }, + { + Value: syscall.PTRACE_SINGLEBLOCK, + Name: "PTRACE_SINGLEBLOCK", + }, +} diff --git a/pkg/sentry/strace/socket.go b/pkg/sentry/strace/socket.go new file mode 100644 index 000000000..48c072e96 --- /dev/null +++ b/pkg/sentry/strace/socket.go @@ -0,0 +1,674 @@ +// Copyright 2018 Google Inc. +// +// 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{ + { + Value: linux.AF_UNSPEC, + Name: "AF_UNSPEC", + }, + { + Value: linux.AF_UNIX, + Name: "AF_UNIX", + }, + { + Value: linux.AF_INET, + Name: "AF_INET", + }, + { + Value: linux.AF_AX25, + Name: "AF_AX25", + }, + { + Value: linux.AF_IPX, + Name: "AF_IPX", + }, + { + Value: linux.AF_APPLETALK, + Name: "AF_APPLETALK", + }, + { + Value: linux.AF_NETROM, + Name: "AF_NETROM", + }, + { + Value: linux.AF_BRIDGE, + Name: "AF_BRIDGE", + }, + { + Value: linux.AF_ATMPVC, + Name: "AF_ATMPVC", + }, + { + Value: linux.AF_X25, + Name: "AF_X25", + }, + { + Value: linux.AF_INET6, + Name: "AF_INET6", + }, + { + Value: linux.AF_ROSE, + Name: "AF_ROSE", + }, + { + Value: linux.AF_DECnet, + Name: "AF_DECnet", + }, + { + Value: linux.AF_NETBEUI, + Name: "AF_NETBEUI", + }, + { + Value: linux.AF_SECURITY, + Name: "AF_SECURITY", + }, + { + Value: linux.AF_KEY, + Name: "AF_KEY", + }, + { + Value: linux.AF_NETLINK, + Name: "AF_NETLINK", + }, + { + Value: linux.AF_PACKET, + Name: "AF_PACKET", + }, + { + Value: linux.AF_ASH, + Name: "AF_ASH", + }, + { + Value: linux.AF_ECONET, + Name: "AF_ECONET", + }, + { + Value: linux.AF_ATMSVC, + Name: "AF_ATMSVC", + }, + { + Value: linux.AF_RDS, + Name: "AF_RDS", + }, + { + Value: linux.AF_SNA, + Name: "AF_SNA", + }, + { + Value: linux.AF_IRDA, + Name: "AF_IRDA", + }, + { + Value: linux.AF_PPPOX, + Name: "AF_PPPOX", + }, + { + Value: linux.AF_WANPIPE, + Name: "AF_WANPIPE", + }, + { + Value: linux.AF_LLC, + Name: "AF_LLC", + }, + { + Value: linux.AF_IB, + Name: "AF_IB", + }, + { + Value: linux.AF_MPLS, + Name: "AF_MPLS", + }, + { + Value: linux.AF_CAN, + Name: "AF_CAN", + }, + { + Value: linux.AF_TIPC, + Name: "AF_TIPC", + }, + { + Value: linux.AF_BLUETOOTH, + Name: "AF_BLUETOOTH", + }, + { + Value: linux.AF_IUCV, + Name: "AF_IUCV", + }, + { + Value: linux.AF_RXRPC, + Name: "AF_RXRPC", + }, + { + Value: linux.AF_ISDN, + Name: "AF_ISDN", + }, + { + Value: linux.AF_PHONET, + Name: "AF_PHONET", + }, + { + Value: linux.AF_IEEE802154, + Name: "AF_IEEE802154", + }, + { + Value: linux.AF_CAIF, + Name: "AF_CAIF", + }, + { + Value: linux.AF_ALG, + Name: "AF_ALG", + }, + { + Value: linux.AF_NFC, + Name: "AF_NFC", + }, + { + Value: linux.AF_VSOCK, + Name: "AF_VSOCK", + }, +} + +// SocketType are the possible socket(2) types. +var SocketType = abi.ValueSet{ + { + Value: linux.SOCK_STREAM, + Name: "SOCK_STREAM", + }, + { + Value: linux.SOCK_DGRAM, + Name: "SOCK_DGRAM", + }, + { + Value: linux.SOCK_RAW, + Name: "SOCK_RAW", + }, + { + Value: linux.SOCK_RDM, + Name: "SOCK_RDM", + }, + { + Value: linux.SOCK_SEQPACKET, + Name: "SOCK_SEQPACKET", + }, + { + Value: linux.SOCK_DCCP, + Name: "SOCK_DCCP", + }, + { + Value: linux.SOCK_PACKET, + Name: "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{ + { + Value: linux.IPPROTO_IP, + Name: "IPPROTO_IP", + }, + { + Value: linux.IPPROTO_ICMP, + Name: "IPPROTO_ICMP", + }, + { + Value: linux.IPPROTO_IGMP, + Name: "IPPROTO_IGMP", + }, + { + Value: linux.IPPROTO_IPIP, + Name: "IPPROTO_IPIP", + }, + { + Value: linux.IPPROTO_TCP, + Name: "IPPROTO_TCP", + }, + { + Value: linux.IPPROTO_EGP, + Name: "IPPROTO_EGP", + }, + { + Value: linux.IPPROTO_PUP, + Name: "IPPROTO_PUP", + }, + { + Value: linux.IPPROTO_UDP, + Name: "IPPROTO_UDP", + }, + { + Value: linux.IPPROTO_IDP, + Name: "IPPROTO_IDP", + }, + { + Value: linux.IPPROTO_TP, + Name: "IPPROTO_TP", + }, + { + Value: linux.IPPROTO_DCCP, + Name: "IPPROTO_DCCP", + }, + { + Value: linux.IPPROTO_IPV6, + Name: "IPPROTO_IPV6", + }, + { + Value: linux.IPPROTO_RSVP, + Name: "IPPROTO_RSVP", + }, + { + Value: linux.IPPROTO_GRE, + Name: "IPPROTO_GRE", + }, + { + Value: linux.IPPROTO_ESP, + Name: "IPPROTO_ESP", + }, + { + Value: linux.IPPROTO_AH, + Name: "IPPROTO_AH", + }, + { + Value: linux.IPPROTO_MTP, + Name: "IPPROTO_MTP", + }, + { + Value: linux.IPPROTO_BEETPH, + Name: "IPPROTO_BEETPH", + }, + { + Value: linux.IPPROTO_ENCAP, + Name: "IPPROTO_ENCAP", + }, + { + Value: linux.IPPROTO_PIM, + Name: "IPPROTO_PIM", + }, + { + Value: linux.IPPROTO_COMP, + Name: "IPPROTO_COMP", + }, + { + Value: linux.IPPROTO_SCTP, + Name: "IPPROTO_SCTP", + }, + { + Value: linux.IPPROTO_UDPLITE, + Name: "IPPROTO_UDPLITE", + }, + { + Value: linux.IPPROTO_MPLS, + Name: "IPPROTO_MPLS", + }, + { + Value: linux.IPPROTO_RAW, + Name: "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: { + { + Value: linux.NETLINK_ROUTE, + Name: "NETLINK_ROUTE", + }, + { + Value: linux.NETLINK_UNUSED, + Name: "NETLINK_UNUSED", + }, + { + Value: linux.NETLINK_USERSOCK, + Name: "NETLINK_USERSOCK", + }, + { + Value: linux.NETLINK_FIREWALL, + Name: "NETLINK_FIREWALL", + }, + { + Value: linux.NETLINK_SOCK_DIAG, + Name: "NETLINK_SOCK_DIAG", + }, + { + Value: linux.NETLINK_NFLOG, + Name: "NETLINK_NFLOG", + }, + { + Value: linux.NETLINK_XFRM, + Name: "NETLINK_XFRM", + }, + { + Value: linux.NETLINK_SELINUX, + Name: "NETLINK_SELINUX", + }, + { + Value: linux.NETLINK_ISCSI, + Name: "NETLINK_ISCSI", + }, + { + Value: linux.NETLINK_AUDIT, + Name: "NETLINK_AUDIT", + }, + { + Value: linux.NETLINK_FIB_LOOKUP, + Name: "NETLINK_FIB_LOOKUP", + }, + { + Value: linux.NETLINK_CONNECTOR, + Name: "NETLINK_CONNECTOR", + }, + { + Value: linux.NETLINK_NETFILTER, + Name: "NETLINK_NETFILTER", + }, + { + Value: linux.NETLINK_IP6_FW, + Name: "NETLINK_IP6_FW", + }, + { + Value: linux.NETLINK_DNRTMSG, + Name: "NETLINK_DNRTMSG", + }, + { + Value: linux.NETLINK_KOBJECT_UEVENT, + Name: "NETLINK_KOBJECT_UEVENT", + }, + { + Value: linux.NETLINK_GENERIC, + Name: "NETLINK_GENERIC", + }, + { + Value: linux.NETLINK_SCSITRANSPORT, + Name: "NETLINK_SCSITRANSPORT", + }, + { + Value: linux.NETLINK_ECRYPTFS, + Name: "NETLINK_ECRYPTFS", + }, + { + Value: linux.NETLINK_RDMA, + Name: "NETLINK_RDMA", + }, + { + Value: linux.NETLINK_CRYPTO, + Name: "NETLINK_CRYPTO", + }, + }, +} + +var controlMessageType = map[int32]string{ + linux.SCM_RIGHTS: "SCM_RIGHTS", + linux.SCM_CREDENTIALS: "SCM_CREDENTIALS", +} + +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) + i += linux.SizeOfControlMessageHeader + + 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 + } + + 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(i+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, ","), + )) + + i += control.AlignUp(length, width) + + 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, + )) + i += control.AlignUp(length, width) + 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, + )) + + i += control.AlignUp(length, width) + + default: + panic("unreachable") + } + } + + 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..4cd16d2f8 --- /dev/null +++ b/pkg/sentry/strace/strace.go @@ -0,0 +1,666 @@ +// Copyright 2018 Google Inc. +// +// 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/linux" + "gvisor.googlesource.com/gvisor/pkg/bits" + "gvisor.googlesource.com/gvisor/pkg/eventchannel" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + 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 + +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, syscall.PathMax) + if err != nil { + return fmt.Sprintf("%#x (error decoding path: %s)", addr, err) + } + return fmt.Sprintf("%#x %s", addr, path) +} + +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 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) +} + +// 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 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 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 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 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 Timeval: + output[arg] = timeval(t, args[arg].Pointer()) + case Rusage: + output[arg] = rusage(t, args[arg].Pointer()) + } + } +} + +// 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 call 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: 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) + } +} diff --git a/pkg/sentry/strace/strace.proto b/pkg/sentry/strace/strace.proto new file mode 100644 index 000000000..914e8c7b0 --- /dev/null +++ b/pkg/sentry/strace/strace.proto @@ -0,0 +1,50 @@ +// Copyright 2018 Google Inc. +// +// 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. + +syntax = "proto3"; + +package gvisor; + +message Strace { + // Process name that made the syscall. + string process = 1; + + // Syscall function name. + string function = 2; + + // List of syscall arguments formatted as strings. + repeated string args = 3; + + oneof info { + StraceEnter enter = 4; + StraceExit exit = 5; + } +} + +message StraceEnter { +} + +message StraceExit { + // Return value formatted as string. + string return = 1; + + // Formatted error string in case syscall failed. + string error = 2; + + // Value of errno upon syscall exit. + int64 err_no = 3; // errno is a macro and gets expanded :-( + + // Time elapsed between syscall enter and exit. + int64 elapsed_ns = 4; +} diff --git a/pkg/sentry/strace/syscalls.go b/pkg/sentry/strace/syscalls.go new file mode 100644 index 000000000..d0e661706 --- /dev/null +++ b/pkg/sentry/strace/syscalls.go @@ -0,0 +1,217 @@ +// Copyright 2018 Google Inc. +// +// 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 + + // 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 + + // 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 exection. + 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 + + // ItimerVal is a pointer to a struct itimerval, formatted after + // syscall execution. + PostItimerVal + + // Timeval is a pointer to a struct timeval, formatted before and after + // syscall execution. + Timeval + + // Utimbuf is a pointer to a struct utimbuf. + Utimbuf + + // 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 + + // Rusage is a struct rusage, formatted after syscall execution. + Rusage +) + +// 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 +} |