summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/issue_reviver.yml2
-rw-r--r--.github/workflows/stale.yml22
-rw-r--r--Makefile3
-rw-r--r--README.md4
-rw-r--r--WORKSPACE1331
-rw-r--r--g3doc/user_guide/compatibility.md75
-rw-r--r--g3doc/user_guide/debugging.md35
-rw-r--r--go.mod128
-rw-r--r--go.sum698
-rw-r--r--nogo.yaml51
-rw-r--r--pkg/abi/linux/BUILD2
-rw-r--r--pkg/abi/linux/errno/BUILD9
-rw-r--r--pkg/abi/linux/errno/errno.go (renamed from pkg/abi/linux/errors.go)14
-rw-r--r--pkg/abi/linux/netfilter_ipv6.go1
-rw-r--r--pkg/abi/linux/signal.go344
-rw-r--r--pkg/abi/linux/xattr.go6
-rw-r--r--pkg/control/server/server.go5
-rw-r--r--pkg/errors/BUILD10
-rw-r--r--pkg/errors/errors.go40
-rw-r--r--pkg/errors/linuxerr/BUILD (renamed from pkg/linuxerr/BUILD)8
-rw-r--r--pkg/errors/linuxerr/linuxerr.go349
-rw-r--r--pkg/errors/linuxerr/linuxerr_test.go306
-rw-r--r--pkg/eventchannel/BUILD2
-rw-r--r--pkg/eventchannel/event.go4
-rw-r--r--pkg/flipcall/BUILD4
-rw-r--r--pkg/flipcall/flipcall.go7
-rw-r--r--pkg/flipcall/packet_window.go (renamed from pkg/flipcall/packet_window_allocator.go)0
-rw-r--r--pkg/iovec/BUILD18
-rw-r--r--pkg/iovec/iovec.go71
-rw-r--r--pkg/iovec/iovec_test.go120
-rw-r--r--pkg/linuxerr/linuxerr.go184
-rw-r--r--pkg/linuxerr/linuxerr_test.go164
-rw-r--r--pkg/marshal/primitive/BUILD2
-rw-r--r--pkg/marshal/primitive/primitive.go25
-rw-r--r--pkg/memutil/BUILD6
-rw-r--r--pkg/memutil/memfd_linux_unsafe.go (renamed from pkg/memutil/memutil_unsafe.go)1
-rw-r--r--pkg/memutil/memutil.go16
-rw-r--r--pkg/memutil/mmap.go (renamed from pkg/flipcall/packet_window_mmap_amd64.go)18
-rw-r--r--pkg/merkletree/merkletree.go18
-rw-r--r--pkg/merkletree/merkletree_test.go40
-rw-r--r--pkg/metric/metric.go74
-rw-r--r--pkg/metric/metric_test.go18
-rw-r--r--pkg/p9/BUILD3
-rw-r--r--pkg/p9/handlers.go42
-rw-r--r--pkg/p9/server.go19
-rw-r--r--pkg/refs/refcounter.go6
-rw-r--r--pkg/ring0/kernel_amd64.go22
-rw-r--r--pkg/safecopy/BUILD1
-rw-r--r--pkg/safecopy/safecopy_unsafe.go14
-rw-r--r--pkg/seccomp/seccomp.go12
-rw-r--r--pkg/sentry/arch/BUILD5
-rw-r--r--pkg/sentry/arch/arch.go14
-rw-r--r--pkg/sentry/arch/arch_aarch64.go5
-rw-r--r--pkg/sentry/arch/arch_x86.go5
-rw-r--r--pkg/sentry/arch/signal.go276
-rw-r--r--pkg/sentry/arch/signal_act.go83
-rw-r--r--pkg/sentry/arch/signal_amd64.go26
-rw-r--r--pkg/sentry/arch/signal_arm64.go26
-rw-r--r--pkg/sentry/arch/signal_info.go66
-rw-r--r--pkg/sentry/arch/signal_stack.go68
-rw-r--r--pkg/sentry/arch/stack.go2
-rw-r--r--pkg/sentry/control/proc.go9
-rw-r--r--pkg/sentry/devices/tundev/BUILD1
-rw-r--r--pkg/sentry/devices/tundev/tundev.go5
-rw-r--r--pkg/sentry/fs/BUILD3
-rw-r--r--pkg/sentry/fs/copy_up.go5
-rw-r--r--pkg/sentry/fs/dev/BUILD1
-rw-r--r--pkg/sentry/fs/dev/net_tun.go5
-rw-r--r--pkg/sentry/fs/dirent.go97
-rw-r--r--pkg/sentry/fs/fdpipe/BUILD3
-rw-r--r--pkg/sentry/fs/fdpipe/pipe.go3
-rw-r--r--pkg/sentry/fs/fdpipe/pipe_opener_test.go6
-rw-r--r--pkg/sentry/fs/fdpipe/pipe_test.go19
-rw-r--r--pkg/sentry/fs/file_overlay.go9
-rw-r--r--pkg/sentry/fs/fs.go26
-rw-r--r--pkg/sentry/fs/fsutil/BUILD1
-rw-r--r--pkg/sentry/fs/fsutil/file.go37
-rw-r--r--pkg/sentry/fs/fsutil/inode.go19
-rw-r--r--pkg/sentry/fs/gofer/BUILD1
-rw-r--r--pkg/sentry/fs/gofer/cache_policy.go2
-rw-r--r--pkg/sentry/fs/gofer/inode.go7
-rw-r--r--pkg/sentry/fs/gofer/inode_state.go10
-rw-r--r--pkg/sentry/fs/gofer/path.go109
-rw-r--r--pkg/sentry/fs/gofer/session.go17
-rw-r--r--pkg/sentry/fs/gofer/socket.go9
-rw-r--r--pkg/sentry/fs/host/BUILD3
-rw-r--r--pkg/sentry/fs/host/file.go3
-rw-r--r--pkg/sentry/fs/host/host.go4
-rw-r--r--pkg/sentry/fs/host/inode.go25
-rw-r--r--pkg/sentry/fs/host/socket.go6
-rw-r--r--pkg/sentry/fs/host/socket_iovec.go10
-rw-r--r--pkg/sentry/fs/host/tty.go7
-rw-r--r--pkg/sentry/fs/host/util.go4
-rw-r--r--pkg/sentry/fs/inode.go7
-rw-r--r--pkg/sentry/fs/inode_operations.go2
-rw-r--r--pkg/sentry/fs/inode_overlay.go21
-rw-r--r--pkg/sentry/fs/inode_overlay_test.go8
-rw-r--r--pkg/sentry/fs/inotify.go15
-rw-r--r--pkg/sentry/fs/mock.go6
-rw-r--r--pkg/sentry/fs/mounts.go15
-rw-r--r--pkg/sentry/fs/overlay.go4
-rw-r--r--pkg/sentry/fs/proc/BUILD1
-rw-r--r--pkg/sentry/fs/proc/exec_args.go4
-rw-r--r--pkg/sentry/fs/proc/net.go4
-rw-r--r--pkg/sentry/fs/proc/proc.go5
-rw-r--r--pkg/sentry/fs/proc/seqfile/BUILD2
-rw-r--r--pkg/sentry/fs/proc/seqfile/seqfile.go4
-rw-r--r--pkg/sentry/fs/proc/sys.go22
-rw-r--r--pkg/sentry/fs/proc/sys_net.go4
-rw-r--r--pkg/sentry/fs/proc/task.go17
-rw-r--r--pkg/sentry/fs/proc/uid_gid_map.go10
-rw-r--r--pkg/sentry/fs/proc/uptime.go4
-rw-r--r--pkg/sentry/fs/ramfs/BUILD1
-rw-r--r--pkg/sentry/fs/ramfs/dir.go33
-rw-r--r--pkg/sentry/fs/ramfs/socket.go4
-rw-r--r--pkg/sentry/fs/splice.go7
-rw-r--r--pkg/sentry/fs/timerfd/BUILD1
-rw-r--r--pkg/sentry/fs/timerfd/timerfd.go5
-rw-r--r--pkg/sentry/fs/tmpfs/BUILD2
-rw-r--r--pkg/sentry/fs/tmpfs/inode_file.go24
-rw-r--r--pkg/sentry/fs/tmpfs/tmpfs.go4
-rw-r--r--pkg/sentry/fs/tty/BUILD1
-rw-r--r--pkg/sentry/fs/tty/dir.go17
-rw-r--r--pkg/sentry/fs/tty/fs.go4
-rw-r--r--pkg/sentry/fs/user/BUILD1
-rw-r--r--pkg/sentry/fs/user/path.go5
-rw-r--r--pkg/sentry/fsbridge/BUILD1
-rw-r--r--pkg/sentry/fsbridge/fs.go5
-rw-r--r--pkg/sentry/fsimpl/cgroupfs/BUILD1
-rw-r--r--pkg/sentry/fsimpl/cgroupfs/base.go25
-rw-r--r--pkg/sentry/fsimpl/cgroupfs/cgroupfs.go17
-rw-r--r--pkg/sentry/fsimpl/devpts/BUILD1
-rw-r--r--pkg/sentry/fsimpl/devpts/devpts.go3
-rw-r--r--pkg/sentry/fsimpl/devpts/master.go3
-rw-r--r--pkg/sentry/fsimpl/devpts/replica.go3
-rw-r--r--pkg/sentry/fsimpl/ext/BUILD3
-rw-r--r--pkg/sentry/fsimpl/ext/block_map_file.go3
-rw-r--r--pkg/sentry/fsimpl/ext/directory.go6
-rw-r--r--pkg/sentry/fsimpl/ext/ext.go6
-rw-r--r--pkg/sentry/fsimpl/ext/ext_test.go8
-rw-r--r--pkg/sentry/fsimpl/ext/extent_file.go5
-rw-r--r--pkg/sentry/fsimpl/ext/file_description.go4
-rw-r--r--pkg/sentry/fsimpl/ext/filesystem.go31
-rw-r--r--pkg/sentry/fsimpl/ext/inode.go5
-rw-r--r--pkg/sentry/fsimpl/ext/regular_file.go9
-rw-r--r--pkg/sentry/fsimpl/ext/symlink.go13
-rw-r--r--pkg/sentry/fsimpl/fuse/BUILD2
-rw-r--r--pkg/sentry/fsimpl/fuse/connection.go8
-rw-r--r--pkg/sentry/fsimpl/fuse/connection_test.go4
-rw-r--r--pkg/sentry/fsimpl/fuse/dev.go21
-rw-r--r--pkg/sentry/fsimpl/fuse/fusefs.go55
-rw-r--r--pkg/sentry/fsimpl/fuse/read_write.go5
-rw-r--r--pkg/sentry/fsimpl/fuse/regular_file.go11
-rw-r--r--pkg/sentry/fsimpl/gofer/BUILD1
-rw-r--r--pkg/sentry/fsimpl/gofer/directory.go8
-rw-r--r--pkg/sentry/fsimpl/gofer/filesystem.go103
-rw-r--r--pkg/sentry/fsimpl/gofer/gofer.go82
-rw-r--r--pkg/sentry/fsimpl/gofer/host_named_pipe.go3
-rw-r--r--pkg/sentry/fsimpl/gofer/regular_file.go63
-rw-r--r--pkg/sentry/fsimpl/gofer/revalidate.go10
-rw-r--r--pkg/sentry/fsimpl/gofer/save_restore.go4
-rw-r--r--pkg/sentry/fsimpl/gofer/special_file.go11
-rw-r--r--pkg/sentry/fsimpl/gofer/symlink.go2
-rw-r--r--pkg/sentry/fsimpl/host/BUILD2
-rw-r--r--pkg/sentry/fsimpl/host/host.go39
-rw-r--r--pkg/sentry/fsimpl/host/socket.go6
-rw-r--r--pkg/sentry/fsimpl/host/socket_iovec.go10
-rw-r--r--pkg/sentry/fsimpl/host/tty.go7
-rw-r--r--pkg/sentry/fsimpl/host/util.go4
-rw-r--r--pkg/sentry/fsimpl/kernfs/BUILD2
-rw-r--r--pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go6
-rw-r--r--pkg/sentry/fsimpl/kernfs/fd_impl_util.go5
-rw-r--r--pkg/sentry/fsimpl/kernfs/filesystem.go60
-rw-r--r--pkg/sentry/fsimpl/kernfs/inode_impl_util.go29
-rw-r--r--pkg/sentry/fsimpl/kernfs/kernfs_test.go14
-rw-r--r--pkg/sentry/fsimpl/kernfs/symlink.go4
-rw-r--r--pkg/sentry/fsimpl/kernfs/synthetic_directory.go12
-rw-r--r--pkg/sentry/fsimpl/overlay/BUILD1
-rw-r--r--pkg/sentry/fsimpl/overlay/copy_up.go9
-rw-r--r--pkg/sentry/fsimpl/overlay/directory.go12
-rw-r--r--pkg/sentry/fsimpl/overlay/filesystem.go57
-rw-r--r--pkg/sentry/fsimpl/overlay/overlay.go24
-rw-r--r--pkg/sentry/fsimpl/overlay/regular_file.go4
-rw-r--r--pkg/sentry/fsimpl/pipefs/BUILD2
-rw-r--r--pkg/sentry/fsimpl/pipefs/pipefs.go4
-rw-r--r--pkg/sentry/fsimpl/proc/BUILD3
-rw-r--r--pkg/sentry/fsimpl/proc/filesystem.go4
-rw-r--r--pkg/sentry/fsimpl/proc/subtasks.go3
-rw-r--r--pkg/sentry/fsimpl/proc/task.go3
-rw-r--r--pkg/sentry/fsimpl/proc/task_files.go25
-rw-r--r--pkg/sentry/fsimpl/proc/task_net.go4
-rw-r--r--pkg/sentry/fsimpl/proc/tasks_files.go9
-rw-r--r--pkg/sentry/fsimpl/proc/tasks_sys.go15
-rw-r--r--pkg/sentry/fsimpl/proc/tasks_test.go6
-rw-r--r--pkg/sentry/fsimpl/proc/yama.go6
-rw-r--r--pkg/sentry/fsimpl/sockfs/BUILD2
-rw-r--r--pkg/sentry/fsimpl/sockfs/sockfs.go4
-rw-r--r--pkg/sentry/fsimpl/sys/BUILD1
-rw-r--r--pkg/sentry/fsimpl/sys/kcov.go3
-rw-r--r--pkg/sentry/fsimpl/sys/sys.go6
-rw-r--r--pkg/sentry/fsimpl/testutil/BUILD1
-rw-r--r--pkg/sentry/fsimpl/testutil/kernel.go10
-rw-r--r--pkg/sentry/fsimpl/timerfd/BUILD1
-rw-r--r--pkg/sentry/fsimpl/timerfd/timerfd.go3
-rw-r--r--pkg/sentry/fsimpl/tmpfs/BUILD3
-rw-r--r--pkg/sentry/fsimpl/tmpfs/benchmark_test.go3
-rw-r--r--pkg/sentry/fsimpl/tmpfs/directory.go6
-rw-r--r--pkg/sentry/fsimpl/tmpfs/filesystem.go64
-rw-r--r--pkg/sentry/fsimpl/tmpfs/pipe_test.go3
-rw-r--r--pkg/sentry/fsimpl/tmpfs/regular_file.go29
-rw-r--r--pkg/sentry/fsimpl/tmpfs/tmpfs.go74
-rw-r--r--pkg/sentry/fsimpl/verity/BUILD3
-rw-r--r--pkg/sentry/fsimpl/verity/filesystem.go72
-rw-r--r--pkg/sentry/fsimpl/verity/verity.go77
-rw-r--r--pkg/sentry/fsimpl/verity/verity_test.go16
-rw-r--r--pkg/sentry/hostfd/hostfd_linux.go9
-rw-r--r--pkg/sentry/hostfd/hostfd_unsafe.go17
-rw-r--r--pkg/sentry/kernel/BUILD7
-rw-r--r--pkg/sentry/kernel/auth/BUILD3
-rw-r--r--pkg/sentry/kernel/auth/credentials.go16
-rw-r--r--pkg/sentry/kernel/auth/id_map.go46
-rw-r--r--pkg/sentry/kernel/auth/user_namespace.go8
-rw-r--r--pkg/sentry/kernel/fasync/BUILD3
-rw-r--r--pkg/sentry/kernel/fasync/fasync.go9
-rw-r--r--pkg/sentry/kernel/fd_table.go4
-rw-r--r--pkg/sentry/kernel/futex/BUILD5
-rw-r--r--pkg/sentry/kernel/futex/futex.go54
-rw-r--r--pkg/sentry/kernel/futex/futex_test.go4
-rw-r--r--pkg/sentry/kernel/kcov.go20
-rw-r--r--pkg/sentry/kernel/kernel.go79
-rw-r--r--pkg/sentry/kernel/pending_signals.go9
-rw-r--r--pkg/sentry/kernel/pending_signals_state.go6
-rw-r--r--pkg/sentry/kernel/pipe/BUILD2
-rw-r--r--pkg/sentry/kernel/pipe/node.go7
-rw-r--r--pkg/sentry/kernel/pipe/node_test.go3
-rw-r--r--pkg/sentry/kernel/pipe/pipe.go7
-rw-r--r--pkg/sentry/kernel/pipe/pipe_unsafe.go2
-rw-r--r--pkg/sentry/kernel/pipe/pipe_util.go6
-rw-r--r--pkg/sentry/kernel/pipe/vfs.go11
-rw-r--r--pkg/sentry/kernel/posixtimer.go25
-rw-r--r--pkg/sentry/kernel/ptrace.go49
-rw-r--r--pkg/sentry/kernel/rseq.go25
-rw-r--r--pkg/sentry/kernel/seccomp.go6
-rw-r--r--pkg/sentry/kernel/semaphore/BUILD1
-rw-r--r--pkg/sentry/kernel/semaphore/semaphore.go37
-rw-r--r--pkg/sentry/kernel/sessions.go30
-rw-r--r--pkg/sentry/kernel/shm/BUILD1
-rw-r--r--pkg/sentry/kernel/shm/shm.go17
-rw-r--r--pkg/sentry/kernel/signal.go15
-rw-r--r--pkg/sentry/kernel/signal_handlers.go17
-rw-r--r--pkg/sentry/kernel/signalfd/BUILD1
-rw-r--r--pkg/sentry/kernel/signalfd/signalfd.go3
-rw-r--r--pkg/sentry/kernel/task.go19
-rw-r--r--pkg/sentry/kernel/task_acct.go6
-rw-r--r--pkg/sentry/kernel/task_block.go5
-rw-r--r--pkg/sentry/kernel/task_cgroup.go12
-rw-r--r--pkg/sentry/kernel/task_clone.go32
-rw-r--r--pkg/sentry/kernel/task_exec.go3
-rw-r--r--pkg/sentry/kernel/task_exit.go46
-rw-r--r--pkg/sentry/kernel/task_identity.go46
-rw-r--r--pkg/sentry/kernel/task_image.go4
-rw-r--r--pkg/sentry/kernel/task_sched.go14
-rw-r--r--pkg/sentry/kernel/task_signals.go141
-rw-r--r--pkg/sentry/kernel/task_start.go6
-rw-r--r--pkg/sentry/kernel/task_syscall.go6
-rw-r--r--pkg/sentry/kernel/task_usermem.go5
-rw-r--r--pkg/sentry/kernel/thread_group.go26
-rw-r--r--pkg/sentry/kernel/time/BUILD2
-rw-r--r--pkg/sentry/kernel/time/time.go6
-rw-r--r--pkg/sentry/kernel/timekeeper.go42
-rw-r--r--pkg/sentry/kernel/timekeeper_test.go4
-rw-r--r--pkg/sentry/loader/BUILD2
-rw-r--r--pkg/sentry/loader/elf.go15
-rw-r--r--pkg/sentry/loader/interpreter.go2
-rw-r--r--pkg/sentry/loader/loader.go8
-rw-r--r--pkg/sentry/loader/vdso.go3
-rw-r--r--pkg/sentry/mm/BUILD3
-rw-r--r--pkg/sentry/mm/aio_context.go15
-rw-r--r--pkg/sentry/mm/mm_test.go8
-rw-r--r--pkg/sentry/mm/shm.go6
-rw-r--r--pkg/sentry/mm/special_mappable.go5
-rw-r--r--pkg/sentry/mm/syscalls.go74
-rw-r--r--pkg/sentry/mm/vma.go7
-rw-r--r--pkg/sentry/pgalloc/BUILD1
-rw-r--r--pkg/sentry/pgalloc/pgalloc.go23
-rw-r--r--pkg/sentry/platform/kvm/BUILD1
-rw-r--r--pkg/sentry/platform/kvm/bluepill_arm64.go23
-rw-r--r--pkg/sentry/platform/kvm/bluepill_arm64_unsafe.go29
-rw-r--r--pkg/sentry/platform/kvm/context.go7
-rw-r--r--pkg/sentry/platform/kvm/kvm_amd64_test.go5
-rw-r--r--pkg/sentry/platform/kvm/kvm_test.go33
-rw-r--r--pkg/sentry/platform/kvm/machine_amd64.go36
-rw-r--r--pkg/sentry/platform/kvm/machine_arm64.go12
-rw-r--r--pkg/sentry/platform/kvm/machine_arm64_unsafe.go21
-rw-r--r--pkg/sentry/platform/platform.go6
-rw-r--r--pkg/sentry/platform/ptrace/ptrace.go4
-rw-r--r--pkg/sentry/platform/ptrace/ptrace_unsafe.go2
-rw-r--r--pkg/sentry/platform/ptrace/subprocess.go3
-rw-r--r--pkg/sentry/platform/ptrace/subprocess_amd64.go2
-rw-r--r--pkg/sentry/platform/ptrace/subprocess_arm64.go2
-rw-r--r--pkg/sentry/sighandling/sighandling_unsafe.go13
-rw-r--r--pkg/sentry/socket/control/BUILD2
-rw-r--r--pkg/sentry/socket/control/control.go40
-rw-r--r--pkg/sentry/socket/control/control_vfs2.go4
-rw-r--r--pkg/sentry/socket/hostinet/BUILD1
-rw-r--r--pkg/sentry/socket/hostinet/socket.go194
-rw-r--r--pkg/sentry/socket/hostinet/socket_unsafe.go18
-rw-r--r--pkg/sentry/socket/hostinet/socket_vfs2.go5
-rw-r--r--pkg/sentry/socket/hostinet/stack.go18
-rw-r--r--pkg/sentry/socket/netfilter/BUILD1
-rw-r--r--pkg/sentry/socket/netfilter/extensions.go16
-rw-r--r--pkg/sentry/socket/netfilter/ipv4.go9
-rw-r--r--pkg/sentry/socket/netfilter/ipv6.go9
-rw-r--r--pkg/sentry/socket/netfilter/netfilter.go18
-rw-r--r--pkg/sentry/socket/netfilter/owner_matcher.go24
-rw-r--r--pkg/sentry/socket/netfilter/targets.go5
-rw-r--r--pkg/sentry/socket/netfilter/tcp_matcher.go5
-rw-r--r--pkg/sentry/socket/netfilter/udp_matcher.go5
-rw-r--r--pkg/sentry/socket/netlink/BUILD2
-rw-r--r--pkg/sentry/socket/netlink/socket.go6
-rw-r--r--pkg/sentry/socket/netlink/socket_vfs2.go5
-rw-r--r--pkg/sentry/socket/netstack/BUILD2
-rw-r--r--pkg/sentry/socket/netstack/netstack.go21
-rw-r--r--pkg/sentry/socket/netstack/stack.go12
-rw-r--r--pkg/sentry/socket/netstack/tun.go4
-rw-r--r--pkg/sentry/socket/socket.go2
-rw-r--r--pkg/sentry/socket/unix/BUILD1
-rw-r--r--pkg/sentry/socket/unix/unix.go5
-rw-r--r--pkg/sentry/socket/unix/unix_vfs2.go7
-rw-r--r--pkg/sentry/state/BUILD2
-rw-r--r--pkg/sentry/state/state.go8
-rw-r--r--pkg/sentry/strace/BUILD2
-rw-r--r--pkg/sentry/strace/clone.go46
-rw-r--r--pkg/sentry/strace/linux64_amd64.go2
-rw-r--r--pkg/sentry/strace/linux64_arm64.go2
-rw-r--r--pkg/sentry/strace/mmap.go92
-rw-r--r--pkg/sentry/strace/open.go42
-rw-r--r--pkg/sentry/strace/signal.go4
-rw-r--r--pkg/sentry/strace/strace.go7
-rw-r--r--pkg/sentry/strace/syscalls.go6
-rw-r--r--pkg/sentry/syscalls/BUILD1
-rw-r--r--pkg/sentry/syscalls/epoll.go26
-rw-r--r--pkg/sentry/syscalls/linux/BUILD1
-rw-r--r--pkg/sentry/syscalls/linux/error.go27
-rw-r--r--pkg/sentry/syscalls/linux/linux64.go33
-rw-r--r--pkg/sentry/syscalls/linux/sigset.go3
-rw-r--r--pkg/sentry/syscalls/linux/sys_aio.go31
-rw-r--r--pkg/sentry/syscalls/linux/sys_capability.go11
-rw-r--r--pkg/sentry/syscalls/linux/sys_epoll.go7
-rw-r--r--pkg/sentry/syscalls/linux/sys_eventfd.go4
-rw-r--r--pkg/sentry/syscalls/linux/sys_file.go193
-rw-r--r--pkg/sentry/syscalls/linux/sys_futex.go11
-rw-r--r--pkg/sentry/syscalls/linux/sys_getdents.go7
-rw-r--r--pkg/sentry/syscalls/linux/sys_identity.go8
-rw-r--r--pkg/sentry/syscalls/linux/sys_inotify.go9
-rw-r--r--pkg/sentry/syscalls/linux/sys_lseek.go5
-rw-r--r--pkg/sentry/syscalls/linux/sys_membarrier.go30
-rw-r--r--pkg/sentry/syscalls/linux/sys_mempolicy.go35
-rw-r--r--pkg/sentry/syscalls/linux/sys_mmap.go32
-rw-r--r--pkg/sentry/syscalls/linux/sys_mount.go19
-rw-r--r--pkg/sentry/syscalls/linux/sys_pipe.go4
-rw-r--r--pkg/sentry/syscalls/linux/sys_poll.go19
-rw-r--r--pkg/sentry/syscalls/linux/sys_prctl.go32
-rw-r--r--pkg/sentry/syscalls/linux/sys_random.go6
-rw-r--r--pkg/sentry/syscalls/linux/sys_read.go49
-rw-r--r--pkg/sentry/syscalls/linux/sys_rlimit.go13
-rw-r--r--pkg/sentry/syscalls/linux/sys_rseq.go3
-rw-r--r--pkg/sentry/syscalls/linux/sys_rusage.go4
-rw-r--r--pkg/sentry/syscalls/linux/sys_sched.go15
-rw-r--r--pkg/sentry/syscalls/linux/sys_seccomp.go8
-rw-r--r--pkg/sentry/syscalls/linux/sys_sem.go45
-rw-r--r--pkg/sentry/syscalls/linux/sys_shm.go12
-rw-r--r--pkg/sentry/syscalls/linux/sys_signal.go88
-rw-r--r--pkg/sentry/syscalls/linux/sys_socket.go83
-rw-r--r--pkg/sentry/syscalls/linux/sys_splice.go47
-rw-r--r--pkg/sentry/syscalls/linux/sys_stat.go15
-rw-r--r--pkg/sentry/syscalls/linux/sys_sync.go13
-rw-r--r--pkg/sentry/syscalls/linux/sys_syslog.go3
-rw-r--r--pkg/sentry/syscalls/linux/sys_thread.go43
-rw-r--r--pkg/sentry/syscalls/linux/sys_time.go25
-rw-r--r--pkg/sentry/syscalls/linux/sys_timerfd.go16
-rw-r--r--pkg/sentry/syscalls/linux/sys_tls_amd64.go5
-rw-r--r--pkg/sentry/syscalls/linux/sys_utsname.go10
-rw-r--r--pkg/sentry/syscalls/linux/sys_write.go41
-rw-r--r--pkg/sentry/syscalls/linux/sys_xattr.go21
-rw-r--r--pkg/sentry/syscalls/linux/timespec.go3
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/BUILD1
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/aio.go22
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/epoll.go24
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/eventfd.go4
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/execve.go8
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/fd.go51
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/filesystem.go8
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/fscontext.go3
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/getdents.go11
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/inotify.go10
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/ioctl.go8
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/lock.go6
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/memfd.go4
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/mmap.go11
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/mount.go13
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/path.go6
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/pipe.go7
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/poll.go23
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/read_write.go61
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/setstat.go50
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/signal.go11
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/socket.go83
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/splice.go57
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/stat.go22
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/sync.go13
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/timerfd.go16
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/xattr.go21
-rw-r--r--pkg/sentry/syscalls/syscalls.go5
-rw-r--r--pkg/sentry/time/BUILD5
-rw-r--r--pkg/sentry/time/calibrated_clock.go4
-rw-r--r--pkg/sentry/time/calibrated_clock_test.go1
-rw-r--r--pkg/sentry/time/sampler.go7
-rw-r--r--pkg/sentry/time/sampler_amd64.go26
-rw-r--r--pkg/sentry/time/sampler_arm64.go42
-rw-r--r--pkg/sentry/time/tsc_arm64.s6
-rw-r--r--pkg/sentry/usage/memory.go2
-rw-r--r--pkg/sentry/usage/memory_unsafe.go6
-rw-r--r--pkg/sentry/vfs/BUILD2
-rw-r--r--pkg/sentry/vfs/anonfs.go27
-rw-r--r--pkg/sentry/vfs/dentry.go29
-rw-r--r--pkg/sentry/vfs/device.go6
-rw-r--r--pkg/sentry/vfs/epoll.go3
-rw-r--r--pkg/sentry/vfs/file_description.go30
-rw-r--r--pkg/sentry/vfs/file_description_impl_util.go49
-rw-r--r--pkg/sentry/vfs/file_description_impl_util_test.go11
-rw-r--r--pkg/sentry/vfs/inotify.go17
-rw-r--r--pkg/sentry/vfs/memxattr/BUILD2
-rw-r--r--pkg/sentry/vfs/memxattr/xattr.go42
-rw-r--r--pkg/sentry/vfs/mount.go27
-rw-r--r--pkg/sentry/vfs/opath.go32
-rw-r--r--pkg/sentry/vfs/permissions.go31
-rw-r--r--pkg/sentry/vfs/resolving_path.go5
-rw-r--r--pkg/sentry/vfs/vfs.go49
-rw-r--r--pkg/sentry/watchdog/watchdog.go13
-rw-r--r--pkg/shim/BUILD1
-rw-r--r--pkg/shim/debug.go48
-rw-r--r--pkg/shim/proc/BUILD2
-rw-r--r--pkg/shim/proc/deleted_state.go30
-rw-r--r--pkg/shim/proc/exec.go106
-rw-r--r--pkg/shim/proc/exec_state.go84
-rw-r--r--pkg/shim/proc/init.go84
-rw-r--r--pkg/shim/proc/init_state.go175
-rw-r--r--pkg/shim/proc/proc.go18
-rw-r--r--pkg/shim/runsc/runsc.go87
-rw-r--r--pkg/shim/service.go73
-rw-r--r--pkg/shim/utils/BUILD14
-rw-r--r--pkg/shim/utils/errors.go74
-rw-r--r--pkg/shim/utils/errors_test.go50
-rw-r--r--pkg/sync/BUILD40
-rw-r--r--pkg/sync/atomicptr/BUILD (renamed from pkg/sync/atomicptrtest/BUILD)13
-rw-r--r--pkg/sync/atomicptr/atomicptr_test.go (renamed from pkg/sync/atomicptrtest/atomicptr_test.go)0
-rw-r--r--pkg/sync/atomicptr/generic_atomicptr_unsafe.go (renamed from pkg/sync/generic_atomicptr_unsafe.go)0
-rw-r--r--pkg/sync/atomicptrmap/BUILD (renamed from pkg/sync/atomicptrmaptest/BUILD)25
-rw-r--r--pkg/sync/atomicptrmap/atomicptrmap.go (renamed from pkg/sync/atomicptrmaptest/atomicptrmap.go)0
-rw-r--r--pkg/sync/atomicptrmap/atomicptrmap_test.go (renamed from pkg/sync/atomicptrmaptest/atomicptrmap_test.go)0
-rw-r--r--pkg/sync/atomicptrmap/generic_atomicptrmap_unsafe.go (renamed from pkg/sync/generic_atomicptrmap_unsafe.go)0
-rw-r--r--pkg/sync/mutex_test.go2
-rw-r--r--pkg/sync/mutex_unsafe.go14
-rw-r--r--pkg/sync/rwmutex_test.go2
-rw-r--r--pkg/sync/rwmutex_unsafe.go8
-rw-r--r--pkg/sync/seqatomic/BUILD (renamed from pkg/sync/seqatomictest/BUILD)17
-rw-r--r--pkg/sync/seqatomic/generic_seqatomic_unsafe.go (renamed from pkg/sync/generic_seqatomic_unsafe.go)0
-rw-r--r--pkg/sync/seqatomic/seqatomic_test.go (renamed from pkg/sync/seqatomictest/seqatomic_test.go)0
-rw-r--r--pkg/syserr/BUILD4
-rw-r--r--pkg/syserr/netstack.go56
-rw-r--r--pkg/syserr/syserr.go302
-rw-r--r--pkg/syserror/syserror.go74
-rw-r--r--pkg/tcpip/BUILD1
-rw-r--r--pkg/tcpip/header/checksum.go62
-rw-r--r--pkg/tcpip/header/checksum_test.go203
-rw-r--r--pkg/tcpip/header/interfaces.go38
-rw-r--r--pkg/tcpip/header/ipv4.go12
-rw-r--r--pkg/tcpip/header/ndp_options.go150
-rw-r--r--pkg/tcpip/header/ndp_router_advert.go94
-rw-r--r--pkg/tcpip/header/ndp_test.go339
-rw-r--r--pkg/tcpip/header/tcp.go29
-rw-r--r--pkg/tcpip/header/udp.go29
-rw-r--r--pkg/tcpip/link/fdbased/BUILD1
-rw-r--r--pkg/tcpip/link/fdbased/endpoint.go221
-rw-r--r--pkg/tcpip/link/rawfile/rawfile_unsafe.go61
-rw-r--r--pkg/tcpip/link/tun/BUILD1
-rw-r--r--pkg/tcpip/link/tun/device.go29
-rw-r--r--pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go10
-rw-r--r--pkg/tcpip/network/ip_test.go40
-rw-r--r--pkg/tcpip/network/ipv4/icmp.go5
-rw-r--r--pkg/tcpip/network/ipv4/ipv4.go32
-rw-r--r--pkg/tcpip/network/ipv6/icmp.go4
-rw-r--r--pkg/tcpip/network/ipv6/icmp_test.go4
-rw-r--r--pkg/tcpip/network/ipv6/ipv6.go39
-rw-r--r--pkg/tcpip/network/ipv6/ndp.go287
-rw-r--r--pkg/tcpip/network/ipv6/ndp_test.go13
-rw-r--r--pkg/tcpip/ports/BUILD1
-rw-r--r--pkg/tcpip/ports/ports.go40
-rw-r--r--pkg/tcpip/stack/BUILD2
-rw-r--r--pkg/tcpip/stack/addressable_endpoint_state.go2
-rw-r--r--pkg/tcpip/stack/conntrack.go67
-rw-r--r--pkg/tcpip/stack/iptables.go4
-rw-r--r--pkg/tcpip/stack/iptables_targets.go105
-rw-r--r--pkg/tcpip/stack/iptables_types.go2
-rw-r--r--pkg/tcpip/stack/ndp_test.go1778
-rw-r--r--pkg/tcpip/stack/nic.go21
-rw-r--r--pkg/tcpip/stack/packet_buffer.go2
-rw-r--r--pkg/tcpip/stack/registration.go5
-rw-r--r--pkg/tcpip/stack/stack.go7
-rw-r--r--pkg/tcpip/stack/tcp.go3
-rw-r--r--pkg/tcpip/stack/transport_demuxer.go23
-rw-r--r--pkg/tcpip/stack/transport_demuxer_test.go50
-rw-r--r--pkg/tcpip/tcpip.go8
-rw-r--r--pkg/tcpip/tests/integration/iptables_test.go285
-rw-r--r--pkg/tcpip/tests/integration/loopback_test.go11
-rw-r--r--pkg/tcpip/transport/icmp/endpoint.go9
-rw-r--r--pkg/tcpip/transport/icmp/protocol.go2
-rw-r--r--pkg/tcpip/transport/packet/endpoint.go5
-rw-r--r--pkg/tcpip/transport/raw/endpoint.go20
-rw-r--r--pkg/tcpip/transport/tcp/accept.go23
-rw-r--r--pkg/tcpip/transport/tcp/connect.go141
-rw-r--r--pkg/tcpip/transport/tcp/dispatcher.go2
-rw-r--r--pkg/tcpip/transport/tcp/endpoint.go171
-rw-r--r--pkg/tcpip/transport/tcp/forwarder.go5
-rw-r--r--pkg/tcpip/transport/tcp/rcv.go1
-rw-r--r--pkg/tcpip/transport/tcp/tcp_test.go157
-rw-r--r--pkg/tcpip/transport/udp/endpoint.go150
-rw-r--r--pkg/urpc/urpc.go51
-rw-r--r--pkg/usermem/BUILD3
-rw-r--r--pkg/usermem/bytes_io.go5
-rw-r--r--pkg/usermem/marshal.go43
-rw-r--r--pkg/usermem/usermem.go10
-rw-r--r--pkg/usermem/usermem_test.go7
-rw-r--r--runsc/boot/BUILD3
-rw-r--r--runsc/boot/controller.go17
-rw-r--r--runsc/boot/filter/config.go11
-rw-r--r--runsc/boot/fs.go18
-rw-r--r--runsc/boot/loader.go30
-rw-r--r--runsc/boot/loader_test.go2
-rw-r--r--runsc/boot/vfs.go28
-rw-r--r--runsc/cmd/boot.go12
-rw-r--r--runsc/cmd/gofer.go18
-rw-r--r--runsc/config/config.go24
-rw-r--r--runsc/config/flags.go1
-rw-r--r--runsc/container/container_test.go141
-rw-r--r--runsc/sandbox/sandbox.go6
-rw-r--r--runsc/specutils/specutils.go2
-rw-r--r--test/benchmarks/database/redis_test.go82
-rw-r--r--test/cmd/test_app/BUILD2
-rw-r--r--test/cmd/test_app/main.go (renamed from test/cmd/test_app/test_app.go)0
-rw-r--r--test/fuse/BUILD140
-rw-r--r--test/packetimpact/runner/dut.go5
-rw-r--r--test/packetimpact/testbench/dut.go87
-rw-r--r--test/packetimpact/testbench/layers.go2
-rw-r--r--test/packetimpact/testbench/rawsockets.go20
-rw-r--r--test/packetimpact/testbench/testbench.go4
-rw-r--r--test/packetimpact/tests/BUILD1
-rw-r--r--test/packetimpact/tests/generic_dgram_socket_send_recv_test.go9
-rw-r--r--test/packetimpact/tests/tcp_connect_icmp_error_test.go35
-rw-r--r--test/packetimpact/tests/tcp_info_test.go31
-rw-r--r--test/packetimpact/tests/tcp_linger_test.go82
-rw-r--r--test/packetimpact/tests/tcp_listen_backlog_test.go14
-rw-r--r--test/packetimpact/tests/tcp_network_unreachable_test.go8
-rw-r--r--test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go96
-rw-r--r--test/packetimpact/tests/tcp_syncookie_test.go143
-rw-r--r--test/packetimpact/tests/udp_icmp_error_propagation_test.go19
-rw-r--r--test/runner/BUILD3
-rw-r--r--test/runner/defs.bzl3
-rw-r--r--test/runner/main.go (renamed from test/runner/runner.go)50
-rw-r--r--test/runner/setup_container/BUILD19
-rw-r--r--test/runner/setup_container/setup_container.cc79
-rw-r--r--test/syscalls/BUILD8
-rw-r--r--test/syscalls/linux/BUILD38
-rw-r--r--test/syscalls/linux/accept_bind.cc15
-rw-r--r--test/syscalls/linux/exec.cc4
-rw-r--r--test/syscalls/linux/ip_socket_test_util.cc16
-rw-r--r--test/syscalls/linux/ip_socket_test_util.h16
-rw-r--r--test/syscalls/linux/packet_socket.cc14
-rw-r--r--test/syscalls/linux/packet_socket_raw.cc14
-rw-r--r--test/syscalls/linux/ping_socket.cc221
-rw-r--r--test/syscalls/linux/pipe.cc1
-rw-r--r--test/syscalls/linux/poll.cc4
-rw-r--r--test/syscalls/linux/prctl.cc9
-rw-r--r--test/syscalls/linux/priority.cc23
-rw-r--r--test/syscalls/linux/proc.cc9
-rw-r--r--test/syscalls/linux/ptrace.cc458
-rw-r--r--test/syscalls/linux/raw_socket_hdrincl.cc21
-rw-r--r--test/syscalls/linux/raw_socket_icmp.cc4
-rw-r--r--test/syscalls/linux/rename.cc56
-rw-r--r--test/syscalls/linux/semaphore.cc17
-rw-r--r--test/syscalls/linux/socket_bind_to_device_distribution.cc28
-rw-r--r--test/syscalls/linux/socket_generic_stress.cc48
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc827
-rw-r--r--test/syscalls/linux/socket_inet_loopback_isolated.cc489
-rw-r--r--test/syscalls/linux/socket_inet_loopback_nogotsan.cc170
-rw-r--r--test/syscalls/linux/socket_inet_loopback_test_params.h86
-rw-r--r--test/syscalls/linux/socket_netdevice.cc5
-rw-r--r--test/syscalls/linux/socket_test_util.cc273
-rw-r--r--test/syscalls/linux/socket_test_util.h46
-rw-r--r--test/syscalls/linux/tcp_socket.cc56
-rw-r--r--test/syscalls/linux/tuntap.cc44
-rw-r--r--test/syscalls/linux/udp_socket.cc12
-rw-r--r--test/syscalls/linux/uidgid.cc4
-rw-r--r--test/syscalls/linux/uname.cc3
-rw-r--r--test/util/posix_error.h10
-rw-r--r--tools/bazeldefs/BUILD5
-rw-r--r--tools/checkescape/BUILD1
-rw-r--r--tools/checkescape/checkescape.go178
-rw-r--r--tools/checklocks/BUILD9
-rw-r--r--tools/checklocks/README.md83
-rw-r--r--tools/checklocks/analysis.go628
-rw-r--r--tools/checklocks/annotations.go129
-rw-r--r--tools/checklocks/checklocks.go758
-rw-r--r--tools/checklocks/facts.go614
-rw-r--r--tools/checklocks/state.go315
-rw-r--r--tools/checklocks/test/BUILD14
-rw-r--r--tools/checklocks/test/alignment.go51
-rw-r--r--tools/checklocks/test/atomics.go91
-rw-r--r--tools/checklocks/test/basics.go145
-rw-r--r--tools/checklocks/test/branches.go56
-rw-r--r--tools/checklocks/test/closures.go100
-rw-r--r--tools/checklocks/test/defer.go (renamed from pkg/flipcall/packet_window_mmap_arm64.go)27
-rw-r--r--tools/checklocks/test/incompat.go54
-rw-r--r--tools/checklocks/test/methods.go117
-rw-r--r--tools/checklocks/test/parameters.go48
-rw-r--r--tools/checklocks/test/return.go61
-rw-r--r--tools/checklocks/test/test.go328
-rw-r--r--tools/github/BUILD2
-rw-r--r--tools/github/reviver/BUILD2
-rw-r--r--tools/go_generics/BUILD2
-rw-r--r--tools/go_generics/main.go (renamed from tools/go_generics/generics.go)0
-rw-r--r--tools/nogo/BUILD2
-rw-r--r--tools/nogo/check/main.go18
-rw-r--r--tools/nogo/defs.bzl39
-rw-r--r--tools/nogo/nogo.go148
-rw-r--r--tools/nogo/objdump/BUILD10
-rw-r--r--tools/nogo/objdump/objdump.go96
638 files changed, 16179 insertions, 9716 deletions
diff --git a/.github/workflows/issue_reviver.yml b/.github/workflows/issue_reviver.yml
index f2d584ac0..a6a9b1b4f 100644
--- a/.github/workflows/issue_reviver.yml
+++ b/.github/workflows/issue_reviver.yml
@@ -1,5 +1,5 @@
# This workflow revives issues that are still referenced in the code, and may
-# have been accidentally closed or marked stale.
+# have been accidentally closed.
name: "Issue reviver"
"on":
schedule:
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
deleted file mode 100644
index ce300869c..000000000
--- a/.github/workflows/stale.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-# The stale workflow closes stale issues and pull requests, unless specific
-# tags have been applied in order to keep them open.
-name: "Stale issues"
-"on":
- schedule:
- - cron: "0 0 * * *"
-
-jobs:
- stale:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/stale@v3
- with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- stale-issue-label: 'stale'
- stale-pr-label: 'stale'
- exempt-issue-labels: 'revived, exported, type: bug, type: cleanup, type: enhancement, type: process, type: proposal, type: question'
- exempt-pr-labels: 'ready to pull, exported'
- stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove the stale label or comment or this will be closed in 30 days.'
- stale-pr-message: 'This pull request is stale because it has been open 90 days with no activity. Remove the stale label or comment or this will be closed in 30 days.'
- days-before-stale: 90
- days-before-close: 30
diff --git a/Makefile b/Makefile
index 5aefef0c4..68fb4c892 100644
--- a/Makefile
+++ b/Makefile
@@ -369,7 +369,8 @@ benchmark-platforms: load-benchmarks $(RUNTIME_BIN) ## Runs benchmarks for runc
.PHONY: benchmark-platforms
run-benchmark: load-benchmarks $(RUNTIME_BIN) ## Runs single benchmark and optionally sends data to BigQuery.
- @$(call run_benchmark,$(RUNTIME)$(BENCH_VFS),$(BENCH_RUNTIME_ARGS) $(BENCH_VFS))
+ @if test "$(RUNTIME)" = "runc"; then $(call run_benchmark,$(RUNTIME)); fi;
+ @if test "$(RUNTIME)" != "runc"; then $(call run_benchmark,$(RUNTIME)$(BENCH_VFS),$(BENCH_RUNTIME_ARGS) $(BENCH_VFS)); fi;
.PHONY: run-benchmark
##
diff --git a/README.md b/README.md
index 35310fb58..aa8b8c068 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,10 @@
![gVisor](g3doc/logo.png)
[![Build status](https://badge.buildkite.com/3b159f20b9830461a71112566c4171c0bdfd2f980a8e4c0ae6.svg?branch=master)](https://buildkite.com/gvisor/pipeline)
+[![Issue reviver](https://github.com/google/gvisor/actions/workflows/issue_reviver.yml/badge.svg)](https://github.com/google/gvisor/actions/workflows/issue_reviver.yml)
[![gVisor chat](https://badges.gitter.im/gvisor/community.png)](https://gitter.im/gvisor/community)
[![code search](https://img.shields.io/badge/code-search-blue)](https://cs.opensource.google/gvisor/gvisor)
-[![Issue reviver](https://github.com/google/gvisor/actions/workflows/issue_reviver.yml/badge.svg)](https://github.com/google/gvisor/actions/workflows/issue_reviver.yml)
-[![Stale issues](https://github.com/google/gvisor/actions/workflows/stale.yml/badge.svg)](https://github.com/google/gvisor/actions/workflows/stale.yml)
-
## What is gVisor?
**gVisor** is an application kernel, written in Go, that implements a
diff --git a/WORKSPACE b/WORKSPACE
index f36419d72..4af8b4a06 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -81,8 +81,8 @@ gazelle_dependencies()
go_repository(
name = "org_golang_x_sys",
importpath = "golang.org/x/sys",
- sum = "h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=",
- version = "v0.0.0-20210119212857-b64e53b001e4",
+ sum = "h1:+39ahH47SWi1PhMRAHfIrm8f69HRZ5K2koXH6dmO8TQ=",
+ version = "v0.0.0-20210314195730-07df6a141424",
)
# Load C++ rules.
@@ -131,10 +131,10 @@ go_repository(
)
go_repository(
- name = "com_github_google_go_github_v32",
- importpath = "github.com/google/go-github/v32",
- sum = "h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=",
- version = "v32.1.0",
+ name = "com_github_google_go_github_v35",
+ importpath = "github.com/google/go-github/v35",
+ sum = "h1:s/soW8jauhjUC3rh8JI0FePuocj0DEI9DNBg/bVplE8=",
+ version = "v35.2.0",
)
go_repository(
@@ -179,6 +179,1049 @@ go_repository(
version = "v1.1.0",
)
+go_repository(
+ name = "com_github_alecthomas_template",
+ importpath = "github.com/alecthomas/template",
+ sum = "h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=",
+ version = "v0.0.0-20190718012654-fb15b899a751",
+)
+
+go_repository(
+ name = "com_github_alecthomas_units",
+ importpath = "github.com/alecthomas/units",
+ sum = "h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=",
+ version = "v0.0.0-20190717042225-c3de453c63f4",
+)
+
+go_repository(
+ name = "com_github_alexflint_go_filemutex",
+ importpath = "github.com/alexflint/go-filemutex",
+ sum = "h1:AMzIhMUqU3jMrZiTuW0zkYeKlKDAFD+DG20IoO421/Y=",
+ version = "v0.0.0-20171022225611-72bdc8eae2ae",
+)
+
+go_repository(
+ name = "com_github_antihax_optional",
+ importpath = "github.com/antihax/optional",
+ sum = "h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=",
+ version = "v1.0.0",
+)
+
+go_repository(
+ name = "com_github_armon_consul_api",
+ importpath = "github.com/armon/consul-api",
+ sum = "h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA=",
+ version = "v0.0.0-20180202201655-eb2c6b5be1b6",
+)
+
+go_repository(
+ name = "com_github_asaskevich_govalidator",
+ importpath = "github.com/asaskevich/govalidator",
+ sum = "h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=",
+ version = "v0.0.0-20190424111038-f61b66f89f4a",
+)
+
+go_repository(
+ name = "com_github_aws_aws_sdk_go",
+ importpath = "github.com/aws/aws-sdk-go",
+ sum = "h1:m45+Ru/wA+73cOZXiEGLDH2d9uLN3iHqMc0/z4noDXE=",
+ version = "v1.15.11",
+)
+
+go_repository(
+ name = "com_github_azure_azure_sdk_for_go",
+ importpath = "github.com/Azure/azure-sdk-for-go",
+ sum = "h1:KnPIugL51v3N3WwvaSmZbxukD1WuWXOiE9fRdu32f2I=",
+ version = "v16.2.1+incompatible",
+)
+
+go_repository(
+ name = "com_github_azure_go_ansiterm",
+ importpath = "github.com/Azure/go-ansiterm",
+ sum = "h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=",
+ version = "v0.0.0-20170929234023-d6e3b3328b78",
+)
+
+go_repository(
+ name = "com_github_azure_go_autorest",
+ importpath = "github.com/Azure/go-autorest",
+ sum = "h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=",
+ version = "v14.2.0+incompatible",
+)
+
+go_repository(
+ name = "com_github_beorn7_perks",
+ importpath = "github.com/beorn7/perks",
+ sum = "h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=",
+ version = "v1.0.1",
+)
+
+go_repository(
+ name = "com_github_bgentry_speakeasy",
+ importpath = "github.com/bgentry/speakeasy",
+ sum = "h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=",
+ version = "v0.1.0",
+)
+
+go_repository(
+ name = "com_github_bitly_go_simplejson",
+ importpath = "github.com/bitly/go-simplejson",
+ sum = "h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=",
+ version = "v0.5.0",
+)
+
+go_repository(
+ name = "com_github_blang_semver",
+ importpath = "github.com/blang/semver",
+ sum = "h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=",
+ version = "v3.5.1+incompatible",
+)
+
+go_repository(
+ name = "com_github_bmizerany_assert",
+ importpath = "github.com/bmizerany/assert",
+ sum = "h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=",
+ version = "v0.0.0-20160611221934-b7ed37b82869",
+)
+
+go_repository(
+ name = "com_github_bshuster_repo_logrus_logstash_hook",
+ importpath = "github.com/bshuster-repo/logrus-logstash-hook",
+ sum = "h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA=",
+ version = "v0.4.1",
+)
+
+go_repository(
+ name = "com_github_buger_jsonparser",
+ importpath = "github.com/buger/jsonparser",
+ sum = "h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA=",
+ version = "v0.0.0-20180808090653-f4dd9f5a6b44",
+)
+
+go_repository(
+ name = "com_github_bugsnag_bugsnag_go",
+ importpath = "github.com/bugsnag/bugsnag-go",
+ sum = "h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=",
+ version = "v0.0.0-20141110184014-b1d153021fcd",
+)
+
+go_repository(
+ name = "com_github_bugsnag_osext",
+ importpath = "github.com/bugsnag/osext",
+ sum = "h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=",
+ version = "v0.0.0-20130617224835-0dd3f918b21b",
+)
+
+go_repository(
+ name = "com_github_bugsnag_panicwrap",
+ importpath = "github.com/bugsnag/panicwrap",
+ sum = "h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=",
+ version = "v0.0.0-20151223152923-e2c28503fcd0",
+)
+
+go_repository(
+ name = "com_github_cespare_xxhash",
+ importpath = "github.com/cespare/xxhash",
+ sum = "h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=",
+ version = "v1.1.0",
+)
+
+go_repository(
+ name = "com_github_cespare_xxhash_v2",
+ importpath = "github.com/cespare/xxhash/v2",
+ sum = "h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=",
+ version = "v2.1.1",
+)
+
+go_repository(
+ name = "com_github_checkpoint_restore_go_criu_v4",
+ importpath = "github.com/checkpoint-restore/go-criu/v4",
+ sum = "h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo=",
+ version = "v4.1.0",
+)
+
+go_repository(
+ name = "com_github_cncf_xds_go",
+ importpath = "github.com/cncf/xds/go",
+ sum = "h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c=",
+ version = "v0.0.0-20210312221358-fbca930ec8ed",
+)
+
+go_repository(
+ name = "com_github_cockroachdb_datadriven",
+ importpath = "github.com/cockroachdb/datadriven",
+ sum = "h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=",
+ version = "v0.0.0-20190809214429-80d97fb3cbaa",
+)
+
+go_repository(
+ name = "com_github_containerd_btrfs",
+ importpath = "github.com/containerd/btrfs",
+ sum = "h1:osn1exbzdub9L5SouXO5swW4ea/xVdJZ3wokxN5GrnA=",
+ version = "v1.0.0",
+)
+
+go_repository(
+ name = "com_github_containerd_go_cni",
+ importpath = "github.com/containerd/go-cni",
+ sum = "h1:YbJAhpTevL2v6u8JC1NhCYRwf+3Vzxcc5vGnYoJ7VeE=",
+ version = "v1.0.2",
+)
+
+go_repository(
+ name = "com_github_containerd_imgcrypt",
+ importpath = "github.com/containerd/imgcrypt",
+ sum = "h1:31GdXSmR2bZRPuZ/5lgkXivzNSejJQPsVOq82qqzP/M=",
+ version = "v1.0.3",
+)
+
+go_repository(
+ name = "com_github_containernetworking_cni",
+ importpath = "github.com/containernetworking/cni",
+ sum = "h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI=",
+ version = "v0.8.1",
+)
+
+go_repository(
+ name = "com_github_containernetworking_plugins",
+ importpath = "github.com/containernetworking/plugins",
+ sum = "h1:bU7QieuAp+sACI2vCzESJ3FoT860urYP+lThyZkb/2M=",
+ version = "v0.8.7",
+)
+
+go_repository(
+ name = "com_github_containers_ocicrypt",
+ importpath = "github.com/containers/ocicrypt",
+ sum = "h1:vYgl+RZ9Q3DPMuTfxmN+qp0X2Bj52uuY2vnt6GzVe1c=",
+ version = "v1.0.3",
+)
+
+go_repository(
+ name = "com_github_coreos_bbolt",
+ importpath = "github.com/coreos/bbolt",
+ sum = "h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=",
+ version = "v1.3.2",
+)
+
+go_repository(
+ name = "com_github_coreos_etcd",
+ importpath = "github.com/coreos/etcd",
+ sum = "h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=",
+ version = "v3.3.10+incompatible",
+)
+
+go_repository(
+ name = "com_github_coreos_go_iptables",
+ importpath = "github.com/coreos/go-iptables",
+ sum = "h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=",
+ version = "v0.5.0",
+)
+
+go_repository(
+ name = "com_github_coreos_go_oidc",
+ importpath = "github.com/coreos/go-oidc",
+ sum = "h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=",
+ version = "v2.1.0+incompatible",
+)
+
+go_repository(
+ name = "com_github_coreos_go_semver",
+ importpath = "github.com/coreos/go-semver",
+ sum = "h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=",
+ version = "v0.3.0",
+)
+
+go_repository(
+ name = "com_github_coreos_go_systemd",
+ importpath = "github.com/coreos/go-systemd",
+ sum = "h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=",
+ version = "v0.0.0-20190321100706-95778dfbb74e",
+)
+
+go_repository(
+ name = "com_github_coreos_pkg",
+ importpath = "github.com/coreos/pkg",
+ sum = "h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=",
+ version = "v0.0.0-20180928190104-399ea9e2e55f",
+)
+
+go_repository(
+ name = "com_github_creack_pty",
+ importpath = "github.com/creack/pty",
+ sum = "h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=",
+ version = "v1.1.7",
+)
+
+go_repository(
+ name = "com_github_cyphar_filepath_securejoin",
+ importpath = "github.com/cyphar/filepath-securejoin",
+ sum = "h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=",
+ version = "v0.2.2",
+)
+
+go_repository(
+ name = "com_github_d2g_dhcp4",
+ importpath = "github.com/d2g/dhcp4",
+ sum = "h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc=",
+ version = "v0.0.0-20170904100407-a1d1b6c41b1c",
+)
+
+go_repository(
+ name = "com_github_d2g_dhcp4client",
+ importpath = "github.com/d2g/dhcp4client",
+ sum = "h1:suYBsYZIkSlUMEz4TAYCczKf62IA2UWC+O8+KtdOhCo=",
+ version = "v1.0.0",
+)
+
+go_repository(
+ name = "com_github_d2g_dhcp4server",
+ importpath = "github.com/d2g/dhcp4server",
+ sum = "h1:+CpLbZIeUn94m02LdEKPcgErLJ347NUwxPKs5u8ieiY=",
+ version = "v0.0.0-20181031114812-7d4a0a7f59a5",
+)
+
+go_repository(
+ name = "com_github_d2g_hardwareaddr",
+ importpath = "github.com/d2g/hardwareaddr",
+ sum = "h1:itqmmf1PFpC4n5JW+j4BU7X4MTfVurhYRTjODoPb2Y8=",
+ version = "v0.0.0-20190221164911-e7d9fbe030e4",
+)
+
+go_repository(
+ name = "com_github_denverdino_aliyungo",
+ importpath = "github.com/denverdino/aliyungo",
+ sum = "h1:p6poVbjHDkKa+wtC8frBMwQtT3BmqGYBjzMwJ63tuR4=",
+ version = "v0.0.0-20190125010748-a747050bb1ba",
+)
+
+go_repository(
+ name = "com_github_dgryski_go_sip13",
+ importpath = "github.com/dgryski/go-sip13",
+ sum = "h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4=",
+ version = "v0.0.0-20181026042036-e10d5fee7954",
+)
+
+go_repository(
+ name = "com_github_dnaeon_go_vcr",
+ importpath = "github.com/dnaeon/go-vcr",
+ sum = "h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=",
+ version = "v1.0.1",
+)
+
+go_repository(
+ name = "com_github_docker_go_metrics",
+ importpath = "github.com/docker/go-metrics",
+ sum = "h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=",
+ version = "v0.0.1",
+)
+
+go_repository(
+ name = "com_github_docker_libtrust",
+ importpath = "github.com/docker/libtrust",
+ sum = "h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=",
+ version = "v0.0.0-20150114040149-fa567046d9b1",
+)
+
+go_repository(
+ name = "com_github_docopt_docopt_go",
+ importpath = "github.com/docopt/docopt-go",
+ sum = "h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=",
+ version = "v0.0.0-20180111231733-ee0de3bc6815",
+)
+
+go_repository(
+ name = "com_github_fatih_color",
+ importpath = "github.com/fatih/color",
+ sum = "h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=",
+ version = "v1.7.0",
+)
+
+go_repository(
+ name = "com_github_form3tech_oss_jwt_go",
+ importpath = "github.com/form3tech-oss/jwt-go",
+ sum = "h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=",
+ version = "v3.2.2+incompatible",
+)
+
+go_repository(
+ name = "com_github_frankban_quicktest",
+ importpath = "github.com/frankban/quicktest",
+ sum = "h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=",
+ version = "v1.11.3",
+)
+
+go_repository(
+ name = "com_github_fullsailor_pkcs7",
+ importpath = "github.com/fullsailor/pkcs7",
+ sum = "h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU=",
+ version = "v0.0.0-20190404230743-d7302db945fa",
+)
+
+go_repository(
+ name = "com_github_garyburd_redigo",
+ importpath = "github.com/garyburd/redigo",
+ sum = "h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko=",
+ version = "v0.0.0-20150301180006-535138d7bcd7",
+)
+
+go_repository(
+ name = "com_github_go_ini_ini",
+ importpath = "github.com/go-ini/ini",
+ sum = "h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=",
+ version = "v1.25.4",
+)
+
+go_repository(
+ name = "com_github_go_kit_kit",
+ importpath = "github.com/go-kit/kit",
+ sum = "h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=",
+ version = "v0.9.0",
+)
+
+go_repository(
+ name = "com_github_go_logfmt_logfmt",
+ importpath = "github.com/go-logfmt/logfmt",
+ sum = "h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=",
+ version = "v0.4.0",
+)
+
+go_repository(
+ name = "com_github_go_stack_stack",
+ importpath = "github.com/go-stack/stack",
+ sum = "h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=",
+ version = "v1.8.0",
+)
+
+go_repository(
+ name = "com_github_godbus_dbus",
+ importpath = "github.com/godbus/dbus",
+ sum = "h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=",
+ version = "v0.0.0-20190422162347-ade71ed3457e",
+)
+
+go_repository(
+ name = "com_github_gopherjs_gopherjs",
+ importpath = "github.com/gopherjs/gopherjs",
+ sum = "h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=",
+ version = "v0.0.0-20181017120253-0766667cb4d1",
+)
+
+go_repository(
+ name = "com_github_gorilla_handlers",
+ importpath = "github.com/gorilla/handlers",
+ sum = "h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo=",
+ version = "v0.0.0-20150720190736-60c7bfde3e33",
+)
+
+go_repository(
+ name = "com_github_gorilla_mux",
+ importpath = "github.com/gorilla/mux",
+ sum = "h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=",
+ version = "v1.7.2",
+)
+
+go_repository(
+ name = "com_github_gorilla_websocket",
+ importpath = "github.com/gorilla/websocket",
+ sum = "h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=",
+ version = "v1.4.2",
+)
+
+go_repository(
+ name = "com_github_grpc_ecosystem_go_grpc_middleware",
+ importpath = "github.com/grpc-ecosystem/go-grpc-middleware",
+ sum = "h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=",
+ version = "v1.0.1-0.20190118093823-f849b5445de4",
+)
+
+go_repository(
+ name = "com_github_grpc_ecosystem_go_grpc_prometheus",
+ importpath = "github.com/grpc-ecosystem/go-grpc-prometheus",
+ sum = "h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=",
+ version = "v1.2.0",
+)
+
+go_repository(
+ name = "com_github_grpc_ecosystem_grpc_gateway",
+ importpath = "github.com/grpc-ecosystem/grpc-gateway",
+ sum = "h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=",
+ version = "v1.16.0",
+)
+
+go_repository(
+ name = "com_github_hashicorp_hcl",
+ importpath = "github.com/hashicorp/hcl",
+ sum = "h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=",
+ version = "v1.0.0",
+)
+
+go_repository(
+ name = "com_github_j_keck_arping",
+ importpath = "github.com/j-keck/arping",
+ sum = "h1:742eGXur0715JMq73aD95/FU0XpVKXqNuTnEfXsLOYQ=",
+ version = "v0.0.0-20160618110441-2cf9dc699c56",
+)
+
+go_repository(
+ name = "com_github_jmespath_go_jmespath",
+ importpath = "github.com/jmespath/go-jmespath",
+ sum = "h1:SMvOWPJCES2GdFracYbBQh93GXac8fq7HeN6JnpduB8=",
+ version = "v0.0.0-20160803190731-bd40a432e4c7",
+)
+
+go_repository(
+ name = "com_github_jonboulle_clockwork",
+ importpath = "github.com/jonboulle/clockwork",
+ sum = "h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=",
+ version = "v0.1.0",
+)
+
+go_repository(
+ name = "com_github_jtolds_gls",
+ importpath = "github.com/jtolds/gls",
+ sum = "h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=",
+ version = "v4.20.0+incompatible",
+)
+
+go_repository(
+ name = "com_github_julienschmidt_httprouter",
+ importpath = "github.com/julienschmidt/httprouter",
+ sum = "h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=",
+ version = "v1.2.0",
+)
+
+go_repository(
+ name = "com_github_klauspost_compress",
+ importpath = "github.com/klauspost/compress",
+ sum = "h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4=",
+ version = "v1.11.13",
+)
+
+go_repository(
+ name = "com_github_kr_logfmt",
+ importpath = "github.com/kr/logfmt",
+ sum = "h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=",
+ version = "v0.0.0-20140226030751-b84e30acd515",
+)
+
+go_repository(
+ name = "com_github_magiconair_properties",
+ importpath = "github.com/magiconair/properties",
+ sum = "h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=",
+ version = "v1.8.0",
+)
+
+go_repository(
+ name = "com_github_marstr_guid",
+ importpath = "github.com/marstr/guid",
+ sum = "h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI=",
+ version = "v1.1.0",
+)
+
+go_repository(
+ name = "com_github_mattn_go_colorable",
+ importpath = "github.com/mattn/go-colorable",
+ sum = "h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=",
+ version = "v0.0.9",
+)
+
+go_repository(
+ name = "com_github_mattn_go_isatty",
+ importpath = "github.com/mattn/go-isatty",
+ sum = "h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=",
+ version = "v0.0.4",
+)
+
+go_repository(
+ name = "com_github_mattn_go_runewidth",
+ importpath = "github.com/mattn/go-runewidth",
+ sum = "h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=",
+ version = "v0.0.2",
+)
+
+go_repository(
+ name = "com_github_mattn_go_shellwords",
+ importpath = "github.com/mattn/go-shellwords",
+ sum = "h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=",
+ version = "v1.0.3",
+)
+
+go_repository(
+ name = "com_github_matttproud_golang_protobuf_extensions",
+ importpath = "github.com/matttproud/golang_protobuf_extensions",
+ sum = "h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=",
+ version = "v1.0.2-0.20181231171920-c182affec369",
+)
+
+go_repository(
+ name = "com_github_miekg_pkcs11",
+ importpath = "github.com/miekg/pkcs11",
+ sum = "h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=",
+ version = "v1.0.3",
+)
+
+go_repository(
+ name = "com_github_mistifyio_go_zfs",
+ importpath = "github.com/mistifyio/go-zfs",
+ sum = "h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=",
+ version = "v2.1.2-0.20190413222219-f784269be439+incompatible",
+)
+
+go_repository(
+ name = "com_github_mitchellh_go_homedir",
+ importpath = "github.com/mitchellh/go-homedir",
+ sum = "h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=",
+ version = "v1.1.0",
+)
+
+go_repository(
+ name = "com_github_mitchellh_mapstructure",
+ importpath = "github.com/mitchellh/mapstructure",
+ sum = "h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=",
+ version = "v1.1.2",
+)
+
+go_repository(
+ name = "com_github_mitchellh_osext",
+ importpath = "github.com/mitchellh/osext",
+ sum = "h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ=",
+ version = "v0.0.0-20151018003038-5e2d6d41470f",
+)
+
+go_repository(
+ name = "com_github_moby_locker",
+ importpath = "github.com/moby/locker",
+ sum = "h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=",
+ version = "v1.0.1",
+)
+
+go_repository(
+ name = "com_github_moby_sys_mountinfo",
+ importpath = "github.com/moby/sys/mountinfo",
+ sum = "h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=",
+ version = "v0.4.1",
+)
+
+go_repository(
+ name = "com_github_moby_sys_symlink",
+ importpath = "github.com/moby/sys/symlink",
+ sum = "h1:MTFZ74KtNI6qQQpuBxU+uKCim4WtOMokr03hCfJcazE=",
+ version = "v0.1.0",
+)
+
+go_repository(
+ name = "com_github_moby_term",
+ importpath = "github.com/moby/term",
+ sum = "h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=",
+ version = "v0.0.0-20200312100748-672ec06f55cd",
+)
+
+go_repository(
+ name = "com_github_mrunalp_fileutils",
+ importpath = "github.com/mrunalp/fileutils",
+ sum = "h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=",
+ version = "v0.5.0",
+)
+
+go_repository(
+ name = "com_github_mwitkow_go_conntrack",
+ importpath = "github.com/mwitkow/go-conntrack",
+ sum = "h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=",
+ version = "v0.0.0-20161129095857-cc309e4a2223",
+)
+
+go_repository(
+ name = "com_github_ncw_swift",
+ importpath = "github.com/ncw/swift",
+ sum = "h1:4DQRPj35Y41WogBxyhOXlrI37nzGlyEcsforeudyYPQ=",
+ version = "v1.0.47",
+)
+
+go_repository(
+ name = "com_github_nxadm_tail",
+ importpath = "github.com/nxadm/tail",
+ sum = "h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=",
+ version = "v1.4.4",
+)
+
+go_repository(
+ name = "com_github_oklog_ulid",
+ importpath = "github.com/oklog/ulid",
+ sum = "h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=",
+ version = "v1.3.1",
+)
+
+go_repository(
+ name = "com_github_olekukonko_tablewriter",
+ importpath = "github.com/olekukonko/tablewriter",
+ sum = "h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78=",
+ version = "v0.0.0-20170122224234-a0225b3f23b5",
+)
+
+go_repository(
+ name = "com_github_oneofone_xxhash",
+ importpath = "github.com/OneOfOne/xxhash",
+ sum = "h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=",
+ version = "v1.2.2",
+)
+
+go_repository(
+ name = "com_github_opencontainers_runtime_tools",
+ importpath = "github.com/opencontainers/runtime-tools",
+ sum = "h1:H7DMc6FAjgwZZi8BRqjrAAHWoqEr5e5L6pS4V0ezet4=",
+ version = "v0.0.0-20181011054405-1d69bd0f9c39",
+)
+
+go_repository(
+ name = "com_github_opencontainers_selinux",
+ importpath = "github.com/opencontainers/selinux",
+ sum = "h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM=",
+ version = "v1.8.0",
+)
+
+go_repository(
+ name = "com_github_pelletier_go_toml",
+ importpath = "github.com/pelletier/go-toml",
+ sum = "h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=",
+ version = "v1.8.1",
+)
+
+go_repository(
+ name = "com_github_pquerna_cachecontrol",
+ importpath = "github.com/pquerna/cachecontrol",
+ sum = "h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM=",
+ version = "v0.0.0-20171018203845-0dec1b30a021",
+)
+
+go_repository(
+ name = "com_github_prometheus_client_golang",
+ importpath = "github.com/prometheus/client_golang",
+ sum = "h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=",
+ version = "v1.7.1",
+)
+
+go_repository(
+ name = "com_github_prometheus_common",
+ importpath = "github.com/prometheus/common",
+ sum = "h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=",
+ version = "v0.10.0",
+)
+
+go_repository(
+ name = "com_github_prometheus_tsdb",
+ importpath = "github.com/prometheus/tsdb",
+ sum = "h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=",
+ version = "v0.7.1",
+)
+
+go_repository(
+ name = "com_github_rogpeppe_fastuuid",
+ importpath = "github.com/rogpeppe/fastuuid",
+ sum = "h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=",
+ version = "v1.2.0",
+)
+
+go_repository(
+ name = "com_github_safchain_ethtool",
+ importpath = "github.com/safchain/ethtool",
+ sum = "h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U=",
+ version = "v0.0.0-20190326074333-42ed695e3de8",
+)
+
+go_repository(
+ name = "com_github_satori_go_uuid",
+ importpath = "github.com/satori/go.uuid",
+ sum = "h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=",
+ version = "v1.2.0",
+)
+
+go_repository(
+ name = "com_github_seccomp_libseccomp_golang",
+ importpath = "github.com/seccomp/libseccomp-golang",
+ sum = "h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=",
+ version = "v0.9.1",
+)
+
+go_repository(
+ name = "com_github_shopify_logrus_bugsnag",
+ importpath = "github.com/Shopify/logrus-bugsnag",
+ sum = "h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=",
+ version = "v0.0.0-20171204204709-577dee27f20d",
+)
+
+go_repository(
+ name = "com_github_smartystreets_assertions",
+ importpath = "github.com/smartystreets/assertions",
+ sum = "h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=",
+ version = "v0.0.0-20180927180507-b2de0cb4f26d",
+)
+
+go_repository(
+ name = "com_github_smartystreets_goconvey",
+ importpath = "github.com/smartystreets/goconvey",
+ sum = "h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=",
+ version = "v0.0.0-20190330032615-68dc04aab96a",
+)
+
+go_repository(
+ name = "com_github_soheilhy_cmux",
+ importpath = "github.com/soheilhy/cmux",
+ sum = "h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=",
+ version = "v0.1.4",
+)
+
+go_repository(
+ name = "com_github_spaolacci_murmur3",
+ importpath = "github.com/spaolacci/murmur3",
+ sum = "h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=",
+ version = "v0.0.0-20180118202830-f09979ecbc72",
+)
+
+go_repository(
+ name = "com_github_spf13_cast",
+ importpath = "github.com/spf13/cast",
+ sum = "h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=",
+ version = "v1.3.0",
+)
+
+go_repository(
+ name = "com_github_spf13_jwalterweatherman",
+ importpath = "github.com/spf13/jwalterweatherman",
+ sum = "h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=",
+ version = "v1.0.0",
+)
+
+go_repository(
+ name = "com_github_spf13_viper",
+ importpath = "github.com/spf13/viper",
+ sum = "h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=",
+ version = "v1.4.0",
+)
+
+go_repository(
+ name = "com_github_stefanberger_go_pkcs11uri",
+ importpath = "github.com/stefanberger/go-pkcs11uri",
+ sum = "h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I=",
+ version = "v0.0.0-20201008174630-78d3cae3a980",
+)
+
+go_repository(
+ name = "com_github_tchap_go_patricia",
+ importpath = "github.com/tchap/go-patricia",
+ sum = "h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck=",
+ version = "v2.2.6+incompatible",
+)
+
+go_repository(
+ name = "com_github_tmc_grpc_websocket_proxy",
+ importpath = "github.com/tmc/grpc-websocket-proxy",
+ sum = "h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=",
+ version = "v0.0.0-20190109142713-0ad062ec5ee5",
+)
+
+go_repository(
+ name = "com_github_ugorji_go",
+ importpath = "github.com/ugorji/go",
+ sum = "h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=",
+ version = "v1.1.4",
+)
+
+go_repository(
+ name = "com_github_willf_bitset",
+ importpath = "github.com/willf/bitset",
+ sum = "h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=",
+ version = "v1.1.11",
+)
+
+go_repository(
+ name = "com_github_xiang90_probing",
+ importpath = "github.com/xiang90/probing",
+ sum = "h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=",
+ version = "v0.0.0-20190116061207-43a291ad63a2",
+)
+
+go_repository(
+ name = "com_github_xordataexchange_crypt",
+ importpath = "github.com/xordataexchange/crypt",
+ sum = "h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow=",
+ version = "v0.0.3-0.20170626215501-b2862e3d0a77",
+)
+
+go_repository(
+ name = "com_github_yvasiyarov_go_metrics",
+ importpath = "github.com/yvasiyarov/go-metrics",
+ sum = "h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=",
+ version = "v0.0.0-20140926110328-57bccd1ccd43",
+)
+
+go_repository(
+ name = "com_github_yvasiyarov_gorelic",
+ importpath = "github.com/yvasiyarov/gorelic",
+ sum = "h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=",
+ version = "v0.0.0-20141212073537-a9bba5b9ab50",
+)
+
+go_repository(
+ name = "com_github_yvasiyarov_newrelic_platform_go",
+ importpath = "github.com/yvasiyarov/newrelic_platform_go",
+ sum = "h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=",
+ version = "v0.0.0-20140908184405-b21fdbd4370f",
+)
+
+go_repository(
+ name = "in_gopkg_airbrake_gobrake_v2",
+ importpath = "gopkg.in/airbrake/gobrake.v2",
+ sum = "h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=",
+ version = "v2.0.9",
+)
+
+go_repository(
+ name = "in_gopkg_alecthomas_kingpin_v2",
+ importpath = "gopkg.in/alecthomas/kingpin.v2",
+ sum = "h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=",
+ version = "v2.2.6",
+)
+
+go_repository(
+ name = "in_gopkg_cheggaaa_pb_v1",
+ importpath = "gopkg.in/cheggaaa/pb.v1",
+ sum = "h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I=",
+ version = "v1.0.25",
+)
+
+go_repository(
+ name = "in_gopkg_gemnasium_logrus_airbrake_hook_v2",
+ importpath = "gopkg.in/gemnasium/logrus-airbrake-hook.v2",
+ sum = "h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=",
+ version = "v2.1.2",
+)
+
+go_repository(
+ name = "in_gopkg_natefinch_lumberjack_v2",
+ importpath = "gopkg.in/natefinch/lumberjack.v2",
+ sum = "h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=",
+ version = "v2.0.0",
+)
+
+go_repository(
+ name = "in_gopkg_resty_v1",
+ importpath = "gopkg.in/resty.v1",
+ sum = "h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=",
+ version = "v1.12.0",
+)
+
+go_repository(
+ name = "in_gopkg_square_go_jose_v2",
+ importpath = "gopkg.in/square/go-jose.v2",
+ sum = "h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=",
+ version = "v2.5.1",
+)
+
+go_repository(
+ name = "in_gopkg_yaml_v3",
+ importpath = "gopkg.in/yaml.v3",
+ sum = "h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=",
+ version = "v3.0.0-20200313102051-9f266ea9e77c",
+)
+
+go_repository(
+ name = "io_etcd_go_bbolt",
+ importpath = "go.etcd.io/bbolt",
+ sum = "h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=",
+ version = "v1.3.5",
+)
+
+go_repository(
+ name = "io_etcd_go_etcd",
+ importpath = "go.etcd.io/etcd",
+ sum = "h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo=",
+ version = "v0.5.0-alpha.5.0.20200910180754-dd1b699fc489",
+)
+
+go_repository(
+ name = "io_k8s_component_base",
+ importpath = "k8s.io/component-base",
+ sum = "h1:wHmQb3b/QCTODA/MGih9x7XZQmhdDlA8AxJnAID6T2A=",
+ version = "v0.16.13",
+)
+
+go_repository(
+ name = "io_k8s_cri_api",
+ importpath = "k8s.io/cri-api",
+ sum = "h1:iXX0K2pRrbR8yXbZtDK/bSnmg/uSqIFiVJK1x4LUOMc=",
+ version = "v0.20.6",
+)
+
+go_repository(
+ name = "io_k8s_klog_v2",
+ importpath = "k8s.io/klog/v2",
+ sum = "h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=",
+ version = "v2.4.0",
+)
+
+go_repository(
+ name = "io_k8s_kubernetes",
+ importpath = "k8s.io/kubernetes",
+ sum = "h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8=",
+ version = "v1.13.0",
+)
+
+go_repository(
+ name = "io_k8s_sigs_apiserver_network_proxy_konnectivity_client",
+ importpath = "sigs.k8s.io/apiserver-network-proxy/konnectivity-client",
+ sum = "h1:4uqm9Mv+w2MmBYD+F4qf/v6tDFUdPOk29C095RbU5mY=",
+ version = "v0.0.15",
+)
+
+go_repository(
+ name = "io_k8s_sigs_structured_merge_diff_v4",
+ importpath = "sigs.k8s.io/structured-merge-diff/v4",
+ sum = "h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c=",
+ version = "v4.0.3",
+)
+
+go_repository(
+ name = "io_opentelemetry_go_proto_otlp",
+ importpath = "go.opentelemetry.io/proto/otlp",
+ sum = "h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8=",
+ version = "v0.7.0",
+)
+
+go_repository(
+ name = "org_golang_google_cloud",
+ importpath = "google.golang.org/cloud",
+ sum = "h1:Cpp2P6TPjujNoC5M2KHY6g7wfyLYfIWRZaSdIKfDasA=",
+ version = "v0.0.0-20151119220103-975617b05ea8",
+)
+
+go_repository(
+ name = "org_mozilla_go_pkcs7",
+ importpath = "go.mozilla.org/pkcs7",
+ sum = "h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M=",
+ version = "v0.0.0-20200128120323-432b2356ecb1",
+)
+
+go_repository(
+ name = "org_uber_go_zap",
+ importpath = "go.uber.org/zap",
+ sum = "h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=",
+ version = "v1.10.0",
+)
+
+go_repository(
+ name = "tools_gotest_v3",
+ importpath = "gotest.tools/v3",
+ sum = "h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=",
+ version = "v3.0.3",
+)
+
+go_repository(
+ name = "com_github_bazelbuild_rules_go",
+ importpath = "github.com/bazelbuild/rules_go",
+ sum = "h1:KViqR7qKXwz+LrNdIauCDU21kneCk+4DnYjpvlJwH50=",
+ version = "v0.27.0",
+)
+
rules_proto_dependencies()
rules_proto_toolchains()
@@ -210,6 +1253,20 @@ load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
rules_pkg_dependencies()
+# System Call test dependencies.
+# grpc also has a dependency on abseil but as this is before grpc dependency
+# declaration, it will take precedence over grpc's one
+# Version LTS 20210324.2
+http_archive(
+ name = "com_google_absl",
+ sha256 = "1764491a199eb9325b177126547f03d244f86b4ff28f16f206c7b3e7e4f777ec",
+ strip_prefix = "abseil-cpp-278e0a071885a22dcd2fd1b5576cc44757299343",
+ urls = [
+ "https://mirror.bazel.build/github.com/abseil/abseil-cpp/archive/278e0a071885a22dcd2fd1b5576cc44757299343.tar.gz",
+ "https://github.com/abseil/abseil-cpp/archive/278e0a071885a22dcd2fd1b5576cc44757299343.tar.gz"
+ ],
+)
+
# Load C++ grpc rules.
http_archive(
name = "com_github_grpc_grpc",
@@ -228,16 +1285,6 @@ load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps")
grpc_extra_deps()
-# System Call test dependencies.
-http_archive(
- name = "com_google_absl",
- sha256 = "56775f1283a59e6274c28d99981a9717ff4e0b1161e9129fdb2fcf22531d8d93",
- strip_prefix = "abseil-cpp-a0d1e098c2f99694fa399b175a7ccf920762030e",
- urls = [
- "https://mirror.bazel.build/github.com/abseil/abseil-cpp/archive/a0d1e098c2f99694fa399b175a7ccf920762030e.tar.gz",
- "https://github.com/abseil/abseil-cpp/archive/a0d1e098c2f99694fa399b175a7ccf920762030e.tar.gz",
- ],
-)
http_archive(
name = "com_google_googletest",
@@ -310,8 +1357,8 @@ go_repository(
go_repository(
name = "com_github_golang_mock",
importpath = "github.com/golang/mock",
- sum = "h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=",
- version = "v1.4.4",
+ sum = "h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=",
+ version = "v1.5.0",
)
go_repository(
@@ -324,15 +1371,15 @@ go_repository(
go_repository(
name = "com_github_google_uuid",
importpath = "github.com/google/uuid",
- sum = "h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=",
- version = "v1.1.2",
+ sum = "h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=",
+ version = "v1.2.0",
)
go_repository(
name = "com_github_kr_pretty",
importpath = "github.com/kr/pretty",
- sum = "h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=",
- version = "v0.1.0",
+ sum = "h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=",
+ version = "v0.2.1",
)
go_repository(
@@ -374,50 +1421,50 @@ go_repository(
name = "org_golang_google_grpc",
build_file_proto_mode = "disable",
importpath = "google.golang.org/grpc",
- sum = "h1:iGG0ZwQMaxJT/qsL0nnzZCg+4aiWHuQy3MytzLieAjo=",
- version = "v1.36.0-dev.0.20210208035533-9280052d3665",
+ sum = "h1:c+E5hkHV2oYLLcjZ0Uulu4thvOFKB0a9TWvowIWqgu4=",
+ version = "v1.39.0-dev.0.20210518002758-2713b77e8526",
)
go_repository(
name = "in_gopkg_check_v1",
importpath = "gopkg.in/check.v1",
- sum = "h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=",
- version = "v1.0.0-20180628173108-788fd7840127",
+ sum = "h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=",
+ version = "v1.0.0-20190902080502-41f04d3bba15",
)
go_repository(
name = "org_golang_x_crypto",
importpath = "golang.org/x/crypto",
- sum = "h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=",
- version = "v0.0.0-20200622213623-75b288015ac9",
+ sum = "h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=",
+ version = "v0.0.0-20210220033148-5ea612d1eb83",
)
go_repository(
name = "org_golang_x_mod",
importpath = "golang.org/x/mod",
- sum = "h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=",
- version = "v0.4.0",
+ sum = "h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=",
+ version = "v0.4.1",
)
go_repository(
name = "org_golang_x_net",
importpath = "golang.org/x/net",
- sum = "h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=",
- version = "v0.0.0-20201224014010-6772e930b67b",
+ sum = "h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=",
+ version = "v0.0.0-20210226172049-e18ecbb05110",
)
go_repository(
name = "org_golang_x_sync",
importpath = "golang.org/x/sync",
- sum = "h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=",
- version = "v0.0.0-20201020160332-67f06af15bc9",
+ sum = "h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=",
+ version = "v0.0.0-20210220032951-036812b2e83c",
)
go_repository(
name = "org_golang_x_text",
importpath = "golang.org/x/text",
- sum = "h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=",
- version = "v0.3.4",
+ sum = "h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=",
+ version = "v0.3.5",
)
go_repository(
@@ -444,22 +1491,22 @@ go_repository(
go_repository(
name = "com_github_google_btree",
importpath = "github.com/google/btree",
- sum = "h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=",
- version = "v1.0.0",
+ sum = "h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=",
+ version = "v1.0.1",
)
go_repository(
name = "com_github_golang_protobuf",
importpath = "github.com/golang/protobuf",
- sum = "h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=",
- version = "v1.4.3",
+ sum = "h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=",
+ version = "v1.5.0",
)
go_repository(
name = "org_golang_x_oauth2",
importpath = "golang.org/x/oauth2",
- sum = "h1:Lm4OryKCca1vehdsWogr9N4t7NfZxLbJoc/H0w4K4S4=",
- version = "v0.0.0-20201208152858-08078c50e5b5",
+ sum = "h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc=",
+ version = "v0.0.0-20210402161424-2e8d93401602",
)
go_repository(
@@ -514,8 +1561,8 @@ go_repository(
go_repository(
name = "com_github_konsorten_go_windows_terminal_sequences",
importpath = "github.com/konsorten/go-windows-terminal-sequences",
- sum = "h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=",
- version = "v1.0.2",
+ sum = "h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=",
+ version = "v1.0.3",
)
go_repository(
@@ -528,8 +1575,8 @@ go_repository(
go_repository(
name = "com_github_stretchr_testify",
importpath = "github.com/stretchr/testify",
- sum = "h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=",
- version = "v1.5.1",
+ sum = "h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=",
+ version = "v1.6.1",
)
go_repository(
@@ -542,15 +1589,15 @@ go_repository(
go_repository(
name = "com_github_microsoft_go_winio",
importpath = "github.com/Microsoft/go-winio",
- sum = "h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=",
- version = "v0.4.16",
+ sum = "h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=",
+ version = "v0.5.0",
)
go_repository(
name = "com_github_stretchr_objx",
importpath = "github.com/stretchr/objx",
- sum = "h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=",
- version = "v0.1.1",
+ sum = "h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=",
+ version = "v0.2.0",
)
go_repository(
@@ -570,15 +1617,15 @@ go_repository(
go_repository(
name = "com_google_cloud_go",
importpath = "cloud.google.com/go",
- sum = "h1:XgtDnVJRCPEUG21gjFiRPz4zI1Mjg16R+NYQjfmU4XY=",
- version = "v0.75.0",
+ sum = "h1:oqqswrt4x6b9OGBnNqdssxBl1xf0rSUNjU2BR4BZar0=",
+ version = "v0.79.0",
)
go_repository(
name = "io_opencensus_go",
importpath = "go.opencensus.io",
- sum = "h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=",
- version = "v0.22.5",
+ sum = "h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=",
+ version = "v0.23.0",
)
go_repository(
@@ -634,8 +1681,8 @@ go_repository(
go_repository(
name = "com_github_containerd_continuity",
importpath = "github.com/containerd/continuity",
- sum = "h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg=",
- version = "v0.0.0-20210208174643-50096c924a4e",
+ sum = "h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8=",
+ version = "v0.1.0",
)
go_repository(
@@ -676,8 +1723,8 @@ go_repository(
go_repository(
name = "com_github_envoyproxy_go_control_plane",
importpath = "github.com/envoyproxy/go-control-plane",
- sum = "h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE=",
- version = "v0.9.9-0.20201210154907-fd9021fe5dad",
+ sum = "h1:dulLQAYQFYtG5MTplgNGHWuV2D+OBD+Z8lmDBmbLg+s=",
+ version = "v0.9.9-0.20210512163311-63b5d3c536b0",
)
go_repository(
@@ -690,15 +1737,15 @@ go_repository(
go_repository(
name = "com_github_gogo_googleapis",
importpath = "github.com/gogo/googleapis",
- sum = "h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI=",
- version = "v1.4.0",
+ sum = "h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=",
+ version = "v1.4.1",
)
go_repository(
name = "com_github_gogo_protobuf",
importpath = "github.com/gogo/protobuf",
- sum = "h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=",
- version = "v1.3.1",
+ sum = "h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=",
+ version = "v1.3.2",
)
go_repository(
@@ -711,8 +1758,8 @@ go_repository(
go_repository(
name = "com_github_google_go_cmp",
importpath = "github.com/google/go-cmp",
- sum = "h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=",
- version = "v0.5.4",
+ sum = "h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=",
+ version = "v0.5.5",
)
go_repository(
@@ -739,8 +1786,8 @@ go_repository(
go_repository(
name = "com_github_kisielk_errcheck",
importpath = "github.com/kisielk/errcheck",
- sum = "h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E=",
- version = "v1.2.0",
+ sum = "h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=",
+ version = "v1.5.0",
)
go_repository(
@@ -760,8 +1807,8 @@ go_repository(
go_repository(
name = "com_github_opencontainers_runc",
importpath = "github.com/opencontainers/runc",
- sum = "h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=",
- version = "v0.1.1",
+ sum = "h1:4+xo8mtWixbHoEm451+WJNUrq12o2/tDsyK9Vgc/NcA=",
+ version = "v1.0.0-rc90",
)
go_repository(
@@ -781,22 +1828,22 @@ go_repository(
go_repository(
name = "com_github_prometheus_client_model",
importpath = "github.com/prometheus/client_model",
- sum = "h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=",
- version = "v0.0.0-20190812154241-14fe0d1b01d4",
+ sum = "h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=",
+ version = "v0.2.0",
)
go_repository(
name = "com_github_prometheus_procfs",
importpath = "github.com/prometheus/procfs",
- sum = "h1:Lo6mRUjdS99f3zxYOUalftWHUoOGaDRqFk1+j0Q57/I=",
- version = "v0.0.0-20190522114515-bc1a522cf7b1",
+ sum = "h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=",
+ version = "v0.6.0",
)
go_repository(
name = "com_github_spf13_cobra",
importpath = "github.com/spf13/cobra",
- sum = "h1:GQkkv3XSnxhAMjdq2wLfEnptEVr+2BNvmHizILHn+d4=",
- version = "v0.0.2-0.20171109065643-2da4a54c5cee",
+ sum = "h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=",
+ version = "v1.0.0",
)
go_repository(
@@ -844,15 +1891,15 @@ go_repository(
go_repository(
name = "org_golang_google_genproto",
importpath = "google.golang.org/genproto",
- sum = "h1:n7yjMkxUgbEahYENvAGVlxMUW8TF/KEavLez31znfDw=",
- version = "v0.0.0-20210108203827-ffc7fda8c3d7",
+ sum = "h1:YRBxgxUW6GFi+AKsn8WGA9k1SZohK+gGuEqdeT5aoNQ=",
+ version = "v0.0.0-20210312152112-fc591d9ea70f",
)
go_repository(
name = "org_golang_google_protobuf",
importpath = "google.golang.org/protobuf",
- sum = "h1:jEdfCm+8YTWSYgU4L7Nq0jjU+q9RxIhi0cXLTY+Ih3A=",
- version = "v1.25.1-0.20201020201750-d3470999428b",
+ sum = "h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=",
+ version = "v1.26.0",
)
go_repository(
@@ -928,8 +1975,8 @@ go_repository(
go_repository(
name = "com_github_google_pprof",
importpath = "github.com/google/pprof",
- sum = "h1:LB1NXQJhB+dF+5kZVLIn85HJqGvK3zKHIku3bdy3IRc=",
- version = "v0.0.0-20210115211752-39141e76b647",
+ sum = "h1:l2YRhr+YLzmSp7KJMswRVk/lO5SwoFIcCLzJsVj+YPc=",
+ version = "v0.0.0-20210423192551-a2663126120b",
)
go_repository(
@@ -991,8 +2038,8 @@ go_repository(
go_repository(
name = "org_golang_google_api",
importpath = "google.golang.org/api",
- sum = "h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE=",
- version = "v0.36.0",
+ sum = "h1:uqATLkpxiBrhrvFoebXUjvyzE9nQf+pVyy0Z0IHE+fc=",
+ version = "v0.42.0",
)
go_repository(
@@ -1054,8 +2101,8 @@ go_repository(
go_repository(
name = "com_github_cilium_ebpf",
importpath = "github.com/cilium/ebpf",
- sum = "h1:Fv93L3KKckEcEHR3oApXVzyBTDA8WAm6VXhPE00N3f8=",
- version = "v0.2.0",
+ sum = "h1:QlHdikaxALkqWasW8hAC1mfR0jdmvbfaBdBPFmRSglA=",
+ version = "v0.4.0",
)
go_repository(
@@ -1096,43 +2143,43 @@ go_repository(
go_repository(
name = "com_github_azure_go_autorest_autorest",
importpath = "github.com/Azure/go-autorest/autorest",
- sum = "h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=",
- version = "v0.9.0",
+ sum = "h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ=",
+ version = "v0.11.1",
)
go_repository(
name = "com_github_azure_go_autorest_autorest_adal",
importpath = "github.com/Azure/go-autorest/autorest/adal",
- sum = "h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=",
- version = "v0.5.0",
+ sum = "h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=",
+ version = "v0.9.5",
)
go_repository(
name = "com_github_azure_go_autorest_autorest_date",
importpath = "github.com/Azure/go-autorest/autorest/date",
- sum = "h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=",
- version = "v0.1.0",
+ sum = "h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=",
+ version = "v0.3.0",
)
go_repository(
name = "com_github_azure_go_autorest_autorest_mocks",
importpath = "github.com/Azure/go-autorest/autorest/mocks",
- sum = "h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0=",
- version = "v0.2.0",
+ sum = "h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=",
+ version = "v0.4.1",
)
go_repository(
name = "com_github_azure_go_autorest_logger",
importpath = "github.com/Azure/go-autorest/logger",
- sum = "h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=",
- version = "v0.1.0",
+ sum = "h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=",
+ version = "v0.2.0",
)
go_repository(
name = "com_github_azure_go_autorest_tracing",
importpath = "github.com/Azure/go-autorest/tracing",
- sum = "h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=",
- version = "v0.5.0",
+ sum = "h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=",
+ version = "v0.6.0",
)
go_repository(
@@ -1152,86 +2199,86 @@ go_repository(
go_repository(
name = "com_github_elazarl_goproxy",
importpath = "github.com/elazarl/goproxy",
- sum = "h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=",
- version = "v0.0.0-20170405201442-c4fc26588b6e",
+ sum = "h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=",
+ version = "v0.0.0-20180725130230-947c36da3153",
)
go_repository(
name = "com_github_emicklei_go_restful",
importpath = "github.com/emicklei/go-restful",
- sum = "h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=",
- version = "v0.0.0-20170410110728-ff4f55a20633",
+ sum = "h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=",
+ version = "v2.9.5+incompatible",
)
go_repository(
name = "com_github_evanphx_json_patch",
importpath = "github.com/evanphx/json-patch",
- sum = "h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=",
- version = "v4.2.0+incompatible",
+ sum = "h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=",
+ version = "v4.9.0+incompatible",
)
go_repository(
name = "com_github_fsnotify_fsnotify",
importpath = "github.com/fsnotify/fsnotify",
- sum = "h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=",
- version = "v1.4.7",
+ sum = "h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=",
+ version = "v1.4.9",
)
go_repository(
name = "com_github_ghodss_yaml",
importpath = "github.com/ghodss/yaml",
- sum = "h1:ZktWZesgun21uEDrwW7iEV1zPCGQldM2atlJZ3TdvVM=",
- version = "v0.0.0-20150909031657-73d445a93680",
+ sum = "h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=",
+ version = "v1.0.0",
)
go_repository(
name = "com_github_go_logr_logr",
importpath = "github.com/go-logr/logr",
- sum = "h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=",
- version = "v0.1.0",
+ sum = "h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=",
+ version = "v0.2.0",
)
go_repository(
name = "com_github_go_openapi_jsonpointer",
importpath = "github.com/go-openapi/jsonpointer",
- sum = "h1:wSt/4CYxs70xbATrGXhokKF1i0tZjENLOo1ioIO13zk=",
- version = "v0.0.0-20160704185906-46af16f9f7b1",
+ sum = "h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=",
+ version = "v0.19.3",
)
go_repository(
name = "com_github_go_openapi_jsonreference",
importpath = "github.com/go-openapi/jsonreference",
- sum = "h1:tF+augKRWlWx0J0B7ZyyKSiTyV6E1zZe+7b3qQlcEf8=",
- version = "v0.0.0-20160704190145-13c6e3589ad9",
+ sum = "h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=",
+ version = "v0.19.3",
)
go_repository(
name = "com_github_go_openapi_spec",
importpath = "github.com/go-openapi/spec",
- sum = "h1:C1JKChikHGpXwT5UQDFaryIpDtyyGL/CR6C2kB7F1oc=",
- version = "v0.0.0-20160808142527-6aced65f8501",
+ sum = "h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=",
+ version = "v0.19.0",
)
go_repository(
name = "com_github_go_openapi_swag",
importpath = "github.com/go-openapi/swag",
- sum = "h1:zP3nY8Tk2E6RTkqGYrarZXuzh+ffyLDljLxCy1iJw80=",
- version = "v0.0.0-20160704191624-1d0bd113de87",
+ sum = "h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=",
+ version = "v0.19.5",
)
go_repository(
name = "com_github_google_gofuzz",
importpath = "github.com/google/gofuzz",
- sum = "h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=",
- version = "v1.0.0",
+ sum = "h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=",
+ version = "v1.1.0",
)
go_repository(
name = "com_github_googleapis_gnostic",
build_file_proto_mode = "disable_global",
importpath = "github.com/googleapis/gnostic",
- sum = "h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=",
- version = "v0.0.0-20170729233727-0c5108395e2d",
+ sum = "h1:BXDUo8p/DaxC+4FJY/SSx3gvnx9C1VdHNgaUkiEL5mk=",
+ version = "v0.4.0",
)
go_repository(
@@ -1258,22 +2305,22 @@ go_repository(
go_repository(
name = "com_github_imdario_mergo",
importpath = "github.com/imdario/mergo",
- sum = "h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=",
- version = "v0.3.5",
+ sum = "h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=",
+ version = "v0.3.9",
)
go_repository(
name = "com_github_json_iterator_go",
importpath = "github.com/json-iterator/go",
- sum = "h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=",
- version = "v1.1.7",
+ sum = "h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=",
+ version = "v1.1.10",
)
go_repository(
name = "com_github_mailru_easyjson",
importpath = "github.com/mailru/easyjson",
- sum = "h1:TpvdAwDAt1K4ANVOfcihouRdvP+MgAfDWwBuct4l6ZY=",
- version = "v0.0.0-20160728113105-d5b7844b561a",
+ sum = "h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=",
+ version = "v0.7.0",
)
go_repository(
@@ -1300,8 +2347,8 @@ go_repository(
go_repository(
name = "com_github_munnerz_goautoneg",
importpath = "github.com/munnerz/goautoneg",
- sum = "h1:7PxY7LVfSZm7PEeBTyK1rj1gABdCO2mbri6GKO1cMDs=",
- version = "v0.0.0-20120707110453-a547fc61f48d",
+ sum = "h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=",
+ version = "v0.0.0-20191010083416-a7dc8b61c822",
)
go_repository(
@@ -1321,15 +2368,15 @@ go_repository(
go_repository(
name = "com_github_onsi_ginkgo",
importpath = "github.com/onsi/ginkgo",
- sum = "h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=",
- version = "v1.8.0",
+ sum = "h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=",
+ version = "v1.12.1",
)
go_repository(
name = "com_github_onsi_gomega",
importpath = "github.com/onsi/gomega",
- sum = "h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=",
- version = "v1.5.0",
+ sum = "h1:Gwkk+PTu/nfOwNMtUB/mRUv0X7ewW5dO4AERT1ThVKo=",
+ version = "v1.10.0",
)
go_repository(
@@ -1342,15 +2389,15 @@ go_repository(
go_repository(
name = "com_github_puerkitobio_purell",
importpath = "github.com/PuerkitoBio/purell",
- sum = "h1:0GoNN3taZV6QI81IXgCbxMyEaJDXMSIjArYBCYzVVvs=",
- version = "v1.0.0",
+ sum = "h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=",
+ version = "v1.1.1",
)
go_repository(
name = "com_github_puerkitobio_urlesc",
importpath = "github.com/PuerkitoBio/urlesc",
- sum = "h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE=",
- version = "v0.0.0-20160726150825-5bd2802263f2",
+ sum = "h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=",
+ version = "v0.0.0-20170810143723-de5bf2ad4578",
)
go_repository(
@@ -1407,8 +2454,8 @@ go_repository(
go_repository(
name = "io_k8s_gengo",
importpath = "k8s.io/gengo",
- sum = "h1:4s3/R4+OYYYUKptXPhZKjQ04WJ6EhQQVFdjOFvCazDk=",
- version = "v0.0.0-20190128074634-0689ccc1d7d6",
+ sum = "h1:sAvhNk5RRuc6FNYGqe7Ygz3PSo/2wGWbulskmzRX8Vs=",
+ version = "v0.0.0-20200413195148-3a45101e95ac",
)
go_repository(
@@ -1435,15 +2482,15 @@ go_repository(
go_repository(
name = "io_k8s_sigs_yaml",
importpath = "sigs.k8s.io/yaml",
- sum = "h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=",
- version = "v1.1.0",
+ sum = "h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=",
+ version = "v1.2.0",
)
go_repository(
name = "io_k8s_utils",
importpath = "k8s.io/utils",
- sum = "h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE=",
- version = "v0.0.0-20190801114015-581e00157fb1",
+ sum = "h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=",
+ version = "v0.0.0-20201110183641-67b214c5f920",
)
go_repository(
diff --git a/g3doc/user_guide/compatibility.md b/g3doc/user_guide/compatibility.md
index 9d3e3680f..76e879a01 100644
--- a/g3doc/user_guide/compatibility.md
+++ b/g3doc/user_guide/compatibility.md
@@ -49,43 +49,44 @@ Most common utilities work. Note that:
<!-- mdformat off(don't wrap the table) -->
-| Tool | Status |
-|:--------:|:-----------------------------------------:|
-| apt-get | Working. |
-| bundle | Working. |
-| cat | Working. |
-| curl | Working. |
-| dd | Working. |
-| df | Working. |
-| dig | Working. |
-| drill | Working. |
-| env | Working. |
-| find | Working. |
-| gdb | Working. |
-| gosu | Working. |
-| grep | Working (unless stdin is a pipe and stdout is /dev/null). |
-| ifconfig | Works partially, like ip. Full support [in progress](https://gvisor.dev/issue/578). |
-| ip | Some subcommands work (e.g. addr, route). Full support [in progress](https://gvisor.dev/issue/578). |
-| less | Working. |
-| ls | Working. |
-| lsof | Working. |
-| mount | Works in readonly mode. gVisor doesn't currently support creating new mounts at runtime. |
-| nc | Working. |
-| nmap | Not working. |
-| netstat | [In progress](https://gvisor.dev/issue/2112). |
-| nslookup | Working. |
-| ping | Working. |
-| ps | Working. |
-| route | Working. |
-| ss | [In progress](https://gvisor.dev/issue/2114). |
-| sshd | Partially working. Job control [in progress](https://gvisor.dev/issue/154). |
-| strace | Working. |
-| tar | Working. |
-| tcpdump | [In progress](https://gvisor.dev/issue/173). |
-| top | Working. |
-| uptime | Working. |
-| vim | Working. |
-| wget | Working. |
+| Tool | Status |
+| :--------: | :-----------------------------------------: |
+| apt-get | Working. |
+| bundle | Working. |
+| cat | Working. |
+| curl | Working. |
+| dd | Working. |
+| df | Working. |
+| dig | Working. |
+| drill | Working. |
+| env | Working. |
+| find | Working. |
+| gcore | Working. |
+| gdb | Working. |
+| gosu | Working. |
+| grep | Working (unless stdin is a pipe and stdout is /dev/null). |
+| ifconfig | Works partially, like ip. Full support [in progress](https://gvisor.dev/issue/578). |
+| ip | Some subcommands work (e.g. addr, route). Full support [in progress](https://gvisor.dev/issue/578). |
+| less | Working. |
+| ls | Working. |
+| lsof | Working. |
+| mount | Works in readonly mode. gVisor doesn't currently support creating new mounts at runtime. |
+| nc | Working. |
+| nmap | Not working. |
+| netstat | [In progress](https://gvisor.dev/issue/2112). |
+| nslookup | Working. |
+| ping | Working. |
+| ps | Working. |
+| route | Working. |
+| ss | [In progress](https://gvisor.dev/issue/2114). |
+| sshd | Partially working. Job control [in progress](https://gvisor.dev/issue/154). |
+| strace | Working. |
+| tar | Working. |
+| tcpdump | Working. [Promiscuous mode in progress](https://gvisor.dev/issue/3333). |
+| top | Working. |
+| uptime | Working. |
+| vim | Working. |
+| wget | Working. |
<!-- mdformat on -->
diff --git a/g3doc/user_guide/debugging.md b/g3doc/user_guide/debugging.md
index 54fdce34f..2291b5fab 100644
--- a/g3doc/user_guide/debugging.md
+++ b/g3doc/user_guide/debugging.md
@@ -61,24 +61,39 @@ You can debug gVisor like any other Golang program. If you're running with
Docker, you'll need to find the sandbox PID and attach the debugger as root.
Here is an example:
+Install a runsc with debug symbols (you can also use the
+[nightly release](../install/#nightly)):
+
```bash
-# Get a runsc with debug symbols (download nightly or build with symbols).
-bazel build -c dbg //runsc:runsc
+make dev BAZEL_OPTIONS="-c dbg"
+```
-# Start the container you want to debug.
-docker run --runtime=runsc --rm --name=test -d alpine sleep 1000
+Start the container you want to debug using the runsc runtime with debug
+options:
-# Find the sandbox PID.
-docker inspect test | grep Pid | head -n 1
+```bash
+docker run --runtime=$(git branch --show-current)-d --rm --name=test -p 8080:80 -d nginx
+```
-# Attach your favorite debugger.
-sudo dlv attach <PID>
+Find the PID and attach your favorite debugger:
+
+```bash
+sudo dlv attach $(docker inspect test | grep Pid | head -n 1 | grep -oe "[0-9]*")
+```
-# Set a breakpoint and resume.
-break mm.MemoryManager.MMap
+Set a breakpoint for accept:
+
+```bash
+break gvisor.dev/gvisor/pkg/sentry/socket/netstack.(*SocketOperations).Accept
continue
```
+In a different window connect to nginx to trigger the breakpoint:
+
+```bash
+curl http://localhost:8080/
+```
+
## Profiling
`runsc` integrates with Go profiling tools and gives you easy commands to
diff --git a/go.mod b/go.mod
index 870fb0b83..9ddaa50f6 100644
--- a/go.mod
+++ b/go.mod
@@ -3,57 +3,151 @@ module gvisor.dev/gvisor
go 1.16
require (
- cloud.google.com/go v0.75.0 // indirect
+ cloud.google.com/go v0.79.0 // indirect
+ github.com/Azure/azure-sdk-for-go v16.2.1+incompatible // indirect
+ github.com/Azure/go-autorest/autorest v0.11.1 // indirect
+ github.com/Azure/go-autorest/autorest/adal v0.9.5 // indirect
github.com/BurntSushi/toml v0.3.1 // indirect
- github.com/Microsoft/go-winio v0.4.16 // indirect
+ github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/Microsoft/hcsshim v0.8.14 // indirect
+ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
+ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
+ github.com/aws/aws-sdk-go v1.15.11 // indirect
+ github.com/bazelbuild/rules_go v0.27.0 // indirect
+ github.com/bitly/go-simplejson v0.5.0 // indirect
+ github.com/blang/semver v3.5.1+incompatible // indirect
+ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
+ github.com/bshuster-repo/logrus-logstash-hook v0.4.1 // indirect
+ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd // indirect
+ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b // indirect
+ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 // indirect
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422 // indirect
+ github.com/checkpoint-restore/go-criu/v4 v4.1.0 // indirect
+ github.com/cilium/ebpf v0.4.0 // indirect
+ github.com/containerd/btrfs v1.0.0 // indirect
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327 // indirect
github.com/containerd/console v1.0.1 // indirect
github.com/containerd/containerd v1.3.9 // indirect
- github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e // indirect
+ github.com/containerd/continuity v0.1.0 // indirect
github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00 // indirect
+ github.com/containerd/go-cni v1.0.2 // indirect
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328 // indirect
+ github.com/containerd/imgcrypt v1.0.3 // indirect
github.com/containerd/ttrpc v1.0.2 // indirect
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737 // indirect
+ github.com/containernetworking/cni v0.8.1 // indirect
+ github.com/containernetworking/plugins v0.8.7 // indirect
+ github.com/coreos/go-iptables v0.5.0 // indirect
+ github.com/coreos/go-oidc v2.1.0+incompatible // indirect
+ github.com/coreos/go-semver v0.3.0 // indirect
+ github.com/cyphar/filepath-securejoin v0.2.2 // indirect
+ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba // indirect
+ github.com/dnaeon/go-vcr v1.0.1 // indirect
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible // indirect
github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55 // indirect
github.com/docker/go-connections v0.3.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
+ github.com/docker/go-metrics v0.0.1 // indirect
+ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 // indirect
+ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 // indirect
+ github.com/emicklei/go-restful v2.9.5+incompatible // indirect
+ github.com/evanphx/json-patch v4.9.0+incompatible // indirect
+ github.com/fsnotify/fsnotify v1.4.9 // indirect
+ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa // indirect
+ github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 // indirect
+ github.com/go-openapi/jsonreference v0.19.3 // indirect
+ github.com/go-openapi/spec v0.19.0 // indirect
+ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e // indirect
github.com/gofrs/flock v0.8.0 // indirect
- github.com/gogo/googleapis v1.4.0 // indirect
- github.com/gogo/protobuf v1.3.1 // indirect
- github.com/golang/mock v1.4.4 // indirect
- github.com/google/btree v1.0.0 // indirect
- github.com/google/go-cmp v0.5.4 // indirect
+ github.com/gogo/googleapis v1.4.1 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/golang/mock v1.5.0 // indirect
+ github.com/golang/protobuf v1.5.0 // indirect
+ github.com/google/btree v1.0.1 // indirect
+ github.com/google/go-cmp v0.5.5 // indirect
github.com/google/go-github/v32 v32.1.0 // indirect
- github.com/google/pprof v0.0.0-20210115211752-39141e76b647 // indirect
+ github.com/google/gofuzz v1.1.0 // indirect
+ github.com/google/pprof v0.0.0-20210423192551-a2663126120b // indirect
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8 // indirect
+ github.com/google/uuid v1.2.0 // indirect
+ github.com/googleapis/gnostic v0.4.0 // indirect
+ github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 // indirect
+ github.com/gorilla/mux v1.7.2 // indirect
+ github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
+ github.com/imdario/mergo v0.3.9 // indirect
+ github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7 // indirect
+ github.com/klauspost/compress v1.11.13 // indirect
+ github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1 // indirect
+ github.com/mailru/easyjson v0.7.0 // indirect
+ github.com/marstr/guid v1.1.0 // indirect
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a // indirect
+ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
+ github.com/miekg/pkcs11 v1.0.3 // indirect
+ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible // indirect
+ github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f // indirect
+ github.com/moby/locker v1.0.1 // indirect
+ github.com/moby/sys/mountinfo v0.4.1 // indirect
+ github.com/moby/sys/symlink v0.1.0 // indirect
+ github.com/moby/term v0.0.0-20200312100748-672ec06f55cd // indirect
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9 // indirect
- github.com/opencontainers/image-spec v1.0.1 // indirect
- github.com/opencontainers/runc v0.1.1 // indirect
+ github.com/mrunalp/fileutils v0.5.0 // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/ncw/swift v1.0.47 // indirect
+ github.com/onsi/ginkgo v1.12.1 // indirect
+ github.com/onsi/gomega v1.10.0 // indirect
+ github.com/opencontainers/runc v1.0.0-rc90 // indirect
github.com/opencontainers/runtime-spec v1.0.2 // indirect
+ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39 // indirect
+ github.com/opencontainers/selinux v1.8.0 // indirect
github.com/pborman/uuid v1.2.0 // indirect
+ github.com/pelletier/go-toml v1.8.1 // indirect
+ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect
+ github.com/prometheus/client_golang v1.7.1 // indirect
+ github.com/prometheus/procfs v0.6.0 // indirect
+ github.com/satori/go.uuid v1.2.0 // indirect
+ github.com/seccomp/libseccomp-golang v0.9.1 // indirect
github.com/sirupsen/logrus v1.7.0 // indirect
+ github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
+ github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
+ github.com/stretchr/objx v0.2.0 // indirect
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect
+ github.com/tchap/go-patricia v2.2.6+incompatible // indirect
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 // indirect
+ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 // indirect
+ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f // indirect
+ go.etcd.io/bbolt v1.3.5 // indirect
+ go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 // indirect
go.uber.org/multierr v1.6.0 // indirect
- golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
- golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 // indirect
- golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect
+ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
+ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
+ golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect
+ golang.org/x/sys v0.0.0-20210314195730-07df6a141424 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.1.0 // indirect
- google.golang.org/api v0.36.0 // indirect
- google.golang.org/grpc v1.36.0-dev.0.20210208035533-9280052d3665 // indirect
- google.golang.org/protobuf v1.25.1-0.20201020201750-d3470999428b // indirect
+ google.golang.org/api v0.42.0 // indirect
+ google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 // indirect
+ google.golang.org/grpc v1.39.0-dev.0.20210518002758-2713b77e8526 // indirect
+ google.golang.org/protobuf v1.26.0 // indirect
+ gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
+ gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
+ gotest.tools/v3 v3.0.3 // indirect
honnef.co/go/tools v0.1.1 // indirect
k8s.io/api v0.16.13 // indirect
k8s.io/apimachinery v0.16.14-rc.0 // indirect
k8s.io/client-go v0.16.13 // indirect
+ k8s.io/component-base v0.16.13 // indirect
+ k8s.io/cri-api v0.20.6 // indirect
+ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac // indirect
+ k8s.io/klog/v2 v2.4.0 // indirect
+ k8s.io/kubernetes v1.13.0 // indirect
+ k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
+ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15 // indirect
+ sigs.k8s.io/structured-merge-diff/v4 v4.0.3 // indirect
+ sigs.k8s.io/yaml v1.2.0 // indirect
)
diff --git a/go.sum b/go.sum
index bd3d0b0f7..d7066a22d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,4 @@
+bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898 h1:SC+c6A1qTFstO9qmB86mPV2IpYme/2ZoEQ0hrP+wo+Q=
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
@@ -15,135 +16,361 @@ cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZ
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
-cloud.google.com/go v0.75.0 h1:XgtDnVJRCPEUG21gjFiRPz4zI1Mjg16R+NYQjfmU4XY=
-cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
+cloud.google.com/go v0.79.0 h1:oqqswrt4x6b9OGBnNqdssxBl1xf0rSUNjU2BR4BZar0=
+cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbxukD1WuWXOiE9fRdu32f2I=
+github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
+github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
+github.com/Azure/go-autorest/autorest v0.11.1 h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ=
+github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
+github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=
+github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
+github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
+github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
+github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
+github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
+github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
+github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
-github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
+github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/Microsoft/hcsshim v0.8.14 h1:lbPVK25c1cu5xTLITwpUcxoA9vKrKErASPYygvouJns=
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae h1:AMzIhMUqU3jMrZiTuW0zkYeKlKDAFD+DG20IoO421/Y=
+github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
+github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/aws/aws-sdk-go v1.15.11 h1:m45+Ru/wA+73cOZXiEGLDH2d9uLN3iHqMc0/z4noDXE=
+github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
+github.com/bazelbuild/rules_go v0.27.0 h1:KViqR7qKXwz+LrNdIauCDU21kneCk+4DnYjpvlJwH50=
+github.com/bazelbuild/rules_go v0.27.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
+github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
+github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA=
+github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
+github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA=
+github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422 h1:8eZxmY1yvxGHzdzTEhI09npjMVGzNAdrqzruTX6jcK4=
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
+github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/checkpoint-restore/go-criu/v4 v4.1.0 h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo=
+github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
+github.com/cilium/ebpf v0.4.0 h1:QlHdikaxALkqWasW8hAC1mfR0jdmvbfaBdBPFmRSglA=
+github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/containerd/btrfs v1.0.0 h1:osn1exbzdub9L5SouXO5swW4ea/xVdJZ3wokxN5GrnA=
+github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327 h1:7grrpcfCtbZLsjtB0DgMuzs1umsJmpzaHMZ6cO6iAWw=
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
+github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.9 h1:K2U/F4jGAMBqeUssfgJRbFuomLcS2Fxo1vR3UM/Mbh8=
github.com/containerd/containerd v1.3.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
-github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg=
-github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
+github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8=
+github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00 h1:lsjC5ENBl+Zgf38+B0ymougXFp0BaubeIVETltYZTQw=
github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/go-cni v1.0.2 h1:YbJAhpTevL2v6u8JC1NhCYRwf+3Vzxcc5vGnYoJ7VeE=
+github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328 h1:PRTagVMbJcCezLcHXe8UJvR1oBzp2lG3CEumeFOLOds=
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
+github.com/containerd/imgcrypt v1.0.3 h1:31GdXSmR2bZRPuZ/5lgkXivzNSejJQPsVOq82qqzP/M=
+github.com/containerd/imgcrypt v1.0.3/go.mod h1:v4X3p/H0lzcvVE0r7whbRYjYuK9Y2KEJnL08tXT63Is=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
+github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v1.0.2 h1:2/O3oTZN36q2xRolk0a2WWGgh7/Vf/liElg5hFYLX9U=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737 h1:HovfQDS/K3Mr7eyS0QJLxE1CbVUhjZCl6g3OhFJgP1o=
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
+github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI=
+github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/plugins v0.8.7 h1:bU7QieuAp+sACI2vCzESJ3FoT860urYP+lThyZkb/2M=
+github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0=
+github.com/containers/ocicrypt v1.0.3 h1:vYgl+RZ9Q3DPMuTfxmN+qp0X2Bj52uuY2vnt6GzVe1c=
+github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQj8jcy0EVG6g=
+github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
+github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0 h1:kq/SbG2BCKLkDKkjQf5OWwKWUKj1lgs3lFI4PxnR5lg=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
+github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc=
+github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
+github.com/d2g/dhcp4client v1.0.0 h1:suYBsYZIkSlUMEz4TAYCczKf62IA2UWC+O8+KtdOhCo=
+github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
+github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5 h1:+CpLbZIeUn94m02LdEKPcgErLJ347NUwxPKs5u8ieiY=
+github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
+github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 h1:itqmmf1PFpC4n5JW+j4BU7X4MTfVurhYRTjODoPb2Y8=
+github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba h1:p6poVbjHDkKa+wtC8frBMwQtT3BmqGYBjzMwJ63tuR4=
+github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
+github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55 h1:5AkIsnQpeL7eaqsM+Vl4Xbj5eIZFpPZZzXtNyfzzK/w=
github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o=
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
+github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
+github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 h1:dulLQAYQFYtG5MTplgNGHWuV2D+OBD+Z8lmDBmbLg+s=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
+github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
+github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
+github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
+github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU=
+github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
+github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko=
+github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
+github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
+github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
+github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079 h1:JFTFz3HZTGmgMz4E1TabNBNJljROSYgja1b4l50FNVs=
-github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
-github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI=
-github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
+github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
+github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
+github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
@@ -154,8 +381,9 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -170,11 +398,13 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -183,16 +413,22 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
-github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
+github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -202,140 +438,399 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210115211752-39141e76b647 h1:LB1NXQJhB+dF+5kZVLIn85HJqGvK3zKHIku3bdy3IRc=
-github.com/google/pprof v0.0.0-20210115211752-39141e76b647/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210423192551-a2663126120b h1:l2YRhr+YLzmSp7KJMswRVk/lO5SwoFIcCLzJsVj+YPc=
+github.com/google/pprof v0.0.0-20210423192551-a2663126120b/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8 h1:8nlgEAjIalk6uj/CGKCdOO8CQqTeysvcW4RFZ6HbkGM=
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/googleapis/gnostic v0.4.0 h1:BXDUo8p/DaxC+4FJY/SSx3gvnx9C1VdHNgaUkiEL5mk=
+github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
+github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo=
+github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
+github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
+github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56 h1:742eGXur0715JMq73aD95/FU0XpVKXqNuTnEfXsLOYQ=
+github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
+github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7 h1:SMvOWPJCES2GdFracYbBQh93GXac8fq7HeN6JnpduB8=
+github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4=
+github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1 h1:zc0R6cOw98cMengLA0fvU55mqbnN7sd/tBMLzSejp+M=
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
+github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI=
+github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a h1:+J2gw7Bw77w/fbK7wnNJJDKmw1IbWft2Ul5BzrG1Qm8=
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
+github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=
+github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
+github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
+github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=
+github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ=
+github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
+github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
+github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
+github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
+github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
+github.com/moby/sys/symlink v0.1.0 h1:MTFZ74KtNI6qQQpuBxU+uKCim4WtOMokr03hCfJcazE=
+github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
+github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
+github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9 h1:Sha2bQdoWE5YQPTlJOL31rmce94/tYi113SlFo1xQ2c=
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=
+github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/ncw/swift v1.0.47 h1:4DQRPj35Y41WogBxyhOXlrI37nzGlyEcsforeudyYPQ=
+github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.0 h1:Gwkk+PTu/nfOwNMtUB/mRUv0X7ewW5dO4AERT1ThVKo=
+github.com/onsi/gomega v1.10.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
-github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc90 h1:4+xo8mtWixbHoEm451+WJNUrq12o2/tDsyK9Vgc/NcA=
+github.com/opencontainers/runc v1.0.0-rc90/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39 h1:H7DMc6FAjgwZZi8BRqjrAAHWoqEr5e5L6pS4V0ezet4=
+github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM=
+github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
+github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
+github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U=
+github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo=
+github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
+github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
+github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I=
+github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck=
+github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 h1:7SWt9pGCMaw+N1ZhRsaLKaYNviFhxambdoaoYlDqz1w=
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
+github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
+github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
+github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
+github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
+github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
+github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
+github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
+github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
+go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo=
+go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
+go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M=
+go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -345,8 +840,10 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -361,6 +858,7 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@@ -368,22 +866,32 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -401,8 +909,11 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
-golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -410,8 +921,12 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 h1:Lm4OryKCca1vehdsWogr9N4t7NfZxLbJoc/H0w4K4S4=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc=
+golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -420,12 +935,16 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -434,17 +953,25 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -460,14 +987,25 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210314195730-07df6a141424 h1:+39ahH47SWi1PhMRAHfIrm8f69HRZ5K2koXH6dmO8TQ=
+golang.org/x/sys v0.0.0-20210314195730-07df6a141424/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -475,12 +1013,15 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -489,11 +1030,13 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -520,13 +1063,16 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -551,8 +1097,11 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
-google.golang.org/api v0.36.0 h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
+google.golang.org/api v0.42.0 h1:uqATLkpxiBrhrvFoebXUjvyzE9nQf+pVyy0Z0IHE+fc=
+google.golang.org/api v0.42.0/go.mod h1:+Oj4s6ch2SEGtPjGqfUfZonBH0GjQH89gTeKKAEGZKI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -561,6 +1110,8 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 h1:Cpp2P6TPjujNoC5M2KHY6g7wfyLYfIWRZaSdIKfDasA=
+google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -585,6 +1136,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
@@ -593,14 +1145,22 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7 h1:n7yjMkxUgbEahYENvAGVlxMUW8TF/KEavLez31znfDw=
-google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f h1:YRBxgxUW6GFi+AKsn8WGA9k1SZohK+gGuEqdeT5aoNQ=
+google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -610,10 +1170,13 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
-google.golang.org/grpc v1.36.0-dev.0.20210208035533-9280052d3665 h1:iGG0ZwQMaxJT/qsL0nnzZCg+4aiWHuQy3MytzLieAjo=
-google.golang.org/grpc v1.36.0-dev.0.20210208035533-9280052d3665/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.39.0-dev.0.20210518002758-2713b77e8526 h1:c+E5hkHV2oYLLcjZ0Uulu4thvOFKB0a9TWvowIWqgu4=
+google.golang.org/grpc v1.39.0-dev.0.20210518002758-2713b77e8526/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -624,20 +1187,51 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.25.1-0.20201020201750-d3470999428b h1:jEdfCm+8YTWSYgU4L7Nq0jjU+q9RxIhi0cXLTY+Ih3A=
-google.golang.org/protobuf v1.25.1-0.20201020201750-d3470999428b/go.mod h1:hFxJC2f0epmp1elRCiEGJTKAWbwxZ2nvqZdHl3FQXCY=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
+gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
+gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
+gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
+gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
+gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -654,15 +1248,39 @@ k8s.io/apimachinery v0.16.14-rc.0 h1:eUHWTe8VT+VOZVKGfSCcFZDrr9RZ8djLYGjIanaZnXc
k8s.io/apimachinery v0.16.14-rc.0/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ=
k8s.io/client-go v0.16.13 h1:jp76b20+4h8qZBxferSAVZ6MjBEpw3F309zLmPhngag=
k8s.io/client-go v0.16.13/go.mod h1:UKvVT4cajC2iN7DCjLgT0KVY/cbY6DGdUCyRiIfws5M=
+k8s.io/component-base v0.16.13 h1:wHmQb3b/QCTODA/MGih9x7XZQmhdDlA8AxJnAID6T2A=
+k8s.io/component-base v0.16.13/go.mod h1:cNe9ZU2A6tqBG0gPQ4/T/KolI9Cv2NA1+7uvmkA7Cyc=
+k8s.io/cri-api v0.20.6 h1:iXX0K2pRrbR8yXbZtDK/bSnmg/uSqIFiVJK1x4LUOMc=
+k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac h1:sAvhNk5RRuc6FNYGqe7Ygz3PSo/2wGWbulskmzRX8Vs=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
+k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31 h1:PsbYeEz2x7ll6JYUzBEG+DT78910DDTlvn5Ma10F5/E=
k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
+k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8=
+k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
+k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15 h1:4uqm9Mv+w2MmBYD+F4qf/v6tDFUdPOk29C095RbU5mY=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/nogo.yaml b/nogo.yaml
index f4b62cf5b..9b7fc5c8f 100644
--- a/nogo.yaml
+++ b/nogo.yaml
@@ -46,6 +46,8 @@ global:
- "(field|method|struct|type) .* should be .*"
# Generated proto code sometimes duplicates imports with aliases.
- "duplicate import"
+ # These will never be annotated.
+ - "unexpected call to atomic function"
internal:
suppress:
# We use ALL_CAPS for system definitions,
@@ -55,6 +57,10 @@ global:
# Same story for underscores.
- "should not use ALL_CAPS in Go names"
- "should not use underscores in Go names"
+ # These need to be annotated.
+ - "unexpected call to atomic function.*"
+ - "return with unexpected locks held.*"
+ - "incompatible return states.*"
exclude:
# Generated: exempt all.
- pkg/shim/runtimeoptions/runtimeoptions_cri.go
@@ -76,49 +82,7 @@ analyzers:
checklocks:
internal:
exclude:
- - "^-$" # b/181776900: analyzer fails on buildkite
- - pkg/sentry/fs/dirent.go # unsupported usage.
- - pkg/sentry/fs/fsutil/inode_cached.go # unsupported usage.
- - pkg/sentry/fs/gofer/inode_state.go # unsupported usage.
- - pkg/sentry/fs/gofer/session.go # unsupported usage.
- - pkg/sentry/fs/ramfs/dir.go # unsupported usage.
- - pkg/sentry/fsimpl/fuse/connection.go # unsupported usage.
- - pkg/sentry/fsimpl/kernfs/filesystem.go # unsupported usage.
- - pkg/sentry/fsimpl/kernfs/inode_impl_util.go # unsupported usage.
- - pkg/sentry/fsimpl/fuse/dev_test.go # unsupported usage.
- - pkg/sentry/fsimpl/gofer/filesystem.go # unsupported usage.
- - pkg/sentry/fsimpl/gofer/gofer.go # unsupported usage.
- - pkg/sentry/fsimpl/gofer/regular_file.go # unsupported usage.
- - pkg/sentry/fsimpl/gofer/revalidate.go # unsupported usage.
- - pkg/sentry/fsimpl/gofer/special_file.go # unsupported usage.
- - pkg/sentry/fsimpl/gofer/symlink.go # unsupported usage.
- - pkg/sentry/fsimpl/overlay/copy_up.go # unsupported usage.
- - pkg/sentry/fsimpl/overlay/filesystem.go # unsupported usage.
- - pkg/sentry/fsimpl/tmpfs/filesystem.go # unsupported usage.
- - pkg/sentry/fsimpl/verity/filesystem.go # unsupported usage.
- - pkg/sentry/kernel/futex/futex.go # unsupported usage.
- - pkg/sentry/kernel/pipe/vfs.go # unsupported usage.
- - pkg/sentry/mm/syscalls.go # unsupported usage.
- - pkg/sentry/kernel/fd_table.go # unsupported usage.
- - pkg/sentry/kernel/ptrace.go # unsupported usage.
- - pkg/sentry/time/calibrated_clock_test.go # unsupported usage.
- - pkg/sentry/kernel/task_context.go # unsupported usage.
- - pkg/sentry/pgalloc/pgalloc.go # unsupported usage.
- - pkg/sentry/socket/unix/transport/connectioned.go # unsupported usage.
- - pkg/sentry/vfs/dentry.go # unsupported usage.
- - pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go # unsupported usage.
- - pkg/tcpip/stack/conntrack.go # unsupported usage.
- - pkg/tcpip/transport/packet/endpoint_state.go # unsupported usage.
- - pkg/tcpip/transport/raw/endpoint_state.go # unsupported usage.
- - pkg/tcpip/transport/icmp/endpoint.go # unsupported usage.
- - pkg/tcpip/transport/icmp/endpoint_state.go # unsupported usage.
- - pkg/tcpip/transport/tcp/accept.go # unsupported usage.
- - pkg/tcpip/transport/tcp/connect.go # unsupported usage.
- - pkg/tcpip/transport/tcp/dispatcher.go # unsupported usage (TryLock)
- - pkg/tcpip/transport/tcp/endpoint.go # unsupported usage.
- - pkg/tcpip/transport/tcp/endpoint_state.go # unsupported usage.
- - pkg/tcpip/transport/udp/endpoint.go # unsupported usage (defer unlock in anonymous function)
- - pkg/tcpip/transport/udp/endpoint_state.go # unsupported usage (missing nested mutex annotation support)
+ - "^-$" # b/181776900: analyzer fails on buildkite.
shadow: # Disable for now.
generated:
exclude: [".*"]
@@ -177,6 +141,7 @@ analyzers:
- pkg/sentry/platform/kvm/bluepill_unsafe.go # Special case.
- pkg/sentry/platform/kvm/machine_unsafe.go # Special case.
- pkg/sentry/platform/safecopy/safecopy_unsafe.go # Special case.
+ - pkg/sentry/usage/memory_unsafe.go # Special case.
- pkg/sentry/vfs/mount_unsafe.go # Special case.
- pkg/state/decode_unsafe.go # Special case.
unusedresult:
diff --git a/pkg/abi/linux/BUILD b/pkg/abi/linux/BUILD
index 29ead20d0..eb004a7f6 100644
--- a/pkg/abi/linux/BUILD
+++ b/pkg/abi/linux/BUILD
@@ -21,7 +21,6 @@ go_library(
"epoll.go",
"epoll_amd64.go",
"epoll_arm64.go",
- "errors.go",
"errqueue.go",
"eventfd.go",
"exec.go",
@@ -79,6 +78,7 @@ go_library(
"//pkg/abi",
"//pkg/bits",
"//pkg/context",
+ "//pkg/hostarch",
"//pkg/marshal",
"//pkg/marshal/primitive",
],
diff --git a/pkg/abi/linux/errno/BUILD b/pkg/abi/linux/errno/BUILD
new file mode 100644
index 000000000..d003342d5
--- /dev/null
+++ b/pkg/abi/linux/errno/BUILD
@@ -0,0 +1,9 @@
+load("//tools:defs.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "errno",
+ srcs = ["errno.go"],
+ visibility = ["//visibility:public"],
+)
diff --git a/pkg/abi/linux/errors.go b/pkg/abi/linux/errno/errno.go
index b08b2687e..5a09c6605 100644
--- a/pkg/abi/linux/errors.go
+++ b/pkg/abi/linux/errno/errno.go
@@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package linux
+// Package errno holds errno codes for abi/linux.
+package errno
// Errno represents a Linux errno value.
-type Errno int
+type Errno uint32
// Errno values from include/uapi/asm-generic/errno-base.h.
const (
@@ -60,8 +61,8 @@ const (
ENOLCK
ENOSYS
ENOTEMPTY
- ELOOP //40
- _ // Skip for EWOULDBLOCK = EAGAIN
+ ELOOP // 40
+ _ // Skip for EWOULDBLOCK = EAGAIN.
ENOMSG //42
EIDRM
ECHRNG
@@ -78,13 +79,13 @@ const (
ENOANO
EBADRQC
EBADSLT
- _ // Skip for EDEADLOCK = EDEADLK
+ _ // Skip for EDEADLOCK = EDEADLK.
EBFONT
ENOSTR // 60
ENODATA
ETIME
ENOSR
- ENONET
+ _ // Skip for ENOENT = ENONET.
ENOPKG
EREMOTE
ENOLINK
@@ -160,4 +161,5 @@ const (
const (
EWOULDBLOCK = EAGAIN
EDEADLOCK = EDEADLK
+ ENONET = ENOENT
)
diff --git a/pkg/abi/linux/netfilter_ipv6.go b/pkg/abi/linux/netfilter_ipv6.go
index b088b207c..f8c0e891e 100644
--- a/pkg/abi/linux/netfilter_ipv6.go
+++ b/pkg/abi/linux/netfilter_ipv6.go
@@ -41,7 +41,6 @@ const (
// IP6T_ORIGINAL_DST is the ip6tables SOL_IPV6 socket option. Corresponds to
// the value in include/uapi/linux/netfilter_ipv6/ip6_tables.h.
-// TODO(gvisor.dev/issue/3549): Support IPv6 original destination.
const IP6T_ORIGINAL_DST = 80
// IP6TReplace is the argument for the IP6T_SO_SET_REPLACE sockopt. It
diff --git a/pkg/abi/linux/signal.go b/pkg/abi/linux/signal.go
index 6ca57ffbb..06a4c6401 100644
--- a/pkg/abi/linux/signal.go
+++ b/pkg/abi/linux/signal.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/bits"
+ "gvisor.dev/gvisor/pkg/hostarch"
)
const (
@@ -165,7 +166,7 @@ const (
SIG_IGN = 1
)
-// Signal action flags for rt_sigaction(2), from uapi/asm-generic/signal.h
+// Signal action flags for rt_sigaction(2), from uapi/asm-generic/signal.h.
const (
SA_NOCLDSTOP = 0x00000001
SA_NOCLDWAIT = 0x00000002
@@ -179,21 +180,17 @@ const (
SA_ONESHOT = SA_RESETHAND
)
-// Signal info types.
+// Signal stack flags for signalstack(2), from include/uapi/linux/signal.h.
const (
- SI_MASK = 0xffff0000
- SI_KILL = 0 << 16
- SI_TIMER = 1 << 16
- SI_POLL = 2 << 16
- SI_FAULT = 3 << 16
- SI_CHLD = 4 << 16
- SI_RT = 5 << 16
- SI_MESGQ = 6 << 16
- SI_SYS = 7 << 16
+ SS_ONSTACK = 1
+ SS_DISABLE = 2
)
// SIGPOLL si_codes.
const (
+ // SI_POLL is defined as __SI_POLL in Linux 2.6.
+ SI_POLL = 2 << 16
+
// POLL_IN indicates that data input available.
POLL_IN = SI_POLL | 1
@@ -213,6 +210,75 @@ const (
POLL_HUP = SI_POLL | 6
)
+// Possible values for si_code.
+const (
+ // SI_USER is sent by kill, sigsend, raise.
+ SI_USER = 0
+
+ // SI_KERNEL is sent by the kernel from somewhere.
+ SI_KERNEL = 0x80
+
+ // SI_QUEUE is sent by sigqueue.
+ SI_QUEUE = -1
+
+ // SI_TIMER is sent by timer expiration.
+ SI_TIMER = -2
+
+ // SI_MESGQ is sent by real time mesq state change.
+ SI_MESGQ = -3
+
+ // SI_ASYNCIO is sent by AIO completion.
+ SI_ASYNCIO = -4
+
+ // SI_SIGIO is sent by queued SIGIO.
+ SI_SIGIO = -5
+
+ // SI_TKILL is sent by tkill system call.
+ SI_TKILL = -6
+
+ // SI_DETHREAD is sent by execve() killing subsidiary threads.
+ SI_DETHREAD = -7
+
+ // SI_ASYNCNL is sent by glibc async name lookup completion.
+ SI_ASYNCNL = -60
+)
+
+// CLD_* codes are only meaningful for SIGCHLD.
+const (
+ // CLD_EXITED indicates that a task exited.
+ CLD_EXITED = 1
+
+ // CLD_KILLED indicates that a task was killed by a signal.
+ CLD_KILLED = 2
+
+ // CLD_DUMPED indicates that a task was killed by a signal and then dumped
+ // core.
+ CLD_DUMPED = 3
+
+ // CLD_TRAPPED indicates that a task was stopped by ptrace.
+ CLD_TRAPPED = 4
+
+ // CLD_STOPPED indicates that a thread group completed a group stop.
+ CLD_STOPPED = 5
+
+ // CLD_CONTINUED indicates that a group-stopped thread group was continued.
+ CLD_CONTINUED = 6
+)
+
+// SYS_* codes are only meaningful for SIGSYS.
+const (
+ // SYS_SECCOMP indicates that a signal originates from seccomp.
+ SYS_SECCOMP = 1
+)
+
+// Possible values for Sigevent.Notify, aka struct sigevent::sigev_notify.
+const (
+ SIGEV_SIGNAL = 0
+ SIGEV_NONE = 1
+ SIGEV_THREAD = 2
+ SIGEV_THREAD_ID = 4
+)
+
// Sigevent represents struct sigevent.
//
// +marshal
@@ -227,10 +293,252 @@ type Sigevent struct {
UnRemainder [44]byte
}
-// Possible values for Sigevent.Notify, aka struct sigevent::sigev_notify.
-const (
- SIGEV_SIGNAL = 0
- SIGEV_NONE = 1
- SIGEV_THREAD = 2
- SIGEV_THREAD_ID = 4
-)
+// SigAction represents struct sigaction.
+//
+// +marshal
+// +stateify savable
+type SigAction struct {
+ Handler uint64
+ Flags uint64
+ Restorer uint64
+ Mask SignalSet
+}
+
+// SignalStack represents information about a user stack, and is equivalent to
+// stack_t.
+//
+// +marshal
+// +stateify savable
+type SignalStack struct {
+ Addr uint64
+ Flags uint32
+ _ uint32
+ Size uint64
+}
+
+// Contains checks if the stack pointer is within this stack.
+func (s *SignalStack) Contains(sp hostarch.Addr) bool {
+ return hostarch.Addr(s.Addr) < sp && sp <= hostarch.Addr(s.Addr+s.Size)
+}
+
+// Top returns the stack's top address.
+func (s *SignalStack) Top() hostarch.Addr {
+ return hostarch.Addr(s.Addr + s.Size)
+}
+
+// IsEnabled returns true iff this signal stack is marked as enabled.
+func (s *SignalStack) IsEnabled() bool {
+ return s.Flags&SS_DISABLE == 0
+}
+
+// SignalInfo represents information about a signal being delivered, and is
+// equivalent to struct siginfo in linux kernel(linux/include/uapi/asm-generic/siginfo.h).
+//
+// +marshal
+// +stateify savable
+type SignalInfo struct {
+ Signo int32 // Signal number
+ Errno int32 // Errno value
+ Code int32 // Signal code
+ _ uint32
+
+ // struct siginfo::_sifields is a union. In SignalInfo, fields in the union
+ // are accessed through methods.
+ //
+ // For reference, here is the definition of _sifields: (_sigfault._trapno,
+ // which does not exist on x86, omitted for clarity)
+ //
+ // union {
+ // int _pad[SI_PAD_SIZE];
+ //
+ // /* kill() */
+ // struct {
+ // __kernel_pid_t _pid; /* sender's pid */
+ // __ARCH_SI_UID_T _uid; /* sender's uid */
+ // } _kill;
+ //
+ // /* POSIX.1b timers */
+ // struct {
+ // __kernel_timer_t _tid; /* timer id */
+ // int _overrun; /* overrun count */
+ // char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
+ // sigval_t _sigval; /* same as below */
+ // int _sys_private; /* not to be passed to user */
+ // } _timer;
+ //
+ // /* POSIX.1b signals */
+ // struct {
+ // __kernel_pid_t _pid; /* sender's pid */
+ // __ARCH_SI_UID_T _uid; /* sender's uid */
+ // sigval_t _sigval;
+ // } _rt;
+ //
+ // /* SIGCHLD */
+ // struct {
+ // __kernel_pid_t _pid; /* which child */
+ // __ARCH_SI_UID_T _uid; /* sender's uid */
+ // int _status; /* exit code */
+ // __ARCH_SI_CLOCK_T _utime;
+ // __ARCH_SI_CLOCK_T _stime;
+ // } _sigchld;
+ //
+ // /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ // struct {
+ // void *_addr; /* faulting insn/memory ref. */
+ // short _addr_lsb; /* LSB of the reported address */
+ // } _sigfault;
+ //
+ // /* SIGPOLL */
+ // struct {
+ // __ARCH_SI_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */
+ // int _fd;
+ // } _sigpoll;
+ //
+ // /* SIGSYS */
+ // struct {
+ // void *_call_addr; /* calling user insn */
+ // int _syscall; /* triggering system call number */
+ // unsigned int _arch; /* AUDIT_ARCH_* of syscall */
+ // } _sigsys;
+ // } _sifields;
+ //
+ // _sifields is padded so that the size of siginfo is SI_MAX_SIZE = 128
+ // bytes.
+ Fields [128 - 16]byte
+}
+
+// FixSignalCodeForUser fixes up si_code.
+//
+// The si_code we get from Linux may contain the kernel-specific code in the
+// top 16 bits if it's positive (e.g., from ptrace). Linux's
+// copy_siginfo_to_user does
+// err |= __put_user((short)from->si_code, &to->si_code);
+// to mask out those bits and we need to do the same.
+func (s *SignalInfo) FixSignalCodeForUser() {
+ if s.Code > 0 {
+ s.Code &= 0x0000ffff
+ }
+}
+
+// PID returns the si_pid field.
+func (s *SignalInfo) PID() int32 {
+ return int32(hostarch.ByteOrder.Uint32(s.Fields[0:4]))
+}
+
+// SetPID mutates the si_pid field.
+func (s *SignalInfo) SetPID(val int32) {
+ hostarch.ByteOrder.PutUint32(s.Fields[0:4], uint32(val))
+}
+
+// UID returns the si_uid field.
+func (s *SignalInfo) UID() int32 {
+ return int32(hostarch.ByteOrder.Uint32(s.Fields[4:8]))
+}
+
+// SetUID mutates the si_uid field.
+func (s *SignalInfo) SetUID(val int32) {
+ hostarch.ByteOrder.PutUint32(s.Fields[4:8], uint32(val))
+}
+
+// Sigval returns the sigval field, which is aliased to both si_int and si_ptr.
+func (s *SignalInfo) Sigval() uint64 {
+ return hostarch.ByteOrder.Uint64(s.Fields[8:16])
+}
+
+// SetSigval mutates the sigval field.
+func (s *SignalInfo) SetSigval(val uint64) {
+ hostarch.ByteOrder.PutUint64(s.Fields[8:16], val)
+}
+
+// TimerID returns the si_timerid field.
+func (s *SignalInfo) TimerID() TimerID {
+ return TimerID(hostarch.ByteOrder.Uint32(s.Fields[0:4]))
+}
+
+// SetTimerID sets the si_timerid field.
+func (s *SignalInfo) SetTimerID(val TimerID) {
+ hostarch.ByteOrder.PutUint32(s.Fields[0:4], uint32(val))
+}
+
+// Overrun returns the si_overrun field.
+func (s *SignalInfo) Overrun() int32 {
+ return int32(hostarch.ByteOrder.Uint32(s.Fields[4:8]))
+}
+
+// SetOverrun sets the si_overrun field.
+func (s *SignalInfo) SetOverrun(val int32) {
+ hostarch.ByteOrder.PutUint32(s.Fields[4:8], uint32(val))
+}
+
+// Addr returns the si_addr field.
+func (s *SignalInfo) Addr() uint64 {
+ return hostarch.ByteOrder.Uint64(s.Fields[0:8])
+}
+
+// SetAddr sets the si_addr field.
+func (s *SignalInfo) SetAddr(val uint64) {
+ hostarch.ByteOrder.PutUint64(s.Fields[0:8], val)
+}
+
+// Status returns the si_status field.
+func (s *SignalInfo) Status() int32 {
+ return int32(hostarch.ByteOrder.Uint32(s.Fields[8:12]))
+}
+
+// SetStatus mutates the si_status field.
+func (s *SignalInfo) SetStatus(val int32) {
+ hostarch.ByteOrder.PutUint32(s.Fields[8:12], uint32(val))
+}
+
+// CallAddr returns the si_call_addr field.
+func (s *SignalInfo) CallAddr() uint64 {
+ return hostarch.ByteOrder.Uint64(s.Fields[0:8])
+}
+
+// SetCallAddr mutates the si_call_addr field.
+func (s *SignalInfo) SetCallAddr(val uint64) {
+ hostarch.ByteOrder.PutUint64(s.Fields[0:8], val)
+}
+
+// Syscall returns the si_syscall field.
+func (s *SignalInfo) Syscall() int32 {
+ return int32(hostarch.ByteOrder.Uint32(s.Fields[8:12]))
+}
+
+// SetSyscall mutates the si_syscall field.
+func (s *SignalInfo) SetSyscall(val int32) {
+ hostarch.ByteOrder.PutUint32(s.Fields[8:12], uint32(val))
+}
+
+// Arch returns the si_arch field.
+func (s *SignalInfo) Arch() uint32 {
+ return hostarch.ByteOrder.Uint32(s.Fields[12:16])
+}
+
+// SetArch mutates the si_arch field.
+func (s *SignalInfo) SetArch(val uint32) {
+ hostarch.ByteOrder.PutUint32(s.Fields[12:16], val)
+}
+
+// Band returns the si_band field.
+func (s *SignalInfo) Band() int64 {
+ return int64(hostarch.ByteOrder.Uint64(s.Fields[0:8]))
+}
+
+// SetBand mutates the si_band field.
+func (s *SignalInfo) SetBand(val int64) {
+ // Note: this assumes the platform uses `long` as `__ARCH_SI_BAND_T`.
+ // On some platforms, which gVisor doesn't support, `__ARCH_SI_BAND_T` is
+ // `int`. See siginfo.h.
+ hostarch.ByteOrder.PutUint64(s.Fields[0:8], uint64(val))
+}
+
+// FD returns the si_fd field.
+func (s *SignalInfo) FD() uint32 {
+ return hostarch.ByteOrder.Uint32(s.Fields[8:12])
+}
+
+// SetFD mutates the si_fd field.
+func (s *SignalInfo) SetFD(val uint32) {
+ hostarch.ByteOrder.PutUint32(s.Fields[8:12], val)
+}
diff --git a/pkg/abi/linux/xattr.go b/pkg/abi/linux/xattr.go
index 8ef837f27..1fa7a4f4f 100644
--- a/pkg/abi/linux/xattr.go
+++ b/pkg/abi/linux/xattr.go
@@ -23,6 +23,12 @@ const (
XATTR_CREATE = 1
XATTR_REPLACE = 2
+ XATTR_SECURITY_PREFIX = "security."
+ XATTR_SECURITY_PREFIX_LEN = len(XATTR_SECURITY_PREFIX)
+
+ XATTR_SYSTEM_PREFIX = "system."
+ XATTR_SYSTEM_PREFIX_LEN = len(XATTR_SYSTEM_PREFIX)
+
XATTR_TRUSTED_PREFIX = "trusted."
XATTR_TRUSTED_PREFIX_LEN = len(XATTR_TRUSTED_PREFIX)
diff --git a/pkg/control/server/server.go b/pkg/control/server/server.go
index 629dae8f4..889568177 100644
--- a/pkg/control/server/server.go
+++ b/pkg/control/server/server.go
@@ -22,6 +22,7 @@ package server
import (
"os"
+ "time"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sync"
@@ -65,13 +66,13 @@ func (s *Server) Wait() {
// Stop stops the server. Note that this function should only be called once
// and the server should not be used afterwards.
-func (s *Server) Stop() {
+func (s *Server) Stop(timeout time.Duration) {
s.socket.Close()
s.Wait()
// This will cause existing clients to be terminated safely. If the
// registered handlers have a Stop callback, it will be called.
- s.server.Stop()
+ s.server.Stop(timeout)
}
// StartServing starts listening for connect and spawns the main service
diff --git a/pkg/errors/BUILD b/pkg/errors/BUILD
new file mode 100644
index 000000000..36ea5da0c
--- /dev/null
+++ b/pkg/errors/BUILD
@@ -0,0 +1,10 @@
+load("//tools:defs.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "errors",
+ srcs = ["errors.go"],
+ visibility = ["//:sandbox"],
+ deps = ["//pkg/abi/linux/errno"],
+)
diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go
new file mode 100644
index 000000000..7984a770e
--- /dev/null
+++ b/pkg/errors/errors.go
@@ -0,0 +1,40 @@
+// Copyright 2021 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 errors holds the standardized error definition for gVisor.
+package errors
+
+import (
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
+)
+
+// Error represents a syscall errno with a descriptive message.
+type Error struct {
+ errno errno.Errno
+ message string
+}
+
+// New creates a new *Error.
+func New(err errno.Errno, message string) *Error {
+ return &Error{
+ errno: err,
+ message: message,
+ }
+}
+
+// Error implements error.Error.
+func (e *Error) Error() string { return e.message }
+
+// Errno returns the underlying errno.Errno value.
+func (e *Error) Errno() errno.Errno { return e.errno }
diff --git a/pkg/linuxerr/BUILD b/pkg/errors/linuxerr/BUILD
index c5abbd34f..201727780 100644
--- a/pkg/linuxerr/BUILD
+++ b/pkg/errors/linuxerr/BUILD
@@ -6,7 +6,11 @@ go_library(
name = "linuxerr",
srcs = ["linuxerr.go"],
visibility = ["//visibility:public"],
- deps = ["//pkg/abi/linux"],
+ deps = [
+ "//pkg/abi/linux/errno",
+ "//pkg/errors",
+ "@org_golang_x_sys//unix:go_default_library",
+ ],
)
go_test(
@@ -14,6 +18,8 @@ go_test(
srcs = ["linuxerr_test.go"],
deps = [
":linuxerr",
+ "//pkg/abi/linux/errno",
+ "//pkg/errors",
"//pkg/syserror",
"@org_golang_x_sys//unix:go_default_library",
],
diff --git a/pkg/errors/linuxerr/linuxerr.go b/pkg/errors/linuxerr/linuxerr.go
new file mode 100644
index 000000000..f9f8412e0
--- /dev/null
+++ b/pkg/errors/linuxerr/linuxerr.go
@@ -0,0 +1,349 @@
+// Copyright 2021 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 linuxerr contains syscall error codes exported as an error interface
+// pointers. This allows for fast comparison and return operations comperable
+// to unix.Errno constants.
+package linuxerr
+
+import (
+ "fmt"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
+ "gvisor.dev/gvisor/pkg/errors"
+)
+
+const maxErrno uint32 = errno.EHWPOISON + 1
+
+var (
+ NOERROR = errors.New(errno.NOERRNO, "not an error")
+ EPERM = errors.New(errno.EPERM, "operation not permitted")
+ ENOENT = errors.New(errno.ENOENT, "no such file or directory")
+ ESRCH = errors.New(errno.ESRCH, "no such process")
+ EINTR = errors.New(errno.EINTR, "interrupted system call")
+ EIO = errors.New(errno.EIO, "I/O error")
+ ENXIO = errors.New(errno.ENXIO, "no such device or address")
+ E2BIG = errors.New(errno.E2BIG, "argument list too long")
+ ENOEXEC = errors.New(errno.ENOEXEC, "exec format error")
+ EBADF = errors.New(errno.EBADF, "bad file number")
+ ECHILD = errors.New(errno.ECHILD, "no child processes")
+ EAGAIN = errors.New(errno.EAGAIN, "try again")
+ ENOMEM = errors.New(errno.ENOMEM, "out of memory")
+ EACCES = errors.New(errno.EACCES, "permission denied")
+ EFAULT = errors.New(errno.EFAULT, "bad address")
+ ENOTBLK = errors.New(errno.ENOTBLK, "block device required")
+ EBUSY = errors.New(errno.EBUSY, "device or resource busy")
+ EEXIST = errors.New(errno.EEXIST, "file exists")
+ EXDEV = errors.New(errno.EXDEV, "cross-device link")
+ ENODEV = errors.New(errno.ENODEV, "no such device")
+ ENOTDIR = errors.New(errno.ENOTDIR, "not a directory")
+ EISDIR = errors.New(errno.EISDIR, "is a directory")
+ EINVAL = errors.New(errno.EINVAL, "invalid argument")
+ ENFILE = errors.New(errno.ENFILE, "file table overflow")
+ EMFILE = errors.New(errno.EMFILE, "too many open files")
+ ENOTTY = errors.New(errno.ENOTTY, "not a typewriter")
+ ETXTBSY = errors.New(errno.ETXTBSY, "text file busy")
+ EFBIG = errors.New(errno.EFBIG, "file too large")
+ ENOSPC = errors.New(errno.ENOSPC, "no space left on device")
+ ESPIPE = errors.New(errno.ESPIPE, "illegal seek")
+ EROFS = errors.New(errno.EROFS, "read-only file system")
+ EMLINK = errors.New(errno.EMLINK, "too many links")
+ EPIPE = errors.New(errno.EPIPE, "broken pipe")
+ EDOM = errors.New(errno.EDOM, "math argument out of domain of func")
+ ERANGE = errors.New(errno.ERANGE, "math result not representable")
+
+ // Errno values from include/uapi/asm-generic/errno.h.
+ EDEADLK = errors.New(errno.EDEADLK, "resource deadlock would occur")
+ ENAMETOOLONG = errors.New(errno.ENAMETOOLONG, "file name too long")
+ ENOLCK = errors.New(errno.ENOLCK, "no record locks available")
+ ENOSYS = errors.New(errno.ENOSYS, "invalid system call number")
+ ENOTEMPTY = errors.New(errno.ENOTEMPTY, "directory not empty")
+ ELOOP = errors.New(errno.ELOOP, "too many symbolic links encountered")
+ ENOMSG = errors.New(errno.ENOMSG, "no message of desired type")
+ EIDRM = errors.New(errno.EIDRM, "identifier removed")
+ ECHRNG = errors.New(errno.ECHRNG, "channel number out of range")
+ EL2NSYNC = errors.New(errno.EL2NSYNC, "level 2 not synchronized")
+ EL3HLT = errors.New(errno.EL3HLT, "level 3 halted")
+ EL3RST = errors.New(errno.EL3RST, "level 3 reset")
+ ELNRNG = errors.New(errno.ELNRNG, "link number out of range")
+ EUNATCH = errors.New(errno.EUNATCH, "protocol driver not attached")
+ ENOCSI = errors.New(errno.ENOCSI, "no CSI structure available")
+ EL2HLT = errors.New(errno.EL2HLT, "level 2 halted")
+ EBADE = errors.New(errno.EBADE, "invalid exchange")
+ EBADR = errors.New(errno.EBADR, "invalid request descriptor")
+ EXFULL = errors.New(errno.EXFULL, "exchange full")
+ ENOANO = errors.New(errno.ENOANO, "no anode")
+ EBADRQC = errors.New(errno.EBADRQC, "invalid request code")
+ EBADSLT = errors.New(errno.EBADSLT, "invalid slot")
+ EBFONT = errors.New(errno.EBFONT, "bad font file format")
+ ENOSTR = errors.New(errno.ENOSTR, "device not a stream")
+ ENODATA = errors.New(errno.ENODATA, "no data available")
+ ETIME = errors.New(errno.ETIME, "timer expired")
+ ENOSR = errors.New(errno.ENOSR, "out of streams resources")
+ ENOPKG = errors.New(errno.ENOPKG, "package not installed")
+ EREMOTE = errors.New(errno.EREMOTE, "object is remote")
+ ENOLINK = errors.New(errno.ENOLINK, "link has been severed")
+ EADV = errors.New(errno.EADV, "advertise error")
+ ESRMNT = errors.New(errno.ESRMNT, "srmount error")
+ ECOMM = errors.New(errno.ECOMM, "communication error on send")
+ EPROTO = errors.New(errno.EPROTO, "protocol error")
+ EMULTIHOP = errors.New(errno.EMULTIHOP, "multihop attempted")
+ EDOTDOT = errors.New(errno.EDOTDOT, "RFS specific error")
+ EBADMSG = errors.New(errno.EBADMSG, "not a data message")
+ EOVERFLOW = errors.New(errno.EOVERFLOW, "value too large for defined data type")
+ ENOTUNIQ = errors.New(errno.ENOTUNIQ, "name not unique on network")
+ EBADFD = errors.New(errno.EBADFD, "file descriptor in bad state")
+ EREMCHG = errors.New(errno.EREMCHG, "remote address changed")
+ ELIBACC = errors.New(errno.ELIBACC, "can not access a needed shared library")
+ ELIBBAD = errors.New(errno.ELIBBAD, "accessing a corrupted shared library")
+ ELIBSCN = errors.New(errno.ELIBSCN, ".lib section in a.out corrupted")
+ ELIBMAX = errors.New(errno.ELIBMAX, "attempting to link in too many shared libraries")
+ ELIBEXEC = errors.New(errno.ELIBEXEC, "cannot exec a shared library directly")
+ EILSEQ = errors.New(errno.EILSEQ, "illegal byte sequence")
+ ERESTART = errors.New(errno.ERESTART, "interrupted system call should be restarted")
+ ESTRPIPE = errors.New(errno.ESTRPIPE, "streams pipe error")
+ EUSERS = errors.New(errno.EUSERS, "too many users")
+ ENOTSOCK = errors.New(errno.ENOTSOCK, "socket operation on non-socket")
+ EDESTADDRREQ = errors.New(errno.EDESTADDRREQ, "destination address required")
+ EMSGSIZE = errors.New(errno.EMSGSIZE, "message too long")
+ EPROTOTYPE = errors.New(errno.EPROTOTYPE, "protocol wrong type for socket")
+ ENOPROTOOPT = errors.New(errno.ENOPROTOOPT, "protocol not available")
+ EPROTONOSUPPORT = errors.New(errno.EPROTONOSUPPORT, "protocol not supported")
+ ESOCKTNOSUPPORT = errors.New(errno.ESOCKTNOSUPPORT, "socket type not supported")
+ EOPNOTSUPP = errors.New(errno.EOPNOTSUPP, "operation not supported on transport endpoint")
+ EPFNOSUPPORT = errors.New(errno.EPFNOSUPPORT, "protocol family not supported")
+ EAFNOSUPPORT = errors.New(errno.EAFNOSUPPORT, "address family not supported by protocol")
+ EADDRINUSE = errors.New(errno.EADDRINUSE, "address already in use")
+ EADDRNOTAVAIL = errors.New(errno.EADDRNOTAVAIL, "cannot assign requested address")
+ ENETDOWN = errors.New(errno.ENETDOWN, "network is down")
+ ENETUNREACH = errors.New(errno.ENETUNREACH, "network is unreachable")
+ ENETRESET = errors.New(errno.ENETRESET, "network dropped connection because of reset")
+ ECONNABORTED = errors.New(errno.ECONNABORTED, "software caused connection abort")
+ ECONNRESET = errors.New(errno.ECONNRESET, "connection reset by peer")
+ ENOBUFS = errors.New(errno.ENOBUFS, "no buffer space available")
+ EISCONN = errors.New(errno.EISCONN, "transport endpoint is already connected")
+ ENOTCONN = errors.New(errno.ENOTCONN, "transport endpoint is not connected")
+ ESHUTDOWN = errors.New(errno.ESHUTDOWN, "cannot send after transport endpoint shutdown")
+ ETOOMANYREFS = errors.New(errno.ETOOMANYREFS, "too many references: cannot splice")
+ ETIMEDOUT = errors.New(errno.ETIMEDOUT, "connection timed out")
+ ECONNREFUSED = errors.New(errno.ECONNREFUSED, "connection refused")
+ EHOSTDOWN = errors.New(errno.EHOSTDOWN, "host is down")
+ EHOSTUNREACH = errors.New(errno.EHOSTUNREACH, "no route to host")
+ EALREADY = errors.New(errno.EALREADY, "operation already in progress")
+ EINPROGRESS = errors.New(errno.EINPROGRESS, "operation now in progress")
+ ESTALE = errors.New(errno.ESTALE, "stale file handle")
+ EUCLEAN = errors.New(errno.EUCLEAN, "structure needs cleaning")
+ ENOTNAM = errors.New(errno.ENOTNAM, "not a XENIX named type file")
+ ENAVAIL = errors.New(errno.ENAVAIL, "no XENIX semaphores available")
+ EISNAM = errors.New(errno.EISNAM, "is a named type file")
+ EREMOTEIO = errors.New(errno.EREMOTEIO, "remote I/O error")
+ EDQUOT = errors.New(errno.EDQUOT, "quota exceeded")
+ ENOMEDIUM = errors.New(errno.ENOMEDIUM, "no medium found")
+ EMEDIUMTYPE = errors.New(errno.EMEDIUMTYPE, "wrong medium type")
+ ECANCELED = errors.New(errno.ECANCELED, "operation Canceled")
+ ENOKEY = errors.New(errno.ENOKEY, "required key not available")
+ EKEYEXPIRED = errors.New(errno.EKEYEXPIRED, "key has expired")
+ EKEYREVOKED = errors.New(errno.EKEYREVOKED, "key has been revoked")
+ EKEYREJECTED = errors.New(errno.EKEYREJECTED, "key was rejected by service")
+ EOWNERDEAD = errors.New(errno.EOWNERDEAD, "owner died")
+ ENOTRECOVERABLE = errors.New(errno.ENOTRECOVERABLE, "state not recoverable")
+ ERFKILL = errors.New(errno.ERFKILL, "operation not possible due to RF-kill")
+ EHWPOISON = errors.New(errno.EHWPOISON, "memory page has hardware error")
+
+ // Errors equivalent to other errors.
+ EWOULDBLOCK = EAGAIN
+ EDEADLOCK = EDEADLK
+ ENONET = ENOENT
+ ENOATTR = ENODATA
+ ENOTSUP = EOPNOTSUPP
+)
+
+// A nil *errors.Error denotes no error and is placed at the 0 index of
+// errorSlice. Thus, any other empty index should not be nil or a valid error.
+// This marks that index as an invalid error so any comparison to nil or a
+// valid linuxerr fails.
+var errNotValidError = errors.New(errno.Errno(maxErrno), "not a valid error")
+
+// The following errorSlice holds errors by errno for fast translation between
+// errnos (especially uint32(sycall.Errno)) and *Error.
+var errorSlice = []*errors.Error{
+ // Errno values from include/uapi/asm-generic/errno-base.h.
+ errno.NOERRNO: NOERROR,
+ errno.EPERM: EPERM,
+ errno.ENOENT: ENOENT,
+ errno.ESRCH: ESRCH,
+ errno.EINTR: EINTR,
+ errno.EIO: EIO,
+ errno.ENXIO: ENXIO,
+ errno.E2BIG: E2BIG,
+ errno.ENOEXEC: ENOEXEC,
+ errno.EBADF: EBADF,
+ errno.ECHILD: ECHILD,
+ errno.EAGAIN: EAGAIN,
+ errno.ENOMEM: ENOMEM,
+ errno.EACCES: EACCES,
+ errno.EFAULT: EFAULT,
+ errno.ENOTBLK: ENOTBLK,
+ errno.EBUSY: EBUSY,
+ errno.EEXIST: EEXIST,
+ errno.EXDEV: EXDEV,
+ errno.ENODEV: ENODEV,
+ errno.ENOTDIR: ENOTDIR,
+ errno.EISDIR: EISDIR,
+ errno.EINVAL: EINVAL,
+ errno.ENFILE: ENFILE,
+ errno.EMFILE: EMFILE,
+ errno.ENOTTY: ENOTTY,
+ errno.ETXTBSY: ETXTBSY,
+ errno.EFBIG: EFBIG,
+ errno.ENOSPC: ENOSPC,
+ errno.ESPIPE: ESPIPE,
+ errno.EROFS: EROFS,
+ errno.EMLINK: EMLINK,
+ errno.EPIPE: EPIPE,
+ errno.EDOM: EDOM,
+ errno.ERANGE: ERANGE,
+
+ // Errno values from include/uapi/asm-generic/errno.h.
+ errno.EDEADLK: EDEADLK,
+ errno.ENAMETOOLONG: ENAMETOOLONG,
+ errno.ENOLCK: ENOLCK,
+ errno.ENOSYS: ENOSYS,
+ errno.ENOTEMPTY: ENOTEMPTY,
+ errno.ELOOP: ELOOP,
+ errno.ELOOP + 1: errNotValidError, // No valid errno between ELOOP and ENOMSG.
+ errno.ENOMSG: ENOMSG,
+ errno.EIDRM: EIDRM,
+ errno.ECHRNG: ECHRNG,
+ errno.EL2NSYNC: EL2NSYNC,
+ errno.EL3HLT: EL3HLT,
+ errno.EL3RST: EL3RST,
+ errno.ELNRNG: ELNRNG,
+ errno.EUNATCH: EUNATCH,
+ errno.ENOCSI: ENOCSI,
+ errno.EL2HLT: EL2HLT,
+ errno.EBADE: EBADE,
+ errno.EBADR: EBADR,
+ errno.EXFULL: EXFULL,
+ errno.ENOANO: ENOANO,
+ errno.EBADRQC: EBADRQC,
+ errno.EBADSLT: EBADSLT,
+ errno.EBADSLT + 1: errNotValidError, // No valid errno between EBADSLT and ENOPKG.
+ errno.EBFONT: EBFONT,
+ errno.ENOSTR: ENOSTR,
+ errno.ENODATA: ENODATA,
+ errno.ETIME: ETIME,
+ errno.ENOSR: ENOSR,
+ errno.ENOSR + 1: errNotValidError, // No valid errno betweeen ENOSR and ENOPKG.
+ errno.ENOPKG: ENOPKG,
+ errno.EREMOTE: EREMOTE,
+ errno.ENOLINK: ENOLINK,
+ errno.EADV: EADV,
+ errno.ESRMNT: ESRMNT,
+ errno.ECOMM: ECOMM,
+ errno.EPROTO: EPROTO,
+ errno.EMULTIHOP: EMULTIHOP,
+ errno.EDOTDOT: EDOTDOT,
+ errno.EBADMSG: EBADMSG,
+ errno.EOVERFLOW: EOVERFLOW,
+ errno.ENOTUNIQ: ENOTUNIQ,
+ errno.EBADFD: EBADFD,
+ errno.EREMCHG: EREMCHG,
+ errno.ELIBACC: ELIBACC,
+ errno.ELIBBAD: ELIBBAD,
+ errno.ELIBSCN: ELIBSCN,
+ errno.ELIBMAX: ELIBMAX,
+ errno.ELIBEXEC: ELIBEXEC,
+ errno.EILSEQ: EILSEQ,
+ errno.ERESTART: ERESTART,
+ errno.ESTRPIPE: ESTRPIPE,
+ errno.EUSERS: EUSERS,
+ errno.ENOTSOCK: ENOTSOCK,
+ errno.EDESTADDRREQ: EDESTADDRREQ,
+ errno.EMSGSIZE: EMSGSIZE,
+ errno.EPROTOTYPE: EPROTOTYPE,
+ errno.ENOPROTOOPT: ENOPROTOOPT,
+ errno.EPROTONOSUPPORT: EPROTONOSUPPORT,
+ errno.ESOCKTNOSUPPORT: ESOCKTNOSUPPORT,
+ errno.EOPNOTSUPP: EOPNOTSUPP,
+ errno.EPFNOSUPPORT: EPFNOSUPPORT,
+ errno.EAFNOSUPPORT: EAFNOSUPPORT,
+ errno.EADDRINUSE: EADDRINUSE,
+ errno.EADDRNOTAVAIL: EADDRNOTAVAIL,
+ errno.ENETDOWN: ENETDOWN,
+ errno.ENETUNREACH: ENETUNREACH,
+ errno.ENETRESET: ENETRESET,
+ errno.ECONNABORTED: ECONNABORTED,
+ errno.ECONNRESET: ECONNRESET,
+ errno.ENOBUFS: ENOBUFS,
+ errno.EISCONN: EISCONN,
+ errno.ENOTCONN: ENOTCONN,
+ errno.ESHUTDOWN: ESHUTDOWN,
+ errno.ETOOMANYREFS: ETOOMANYREFS,
+ errno.ETIMEDOUT: ETIMEDOUT,
+ errno.ECONNREFUSED: ECONNREFUSED,
+ errno.EHOSTDOWN: EHOSTDOWN,
+ errno.EHOSTUNREACH: EHOSTUNREACH,
+ errno.EALREADY: EALREADY,
+ errno.EINPROGRESS: EINPROGRESS,
+ errno.ESTALE: ESTALE,
+ errno.EUCLEAN: EUCLEAN,
+ errno.ENOTNAM: ENOTNAM,
+ errno.ENAVAIL: ENAVAIL,
+ errno.EISNAM: EISNAM,
+ errno.EREMOTEIO: EREMOTEIO,
+ errno.EDQUOT: EDQUOT,
+ errno.ENOMEDIUM: ENOMEDIUM,
+ errno.EMEDIUMTYPE: EMEDIUMTYPE,
+ errno.ECANCELED: ECANCELED,
+ errno.ENOKEY: ENOKEY,
+ errno.EKEYEXPIRED: EKEYEXPIRED,
+ errno.EKEYREVOKED: EKEYREVOKED,
+ errno.EKEYREJECTED: EKEYREJECTED,
+ errno.EOWNERDEAD: EOWNERDEAD,
+ errno.ENOTRECOVERABLE: ENOTRECOVERABLE,
+ errno.ERFKILL: ERFKILL,
+ errno.EHWPOISON: EHWPOISON,
+}
+
+// ErrorFromErrno gets an error from the list and panics if an invalid entry is requested.
+func ErrorFromErrno(e errno.Errno) *errors.Error {
+ err := errorSlice[e]
+ // Done this way because a single comparison in benchmarks is 2-3 faster
+ // than something like ( if err == nil && err > 0 ).
+ if err != errNotValidError {
+ return err
+ }
+ panic(fmt.Sprintf("invalid error requested with errno: %d", e))
+}
+
+// Equals compars a linuxerr to a given error
+// TODO(b/34162363): Remove when syserror is removed.
+func Equals(e *errors.Error, err error) bool {
+ if err == nil {
+ return e == NOERROR || e == nil
+ }
+ if e == nil {
+ return err == NOERROR || err == unix.Errno(0)
+ }
+
+ switch err.(type) {
+ case *errors.Error:
+ return e == err
+ case unix.Errno, error:
+ return unix.Errno(e.Errno()) == err
+ }
+ return false
+}
diff --git a/pkg/errors/linuxerr/linuxerr_test.go b/pkg/errors/linuxerr/linuxerr_test.go
new file mode 100644
index 000000000..16e177fd7
--- /dev/null
+++ b/pkg/errors/linuxerr/linuxerr_test.go
@@ -0,0 +1,306 @@
+// 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 syserror_test
+
+import (
+ "errors"
+ "io"
+ "io/fs"
+ "syscall"
+ "testing"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
+ gErrors "gvisor.dev/gvisor/pkg/errors"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/syserror"
+)
+
+var globalError error
+
+func BenchmarkAssignUnix(b *testing.B) {
+ for i := b.N; i > 0; i-- {
+ globalError = unix.EINVAL
+ }
+}
+
+func BenchmarkAssignLinuxerr(b *testing.B) {
+ for i := b.N; i > 0; i-- {
+ globalError = linuxerr.EINVAL
+ }
+}
+
+func BenchmarkAssignSyserror(b *testing.B) {
+ for i := b.N; i > 0; i-- {
+ globalError = linuxerr.ENOMSG
+ }
+}
+
+func BenchmarkCompareUnix(b *testing.B) {
+ globalError = unix.EAGAIN
+ j := 0
+ for i := b.N; i > 0; i-- {
+ if globalError == unix.EINVAL {
+ j++
+ }
+ }
+}
+
+func BenchmarkCompareLinuxerr(b *testing.B) {
+ globalError = linuxerr.E2BIG
+ j := 0
+ for i := b.N; i > 0; i-- {
+ if globalError == linuxerr.EINVAL {
+ j++
+ }
+ }
+}
+
+func BenchmarkCompareSyserror(b *testing.B) {
+ globalError = linuxerr.EAGAIN
+ j := 0
+ for i := b.N; i > 0; i-- {
+ if globalError == linuxerr.EACCES {
+ j++
+ }
+ }
+}
+
+func BenchmarkSwitchUnix(b *testing.B) {
+ globalError = unix.EPERM
+ j := 0
+ for i := b.N; i > 0; i-- {
+ switch globalError {
+ case unix.EINVAL:
+ j++
+ case unix.EINTR:
+ j += 2
+ case unix.EAGAIN:
+ j += 3
+ }
+ }
+}
+
+func BenchmarkSwitchLinuxerr(b *testing.B) {
+ globalError = linuxerr.EPERM
+ j := 0
+ for i := b.N; i > 0; i-- {
+ switch globalError {
+ case linuxerr.EINVAL:
+ j++
+ case linuxerr.EINTR:
+ j += 2
+ case linuxerr.EAGAIN:
+ j += 3
+ }
+ }
+}
+
+func BenchmarkSwitchSyserror(b *testing.B) {
+ globalError = linuxerr.EPERM
+ j := 0
+ for i := b.N; i > 0; i-- {
+ switch globalError {
+ case linuxerr.EACCES:
+ j++
+ case syserror.EINTR:
+ j += 2
+ case linuxerr.EAGAIN:
+ j += 3
+ }
+ }
+}
+
+func BenchmarkReturnUnix(b *testing.B) {
+ var localError error
+ f := func() error {
+ return unix.EINVAL
+ }
+ for i := b.N; i > 0; i-- {
+ localError = f()
+ }
+ if localError != nil {
+ return
+ }
+}
+
+func BenchmarkReturnLinuxerr(b *testing.B) {
+ var localError error
+ f := func() error {
+ return linuxerr.EINVAL
+ }
+ for i := b.N; i > 0; i-- {
+ localError = f()
+ }
+ if localError != nil {
+ return
+ }
+}
+
+func BenchmarkConvertUnixLinuxerr(b *testing.B) {
+ var localError error
+ for i := b.N; i > 0; i-- {
+ localError = linuxerr.ErrorFromErrno(errno.Errno(unix.EINVAL))
+ }
+ if localError != nil {
+ return
+ }
+}
+
+func BenchmarkConvertUnixLinuxerrZero(b *testing.B) {
+ var localError error
+ for i := b.N; i > 0; i-- {
+ localError = linuxerr.ErrorFromErrno(errno.Errno(0))
+ }
+ if localError != nil {
+ return
+ }
+}
+
+type translationTestTable struct {
+ fn string
+ errIn error
+ syscallErrorIn unix.Errno
+ expectedBool bool
+ expectedTranslation unix.Errno
+}
+
+func TestErrorTranslation(t *testing.T) {
+ myError := errors.New("My test error")
+ myError2 := errors.New("Another test error")
+ testTable := []translationTestTable{
+ {"TranslateError", myError, 0, false, 0},
+ {"TranslateError", myError2, 0, false, 0},
+ {"AddErrorTranslation", myError, unix.EAGAIN, true, 0},
+ {"AddErrorTranslation", myError, unix.EAGAIN, false, 0},
+ {"AddErrorTranslation", myError, unix.EPERM, false, 0},
+ {"TranslateError", myError, 0, true, unix.EAGAIN},
+ {"TranslateError", myError2, 0, false, 0},
+ {"AddErrorTranslation", myError2, unix.EPERM, true, 0},
+ {"AddErrorTranslation", myError2, unix.EPERM, false, 0},
+ {"AddErrorTranslation", myError2, unix.EAGAIN, false, 0},
+ {"TranslateError", myError, 0, true, unix.EAGAIN},
+ {"TranslateError", myError2, 0, true, unix.EPERM},
+ }
+ for _, tt := range testTable {
+ switch tt.fn {
+ case "TranslateError":
+ err, ok := syserror.TranslateError(tt.errIn)
+ if ok != tt.expectedBool {
+ t.Fatalf("%v(%v) => %v expected %v", tt.fn, tt.errIn, ok, tt.expectedBool)
+ } else if err != tt.expectedTranslation {
+ t.Fatalf("%v(%v) (error) => %v expected %v", tt.fn, tt.errIn, err, tt.expectedTranslation)
+ }
+ case "AddErrorTranslation":
+ ok := syserror.AddErrorTranslation(tt.errIn, tt.syscallErrorIn)
+ if ok != tt.expectedBool {
+ t.Fatalf("%v(%v) => %v expected %v", tt.fn, tt.errIn, ok, tt.expectedBool)
+ }
+ default:
+ t.Fatalf("Unknown function %v", tt.fn)
+ }
+ }
+}
+
+func TestSyscallErrnoToErrors(t *testing.T) {
+ for _, tc := range []struct {
+ errno syscall.Errno
+ err *gErrors.Error
+ }{
+ {errno: syscall.EACCES, err: linuxerr.EACCES},
+ {errno: syscall.EAGAIN, err: linuxerr.EAGAIN},
+ {errno: syscall.EBADF, err: linuxerr.EBADF},
+ {errno: syscall.EBUSY, err: linuxerr.EBUSY},
+ {errno: syscall.EDOM, err: linuxerr.EDOM},
+ {errno: syscall.EEXIST, err: linuxerr.EEXIST},
+ {errno: syscall.EFAULT, err: linuxerr.EFAULT},
+ {errno: syscall.EFBIG, err: linuxerr.EFBIG},
+ {errno: syscall.EINTR, err: linuxerr.EINTR},
+ {errno: syscall.EINVAL, err: linuxerr.EINVAL},
+ {errno: syscall.EIO, err: linuxerr.EIO},
+ {errno: syscall.ENOTDIR, err: linuxerr.ENOTDIR},
+ {errno: syscall.ENOTTY, err: linuxerr.ENOTTY},
+ {errno: syscall.EPERM, err: linuxerr.EPERM},
+ {errno: syscall.EPIPE, err: linuxerr.EPIPE},
+ {errno: syscall.ESPIPE, err: linuxerr.ESPIPE},
+ {errno: syscall.EWOULDBLOCK, err: linuxerr.EAGAIN},
+ } {
+ t.Run(tc.errno.Error(), func(t *testing.T) {
+ e := linuxerr.ErrorFromErrno(errno.Errno(tc.errno))
+ if e != tc.err {
+ t.Fatalf("Mismatch errors: want: %+v (%d) got: %+v %d", tc.err, tc.err.Errno(), e, e.Errno())
+ }
+ })
+ }
+}
+
+// TestEqualsMethod tests that the Equals method correctly compares syerror,
+// unix.Errno and linuxerr.
+// TODO (b/34162363): Remove this.
+func TestEqualsMethod(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ linuxErr []*gErrors.Error
+ err []error
+ equal bool
+ }{
+ {
+ name: "compare nil",
+ linuxErr: []*gErrors.Error{nil, linuxerr.NOERROR},
+ err: []error{nil, linuxerr.NOERROR, unix.Errno(0)},
+ equal: true,
+ },
+ {
+ name: "linuxerr nil error not",
+ linuxErr: []*gErrors.Error{nil, linuxerr.NOERROR},
+ err: []error{unix.Errno(1), linuxerr.EPERM, linuxerr.EACCES},
+ equal: false,
+ },
+ {
+ name: "linuxerr not nil error nil",
+ linuxErr: []*gErrors.Error{linuxerr.ENOENT},
+ err: []error{nil, unix.Errno(0), linuxerr.NOERROR},
+ equal: false,
+ },
+ {
+ name: "equal errors",
+ linuxErr: []*gErrors.Error{linuxerr.ESRCH},
+ err: []error{linuxerr.ESRCH, syserror.ESRCH, unix.Errno(linuxerr.ESRCH.Errno())},
+ equal: true,
+ },
+ {
+ name: "unequal errors",
+ linuxErr: []*gErrors.Error{linuxerr.ENOENT},
+ err: []error{linuxerr.ESRCH, syserror.ESRCH, unix.Errno(linuxerr.ESRCH.Errno())},
+ equal: false,
+ },
+ {
+ name: "other error",
+ linuxErr: []*gErrors.Error{nil, linuxerr.NOERROR, linuxerr.E2BIG, linuxerr.EINVAL},
+ err: []error{fs.ErrInvalid, io.EOF},
+ equal: false,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ for _, le := range tc.linuxErr {
+ for _, e := range tc.err {
+ if linuxerr.Equals(le, e) != tc.equal {
+ t.Fatalf("Expected %t from Equals method for linuxerr: %s %T and error: %s %T", tc.equal, le, le, e, e)
+ }
+ }
+ }
+ })
+ }
+}
diff --git a/pkg/eventchannel/BUILD b/pkg/eventchannel/BUILD
index a264ae2f0..ad15d3672 100644
--- a/pkg/eventchannel/BUILD
+++ b/pkg/eventchannel/BUILD
@@ -12,13 +12,13 @@ go_library(
visibility = ["//:sandbox"],
deps = [
":eventchannel_go_proto",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/sync",
"//pkg/unet",
"@org_golang_google_protobuf//encoding/prototext:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//types/known/anypb:go_default_library",
- "@org_golang_x_sys//unix:go_default_library",
"@org_golang_x_time//rate:go_default_library",
],
)
diff --git a/pkg/eventchannel/event.go b/pkg/eventchannel/event.go
index 98dfeb1f5..2be2d9d37 100644
--- a/pkg/eventchannel/event.go
+++ b/pkg/eventchannel/event.go
@@ -23,9 +23,9 @@ import (
"encoding/binary"
"fmt"
- "golang.org/x/sys/unix"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/proto"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
pb "gvisor.dev/gvisor/pkg/eventchannel/eventchannel_go_proto"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sync"
@@ -155,7 +155,7 @@ func (s *socketEmitter) Emit(msg proto.Message) (bool, error) {
for done := 0; done < len(p); {
n, err := s.socket.Write(p[done:])
if err != nil {
- return (err == unix.EPIPE), err
+ return linuxerr.Equals(linuxerr.EPIPE, err), err
}
done += n
}
diff --git a/pkg/flipcall/BUILD b/pkg/flipcall/BUILD
index 9730b88c1..c810c7946 100644
--- a/pkg/flipcall/BUILD
+++ b/pkg/flipcall/BUILD
@@ -10,9 +10,7 @@ go_library(
"flipcall_unsafe.go",
"futex_linux.go",
"io.go",
- "packet_window_allocator.go",
- "packet_window_mmap_amd64.go",
- "packet_window_mmap_arm64.go",
+ "packet_window.go",
],
visibility = ["//visibility:public"],
deps = [
diff --git a/pkg/flipcall/flipcall.go b/pkg/flipcall/flipcall.go
index 8d8309a73..f0e4ff487 100644
--- a/pkg/flipcall/flipcall.go
+++ b/pkg/flipcall/flipcall.go
@@ -22,6 +22,7 @@ import (
"sync/atomic"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/memutil"
)
// An Endpoint provides the ability to synchronously transfer data and control
@@ -96,9 +97,9 @@ func (ep *Endpoint) Init(side EndpointSide, pwd PacketWindowDescriptor, opts ...
if pwd.Length > math.MaxUint32 {
return fmt.Errorf("packet window size (%d) exceeds maximum (%d)", pwd.Length, math.MaxUint32)
}
- m, e := packetWindowMmap(pwd)
- if e != 0 {
- return fmt.Errorf("failed to mmap packet window: %v", e)
+ m, err := memutil.MapFile(0, uintptr(pwd.Length), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED, uintptr(pwd.FD), uintptr(pwd.Offset))
+ if err != nil {
+ return fmt.Errorf("failed to mmap packet window: %v", err)
}
ep.packet = m
ep.dataCap = uint32(pwd.Length) - uint32(PacketHeaderBytes)
diff --git a/pkg/flipcall/packet_window_allocator.go b/pkg/flipcall/packet_window.go
index 9122c97b7..9122c97b7 100644
--- a/pkg/flipcall/packet_window_allocator.go
+++ b/pkg/flipcall/packet_window.go
diff --git a/pkg/iovec/BUILD b/pkg/iovec/BUILD
deleted file mode 100644
index f4e9a6af9..000000000
--- a/pkg/iovec/BUILD
+++ /dev/null
@@ -1,18 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "iovec",
- srcs = ["iovec.go"],
- visibility = ["//:sandbox"],
- deps = ["@org_golang_x_sys//unix:go_default_library"],
-)
-
-go_test(
- name = "iovec_test",
- size = "small",
- srcs = ["iovec_test.go"],
- library = ":iovec",
- deps = ["@org_golang_x_sys//unix:go_default_library"],
-)
diff --git a/pkg/iovec/iovec.go b/pkg/iovec/iovec.go
deleted file mode 100644
index a281c05b6..000000000
--- a/pkg/iovec/iovec.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2020 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.
-
-// +build linux
-
-// Package iovec provides helpers to interact with vectorized I/O on host
-// system.
-package iovec
-
-import (
- "golang.org/x/sys/unix"
-)
-
-// MaxIovs is the maximum number of iovecs host platform can accept.
-var MaxIovs = 1024
-
-// Builder is a builder for slice of unix.Iovec.
-type Builder struct {
- iovec []unix.Iovec
- storage [8]unix.Iovec
-
- // overflow tracks the last buffer when iovec length is at MaxIovs.
- overflow []byte
-}
-
-// Add adds buf to b preparing to be written. Zero-length buf won't be added.
-func (b *Builder) Add(buf []byte) {
- if len(buf) == 0 {
- return
- }
- if b.iovec == nil {
- b.iovec = b.storage[:0]
- }
- if len(b.iovec) >= MaxIovs {
- b.addByAppend(buf)
- return
- }
-
- b.iovec = append(b.iovec, unix.Iovec{Base: &buf[0]})
- b.iovec[len(b.iovec)-1].SetLen(len(buf))
-
- // Keep the last buf if iovec is at max capacity. We will need to append to it
- // for later bufs.
- if len(b.iovec) == MaxIovs {
- n := len(buf)
- b.overflow = buf[:n:n]
- }
-}
-
-func (b *Builder) addByAppend(buf []byte) {
- b.overflow = append(b.overflow, buf...)
- b.iovec[len(b.iovec)-1] = unix.Iovec{Base: &b.overflow[0]}
- b.iovec[len(b.iovec)-1].SetLen(len(b.overflow))
-}
-
-// Build returns the final Iovec slice. The length of returned iovec will not
-// excceed MaxIovs.
-func (b *Builder) Build() []unix.Iovec {
- return b.iovec
-}
diff --git a/pkg/iovec/iovec_test.go b/pkg/iovec/iovec_test.go
deleted file mode 100644
index f6deb4208..000000000
--- a/pkg/iovec/iovec_test.go
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2020 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.
-
-// +build linux
-
-package iovec
-
-import (
- "bytes"
- "fmt"
- "testing"
- "unsafe"
-
- "golang.org/x/sys/unix"
-)
-
-func TestBuilderEmpty(t *testing.T) {
- var builder Builder
- iovecs := builder.Build()
- if got, want := len(iovecs), 0; got != want {
- t.Errorf("len(iovecs) = %d, want %d", got, want)
- }
-}
-
-func TestBuilderBuild(t *testing.T) {
- a := []byte{1, 2}
- b := []byte{3, 4, 5}
-
- var builder Builder
- builder.Add(a)
- builder.Add(b)
- builder.Add(nil) // Nil slice won't be added.
- builder.Add([]byte{}) // Empty slice won't be added.
- iovecs := builder.Build()
-
- if got, want := len(iovecs), 2; got != want {
- t.Fatalf("len(iovecs) = %d, want %d", got, want)
- }
- for i, data := range [][]byte{a, b} {
- if got, want := *iovecs[i].Base, data[0]; got != want {
- t.Fatalf("*iovecs[%d].Base = %d, want %d", i, got, want)
- }
- if got, want := iovecs[i].Len, uint64(len(data)); got != want {
- t.Fatalf("iovecs[%d].Len = %d, want %d", i, got, want)
- }
- }
-}
-
-func TestBuilderBuildMaxIov(t *testing.T) {
- for _, test := range []struct {
- numIov int
- }{
- {
- numIov: MaxIovs - 1,
- },
- {
- numIov: MaxIovs,
- },
- {
- numIov: MaxIovs + 1,
- },
- {
- numIov: MaxIovs + 10,
- },
- } {
- name := fmt.Sprintf("numIov=%v", test.numIov)
- t.Run(name, func(t *testing.T) {
- var data []byte
- var builder Builder
- for i := 0; i < test.numIov; i++ {
- buf := []byte{byte(i)}
- builder.Add(buf)
- data = append(data, buf...)
- }
- iovec := builder.Build()
-
- // Check the expected length of iovec.
- wantNum := test.numIov
- if wantNum > MaxIovs {
- wantNum = MaxIovs
- }
- if got, want := len(iovec), wantNum; got != want {
- t.Errorf("len(iovec) = %d, want %d", got, want)
- }
-
- // Test a real read-write.
- var fds [2]int
- if err := unix.Pipe(fds[:]); err != nil {
- t.Fatalf("Pipe: %v", err)
- }
- defer unix.Close(fds[0])
- defer unix.Close(fds[1])
-
- wrote, _, e := unix.RawSyscall(unix.SYS_WRITEV, uintptr(fds[1]), uintptr(unsafe.Pointer(&iovec[0])), uintptr(len(iovec)))
- if int(wrote) != len(data) || e != 0 {
- t.Fatalf("writev: %v, %v; want %v, 0", wrote, e, len(data))
- }
-
- got := make([]byte, len(data))
- if n, err := unix.Read(fds[0], got); n != len(got) || err != nil {
- t.Fatalf("read: %v, %v; want %v, nil", n, err, len(got))
- }
-
- if !bytes.Equal(got, data) {
- t.Errorf("read: got data %v, want %v", got, data)
- }
- })
- }
-}
diff --git a/pkg/linuxerr/linuxerr.go b/pkg/linuxerr/linuxerr.go
deleted file mode 100644
index f45caaadf..000000000
--- a/pkg/linuxerr/linuxerr.go
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2021 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 linuxerr contains syscall error codes exported as an error interface
-// pointers. This allows for fast comparison and return operations comperable
-// to unix.Errno constants.
-package linuxerr
-
-import (
- "gvisor.dev/gvisor/pkg/abi/linux"
-)
-
-// Error represents a syscall errno with a descriptive message.
-type Error struct {
- errno linux.Errno
- message string
-}
-
-func new(err linux.Errno, message string) *Error {
- return &Error{
- errno: err,
- message: message,
- }
-}
-
-// Error implements error.Error.
-func (e *Error) Error() string { return e.message }
-
-// Errno returns the underlying linux.Errno value.
-func (e *Error) Errno() linux.Errno { return e.errno }
-
-// The following varables have the same meaning as their errno equivalent.
-
-// Errno values from include/uapi/asm-generic/errno-base.h.
-var (
- EPERM = new(linux.EPERM, "operation not permitted")
- ENOENT = new(linux.ENOENT, "no such file or directory")
- ESRCH = new(linux.ESRCH, "no such process")
- EINTR = new(linux.EINTR, "interrupted system call")
- EIO = new(linux.EIO, "I/O error")
- ENXIO = new(linux.ENXIO, "no such device or address")
- E2BIG = new(linux.E2BIG, "argument list too long")
- ENOEXEC = new(linux.ENOEXEC, "exec format error")
- EBADF = new(linux.EBADF, "bad file number")
- ECHILD = new(linux.ECHILD, "no child processes")
- EAGAIN = new(linux.EAGAIN, "try again")
- ENOMEM = new(linux.ENOMEM, "out of memory")
- EACCES = new(linux.EACCES, "permission denied")
- EFAULT = new(linux.EFAULT, "bad address")
- ENOTBLK = new(linux.ENOTBLK, "block device required")
- EBUSY = new(linux.EBUSY, "device or resource busy")
- EEXIST = new(linux.EEXIST, "file exists")
- EXDEV = new(linux.EXDEV, "cross-device link")
- ENODEV = new(linux.ENODEV, "no such device")
- ENOTDIR = new(linux.ENOTDIR, "not a directory")
- EISDIR = new(linux.EISDIR, "is a directory")
- EINVAL = new(linux.EINVAL, "invalid argument")
- ENFILE = new(linux.ENFILE, "file table overflow")
- EMFILE = new(linux.EMFILE, "too many open files")
- ENOTTY = new(linux.ENOTTY, "not a typewriter")
- ETXTBSY = new(linux.ETXTBSY, "text file busy")
- EFBIG = new(linux.EFBIG, "file too large")
- ENOSPC = new(linux.ENOSPC, "no space left on device")
- ESPIPE = new(linux.ESPIPE, "illegal seek")
- EROFS = new(linux.EROFS, "read-only file system")
- EMLINK = new(linux.EMLINK, "too many links")
- EPIPE = new(linux.EPIPE, "broken pipe")
- EDOM = new(linux.EDOM, "math argument out of domain of func")
- ERANGE = new(linux.ERANGE, "math result not representable")
-)
-
-// Errno values from include/uapi/asm-generic/errno.h.
-var (
- EDEADLK = new(linux.EDEADLK, "resource deadlock would occur")
- ENAMETOOLONG = new(linux.ENAMETOOLONG, "file name too long")
- ENOLCK = new(linux.ENOLCK, "no record locks available")
- ENOSYS = new(linux.ENOSYS, "invalid system call number")
- ENOTEMPTY = new(linux.ENOTEMPTY, "directory not empty")
- ELOOP = new(linux.ELOOP, "too many symbolic links encountered")
- EWOULDBLOCK = new(linux.EWOULDBLOCK, "operation would block")
- ENOMSG = new(linux.ENOMSG, "no message of desired type")
- EIDRM = new(linux.EIDRM, "identifier removed")
- ECHRNG = new(linux.ECHRNG, "channel number out of range")
- EL2NSYNC = new(linux.EL2NSYNC, "level 2 not synchronized")
- EL3HLT = new(linux.EL3HLT, "level 3 halted")
- EL3RST = new(linux.EL3RST, "level 3 reset")
- ELNRNG = new(linux.ELNRNG, "link number out of range")
- EUNATCH = new(linux.EUNATCH, "protocol driver not attached")
- ENOCSI = new(linux.ENOCSI, "no CSI structure available")
- EL2HLT = new(linux.EL2HLT, "level 2 halted")
- EBADE = new(linux.EBADE, "invalid exchange")
- EBADR = new(linux.EBADR, "invalid request descriptor")
- EXFULL = new(linux.EXFULL, "exchange full")
- ENOANO = new(linux.ENOANO, "no anode")
- EBADRQC = new(linux.EBADRQC, "invalid request code")
- EBADSLT = new(linux.EBADSLT, "invalid slot")
- EDEADLOCK = new(linux.EDEADLOCK, EDEADLK.message)
- EBFONT = new(linux.EBFONT, "bad font file format")
- ENOSTR = new(linux.ENOSTR, "device not a stream")
- ENODATA = new(linux.ENODATA, "no data available")
- ETIME = new(linux.ETIME, "timer expired")
- ENOSR = new(linux.ENOSR, "out of streams resources")
- ENONET = new(linux.ENOENT, "machine is not on the network")
- ENOPKG = new(linux.ENOPKG, "package not installed")
- EREMOTE = new(linux.EREMOTE, "object is remote")
- ENOLINK = new(linux.ENOLINK, "link has been severed")
- EADV = new(linux.EADV, "advertise error")
- ESRMNT = new(linux.ESRMNT, "srmount error")
- ECOMM = new(linux.ECOMM, "communication error on send")
- EPROTO = new(linux.EPROTO, "protocol error")
- EMULTIHOP = new(linux.EMULTIHOP, "multihop attempted")
- EDOTDOT = new(linux.EDOTDOT, "RFS specific error")
- EBADMSG = new(linux.EBADMSG, "not a data message")
- EOVERFLOW = new(linux.EOVERFLOW, "value too large for defined data type")
- ENOTUNIQ = new(linux.ENOTUNIQ, "name not unique on network")
- EBADFD = new(linux.EBADFD, "file descriptor in bad state")
- EREMCHG = new(linux.EREMCHG, "remote address changed")
- ELIBACC = new(linux.ELIBACC, "can not access a needed shared library")
- ELIBBAD = new(linux.ELIBBAD, "accessing a corrupted shared library")
- ELIBSCN = new(linux.ELIBSCN, ".lib section in a.out corrupted")
- ELIBMAX = new(linux.ELIBMAX, "attempting to link in too many shared libraries")
- ELIBEXEC = new(linux.ELIBEXEC, "cannot exec a shared library directly")
- EILSEQ = new(linux.EILSEQ, "illegal byte sequence")
- ERESTART = new(linux.ERESTART, "interrupted system call should be restarted")
- ESTRPIPE = new(linux.ESTRPIPE, "streams pipe error")
- EUSERS = new(linux.EUSERS, "too many users")
- ENOTSOCK = new(linux.ENOTSOCK, "socket operation on non-socket")
- EDESTADDRREQ = new(linux.EDESTADDRREQ, "destination address required")
- EMSGSIZE = new(linux.EMSGSIZE, "message too long")
- EPROTOTYPE = new(linux.EPROTOTYPE, "protocol wrong type for socket")
- ENOPROTOOPT = new(linux.ENOPROTOOPT, "protocol not available")
- EPROTONOSUPPORT = new(linux.EPROTONOSUPPORT, "protocol not supported")
- ESOCKTNOSUPPORT = new(linux.ESOCKTNOSUPPORT, "socket type not supported")
- EOPNOTSUPP = new(linux.EOPNOTSUPP, "operation not supported on transport endpoint")
- EPFNOSUPPORT = new(linux.EPFNOSUPPORT, "protocol family not supported")
- EAFNOSUPPORT = new(linux.EAFNOSUPPORT, "address family not supported by protocol")
- EADDRINUSE = new(linux.EADDRINUSE, "address already in use")
- EADDRNOTAVAIL = new(linux.EADDRNOTAVAIL, "cannot assign requested address")
- ENETDOWN = new(linux.ENETDOWN, "network is down")
- ENETUNREACH = new(linux.ENETUNREACH, "network is unreachable")
- ENETRESET = new(linux.ENETRESET, "network dropped connection because of reset")
- ECONNABORTED = new(linux.ECONNABORTED, "software caused connection abort")
- ECONNRESET = new(linux.ECONNRESET, "connection reset by peer")
- ENOBUFS = new(linux.ENOBUFS, "no buffer space available")
- EISCONN = new(linux.EISCONN, "transport endpoint is already connected")
- ENOTCONN = new(linux.ENOTCONN, "transport endpoint is not connected")
- ESHUTDOWN = new(linux.ESHUTDOWN, "cannot send after transport endpoint shutdown")
- ETOOMANYREFS = new(linux.ETOOMANYREFS, "too many references: cannot splice")
- ETIMEDOUT = new(linux.ETIMEDOUT, "connection timed out")
- ECONNREFUSED = new(linux.ECONNREFUSED, "connection refused")
- EHOSTDOWN = new(linux.EHOSTDOWN, "host is down")
- EHOSTUNREACH = new(linux.EHOSTUNREACH, "no route to host")
- EALREADY = new(linux.EALREADY, "operation already in progress")
- EINPROGRESS = new(linux.EINPROGRESS, "operation now in progress")
- ESTALE = new(linux.ESTALE, "stale file handle")
- EUCLEAN = new(linux.EUCLEAN, "structure needs cleaning")
- ENOTNAM = new(linux.ENOTNAM, "not a XENIX named type file")
- ENAVAIL = new(linux.ENAVAIL, "no XENIX semaphores available")
- EISNAM = new(linux.EISNAM, "is a named type file")
- EREMOTEIO = new(linux.EREMOTEIO, "remote I/O error")
- EDQUOT = new(linux.EDQUOT, "quota exceeded")
- ENOMEDIUM = new(linux.ENOMEDIUM, "no medium found")
- EMEDIUMTYPE = new(linux.EMEDIUMTYPE, "wrong medium type")
- ECANCELED = new(linux.ECANCELED, "operation Canceled")
- ENOKEY = new(linux.ENOKEY, "required key not available")
- EKEYEXPIRED = new(linux.EKEYEXPIRED, "key has expired")
- EKEYREVOKED = new(linux.EKEYREVOKED, "key has been revoked")
- EKEYREJECTED = new(linux.EKEYREJECTED, "key was rejected by service")
- EOWNERDEAD = new(linux.EOWNERDEAD, "owner died")
- ENOTRECOVERABLE = new(linux.ENOTRECOVERABLE, "state not recoverable")
- ERFKILL = new(linux.ERFKILL, "operation not possible due to RF-kill")
- EHWPOISON = new(linux.EHWPOISON, "memory page has hardware error")
-)
diff --git a/pkg/linuxerr/linuxerr_test.go b/pkg/linuxerr/linuxerr_test.go
deleted file mode 100644
index d34937e93..000000000
--- a/pkg/linuxerr/linuxerr_test.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// 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 syserror_test
-
-import (
- "errors"
- "testing"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/linuxerr"
- "gvisor.dev/gvisor/pkg/syserror"
-)
-
-var globalError error
-
-func BenchmarkAssignErrno(b *testing.B) {
- for i := b.N; i > 0; i-- {
- globalError = unix.EINVAL
- }
-}
-
-func BenchmarkLinuxerrAssignError(b *testing.B) {
- for i := b.N; i > 0; i-- {
- globalError = linuxerr.EINVAL
- }
-}
-
-func BenchmarkAssignSyserrorError(b *testing.B) {
- for i := b.N; i > 0; i-- {
- globalError = syserror.EINVAL
- }
-}
-
-func BenchmarkCompareErrno(b *testing.B) {
- globalError = unix.EAGAIN
- j := 0
- for i := b.N; i > 0; i-- {
- if globalError == unix.EINVAL {
- j++
- }
- }
-}
-
-func BenchmarkCompareLinuxerrError(b *testing.B) {
- globalError = linuxerr.E2BIG
- j := 0
- for i := b.N; i > 0; i-- {
- if globalError == linuxerr.EINVAL {
- j++
- }
- }
-}
-
-func BenchmarkCompareSyserrorError(b *testing.B) {
- globalError = syserror.EAGAIN
- j := 0
- for i := b.N; i > 0; i-- {
- if globalError == syserror.EINVAL {
- j++
- }
- }
-}
-
-func BenchmarkSwitchErrno(b *testing.B) {
- globalError = unix.EPERM
- j := 0
- for i := b.N; i > 0; i-- {
- switch globalError {
- case unix.EINVAL:
- j++
- case unix.EINTR:
- j += 2
- case unix.EAGAIN:
- j += 3
- }
- }
-}
-
-func BenchmarkSwitchLinuxerrError(b *testing.B) {
- globalError = linuxerr.EPERM
- j := 0
- for i := b.N; i > 0; i-- {
- switch globalError {
- case linuxerr.EINVAL:
- j++
- case linuxerr.EINTR:
- j += 2
- case linuxerr.EAGAIN:
- j += 3
- }
- }
-}
-
-func BenchmarkSwitchSyserrorError(b *testing.B) {
- globalError = syserror.EPERM
- j := 0
- for i := b.N; i > 0; i-- {
- switch globalError {
- case syserror.EINVAL:
- j++
- case syserror.EINTR:
- j += 2
- case syserror.EAGAIN:
- j += 3
- }
- }
-}
-
-type translationTestTable struct {
- fn string
- errIn error
- syscallErrorIn unix.Errno
- expectedBool bool
- expectedTranslation unix.Errno
-}
-
-func TestErrorTranslation(t *testing.T) {
- myError := errors.New("My test error")
- myError2 := errors.New("Another test error")
- testTable := []translationTestTable{
- {"TranslateError", myError, 0, false, 0},
- {"TranslateError", myError2, 0, false, 0},
- {"AddErrorTranslation", myError, unix.EAGAIN, true, 0},
- {"AddErrorTranslation", myError, unix.EAGAIN, false, 0},
- {"AddErrorTranslation", myError, unix.EPERM, false, 0},
- {"TranslateError", myError, 0, true, unix.EAGAIN},
- {"TranslateError", myError2, 0, false, 0},
- {"AddErrorTranslation", myError2, unix.EPERM, true, 0},
- {"AddErrorTranslation", myError2, unix.EPERM, false, 0},
- {"AddErrorTranslation", myError2, unix.EAGAIN, false, 0},
- {"TranslateError", myError, 0, true, unix.EAGAIN},
- {"TranslateError", myError2, 0, true, unix.EPERM},
- }
- for _, tt := range testTable {
- switch tt.fn {
- case "TranslateError":
- err, ok := syserror.TranslateError(tt.errIn)
- if ok != tt.expectedBool {
- t.Fatalf("%v(%v) => %v expected %v", tt.fn, tt.errIn, ok, tt.expectedBool)
- } else if err != tt.expectedTranslation {
- t.Fatalf("%v(%v) (error) => %v expected %v", tt.fn, tt.errIn, err, tt.expectedTranslation)
- }
- case "AddErrorTranslation":
- ok := syserror.AddErrorTranslation(tt.errIn, tt.syscallErrorIn)
- if ok != tt.expectedBool {
- t.Fatalf("%v(%v) => %v expected %v", tt.fn, tt.errIn, ok, tt.expectedBool)
- }
- default:
- t.Fatalf("Unknown function %v", tt.fn)
- }
- }
-}
diff --git a/pkg/marshal/primitive/BUILD b/pkg/marshal/primitive/BUILD
index 190b57c29..6e5ce136d 100644
--- a/pkg/marshal/primitive/BUILD
+++ b/pkg/marshal/primitive/BUILD
@@ -12,9 +12,7 @@ go_library(
"//:sandbox",
],
deps = [
- "//pkg/context",
"//pkg/hostarch",
"//pkg/marshal",
- "//pkg/usermem",
],
)
diff --git a/pkg/marshal/primitive/primitive.go b/pkg/marshal/primitive/primitive.go
index 6f38992b7..1c49cf082 100644
--- a/pkg/marshal/primitive/primitive.go
+++ b/pkg/marshal/primitive/primitive.go
@@ -19,10 +19,8 @@ package primitive
import (
"io"
- "gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal"
- "gvisor.dev/gvisor/pkg/usermem"
)
// Int8 is a marshal.Marshallable implementation for int8.
@@ -400,26 +398,3 @@ func CopyStringOut(cc marshal.CopyContext, addr hostarch.Addr, src string) (int,
srcP := ByteSlice(src)
return srcP.CopyOut(cc, addr)
}
-
-// IOCopyContext wraps an object implementing hostarch.IO to implement
-// marshal.CopyContext.
-type IOCopyContext struct {
- Ctx context.Context
- IO usermem.IO
- Opts usermem.IOOpts
-}
-
-// CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer.
-func (i *IOCopyContext) CopyScratchBuffer(size int) []byte {
- return make([]byte, size)
-}
-
-// CopyOutBytes implements marshal.CopyContext.CopyOutBytes.
-func (i *IOCopyContext) CopyOutBytes(addr hostarch.Addr, b []byte) (int, error) {
- return i.IO.CopyOut(i.Ctx, addr, b, i.Opts)
-}
-
-// CopyInBytes implements marshal.CopyContext.CopyInBytes.
-func (i *IOCopyContext) CopyInBytes(addr hostarch.Addr, b []byte) (int, error) {
- return i.IO.CopyIn(i.Ctx, addr, b, i.Opts)
-}
diff --git a/pkg/memutil/BUILD b/pkg/memutil/BUILD
index 9d07d98b4..bea595286 100644
--- a/pkg/memutil/BUILD
+++ b/pkg/memutil/BUILD
@@ -4,7 +4,11 @@ package(licenses = ["notice"])
go_library(
name = "memutil",
- srcs = ["memutil_unsafe.go"],
+ srcs = [
+ "memfd_linux_unsafe.go",
+ "memutil.go",
+ "mmap.go",
+ ],
visibility = ["//visibility:public"],
deps = ["@org_golang_x_sys//unix:go_default_library"],
)
diff --git a/pkg/memutil/memutil_unsafe.go b/pkg/memutil/memfd_linux_unsafe.go
index 6676d1ce3..504382213 100644
--- a/pkg/memutil/memutil_unsafe.go
+++ b/pkg/memutil/memfd_linux_unsafe.go
@@ -14,7 +14,6 @@
// +build linux
-// Package memutil provides a wrapper for the memfd_create() system call.
package memutil
import (
diff --git a/pkg/memutil/memutil.go b/pkg/memutil/memutil.go
new file mode 100644
index 000000000..3185882fd
--- /dev/null
+++ b/pkg/memutil/memutil.go
@@ -0,0 +1,16 @@
+// 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 memutil provides utilities for working with shared memory files.
+package memutil
diff --git a/pkg/flipcall/packet_window_mmap_amd64.go b/pkg/memutil/mmap.go
index ced587a2a..7c939293f 100644
--- a/pkg/flipcall/packet_window_mmap_amd64.go
+++ b/pkg/memutil/mmap.go
@@ -12,12 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package flipcall
+package memutil
-import "golang.org/x/sys/unix"
+import (
+ "golang.org/x/sys/unix"
+)
-// Return a memory mapping of the pwd in memory that can be shared outside the sandbox.
-func packetWindowMmap(pwd PacketWindowDescriptor) (uintptr, unix.Errno) {
- m, _, err := unix.RawSyscall6(unix.SYS_MMAP, 0, uintptr(pwd.Length), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED, uintptr(pwd.FD), uintptr(pwd.Offset))
- return m, err
+// MapFile returns a memory mapping configured by the given options as per
+// mmap(2).
+func MapFile(addr, len, prot, flags, fd, offset uintptr) (uintptr, error) {
+ m, _, e := unix.RawSyscall6(unix.SYS_MMAP, addr, len, prot, flags, fd, offset)
+ if e != 0 {
+ return 0, e
+ }
+ return m, nil
}
diff --git a/pkg/merkletree/merkletree.go b/pkg/merkletree/merkletree.go
index ac7868ad9..0b961d3d9 100644
--- a/pkg/merkletree/merkletree.go
+++ b/pkg/merkletree/merkletree.go
@@ -151,21 +151,21 @@ type VerityDescriptor struct {
Mode uint32
UID uint32
GID uint32
- Children map[string]struct{}
+ Children []string
SymlinkTarget string
RootHash []byte
}
-func (d *VerityDescriptor) String() string {
+func (d *VerityDescriptor) encode() []byte {
b := new(bytes.Buffer)
e := gob.NewEncoder(b)
- e.Encode(d.Children)
- return fmt.Sprintf("Name: %s, Size: %d, Mode: %d, UID: %d, GID: %d, Children: %v, Symlink: %s, RootHash: %v", d.Name, d.FileSize, d.Mode, d.UID, d.GID, b.Bytes(), d.SymlinkTarget, d.RootHash)
+ e.Encode(d)
+ return b.Bytes()
}
// verify generates a hash from d, and compares it with expected.
func (d *VerityDescriptor) verify(expected []byte, hashAlgorithms int) error {
- h, err := hashData([]byte(d.String()), hashAlgorithms)
+ h, err := hashData(d.encode(), hashAlgorithms)
if err != nil {
return err
}
@@ -210,7 +210,7 @@ type GenerateParams struct {
GID uint32
// Children is a map of children names for a directory. It should be
// empty for a regular file.
- Children map[string]struct{}
+ Children []string
// SymlinkTarget is the target path of a symlink file, or "" if the file is not a symlink.
SymlinkTarget string
// HashAlgorithms is the algorithms used to hash data.
@@ -242,7 +242,7 @@ func Generate(params *GenerateParams) ([]byte, error) {
// If file is a symlink do not generate root hash for file content.
if params.SymlinkTarget != "" {
- return hashData([]byte(descriptor.String()), params.HashAlgorithms)
+ return hashData(descriptor.encode(), params.HashAlgorithms)
}
layout, err := InitLayout(params.Size, params.HashAlgorithms, params.DataAndTreeInSameFile)
@@ -315,7 +315,7 @@ func Generate(params *GenerateParams) ([]byte, error) {
numBlocks = (numBlocks + layout.hashesPerBlock() - 1) / layout.hashesPerBlock()
}
descriptor.RootHash = root
- return hashData([]byte(descriptor.String()), params.HashAlgorithms)
+ return hashData(descriptor.encode(), params.HashAlgorithms)
}
// VerifyParams contains the params used to verify a portion of a file against
@@ -339,7 +339,7 @@ type VerifyParams struct {
GID uint32
// Children is a map of children names for a directory. It should be
// empty for a regular file.
- Children map[string]struct{}
+ Children []string
// SymlinkTarget is the target path of a symlink file, or "" if the file is not a symlink.
SymlinkTarget string
// HashAlgorithms is the algorithms used to hash data.
diff --git a/pkg/merkletree/merkletree_test.go b/pkg/merkletree/merkletree_test.go
index 5d6f8df1b..1447fd139 100644
--- a/pkg/merkletree/merkletree_test.go
+++ b/pkg/merkletree/merkletree_test.go
@@ -206,112 +206,112 @@ func TestGenerate(t *testing.T) {
data: bytes.Repeat([]byte{0}, hostarch.PageSize),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
dataAndTreeInSameFile: false,
- expectedHash: []byte{9, 115, 238, 230, 38, 140, 195, 70, 207, 144, 202, 118, 23, 113, 32, 129, 226, 239, 177, 69, 161, 26, 14, 113, 16, 37, 30, 96, 19, 148, 132, 27},
+ expectedHash: []byte{78, 38, 225, 107, 61, 246, 26, 6, 71, 163, 254, 97, 112, 200, 87, 232, 190, 87, 231, 160, 119, 124, 61, 229, 49, 126, 90, 223, 134, 51, 77, 182},
},
{
name: "OnePageZeroesSHA256SameFile",
data: bytes.Repeat([]byte{0}, hostarch.PageSize),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
dataAndTreeInSameFile: true,
- expectedHash: []byte{9, 115, 238, 230, 38, 140, 195, 70, 207, 144, 202, 118, 23, 113, 32, 129, 226, 239, 177, 69, 161, 26, 14, 113, 16, 37, 30, 96, 19, 148, 132, 27},
+ expectedHash: []byte{78, 38, 225, 107, 61, 246, 26, 6, 71, 163, 254, 97, 112, 200, 87, 232, 190, 87, 231, 160, 119, 124, 61, 229, 49, 126, 90, 223, 134, 51, 77, 182},
},
{
name: "OnePageZeroesSHA512SeparateFile",
data: bytes.Repeat([]byte{0}, hostarch.PageSize),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA512,
dataAndTreeInSameFile: false,
- expectedHash: []byte{127, 8, 95, 11, 83, 101, 51, 39, 170, 235, 39, 43, 135, 243, 145, 118, 148, 58, 27, 155, 182, 205, 44, 47, 5, 223, 215, 17, 35, 16, 43, 104, 43, 11, 8, 88, 171, 7, 249, 243, 14, 62, 126, 218, 23, 159, 237, 237, 42, 226, 39, 25, 87, 48, 253, 191, 116, 213, 37, 3, 187, 152, 154, 14},
+ expectedHash: []byte{221, 45, 182, 132, 61, 212, 227, 145, 150, 131, 98, 221, 195, 5, 89, 21, 188, 36, 250, 101, 85, 78, 197, 253, 193, 23, 74, 219, 28, 108, 77, 47, 65, 79, 123, 144, 50, 245, 109, 72, 71, 80, 24, 77, 158, 95, 242, 185, 109, 163, 105, 183, 67, 106, 55, 194, 223, 46, 12, 242, 165, 203, 172, 254},
},
{
name: "OnePageZeroesSHA512SameFile",
data: bytes.Repeat([]byte{0}, hostarch.PageSize),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA512,
dataAndTreeInSameFile: true,
- expectedHash: []byte{127, 8, 95, 11, 83, 101, 51, 39, 170, 235, 39, 43, 135, 243, 145, 118, 148, 58, 27, 155, 182, 205, 44, 47, 5, 223, 215, 17, 35, 16, 43, 104, 43, 11, 8, 88, 171, 7, 249, 243, 14, 62, 126, 218, 23, 159, 237, 237, 42, 226, 39, 25, 87, 48, 253, 191, 116, 213, 37, 3, 187, 152, 154, 14},
+ expectedHash: []byte{221, 45, 182, 132, 61, 212, 227, 145, 150, 131, 98, 221, 195, 5, 89, 21, 188, 36, 250, 101, 85, 78, 197, 253, 193, 23, 74, 219, 28, 108, 77, 47, 65, 79, 123, 144, 50, 245, 109, 72, 71, 80, 24, 77, 158, 95, 242, 185, 109, 163, 105, 183, 67, 106, 55, 194, 223, 46, 12, 242, 165, 203, 172, 254},
},
{
name: "MultiplePageZeroesSHA256SeparateFile",
data: bytes.Repeat([]byte{0}, 128*hostarch.PageSize+1),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
dataAndTreeInSameFile: false,
- expectedHash: []byte{247, 158, 42, 215, 180, 106, 0, 28, 77, 64, 132, 162, 74, 65, 250, 161, 243, 66, 129, 44, 197, 8, 145, 14, 94, 206, 156, 184, 145, 145, 20, 185},
+ expectedHash: []byte{131, 122, 73, 143, 4, 202, 193, 156, 218, 169, 196, 223, 70, 100, 117, 191, 241, 113, 134, 11, 229, 231, 105, 157, 156, 0, 66, 213, 122, 145, 174, 8},
},
{
name: "MultiplePageZeroesSHA256SameFile",
data: bytes.Repeat([]byte{0}, 128*hostarch.PageSize+1),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
dataAndTreeInSameFile: true,
- expectedHash: []byte{247, 158, 42, 215, 180, 106, 0, 28, 77, 64, 132, 162, 74, 65, 250, 161, 243, 66, 129, 44, 197, 8, 145, 14, 94, 206, 156, 184, 145, 145, 20, 185},
+ expectedHash: []byte{131, 122, 73, 143, 4, 202, 193, 156, 218, 169, 196, 223, 70, 100, 117, 191, 241, 113, 134, 11, 229, 231, 105, 157, 156, 0, 66, 213, 122, 145, 174, 8},
},
{
name: "MultiplePageZeroesSHA512SeparateFile",
data: bytes.Repeat([]byte{0}, 128*hostarch.PageSize+1),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA512,
dataAndTreeInSameFile: false,
- expectedHash: []byte{100, 121, 14, 30, 104, 200, 142, 182, 190, 78, 23, 68, 157, 174, 23, 75, 174, 250, 250, 25, 66, 45, 235, 103, 129, 49, 78, 127, 173, 154, 121, 35, 37, 115, 60, 217, 26, 205, 253, 253, 236, 145, 107, 109, 232, 19, 72, 92, 4, 191, 181, 205, 191, 57, 234, 177, 144, 235, 143, 30, 15, 197, 109, 81},
+ expectedHash: []byte{211, 48, 232, 110, 240, 51, 99, 241, 123, 138, 42, 76, 94, 86, 59, 200, 3, 246, 137, 148, 189, 226, 111, 103, 146, 29, 12, 218, 40, 182, 33, 99, 193, 163, 238, 26, 184, 13, 165, 187, 68, 173, 139, 9, 208, 59, 0, 192, 180, 50, 221, 35, 43, 119, 194, 16, 64, 84, 116, 63, 158, 195, 194, 226},
},
{
name: "MultiplePageZeroesSHA512SameFile",
data: bytes.Repeat([]byte{0}, 128*hostarch.PageSize+1),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA512,
dataAndTreeInSameFile: true,
- expectedHash: []byte{100, 121, 14, 30, 104, 200, 142, 182, 190, 78, 23, 68, 157, 174, 23, 75, 174, 250, 250, 25, 66, 45, 235, 103, 129, 49, 78, 127, 173, 154, 121, 35, 37, 115, 60, 217, 26, 205, 253, 253, 236, 145, 107, 109, 232, 19, 72, 92, 4, 191, 181, 205, 191, 57, 234, 177, 144, 235, 143, 30, 15, 197, 109, 81},
+ expectedHash: []byte{211, 48, 232, 110, 240, 51, 99, 241, 123, 138, 42, 76, 94, 86, 59, 200, 3, 246, 137, 148, 189, 226, 111, 103, 146, 29, 12, 218, 40, 182, 33, 99, 193, 163, 238, 26, 184, 13, 165, 187, 68, 173, 139, 9, 208, 59, 0, 192, 180, 50, 221, 35, 43, 119, 194, 16, 64, 84, 116, 63, 158, 195, 194, 226},
},
{
name: "SingleASHA256SeparateFile",
data: []byte{'a'},
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
dataAndTreeInSameFile: false,
- expectedHash: []byte{90, 124, 194, 100, 206, 242, 75, 152, 47, 249, 16, 27, 136, 161, 223, 228, 121, 241, 126, 158, 126, 122, 100, 120, 117, 15, 81, 78, 201, 133, 119, 111},
+ expectedHash: []byte{26, 47, 238, 138, 235, 244, 140, 231, 129, 240, 155, 252, 219, 44, 46, 72, 57, 249, 139, 88, 132, 238, 86, 108, 181, 115, 96, 72, 99, 210, 134, 47},
},
{
name: "SingleASHA256SameFile",
data: []byte{'a'},
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
dataAndTreeInSameFile: true,
- expectedHash: []byte{90, 124, 194, 100, 206, 242, 75, 152, 47, 249, 16, 27, 136, 161, 223, 228, 121, 241, 126, 158, 126, 122, 100, 120, 117, 15, 81, 78, 201, 133, 119, 111},
+ expectedHash: []byte{26, 47, 238, 138, 235, 244, 140, 231, 129, 240, 155, 252, 219, 44, 46, 72, 57, 249, 139, 88, 132, 238, 86, 108, 181, 115, 96, 72, 99, 210, 134, 47},
},
{
name: "SingleASHA512SeparateFile",
data: []byte{'a'},
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA512,
dataAndTreeInSameFile: false,
- expectedHash: []byte{24, 10, 13, 25, 113, 62, 169, 99, 151, 70, 166, 113, 81, 81, 163, 85, 5, 25, 29, 15, 46, 37, 104, 120, 142, 218, 52, 178, 187, 83, 30, 166, 101, 87, 70, 196, 188, 61, 123, 20, 13, 254, 126, 52, 212, 111, 75, 203, 33, 233, 233, 47, 181, 161, 43, 193, 131, 41, 99, 33, 164, 73, 89, 152},
+ expectedHash: []byte{44, 30, 224, 12, 102, 119, 163, 171, 119, 175, 212, 121, 231, 188, 125, 171, 79, 28, 144, 234, 75, 122, 44, 75, 15, 101, 173, 92, 233, 109, 234, 60, 173, 148, 125, 85, 94, 234, 95, 91, 16, 196, 88, 175, 23, 129, 226, 110, 24, 238, 5, 49, 186, 128, 72, 188, 193, 180, 207, 193, 203, 119, 40, 191},
},
{
name: "SingleASHA512SameFile",
data: []byte{'a'},
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA512,
dataAndTreeInSameFile: true,
- expectedHash: []byte{24, 10, 13, 25, 113, 62, 169, 99, 151, 70, 166, 113, 81, 81, 163, 85, 5, 25, 29, 15, 46, 37, 104, 120, 142, 218, 52, 178, 187, 83, 30, 166, 101, 87, 70, 196, 188, 61, 123, 20, 13, 254, 126, 52, 212, 111, 75, 203, 33, 233, 233, 47, 181, 161, 43, 193, 131, 41, 99, 33, 164, 73, 89, 152},
+ expectedHash: []byte{44, 30, 224, 12, 102, 119, 163, 171, 119, 175, 212, 121, 231, 188, 125, 171, 79, 28, 144, 234, 75, 122, 44, 75, 15, 101, 173, 92, 233, 109, 234, 60, 173, 148, 125, 85, 94, 234, 95, 91, 16, 196, 88, 175, 23, 129, 226, 110, 24, 238, 5, 49, 186, 128, 72, 188, 193, 180, 207, 193, 203, 119, 40, 191},
},
{
name: "OnePageASHA256SeparateFile",
data: bytes.Repeat([]byte{'a'}, hostarch.PageSize),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
dataAndTreeInSameFile: false,
- expectedHash: []byte{132, 54, 112, 142, 156, 19, 50, 140, 138, 240, 192, 154, 100, 120, 242, 69, 64, 217, 62, 166, 127, 88, 23, 197, 100, 66, 255, 215, 214, 229, 54, 1},
+ expectedHash: []byte{166, 254, 83, 46, 241, 111, 18, 47, 79, 6, 181, 197, 176, 143, 211, 204, 53, 5, 245, 134, 172, 95, 97, 131, 236, 132, 197, 138, 123, 78, 43, 13},
},
{
name: "OnePageASHA256SameFile",
data: bytes.Repeat([]byte{'a'}, hostarch.PageSize),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
dataAndTreeInSameFile: true,
- expectedHash: []byte{132, 54, 112, 142, 156, 19, 50, 140, 138, 240, 192, 154, 100, 120, 242, 69, 64, 217, 62, 166, 127, 88, 23, 197, 100, 66, 255, 215, 214, 229, 54, 1},
+ expectedHash: []byte{166, 254, 83, 46, 241, 111, 18, 47, 79, 6, 181, 197, 176, 143, 211, 204, 53, 5, 245, 134, 172, 95, 97, 131, 236, 132, 197, 138, 123, 78, 43, 13},
},
{
name: "OnePageASHA512SeparateFile",
data: bytes.Repeat([]byte{'a'}, hostarch.PageSize),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA512,
dataAndTreeInSameFile: false,
- expectedHash: []byte{165, 46, 176, 116, 47, 209, 101, 193, 64, 185, 30, 9, 52, 22, 24, 154, 135, 220, 232, 168, 215, 45, 222, 226, 207, 104, 160, 10, 156, 98, 245, 250, 76, 21, 68, 204, 65, 118, 69, 52, 210, 155, 36, 109, 233, 103, 1, 40, 218, 89, 125, 38, 247, 194, 2, 225, 119, 155, 65, 99, 182, 111, 110, 145},
+ expectedHash: []byte{23, 69, 6, 79, 39, 232, 90, 246, 62, 55, 4, 229, 47, 36, 230, 24, 233, 47, 55, 36, 26, 139, 196, 78, 242, 12, 194, 77, 109, 81, 151, 188, 63, 201, 127, 235, 81, 214, 91, 200, 19, 232, 240, 14, 197, 1, 99, 224, 18, 213, 203, 242, 44, 102, 25, 62, 90, 189, 106, 107, 129, 61, 115, 39},
},
{
name: "OnePageASHA512SameFile",
data: bytes.Repeat([]byte{'a'}, hostarch.PageSize),
hashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA512,
dataAndTreeInSameFile: true,
- expectedHash: []byte{165, 46, 176, 116, 47, 209, 101, 193, 64, 185, 30, 9, 52, 22, 24, 154, 135, 220, 232, 168, 215, 45, 222, 226, 207, 104, 160, 10, 156, 98, 245, 250, 76, 21, 68, 204, 65, 118, 69, 52, 210, 155, 36, 109, 233, 103, 1, 40, 218, 89, 125, 38, 247, 194, 2, 225, 119, 155, 65, 99, 182, 111, 110, 145},
+ expectedHash: []byte{23, 69, 6, 79, 39, 232, 90, 246, 62, 55, 4, 229, 47, 36, 230, 24, 233, 47, 55, 36, 26, 139, 196, 78, 242, 12, 194, 77, 109, 81, 151, 188, 63, 201, 127, 235, 81, 214, 91, 200, 19, 232, 240, 14, 197, 1, 99, 224, 18, 213, 203, 242, 44, 102, 25, 62, 90, 189, 106, 107, 129, 61, 115, 39},
},
}
@@ -324,7 +324,7 @@ func TestGenerate(t *testing.T) {
Mode: defaultMode,
UID: defaultUID,
GID: defaultGID,
- Children: make(map[string]struct{}),
+ Children: []string{},
HashAlgorithms: tc.hashAlgorithms,
TreeReader: &tree,
TreeWriter: &tree,
@@ -366,7 +366,7 @@ func prepareVerify(t *testing.T, dataSize int64, hashAlgorithm int, dataAndTreeI
Mode: defaultMode,
UID: defaultUID,
GID: defaultGID,
- Children: make(map[string]struct{}),
+ Children: []string{},
HashAlgorithms: hashAlgorithm,
TreeReader: &tree,
TreeWriter: &tree,
@@ -398,7 +398,7 @@ func prepareVerify(t *testing.T, dataSize int64, hashAlgorithm int, dataAndTreeI
Mode: defaultMode,
UID: defaultUID,
GID: defaultGID,
- Children: make(map[string]struct{}),
+ Children: []string{},
HashAlgorithms: hashAlgorithm,
ReadOffset: verifyStart,
ReadSize: verifySize,
@@ -627,7 +627,7 @@ func TestVerifyModifiedChildren(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
var buf bytes.Buffer
_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, 0 /* verifyStart */, 0 /* verifySize */, &buf)
- params.Children["abc"] = struct{}{}
+ params.Children = append(params.Children, "abc")
if _, err := Verify(&params); errors.Is(err, nil) {
t.Errorf("Verification succeeded when expected to fail")
}
diff --git a/pkg/metric/metric.go b/pkg/metric/metric.go
index fdeee3a5f..4829ae7ce 100644
--- a/pkg/metric/metric.go
+++ b/pkg/metric/metric.go
@@ -36,17 +36,22 @@ var (
// new metric after initialization.
ErrInitializationDone = errors.New("metric cannot be created after initialization is complete")
- // createdSentryMetrics indicates that the sentry metrics are created.
- createdSentryMetrics = false
-
// WeirdnessMetric is a metric with fields created to track the number
// of weird occurrences such as time fallback, partial_result, vsyscall
// count, watchdog startup timeouts and stuck tasks.
- WeirdnessMetric *Uint64Metric
+ WeirdnessMetric = MustCreateNewUint64Metric("/weirdness", true /* sync */, "Increment for weird occurrences of problems such as time fallback, partial result, vsyscalls invoked in the sandbox, watchdog startup timeouts and stuck tasks.",
+ Field{
+ name: "weirdness_type",
+ allowedValues: []string{"time_fallback", "partial_result", "vsyscall_count", "watchdog_stuck_startup", "watchdog_stuck_tasks"},
+ })
// SuspiciousOperationsMetric is a metric with fields created to detect
// operations such as opening an executable file to write from a gofer.
- SuspiciousOperationsMetric *Uint64Metric
+ SuspiciousOperationsMetric = MustCreateNewUint64Metric("/suspicious_operations", true /* sync */, "Increment for suspicious operations such as opening an executable file to write from a gofer.",
+ Field{
+ name: "operation_type",
+ allowedValues: []string{"opened_write_execute_file"},
+ })
)
// Uint64Metric encapsulates a uint64 that represents some kind of metric to be
@@ -84,17 +89,21 @@ var (
// Precondition:
// * All metrics are registered.
// * Initialize/Disable has not been called.
-func Initialize() {
+func Initialize() error {
if initialized {
- panic("Initialize/Disable called more than once")
+ return errors.New("metric.Initialize called after metric.Initialize or metric.Disable")
}
- initialized = true
m := pb.MetricRegistration{}
for _, v := range allMetrics.m {
m.Metrics = append(m.Metrics, v.metadata)
}
- eventchannel.Emit(&m)
+ if err := eventchannel.Emit(&m); err != nil {
+ return fmt.Errorf("unable to emit metric initialize event: %w", err)
+ }
+
+ initialized = true
+ return nil
}
// Disable sends an empty metric registration event over the event channel,
@@ -103,16 +112,18 @@ func Initialize() {
// Precondition:
// * All metrics are registered.
// * Initialize/Disable has not been called.
-func Disable() {
+func Disable() error {
if initialized {
- panic("Initialize/Disable called more than once")
+ return errors.New("metric.Disable called after metric.Initialize or metric.Disable")
}
- initialized = true
m := pb.MetricRegistration{}
if err := eventchannel.Emit(&m); err != nil {
- panic("unable to emit metric disable event: " + err.Error())
+ return fmt.Errorf("unable to emit metric disable event: %w", err)
}
+
+ initialized = true
+ return nil
}
type customUint64Metric struct {
@@ -165,8 +176,8 @@ func RegisterCustomUint64Metric(name string, cumulative, sync bool, units pb.Met
}
// Metrics can exist without fields.
- if len(fields) > 1 {
- panic("Sentry metrics support at most one field")
+ if l := len(fields); l > 1 {
+ return fmt.Errorf("%d fields provided, must be <= 1", l)
}
for _, field := range fields {
@@ -182,7 +193,7 @@ func RegisterCustomUint64Metric(name string, cumulative, sync bool, units pb.Met
// without fields and panics if it returns an error.
func MustRegisterCustomUint64Metric(name string, cumulative, sync bool, description string, value func(...string) uint64, fields ...Field) {
if err := RegisterCustomUint64Metric(name, cumulative, sync, pb.MetricMetadata_UNITS_NONE, description, value, fields...); err != nil {
- panic(fmt.Sprintf("Unable to register metric %q: %v", name, err))
+ panic(fmt.Sprintf("Unable to register metric %q: %s", name, err))
}
}
@@ -209,7 +220,7 @@ func NewUint64Metric(name string, sync bool, units pb.MetricMetadata_Units, desc
func MustCreateNewUint64Metric(name string, sync bool, description string, fields ...Field) *Uint64Metric {
m, err := NewUint64Metric(name, sync, pb.MetricMetadata_UNITS_NONE, description, fields...)
if err != nil {
- panic(fmt.Sprintf("Unable to create metric %q: %v", name, err))
+ panic(fmt.Sprintf("Unable to create metric %q: %s", name, err))
}
return m
}
@@ -219,7 +230,7 @@ func MustCreateNewUint64Metric(name string, sync bool, description string, field
func MustCreateNewUint64NanosecondsMetric(name string, sync bool, description string) *Uint64Metric {
m, err := NewUint64Metric(name, sync, pb.MetricMetadata_UNITS_NANOSECONDS, description)
if err != nil {
- panic(fmt.Sprintf("Unable to create metric %q: %v", name, err))
+ panic(fmt.Sprintf("Unable to create metric %q: %s", name, err))
}
return m
}
@@ -354,7 +365,7 @@ func EmitMetricUpdate() {
m.Metrics = append(m.Metrics, &pb.MetricValue{
Name: k,
- Value: &pb.MetricValue_Uint64Value{t},
+ Value: &pb.MetricValue_Uint64Value{Uint64Value: t},
})
case map[string]uint64:
for fieldValue, metricValue := range t {
@@ -369,7 +380,7 @@ func EmitMetricUpdate() {
m.Metrics = append(m.Metrics, &pb.MetricValue{
Name: k,
FieldValues: []string{fieldValue},
- Value: &pb.MetricValue_Uint64Value{metricValue},
+ Value: &pb.MetricValue_Uint64Value{Uint64Value: metricValue},
})
}
}
@@ -390,26 +401,7 @@ func EmitMetricUpdate() {
}
}
- eventchannel.Emit(&m)
-}
-
-// CreateSentryMetrics creates the sentry metrics during kernel initialization.
-func CreateSentryMetrics() {
- if createdSentryMetrics {
- return
+ if err := eventchannel.Emit(&m); err != nil {
+ log.Warningf("Unable to emit metrics: %s", err)
}
-
- createdSentryMetrics = true
-
- WeirdnessMetric = MustCreateNewUint64Metric("/weirdness", true /* sync */, "Increment for weird occurrences of problems such as time fallback, partial result, vsyscalls invoked in the sandbox, watchdog startup timeouts and stuck tasks.",
- Field{
- name: "weirdness_type",
- allowedValues: []string{"time_fallback", "partial_result", "vsyscall_count", "watchdog_stuck_startup", "watchdog_stuck_tasks"},
- })
-
- SuspiciousOperationsMetric = MustCreateNewUint64Metric("/suspicious_operations", true /* sync */, "Increment for suspicious operations such as opening an executable file to write from a gofer.",
- Field{
- name: "operation_type",
- allowedValues: []string{"opened_write_execute_file"},
- })
}
diff --git a/pkg/metric/metric_test.go b/pkg/metric/metric_test.go
index c71dfd460..1b4a9e73a 100644
--- a/pkg/metric/metric_test.go
+++ b/pkg/metric/metric_test.go
@@ -48,6 +48,8 @@ func (s *sliceEmitter) Reset() {
var emitter sliceEmitter
func init() {
+ reset()
+
eventchannel.AddEmitter(&emitter)
}
@@ -77,7 +79,9 @@ func TestInitialize(t *testing.T) {
t.Fatalf("NewUint64Metric got err %v want nil", err)
}
- Initialize()
+ if err := Initialize(); err != nil {
+ t.Fatalf("Initialize(): %s", err)
+ }
if len(emitter) != 1 {
t.Fatalf("Initialize emitted %d events want 1", len(emitter))
@@ -149,7 +153,9 @@ func TestDisable(t *testing.T) {
t.Fatalf("NewUint64Metric got err %v want nil", err)
}
- Disable()
+ if err := Disable(); err != nil {
+ t.Fatalf("Disable(): %s", err)
+ }
if len(emitter) != 1 {
t.Fatalf("Initialize emitted %d events want 1", len(emitter))
@@ -178,7 +184,9 @@ func TestEmitMetricUpdate(t *testing.T) {
t.Fatalf("NewUint64Metric got err %v want nil", err)
}
- Initialize()
+ if err := Initialize(); err != nil {
+ t.Fatalf("Initialize(): %s", err)
+ }
// Don't care about the registration metrics.
emitter.Reset()
@@ -270,7 +278,9 @@ func TestEmitMetricUpdateWithFields(t *testing.T) {
t.Fatalf("NewUint64Metric got err %v want nil", err)
}
- Initialize()
+ if err := Initialize(); err != nil {
+ t.Fatalf("Initialize(): %s", err)
+ }
// Don't care about the registration metrics.
emitter.Reset()
diff --git a/pkg/p9/BUILD b/pkg/p9/BUILD
index b2291ef97..2b22b2203 100644
--- a/pkg/p9/BUILD
+++ b/pkg/p9/BUILD
@@ -22,6 +22,9 @@ go_library(
"version.go",
],
deps = [
+ "//pkg/abi/linux/errno",
+ "//pkg/errors",
+ "//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdchannel",
"//pkg/flipcall",
diff --git a/pkg/p9/handlers.go b/pkg/p9/handlers.go
index 758e11b13..161b451cc 100644
--- a/pkg/p9/handlers.go
+++ b/pkg/p9/handlers.go
@@ -23,6 +23,9 @@ import (
"sync/atomic"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
+ "gvisor.dev/gvisor/pkg/errors"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
)
@@ -62,6 +65,45 @@ func newErr(err error) *Rlerror {
return &Rlerror{Error: uint32(ExtractErrno(err))}
}
+// ExtractLinuxerrErrno extracts a *errors.Error from a error, best effort.
+// TODO(b/34162363): Merge this with ExtractErrno.
+func ExtractLinuxerrErrno(err error) *errors.Error {
+ switch err {
+ case os.ErrNotExist:
+ return linuxerr.ENOENT
+ case os.ErrExist:
+ return linuxerr.EEXIST
+ case os.ErrPermission:
+ return linuxerr.EACCES
+ case os.ErrInvalid:
+ return linuxerr.EINVAL
+ }
+
+ // Attempt to unwrap.
+ switch e := err.(type) {
+ case *errors.Error:
+ return e
+ case unix.Errno:
+ return linuxerr.ErrorFromErrno(errno.Errno(e))
+ case *os.PathError:
+ return ExtractLinuxerrErrno(e.Err)
+ case *os.SyscallError:
+ return ExtractLinuxerrErrno(e.Err)
+ case *os.LinkError:
+ return ExtractLinuxerrErrno(e.Err)
+ }
+
+ // Default case.
+ log.Warningf("unknown error: %v", err)
+ return linuxerr.EIO
+}
+
+// newErrFromLinuxerr returns an Rlerror from the linuxerr list.
+// TODO(b/34162363): Merge this with newErr.
+func newErrFromLinuxerr(err error) *Rlerror {
+ return &Rlerror{Error: uint32(ExtractLinuxerrErrno(err).Errno())}
+}
+
// handler is implemented for server-handled messages.
//
// See server.go for call information.
diff --git a/pkg/p9/server.go b/pkg/p9/server.go
index ff1172ed6..241ab44ef 100644
--- a/pkg/p9/server.go
+++ b/pkg/p9/server.go
@@ -19,7 +19,8 @@ import (
"runtime/debug"
"sync/atomic"
- "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/fdchannel"
"gvisor.dev/gvisor/pkg/flipcall"
@@ -483,7 +484,7 @@ func (cs *connState) lookupChannel(id uint32) *channel {
func (cs *connState) handle(m message) (r message) {
if !cs.reqGate.Enter() {
// connState.stop() has been called; the connection is shutting down.
- r = newErr(unix.ECONNRESET)
+ r = newErrFromLinuxerr(linuxerr.ECONNRESET)
return
}
defer func() {
@@ -498,15 +499,23 @@ func (cs *connState) handle(m message) (r message) {
// Wrap in an EFAULT error; we don't really have a
// better way to describe this kind of error. It will
// usually manifest as a result of the test framework.
- r = newErr(unix.EFAULT)
+ r = newErrFromLinuxerr(linuxerr.EFAULT)
}
}()
if handler, ok := m.(handler); ok {
// Call the message handler.
r = handler.handle(cs)
+ // TODO(b/34162363):This is only here to make sure the server works with
+ // only linuxerr Errors, as the handlers work with both client and server.
+ // It will be removed a followup, when all the unix.Errno errors are
+ // replaced with linuxerr.
+ if rlError, ok := r.(*Rlerror); ok {
+ e := linuxerr.ErrorFromErrno(errno.Errno(rlError.Error))
+ r = newErrFromLinuxerr(e)
+ }
} else {
// Produce an ENOSYS error.
- r = newErr(unix.ENOSYS)
+ r = newErrFromLinuxerr(linuxerr.ENOSYS)
}
return
}
@@ -553,7 +562,7 @@ func (cs *connState) handleRequest() bool {
// If it's not a connection error, but some other protocol error,
// we can send a response immediately.
cs.sendMu.Lock()
- err := send(cs.conn, tag, newErr(err))
+ err := send(cs.conn, tag, newErrFromLinuxerr(err))
cs.sendMu.Unlock()
if err != nil {
log.Debugf("p9.send: %v", err)
diff --git a/pkg/refs/refcounter.go b/pkg/refs/refcounter.go
index 4aecb8007..1bbcae045 100644
--- a/pkg/refs/refcounter.go
+++ b/pkg/refs/refcounter.go
@@ -261,8 +261,8 @@ func (l *LeakMode) Get() interface{} {
}
// String implements flag.Value.
-func (l *LeakMode) String() string {
- switch *l {
+func (l LeakMode) String() string {
+ switch l {
case UninitializedLeakChecking:
return "uninitialized"
case NoLeakChecking:
@@ -272,7 +272,7 @@ func (l *LeakMode) String() string {
case LeaksLogTraces:
return "log-traces"
}
- panic(fmt.Sprintf("invalid ref leak mode %d", *l))
+ panic(fmt.Sprintf("invalid ref leak mode %d", l))
}
// leakMode stores the current mode for the reference leak checker.
diff --git a/pkg/ring0/kernel_amd64.go b/pkg/ring0/kernel_amd64.go
index 41dfd0bf9..f63af8b76 100644
--- a/pkg/ring0/kernel_amd64.go
+++ b/pkg/ring0/kernel_amd64.go
@@ -254,6 +254,8 @@ func (c *CPU) SwitchToUser(switchOpts SwitchOpts) (vector Vector) {
return
}
+var sentryXCR0 = xgetbv(0)
+
// start is the CPU entrypoint.
//
// This is called from the Start asm stub (see entry_amd64.go); on return the
@@ -265,24 +267,10 @@ func start(c *CPU) {
WriteGS(kernelAddr(c.kernelEntry))
WriteFS(uintptr(c.registers.Fs_base))
- // Initialize floating point.
- //
- // Note that on skylake, the valid XCR0 mask reported seems to be 0xff.
- // This breaks down as:
- //
- // bit0 - x87
- // bit1 - SSE
- // bit2 - AVX
- // bit3-4 - MPX
- // bit5-7 - AVX512
- //
- // For some reason, enabled MPX & AVX512 on platforms that report them
- // seems to be cause a general protection fault. (Maybe there are some
- // virtualization issues and these aren't exported to the guest cpuid.)
- // This needs further investigation, but we can limit the floating
- // point operations to x87, SSE & AVX for now.
fninit()
- xsetbv(0, validXCR0Mask&0x7)
+ // Need to sync XCR0 with the host, because xsave and xrstor can be
+ // called from different contexts.
+ xsetbv(0, sentryXCR0)
// Set the syscall target.
wrmsr(_MSR_LSTAR, kernelFunc(sysenter))
diff --git a/pkg/safecopy/BUILD b/pkg/safecopy/BUILD
index b77c40279..db5787302 100644
--- a/pkg/safecopy/BUILD
+++ b/pkg/safecopy/BUILD
@@ -18,6 +18,7 @@ go_library(
],
visibility = ["//:sandbox"],
deps = [
+ "//pkg/abi/linux",
"//pkg/syserror",
"@org_golang_x_sys//unix:go_default_library",
],
diff --git a/pkg/safecopy/safecopy_unsafe.go b/pkg/safecopy/safecopy_unsafe.go
index efbc2ddc1..2365b2c0d 100644
--- a/pkg/safecopy/safecopy_unsafe.go
+++ b/pkg/safecopy/safecopy_unsafe.go
@@ -20,6 +20,7 @@ import (
"unsafe"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux"
)
// maxRegisterSize is the maximum register size used in memcpy and memclr. It
@@ -342,12 +343,7 @@ func errorFromFaultSignal(addr uintptr, sig int32) error {
// handler however, and if this is function is being used externally then the
// same courtesy is expected.
func ReplaceSignalHandler(sig unix.Signal, handler uintptr, previous *uintptr) error {
- var sa struct {
- handler uintptr
- flags uint64
- restorer uintptr
- mask uint64
- }
+ var sa linux.SigAction
const maskLen = 8
// Get the existing signal handler information, and save the current
@@ -358,14 +354,14 @@ func ReplaceSignalHandler(sig unix.Signal, handler uintptr, previous *uintptr) e
}
// Fail if there isn't a previous handler.
- if sa.handler == 0 {
+ if sa.Handler == 0 {
return fmt.Errorf("previous handler for signal %x isn't set", sig)
}
- *previous = sa.handler
+ *previous = uintptr(sa.Handler)
// Install our own handler.
- sa.handler = handler
+ sa.Handler = uint64(handler)
if _, _, e := unix.RawSyscall6(unix.SYS_RT_SIGACTION, uintptr(sig), uintptr(unsafe.Pointer(&sa)), 0, maskLen, 0, 0); e != 0 {
return e
}
diff --git a/pkg/seccomp/seccomp.go b/pkg/seccomp/seccomp.go
index daea51c4d..8ffa1db37 100644
--- a/pkg/seccomp/seccomp.go
+++ b/pkg/seccomp/seccomp.go
@@ -36,14 +36,10 @@ const (
// Install generates BPF code based on the set of syscalls provided. It only
// allows syscalls that conform to the specification. Syscalls that violate the
-// specification will trigger RET_KILL_PROCESS, except for the cases below.
-//
-// RET_TRAP is used in violations, instead of RET_KILL_PROCESS, in the
-// following cases:
-// 1. Kernel doesn't support RET_KILL_PROCESS: RET_KILL_THREAD only kills the
-// offending thread and often keeps the sentry hanging.
-// 2. Debug: RET_TRAP generates a panic followed by a stack trace which is
-// much easier to debug then RET_KILL_PROCESS which can't be caught.
+// specification will trigger RET_KILL_PROCESS. If RET_KILL_PROCESS is not
+// supported, violations will trigger RET_TRAP instead. RET_KILL_THREAD is not
+// used because it only kills the offending thread and often keeps the sentry
+// hanging.
//
// Be aware that RET_TRAP sends SIGSYS to the process and it may be ignored,
// making it possible for the process to continue running after a violation.
diff --git a/pkg/sentry/arch/BUILD b/pkg/sentry/arch/BUILD
index c9c52530d..068a0c8d9 100644
--- a/pkg/sentry/arch/BUILD
+++ b/pkg/sentry/arch/BUILD
@@ -14,12 +14,8 @@ go_library(
"arch_x86.go",
"arch_x86_impl.go",
"auxv.go",
- "signal.go",
- "signal_act.go",
"signal_amd64.go",
"signal_arm64.go",
- "signal_info.go",
- "signal_stack.go",
"stack.go",
"stack_unsafe.go",
"syscalls_amd64.go",
@@ -32,6 +28,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/context",
"//pkg/cpuid",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/marshal",
diff --git a/pkg/sentry/arch/arch.go b/pkg/sentry/arch/arch.go
index 290863ee6..c9393b091 100644
--- a/pkg/sentry/arch/arch.go
+++ b/pkg/sentry/arch/arch.go
@@ -134,21 +134,13 @@ type Context interface {
// RegisterMap returns a map of all registers.
RegisterMap() (map[string]uintptr, error)
- // NewSignalAct returns a new object that is equivalent to struct sigaction
- // in the guest architecture.
- NewSignalAct() NativeSignalAct
-
- // NewSignalStack returns a new object that is equivalent to stack_t in the
- // guest architecture.
- NewSignalStack() NativeSignalStack
-
// SignalSetup modifies the context in preparation for handling the
// given signal.
//
// st is the stack where the signal handler frame should be
// constructed.
//
- // act is the SignalAct that specifies how this signal is being
+ // act is the SigAction that specifies how this signal is being
// handled.
//
// info is the SignalInfo of the signal being delivered.
@@ -157,7 +149,7 @@ type Context interface {
// stack is not going to be used).
//
// sigset is the signal mask before entering the signal handler.
- SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt *SignalStack, sigset linux.SignalSet) error
+ SignalSetup(st *Stack, act *linux.SigAction, info *linux.SignalInfo, alt *linux.SignalStack, sigset linux.SignalSet) error
// SignalRestore restores context after returning from a signal
// handler.
@@ -167,7 +159,7 @@ type Context interface {
// rt is true if SignalRestore is being entered from rt_sigreturn and
// false if SignalRestore is being entered from sigreturn.
// SignalRestore returns the thread's new signal mask.
- SignalRestore(st *Stack, rt bool) (linux.SignalSet, SignalStack, error)
+ SignalRestore(st *Stack, rt bool) (linux.SignalSet, linux.SignalStack, error)
// CPUIDEmulate emulates a CPUID instruction according to current register state.
CPUIDEmulate(l log.Logger)
diff --git a/pkg/sentry/arch/arch_aarch64.go b/pkg/sentry/arch/arch_aarch64.go
index 08789f517..7def71fef 100644
--- a/pkg/sentry/arch/arch_aarch64.go
+++ b/pkg/sentry/arch/arch_aarch64.go
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cpuid"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
rpb "gvisor.dev/gvisor/pkg/sentry/arch/registers_go_proto"
@@ -237,7 +238,7 @@ func (s *State) PtraceGetRegSet(regset uintptr, dst io.Writer, maxlen int) (int,
}
return s.PtraceGetRegs(dst)
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
@@ -250,7 +251,7 @@ func (s *State) PtraceSetRegSet(regset uintptr, src io.Reader, maxlen int) (int,
}
return s.PtraceSetRegs(src)
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/arch/arch_x86.go b/pkg/sentry/arch/arch_x86.go
index e8e52d3a8..d13e12f8c 100644
--- a/pkg/sentry/arch/arch_x86.go
+++ b/pkg/sentry/arch/arch_x86.go
@@ -23,6 +23,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cpuid"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
rpb "gvisor.dev/gvisor/pkg/sentry/arch/registers_go_proto"
@@ -361,7 +362,7 @@ func (s *State) PtraceGetRegSet(regset uintptr, dst io.Writer, maxlen int) (int,
case _NT_X86_XSTATE:
return s.fpState.PtraceGetXstateRegs(dst, maxlen, s.FeatureSet)
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
@@ -378,7 +379,7 @@ func (s *State) PtraceSetRegSet(regset uintptr, src io.Reader, maxlen int) (int,
case _NT_X86_XSTATE:
return s.fpState.PtraceSetXstateRegs(src, maxlen, s.FeatureSet)
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/arch/signal.go b/pkg/sentry/arch/signal.go
deleted file mode 100644
index 67d7edf68..000000000
--- a/pkg/sentry/arch/signal.go
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright 2020 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 arch
-
-import (
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/hostarch"
-)
-
-// SignalAct represents the action that should be taken when a signal is
-// delivered, and is equivalent to struct sigaction.
-//
-// +marshal
-// +stateify savable
-type SignalAct struct {
- Handler uint64
- Flags uint64
- Restorer uint64 // Only used on amd64.
- Mask linux.SignalSet
-}
-
-// SerializeFrom implements NativeSignalAct.SerializeFrom.
-func (s *SignalAct) SerializeFrom(other *SignalAct) {
- *s = *other
-}
-
-// DeserializeTo implements NativeSignalAct.DeserializeTo.
-func (s *SignalAct) DeserializeTo(other *SignalAct) {
- *other = *s
-}
-
-// SignalStack represents information about a user stack, and is equivalent to
-// stack_t.
-//
-// +marshal
-// +stateify savable
-type SignalStack struct {
- Addr uint64
- Flags uint32
- _ uint32
- Size uint64
-}
-
-// SerializeFrom implements NativeSignalStack.SerializeFrom.
-func (s *SignalStack) SerializeFrom(other *SignalStack) {
- *s = *other
-}
-
-// DeserializeTo implements NativeSignalStack.DeserializeTo.
-func (s *SignalStack) DeserializeTo(other *SignalStack) {
- *other = *s
-}
-
-// SignalInfo represents information about a signal being delivered, and is
-// equivalent to struct siginfo in linux kernel(linux/include/uapi/asm-generic/siginfo.h).
-//
-// +marshal
-// +stateify savable
-type SignalInfo struct {
- Signo int32 // Signal number
- Errno int32 // Errno value
- Code int32 // Signal code
- _ uint32
-
- // struct siginfo::_sifields is a union. In SignalInfo, fields in the union
- // are accessed through methods.
- //
- // For reference, here is the definition of _sifields: (_sigfault._trapno,
- // which does not exist on x86, omitted for clarity)
- //
- // union {
- // int _pad[SI_PAD_SIZE];
- //
- // /* kill() */
- // struct {
- // __kernel_pid_t _pid; /* sender's pid */
- // __ARCH_SI_UID_T _uid; /* sender's uid */
- // } _kill;
- //
- // /* POSIX.1b timers */
- // struct {
- // __kernel_timer_t _tid; /* timer id */
- // int _overrun; /* overrun count */
- // char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
- // sigval_t _sigval; /* same as below */
- // int _sys_private; /* not to be passed to user */
- // } _timer;
- //
- // /* POSIX.1b signals */
- // struct {
- // __kernel_pid_t _pid; /* sender's pid */
- // __ARCH_SI_UID_T _uid; /* sender's uid */
- // sigval_t _sigval;
- // } _rt;
- //
- // /* SIGCHLD */
- // struct {
- // __kernel_pid_t _pid; /* which child */
- // __ARCH_SI_UID_T _uid; /* sender's uid */
- // int _status; /* exit code */
- // __ARCH_SI_CLOCK_T _utime;
- // __ARCH_SI_CLOCK_T _stime;
- // } _sigchld;
- //
- // /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
- // struct {
- // void *_addr; /* faulting insn/memory ref. */
- // short _addr_lsb; /* LSB of the reported address */
- // } _sigfault;
- //
- // /* SIGPOLL */
- // struct {
- // __ARCH_SI_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */
- // int _fd;
- // } _sigpoll;
- //
- // /* SIGSYS */
- // struct {
- // void *_call_addr; /* calling user insn */
- // int _syscall; /* triggering system call number */
- // unsigned int _arch; /* AUDIT_ARCH_* of syscall */
- // } _sigsys;
- // } _sifields;
- //
- // _sifields is padded so that the size of siginfo is SI_MAX_SIZE = 128
- // bytes.
- Fields [128 - 16]byte
-}
-
-// FixSignalCodeForUser fixes up si_code.
-//
-// The si_code we get from Linux may contain the kernel-specific code in the
-// top 16 bits if it's positive (e.g., from ptrace). Linux's
-// copy_siginfo_to_user does
-// err |= __put_user((short)from->si_code, &to->si_code);
-// to mask out those bits and we need to do the same.
-func (s *SignalInfo) FixSignalCodeForUser() {
- if s.Code > 0 {
- s.Code &= 0x0000ffff
- }
-}
-
-// PID returns the si_pid field.
-func (s *SignalInfo) PID() int32 {
- return int32(hostarch.ByteOrder.Uint32(s.Fields[0:4]))
-}
-
-// SetPID mutates the si_pid field.
-func (s *SignalInfo) SetPID(val int32) {
- hostarch.ByteOrder.PutUint32(s.Fields[0:4], uint32(val))
-}
-
-// UID returns the si_uid field.
-func (s *SignalInfo) UID() int32 {
- return int32(hostarch.ByteOrder.Uint32(s.Fields[4:8]))
-}
-
-// SetUID mutates the si_uid field.
-func (s *SignalInfo) SetUID(val int32) {
- hostarch.ByteOrder.PutUint32(s.Fields[4:8], uint32(val))
-}
-
-// Sigval returns the sigval field, which is aliased to both si_int and si_ptr.
-func (s *SignalInfo) Sigval() uint64 {
- return hostarch.ByteOrder.Uint64(s.Fields[8:16])
-}
-
-// SetSigval mutates the sigval field.
-func (s *SignalInfo) SetSigval(val uint64) {
- hostarch.ByteOrder.PutUint64(s.Fields[8:16], val)
-}
-
-// TimerID returns the si_timerid field.
-func (s *SignalInfo) TimerID() linux.TimerID {
- return linux.TimerID(hostarch.ByteOrder.Uint32(s.Fields[0:4]))
-}
-
-// SetTimerID sets the si_timerid field.
-func (s *SignalInfo) SetTimerID(val linux.TimerID) {
- hostarch.ByteOrder.PutUint32(s.Fields[0:4], uint32(val))
-}
-
-// Overrun returns the si_overrun field.
-func (s *SignalInfo) Overrun() int32 {
- return int32(hostarch.ByteOrder.Uint32(s.Fields[4:8]))
-}
-
-// SetOverrun sets the si_overrun field.
-func (s *SignalInfo) SetOverrun(val int32) {
- hostarch.ByteOrder.PutUint32(s.Fields[4:8], uint32(val))
-}
-
-// Addr returns the si_addr field.
-func (s *SignalInfo) Addr() uint64 {
- return hostarch.ByteOrder.Uint64(s.Fields[0:8])
-}
-
-// SetAddr sets the si_addr field.
-func (s *SignalInfo) SetAddr(val uint64) {
- hostarch.ByteOrder.PutUint64(s.Fields[0:8], val)
-}
-
-// Status returns the si_status field.
-func (s *SignalInfo) Status() int32 {
- return int32(hostarch.ByteOrder.Uint32(s.Fields[8:12]))
-}
-
-// SetStatus mutates the si_status field.
-func (s *SignalInfo) SetStatus(val int32) {
- hostarch.ByteOrder.PutUint32(s.Fields[8:12], uint32(val))
-}
-
-// CallAddr returns the si_call_addr field.
-func (s *SignalInfo) CallAddr() uint64 {
- return hostarch.ByteOrder.Uint64(s.Fields[0:8])
-}
-
-// SetCallAddr mutates the si_call_addr field.
-func (s *SignalInfo) SetCallAddr(val uint64) {
- hostarch.ByteOrder.PutUint64(s.Fields[0:8], val)
-}
-
-// Syscall returns the si_syscall field.
-func (s *SignalInfo) Syscall() int32 {
- return int32(hostarch.ByteOrder.Uint32(s.Fields[8:12]))
-}
-
-// SetSyscall mutates the si_syscall field.
-func (s *SignalInfo) SetSyscall(val int32) {
- hostarch.ByteOrder.PutUint32(s.Fields[8:12], uint32(val))
-}
-
-// Arch returns the si_arch field.
-func (s *SignalInfo) Arch() uint32 {
- return hostarch.ByteOrder.Uint32(s.Fields[12:16])
-}
-
-// SetArch mutates the si_arch field.
-func (s *SignalInfo) SetArch(val uint32) {
- hostarch.ByteOrder.PutUint32(s.Fields[12:16], val)
-}
-
-// Band returns the si_band field.
-func (s *SignalInfo) Band() int64 {
- return int64(hostarch.ByteOrder.Uint64(s.Fields[0:8]))
-}
-
-// SetBand mutates the si_band field.
-func (s *SignalInfo) SetBand(val int64) {
- // Note: this assumes the platform uses `long` as `__ARCH_SI_BAND_T`.
- // On some platforms, which gVisor doesn't support, `__ARCH_SI_BAND_T` is
- // `int`. See siginfo.h.
- hostarch.ByteOrder.PutUint64(s.Fields[0:8], uint64(val))
-}
-
-// FD returns the si_fd field.
-func (s *SignalInfo) FD() uint32 {
- return hostarch.ByteOrder.Uint32(s.Fields[8:12])
-}
-
-// SetFD mutates the si_fd field.
-func (s *SignalInfo) SetFD(val uint32) {
- hostarch.ByteOrder.PutUint32(s.Fields[8:12], val)
-}
diff --git a/pkg/sentry/arch/signal_act.go b/pkg/sentry/arch/signal_act.go
deleted file mode 100644
index d3e2324a8..000000000
--- a/pkg/sentry/arch/signal_act.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// 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 arch
-
-import "gvisor.dev/gvisor/pkg/marshal"
-
-// Special values for SignalAct.Handler.
-const (
- // SignalActDefault is SIG_DFL and specifies that the default behavior for
- // a signal should be taken.
- SignalActDefault = 0
-
- // SignalActIgnore is SIG_IGN and specifies that a signal should be
- // ignored.
- SignalActIgnore = 1
-)
-
-// Available signal flags.
-const (
- SignalFlagNoCldStop = 0x00000001
- SignalFlagNoCldWait = 0x00000002
- SignalFlagSigInfo = 0x00000004
- SignalFlagRestorer = 0x04000000
- SignalFlagOnStack = 0x08000000
- SignalFlagRestart = 0x10000000
- SignalFlagInterrupt = 0x20000000
- SignalFlagNoDefer = 0x40000000
- SignalFlagResetHandler = 0x80000000
-)
-
-// IsSigInfo returns true iff this handle expects siginfo.
-func (s SignalAct) IsSigInfo() bool {
- return s.Flags&SignalFlagSigInfo != 0
-}
-
-// IsNoDefer returns true iff this SignalAct has the NoDefer flag set.
-func (s SignalAct) IsNoDefer() bool {
- return s.Flags&SignalFlagNoDefer != 0
-}
-
-// IsRestart returns true iff this SignalAct has the Restart flag set.
-func (s SignalAct) IsRestart() bool {
- return s.Flags&SignalFlagRestart != 0
-}
-
-// IsResetHandler returns true iff this SignalAct has the ResetHandler flag set.
-func (s SignalAct) IsResetHandler() bool {
- return s.Flags&SignalFlagResetHandler != 0
-}
-
-// IsOnStack returns true iff this SignalAct has the OnStack flag set.
-func (s SignalAct) IsOnStack() bool {
- return s.Flags&SignalFlagOnStack != 0
-}
-
-// HasRestorer returns true iff this SignalAct has the Restorer flag set.
-func (s SignalAct) HasRestorer() bool {
- return s.Flags&SignalFlagRestorer != 0
-}
-
-// NativeSignalAct is a type that is equivalent to struct sigaction in the
-// guest architecture.
-type NativeSignalAct interface {
- marshal.Marshallable
-
- // SerializeFrom copies the data in the host SignalAct s into this object.
- SerializeFrom(s *SignalAct)
-
- // DeserializeTo copies the data in this object into the host SignalAct s.
- DeserializeTo(s *SignalAct)
-}
diff --git a/pkg/sentry/arch/signal_amd64.go b/pkg/sentry/arch/signal_amd64.go
index 082ed92b1..58e28dbba 100644
--- a/pkg/sentry/arch/signal_amd64.go
+++ b/pkg/sentry/arch/signal_amd64.go
@@ -76,21 +76,11 @@ const (
type UContext64 struct {
Flags uint64
Link uint64
- Stack SignalStack
+ Stack linux.SignalStack
MContext SignalContext64
Sigset linux.SignalSet
}
-// NewSignalAct implements Context.NewSignalAct.
-func (c *context64) NewSignalAct() NativeSignalAct {
- return &SignalAct{}
-}
-
-// NewSignalStack implements Context.NewSignalStack.
-func (c *context64) NewSignalStack() NativeSignalStack {
- return &SignalStack{}
-}
-
// From Linux 'arch/x86/include/uapi/asm/sigcontext.h' the following is the
// size of the magic cookie at the end of the xsave frame.
//
@@ -110,7 +100,7 @@ func (c *context64) fpuFrameSize() (size int, useXsave bool) {
// SignalSetup implements Context.SignalSetup. (Compare to Linux's
// arch/x86/kernel/signal.c:__setup_rt_frame().)
-func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt *SignalStack, sigset linux.SignalSet) error {
+func (c *context64) SignalSetup(st *Stack, act *linux.SigAction, info *linux.SignalInfo, alt *linux.SignalStack, sigset linux.SignalSet) error {
sp := st.Bottom
// "The 128-byte area beyond the location pointed to by %rsp is considered
@@ -187,7 +177,7 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
// Prior to proceeding, figure out if the frame will exhaust the range
// for the signal stack. This is not allowed, and should immediately
// force signal delivery (reverting to the default handler).
- if act.IsOnStack() && alt.IsEnabled() && !alt.Contains(frameBottom) {
+ if act.Flags&linux.SA_ONSTACK != 0 && alt.IsEnabled() && !alt.Contains(frameBottom) {
return unix.EFAULT
}
@@ -203,7 +193,7 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
return err
}
ucAddr := st.Bottom
- if act.HasRestorer() {
+ if act.Flags&linux.SA_RESTORER != 0 {
// Push the restorer return address.
// Note that this doesn't need to be popped.
if _, err := primitive.CopyUint64Out(st, StackBottomMagic, act.Restorer); err != nil {
@@ -237,15 +227,15 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
// SignalRestore implements Context.SignalRestore. (Compare to Linux's
// arch/x86/kernel/signal.c:sys_rt_sigreturn().)
-func (c *context64) SignalRestore(st *Stack, rt bool) (linux.SignalSet, SignalStack, error) {
+func (c *context64) SignalRestore(st *Stack, rt bool) (linux.SignalSet, linux.SignalStack, error) {
// Copy out the stack frame.
var uc UContext64
if _, err := uc.CopyIn(st, StackBottomMagic); err != nil {
- return 0, SignalStack{}, err
+ return 0, linux.SignalStack{}, err
}
- var info SignalInfo
+ var info linux.SignalInfo
if _, err := info.CopyIn(st, StackBottomMagic); err != nil {
- return 0, SignalStack{}, err
+ return 0, linux.SignalStack{}, err
}
// Restore registers.
diff --git a/pkg/sentry/arch/signal_arm64.go b/pkg/sentry/arch/signal_arm64.go
index da71fb873..80df90076 100644
--- a/pkg/sentry/arch/signal_arm64.go
+++ b/pkg/sentry/arch/signal_arm64.go
@@ -61,7 +61,7 @@ type FpsimdContext struct {
type UContext64 struct {
Flags uint64
Link uint64
- Stack SignalStack
+ Stack linux.SignalStack
Sigset linux.SignalSet
// glibc uses a 1024-bit sigset_t
_pad [120]byte // (1024 - 64) / 8 = 120
@@ -71,18 +71,8 @@ type UContext64 struct {
MContext SignalContext64
}
-// NewSignalAct implements Context.NewSignalAct.
-func (c *context64) NewSignalAct() NativeSignalAct {
- return &SignalAct{}
-}
-
-// NewSignalStack implements Context.NewSignalStack.
-func (c *context64) NewSignalStack() NativeSignalStack {
- return &SignalStack{}
-}
-
// SignalSetup implements Context.SignalSetup.
-func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt *SignalStack, sigset linux.SignalSet) error {
+func (c *context64) SignalSetup(st *Stack, act *linux.SigAction, info *linux.SignalInfo, alt *linux.SignalStack, sigset linux.SignalSet) error {
sp := st.Bottom
// Construct the UContext64 now since we need its size.
@@ -114,7 +104,7 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
// Prior to proceeding, figure out if the frame will exhaust the range
// for the signal stack. This is not allowed, and should immediately
// force signal delivery (reverting to the default handler).
- if act.IsOnStack() && alt.IsEnabled() && !alt.Contains(frameBottom) {
+ if act.Flags&linux.SA_ONSTACK != 0 && alt.IsEnabled() && !alt.Contains(frameBottom) {
return unix.EFAULT
}
@@ -137,7 +127,7 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
c.Regs.Regs[0] = uint64(info.Signo)
c.Regs.Regs[1] = uint64(infoAddr)
c.Regs.Regs[2] = uint64(ucAddr)
- c.Regs.Regs[30] = uint64(act.Restorer)
+ c.Regs.Regs[30] = act.Restorer
// Save the thread's floating point state.
c.sigFPState = append(c.sigFPState, c.fpState)
@@ -147,15 +137,15 @@ func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt
}
// SignalRestore implements Context.SignalRestore.
-func (c *context64) SignalRestore(st *Stack, rt bool) (linux.SignalSet, SignalStack, error) {
+func (c *context64) SignalRestore(st *Stack, rt bool) (linux.SignalSet, linux.SignalStack, error) {
// Copy out the stack frame.
var uc UContext64
if _, err := uc.CopyIn(st, StackBottomMagic); err != nil {
- return 0, SignalStack{}, err
+ return 0, linux.SignalStack{}, err
}
- var info SignalInfo
+ var info linux.SignalInfo
if _, err := info.CopyIn(st, StackBottomMagic); err != nil {
- return 0, SignalStack{}, err
+ return 0, linux.SignalStack{}, err
}
// Restore registers.
diff --git a/pkg/sentry/arch/signal_info.go b/pkg/sentry/arch/signal_info.go
deleted file mode 100644
index f93ee8b46..000000000
--- a/pkg/sentry/arch/signal_info.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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 arch
-
-// Possible values for SignalInfo.Code. These values originate from the Linux
-// kernel's include/uapi/asm-generic/siginfo.h.
-const (
- // SignalInfoUser (properly SI_USER) indicates that a signal was sent from
- // a kill() or raise() syscall.
- SignalInfoUser = 0
-
- // SignalInfoKernel (properly SI_KERNEL) indicates that the signal was sent
- // by the kernel.
- SignalInfoKernel = 0x80
-
- // SignalInfoTimer (properly SI_TIMER) indicates that the signal was sent
- // by an expired timer.
- SignalInfoTimer = -2
-
- // SignalInfoTkill (properly SI_TKILL) indicates that the signal was sent
- // from a tkill() or tgkill() syscall.
- SignalInfoTkill = -6
-
- // CLD_* codes are only meaningful for SIGCHLD.
-
- // CLD_EXITED indicates that a task exited.
- CLD_EXITED = 1
-
- // CLD_KILLED indicates that a task was killed by a signal.
- CLD_KILLED = 2
-
- // CLD_DUMPED indicates that a task was killed by a signal and then dumped
- // core.
- CLD_DUMPED = 3
-
- // CLD_TRAPPED indicates that a task was stopped by ptrace.
- CLD_TRAPPED = 4
-
- // CLD_STOPPED indicates that a thread group completed a group stop.
- CLD_STOPPED = 5
-
- // CLD_CONTINUED indicates that a group-stopped thread group was continued.
- CLD_CONTINUED = 6
-
- // SYS_* codes are only meaningful for SIGSYS.
-
- // SYS_SECCOMP indicates that a signal originates from seccomp.
- SYS_SECCOMP = 1
-
- // TRAP_* codes are only meaningful for SIGTRAP.
-
- // TRAP_BRKPT indicates a breakpoint trap.
- TRAP_BRKPT = 1
-)
diff --git a/pkg/sentry/arch/signal_stack.go b/pkg/sentry/arch/signal_stack.go
deleted file mode 100644
index c732c7503..000000000
--- a/pkg/sentry/arch/signal_stack.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.
-
-// +build 386 amd64 arm64
-
-package arch
-
-import (
- "gvisor.dev/gvisor/pkg/hostarch"
- "gvisor.dev/gvisor/pkg/marshal"
-)
-
-const (
- // SignalStackFlagOnStack is possible set on return from getaltstack,
- // in order to indicate that the thread is currently on the alt stack.
- SignalStackFlagOnStack = 1
-
- // SignalStackFlagDisable is a flag to indicate the stack is disabled.
- SignalStackFlagDisable = 2
-)
-
-// IsEnabled returns true iff this signal stack is marked as enabled.
-func (s SignalStack) IsEnabled() bool {
- return s.Flags&SignalStackFlagDisable == 0
-}
-
-// Top returns the stack's top address.
-func (s SignalStack) Top() hostarch.Addr {
- return hostarch.Addr(s.Addr + s.Size)
-}
-
-// SetOnStack marks this signal stack as in use.
-//
-// Note that there is no corresponding ClearOnStack, and that this should only
-// be called on copies that are serialized to userspace.
-func (s *SignalStack) SetOnStack() {
- s.Flags |= SignalStackFlagOnStack
-}
-
-// Contains checks if the stack pointer is within this stack.
-func (s *SignalStack) Contains(sp hostarch.Addr) bool {
- return hostarch.Addr(s.Addr) < sp && sp <= hostarch.Addr(s.Addr+s.Size)
-}
-
-// NativeSignalStack is a type that is equivalent to stack_t in the guest
-// architecture.
-type NativeSignalStack interface {
- marshal.Marshallable
-
- // SerializeFrom copies the data in the host SignalStack s into this
- // object.
- SerializeFrom(s *SignalStack)
-
- // DeserializeTo copies the data in this object into the host SignalStack
- // s.
- DeserializeTo(s *SignalStack)
-}
diff --git a/pkg/sentry/arch/stack.go b/pkg/sentry/arch/stack.go
index 65a794c7c..85e3515af 100644
--- a/pkg/sentry/arch/stack.go
+++ b/pkg/sentry/arch/stack.go
@@ -45,7 +45,7 @@ type Stack struct {
}
// scratchBufLen is the default length of Stack.scratchBuf. The
-// largest structs the stack regularly serializes are arch.SignalInfo
+// largest structs the stack regularly serializes are linux.SignalInfo
// and arch.UContext64. We'll set the default size as the larger of
// the two, arch.UContext64.
var scratchBufLen = (*UContext64)(nil).SizeBytes()
diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go
index 367849e75..221e98a01 100644
--- a/pkg/sentry/control/proc.go
+++ b/pkg/sentry/control/proc.go
@@ -99,6 +99,9 @@ type ExecArgs struct {
// PIDNamespace is the pid namespace for the process being executed.
PIDNamespace *kernel.PIDNamespace
+
+ // Limits is the limit set for the process being executed.
+ Limits *limits.LimitSet
}
// String prints the arguments as a string.
@@ -151,6 +154,10 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
if pidns == nil {
pidns = proc.Kernel.RootPIDNamespace()
}
+ limitSet := args.Limits
+ if limitSet == nil {
+ limitSet = limits.NewLimitSet()
+ }
initArgs := kernel.CreateProcessArgs{
Filename: args.Filename,
Argv: args.Argv,
@@ -161,7 +168,7 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
Credentials: creds,
FDTable: fdTable,
Umask: 0022,
- Limits: limits.NewLimitSet(),
+ Limits: limitSet,
MaxSymlinkTraversals: linux.MaxSymlinkTraversals,
UTSNamespace: proc.Kernel.RootUTSNamespace(),
IPCNamespace: proc.Kernel.RootIPCNamespace(),
diff --git a/pkg/sentry/devices/tundev/BUILD b/pkg/sentry/devices/tundev/BUILD
index 8b38d574d..37229e7ba 100644
--- a/pkg/sentry/devices/tundev/BUILD
+++ b/pkg/sentry/devices/tundev/BUILD
@@ -9,6 +9,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/sentry/arch",
"//pkg/sentry/fsimpl/devtmpfs",
diff --git a/pkg/sentry/devices/tundev/tundev.go b/pkg/sentry/devices/tundev/tundev.go
index a12eeb8e7..c4889017b 100644
--- a/pkg/sentry/devices/tundev/tundev.go
+++ b/pkg/sentry/devices/tundev/tundev.go
@@ -18,6 +18,7 @@ package tundev
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/devtmpfs"
@@ -77,11 +78,11 @@ func (fd *tunFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArg
switch request {
case linux.TUNSETIFF:
if !t.HasCapability(linux.CAP_NET_ADMIN) {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
stack, ok := t.NetworkContext().(*netstack.Stack)
if !ok {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
var req linux.IFReq
diff --git a/pkg/sentry/fs/BUILD b/pkg/sentry/fs/BUILD
index 0dc100f9b..58fe1e77c 100644
--- a/pkg/sentry/fs/BUILD
+++ b/pkg/sentry/fs/BUILD
@@ -48,6 +48,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/amutex",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/p9",
@@ -110,12 +111,12 @@ go_test(
deps = [
":fs",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/sentry/fs/fsutil",
"//pkg/sentry/fs/ramfs",
"//pkg/sentry/fs/tmpfs",
"//pkg/sentry/kernel/contexttest",
"//pkg/sync",
- "//pkg/syserror",
"//pkg/usermem",
],
)
diff --git a/pkg/sentry/fs/copy_up.go b/pkg/sentry/fs/copy_up.go
index 5aa668873..a8591052c 100644
--- a/pkg/sentry/fs/copy_up.go
+++ b/pkg/sentry/fs/copy_up.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -161,7 +162,7 @@ func doCopyUp(ctx context.Context, d *Dirent) error {
// then try to take copyMu for writing here, we'd deadlock.
t := d.Inode.overlay.lower.StableAttr.Type
if t != RegularFile && t != Directory && t != Symlink {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Wait to get exclusive access to the upper Inode.
@@ -410,7 +411,7 @@ func copyAttributesLocked(ctx context.Context, upper *Inode, lower *Inode) error
return err
}
lowerXattr, err := lower.ListXattr(ctx, linux.XATTR_SIZE_MAX)
- if err != nil && err != syserror.EOPNOTSUPP {
+ if err != nil && !linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
return err
}
diff --git a/pkg/sentry/fs/dev/BUILD b/pkg/sentry/fs/dev/BUILD
index 23a3a9a2d..e28a8961b 100644
--- a/pkg/sentry/fs/dev/BUILD
+++ b/pkg/sentry/fs/dev/BUILD
@@ -18,6 +18,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/rand",
"//pkg/safemem",
diff --git a/pkg/sentry/fs/dev/net_tun.go b/pkg/sentry/fs/dev/net_tun.go
index 77e8d222a..ae9aed7b5 100644
--- a/pkg/sentry/fs/dev/net_tun.go
+++ b/pkg/sentry/fs/dev/net_tun.go
@@ -17,6 +17,7 @@ package dev
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -98,11 +99,11 @@ func (n *netTunFileOperations) Ioctl(ctx context.Context, file *fs.File, io user
switch request {
case linux.TUNSETIFF:
if !t.HasCapability(linux.CAP_NET_ADMIN) {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
stack, ok := t.NetworkContext().(*netstack.Stack)
if !ok {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
var req linux.IFReq
diff --git a/pkg/sentry/fs/dirent.go b/pkg/sentry/fs/dirent.go
index 9d5d40954..8d7660e79 100644
--- a/pkg/sentry/fs/dirent.go
+++ b/pkg/sentry/fs/dirent.go
@@ -22,6 +22,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
@@ -487,11 +488,11 @@ func (d *Dirent) walk(ctx context.Context, root *Dirent, name string, walkMayUnl
// Slow path: load the InodeOperations into memory. Since this is a hot path and the lookup may be
// expensive, if possible release the lock and re-acquire it.
if walkMayUnlock {
- d.mu.Unlock()
+ d.mu.Unlock() // +checklocksforce: results in an inconsistent block.
}
c, err := d.Inode.Lookup(ctx, name)
if walkMayUnlock {
- d.mu.Lock()
+ d.mu.Lock() // +checklocksforce: see above.
}
// No dice.
if err != nil {
@@ -593,21 +594,27 @@ func (d *Dirent) exists(ctx context.Context, root *Dirent, name string) bool {
// lockDirectory should be called for any operation that changes this `d`s
// children (creating or removing them).
-func (d *Dirent) lockDirectory() func() {
+// +checklocksacquire:d.dirMu
+// +checklocksacquire:d.mu
+func (d *Dirent) lockDirectory() {
renameMu.RLock()
d.dirMu.Lock()
d.mu.Lock()
- return func() {
- d.mu.Unlock()
- d.dirMu.Unlock()
- renameMu.RUnlock()
- }
+}
+
+// unlockDirectory is the reverse of lockDirectory.
+// +checklocksrelease:d.dirMu
+// +checklocksrelease:d.mu
+func (d *Dirent) unlockDirectory() {
+ d.mu.Unlock()
+ d.dirMu.Unlock()
+ renameMu.RUnlock() // +checklocksforce: see lockDirectory.
}
// Create creates a new regular file in this directory.
func (d *Dirent) Create(ctx context.Context, root *Dirent, name string, flags FileFlags, perms FilePermissions) (*File, error) {
- unlock := d.lockDirectory()
- defer unlock()
+ d.lockDirectory()
+ defer d.unlockDirectory()
// Does something already exist?
if d.exists(ctx, root, name) {
@@ -669,8 +676,8 @@ func (d *Dirent) finishCreate(ctx context.Context, child *Dirent, name string) {
// genericCreate executes create if name does not exist. Removes a negative Dirent at name if
// create succeeds.
func (d *Dirent) genericCreate(ctx context.Context, root *Dirent, name string, create func() error) error {
- unlock := d.lockDirectory()
- defer unlock()
+ d.lockDirectory()
+ defer d.unlockDirectory()
// Does something already exist?
if d.exists(ctx, root, name) {
@@ -963,7 +970,7 @@ func (d *Dirent) mount(ctx context.Context, inode *Inode) (newChild *Dirent, err
//
// See Linux equivalent in fs/namespace.c:do_add_mount.
if IsSymlink(inode.StableAttr) {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
// Dirent that'll replace d.
@@ -1020,8 +1027,8 @@ func (d *Dirent) Remove(ctx context.Context, root *Dirent, name string, dirPath
panic("Dirent.Remove: root must not be nil")
}
- unlock := d.lockDirectory()
- defer unlock()
+ d.lockDirectory()
+ defer d.unlockDirectory()
// Try to walk to the node.
child, err := d.walk(ctx, root, name, false /* may unlock */)
@@ -1081,8 +1088,8 @@ func (d *Dirent) RemoveDirectory(ctx context.Context, root *Dirent, name string)
panic("Dirent.Remove: root must not be nil")
}
- unlock := d.lockDirectory()
- defer unlock()
+ d.lockDirectory()
+ defer d.unlockDirectory()
// Check for dots.
if name == "." {
@@ -1258,17 +1265,15 @@ func (d *Dirent) dropExtendedReference() {
d.Inode.MountSource.fscache.Remove(d)
}
-// lockForRename takes locks on oldParent and newParent as required by Rename
-// and returns a function that will unlock the locks taken. The returned
-// function must be called even if a non-nil error is returned.
-func lockForRename(oldParent *Dirent, oldName string, newParent *Dirent, newName string) (func(), error) {
+// lockForRename takes locks on oldParent and newParent as required by Rename.
+// On return, unlockForRename must always be called, even with an error.
+// +checklocksacquire:oldParent.mu
+// +checklocksacquire:newParent.mu
+func lockForRename(oldParent *Dirent, oldName string, newParent *Dirent, newName string) error {
renameMu.Lock()
if oldParent == newParent {
oldParent.mu.Lock()
- return func() {
- oldParent.mu.Unlock()
- renameMu.Unlock()
- }, nil
+ return nil // +checklocksforce: only one lock exists.
}
// Renaming between directories is a bit subtle:
@@ -1296,11 +1301,7 @@ func lockForRename(oldParent *Dirent, oldName string, newParent *Dirent, newName
// itself.
err = unix.EINVAL
}
- return func() {
- newParent.mu.Unlock()
- oldParent.mu.Unlock()
- renameMu.Unlock()
- }, err
+ return err
}
child = p
}
@@ -1309,17 +1310,27 @@ func lockForRename(oldParent *Dirent, oldName string, newParent *Dirent, newName
// have no relationship; in either case we can do this:
newParent.mu.Lock()
oldParent.mu.Lock()
- return func() {
+ return nil
+}
+
+// unlockForRename is the opposite of lockForRename.
+// +checklocksrelease:oldParent.mu
+// +checklocksrelease:newParent.mu
+func unlockForRename(oldParent, newParent *Dirent) {
+ if oldParent == newParent {
oldParent.mu.Unlock()
- newParent.mu.Unlock()
- renameMu.Unlock()
- }, nil
+ renameMu.Unlock() // +checklocksforce: only one lock exists.
+ return
+ }
+ newParent.mu.Unlock()
+ oldParent.mu.Unlock()
+ renameMu.Unlock() // +checklocksforce: not tracked.
}
func (d *Dirent) checkSticky(ctx context.Context, victim *Dirent) error {
uattr, err := d.Inode.UnstableAttr(ctx)
if err != nil {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if !uattr.Perms.Sticky {
return nil
@@ -1332,7 +1343,7 @@ func (d *Dirent) checkSticky(ctx context.Context, victim *Dirent) error {
vuattr, err := victim.Inode.UnstableAttr(ctx)
if err != nil {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if vuattr.Owner.UID == creds.EffectiveKUID {
return nil
@@ -1340,7 +1351,7 @@ func (d *Dirent) checkSticky(ctx context.Context, victim *Dirent) error {
if victim.Inode.CheckCapability(ctx, linux.CAP_FOWNER) {
return nil
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// MayDelete determines whether `name`, a child of `d`, can be deleted or
@@ -1352,8 +1363,8 @@ func (d *Dirent) MayDelete(ctx context.Context, root *Dirent, name string) error
return err
}
- unlock := d.lockDirectory()
- defer unlock()
+ d.lockDirectory()
+ defer d.unlockDirectory()
victim, err := d.walk(ctx, root, name, true /* may unlock */)
if err != nil {
@@ -1374,7 +1385,7 @@ func (d *Dirent) mayDelete(ctx context.Context, victim *Dirent) error {
}
if victim.IsRoot() {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return nil
@@ -1391,8 +1402,8 @@ func Rename(ctx context.Context, root *Dirent, oldParent *Dirent, oldName string
}
// Acquire global renameMu lock, and mu locks on oldParent/newParent.
- unlock, err := lockForRename(oldParent, oldName, newParent, newName)
- defer unlock()
+ err := lockForRename(oldParent, oldName, newParent, newName)
+ defer unlockForRename(oldParent, newParent)
if err != nil {
return err
}
@@ -1439,7 +1450,7 @@ func Rename(ctx context.Context, root *Dirent, oldParent *Dirent, oldName string
// replaced is the dirent that is being overwritten by rename.
replaced, err := newParent.walk(ctx, root, newName, false /* may unlock */)
if err != nil {
- if err != syserror.ENOENT {
+ if !linuxerr.Equals(linuxerr.ENOENT, err) {
return err
}
diff --git a/pkg/sentry/fs/fdpipe/BUILD b/pkg/sentry/fs/fdpipe/BUILD
index 2120f2bad..5c889c861 100644
--- a/pkg/sentry/fs/fdpipe/BUILD
+++ b/pkg/sentry/fs/fdpipe/BUILD
@@ -13,6 +13,7 @@ go_library(
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdnotifier",
"//pkg/log",
@@ -38,6 +39,8 @@ go_test(
library = ":fdpipe",
deps = [
"//pkg/context",
+ "//pkg/errors",
+ "//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdnotifier",
"//pkg/hostarch",
diff --git a/pkg/sentry/fs/fdpipe/pipe.go b/pkg/sentry/fs/fdpipe/pipe.go
index 757b7d511..f8a29816b 100644
--- a/pkg/sentry/fs/fdpipe/pipe.go
+++ b/pkg/sentry/fs/fdpipe/pipe.go
@@ -20,6 +20,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/log"
@@ -158,7 +159,7 @@ func (p *pipeOperations) Write(ctx context.Context, file *fs.File, src usermem.I
// isBlockError unwraps os errors and checks if they are caused by EAGAIN or
// EWOULDBLOCK. This is so they can be transformed into syserror.ErrWouldBlock.
func isBlockError(err error) bool {
- if err == syserror.EAGAIN || err == syserror.EWOULDBLOCK {
+ if linuxerr.Equals(linuxerr.EAGAIN, err) || linuxerr.Equals(linuxerr.EWOULDBLOCK, err) {
return true
}
if pe, ok := err.(*os.PathError); ok {
diff --git a/pkg/sentry/fs/fdpipe/pipe_opener_test.go b/pkg/sentry/fs/fdpipe/pipe_opener_test.go
index 7b3ff191f..89d8be741 100644
--- a/pkg/sentry/fs/fdpipe/pipe_opener_test.go
+++ b/pkg/sentry/fs/fdpipe/pipe_opener_test.go
@@ -25,8 +25,8 @@ import (
"github.com/google/uuid"
"golang.org/x/sys/unix"
-
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -515,8 +515,8 @@ func assertReaderHungup(t *testing.T, desc string, reader io.Reader) bool {
}
func assertWriterHungup(t *testing.T, desc string, writer io.Writer) bool {
- if _, err := writer.Write([]byte("hello")); unwrapError(err) != unix.EPIPE {
- t.Errorf("%s: write to self after hangup got error %v, want %v", desc, err, unix.EPIPE)
+ if _, err := writer.Write([]byte("hello")); !linuxerr.Equals(linuxerr.EPIPE, unwrapError(err)) {
+ t.Errorf("%s: write to self after hangup got error %v, want %v", desc, err, linuxerr.EPIPE)
return false
}
return true
diff --git a/pkg/sentry/fs/fdpipe/pipe_test.go b/pkg/sentry/fs/fdpipe/pipe_test.go
index ab0e9dac7..4c8905a7e 100644
--- a/pkg/sentry/fs/fdpipe/pipe_test.go
+++ b/pkg/sentry/fs/fdpipe/pipe_test.go
@@ -21,14 +21,15 @@ import (
"testing"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/errors"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/fdnotifier"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
func singlePipeFD() (int, error) {
@@ -209,17 +210,17 @@ func TestPipeRequest(t *testing.T) {
{
desc: "ReadDir on pipe returns ENOTDIR",
context: &ReadDir{},
- err: unix.ENOTDIR,
+ err: linuxerr.ENOTDIR,
},
{
desc: "Fsync on pipe returns EINVAL",
context: &Fsync{},
- err: unix.EINVAL,
+ err: linuxerr.EINVAL,
},
{
desc: "Seek on pipe returns ESPIPE",
context: &Seek{},
- err: unix.ESPIPE,
+ err: linuxerr.ESPIPE,
},
{
desc: "Readv on pipe from empty buffer returns nil",
@@ -248,7 +249,7 @@ func TestPipeRequest(t *testing.T) {
desc: "Writev on pipe from non-empty buffer and closed partner returns EPIPE",
context: &Writev{Src: usermem.BytesIOSequence([]byte("hello"))},
flags: fs.FileFlags{Write: true},
- err: unix.EPIPE,
+ err: linuxerr.EPIPE,
},
{
desc: "Writev on pipe from non-empty buffer and open partner succeeds",
@@ -307,7 +308,11 @@ func TestPipeRequest(t *testing.T) {
t.Errorf("%s: unknown request type %T", test.desc, test.context)
}
- if unwrapError(err) != test.err {
+ if linuxErr, ok := test.err.(*errors.Error); ok {
+ if !linuxerr.Equals(linuxErr, unwrapError(err)) {
+ t.Errorf("%s: got error %v, want %v", test.desc, err, test.err)
+ }
+ } else if test.err != unwrapError(err) {
t.Errorf("%s: got error %v, want %v", test.desc, err, test.err)
}
}
diff --git a/pkg/sentry/fs/file_overlay.go b/pkg/sentry/fs/file_overlay.go
index 696613f3a..02208ac3d 100644
--- a/pkg/sentry/fs/file_overlay.go
+++ b/pkg/sentry/fs/file_overlay.go
@@ -18,6 +18,7 @@ import (
"io"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -357,7 +358,7 @@ func (*overlayFileOperations) ConfigureMMap(ctx context.Context, file *File, opt
}
if !o.isMappableLocked() {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
// FIXME(jamieliu): This is a copy/paste of fsutil.GenericConfigureMMap,
@@ -417,7 +418,7 @@ func (f *overlayFileOperations) FifoSize(ctx context.Context, overlayFile *File)
err = f.onTop(ctx, overlayFile, func(file *File, ops FileOperations) error {
sz, ok := ops.(FifoSizer)
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
rv, err = sz.FifoSize(ctx, file)
return err
@@ -432,11 +433,11 @@ func (f *overlayFileOperations) SetFifoSize(size int64) (rv int64, err error) {
if f.upper == nil {
// Named pipes cannot be copied up and changes to the lower are prohibited.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
sz, ok := f.upper.FileOperations.(FifoSizer)
if !ok {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
return sz.SetFifoSize(size)
}
diff --git a/pkg/sentry/fs/fs.go b/pkg/sentry/fs/fs.go
index 44587bb37..a346c316b 100644
--- a/pkg/sentry/fs/fs.go
+++ b/pkg/sentry/fs/fs.go
@@ -80,23 +80,33 @@ func AsyncBarrier() {
// Async executes a function asynchronously.
//
// Async must not be called recursively.
+// +checklocksignore
func Async(f func()) {
workMu.RLock()
- go func() { // S/R-SAFE: AsyncBarrier must be called.
- defer workMu.RUnlock() // Ensure RUnlock in case of panic.
- f()
- }()
+ go asyncWork(f) // S/R-SAFE: AsyncBarrier must be called.
+}
+
+// +checklocksignore
+func asyncWork(f func()) {
+ // Ensure RUnlock in case of panic.
+ defer workMu.RUnlock()
+ f()
}
// AsyncWithContext is just like Async, except that it calls the asynchronous
// function with the given context as argument. This function exists to avoid
// needing to allocate an extra function on the heap in a hot path.
+// +checklocksignore
func AsyncWithContext(ctx context.Context, f func(context.Context)) {
workMu.RLock()
- go func() { // S/R-SAFE: AsyncBarrier must be called.
- defer workMu.RUnlock() // Ensure RUnlock in case of panic.
- f(ctx)
- }()
+ go asyncWorkWithContext(ctx, f)
+}
+
+// +checklocksignore
+func asyncWorkWithContext(ctx context.Context, f func(context.Context)) {
+ // Ensure RUnlock in case of panic.
+ defer workMu.RUnlock()
+ f(ctx)
}
// AsyncErrorBarrier waits for all outstanding asynchronous work to complete, or
diff --git a/pkg/sentry/fs/fsutil/BUILD b/pkg/sentry/fs/fsutil/BUILD
index 6469cc3a9..ebc90b41f 100644
--- a/pkg/sentry/fs/fsutil/BUILD
+++ b/pkg/sentry/fs/fsutil/BUILD
@@ -76,6 +76,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/safemem",
diff --git a/pkg/sentry/fs/fsutil/file.go b/pkg/sentry/fs/fsutil/file.go
index dc9efa5df..fe327235a 100644
--- a/pkg/sentry/fs/fsutil/file.go
+++ b/pkg/sentry/fs/fsutil/file.go
@@ -18,6 +18,7 @@ import (
"io"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -45,7 +46,7 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence,
// Does the Inode represents a non-seekable type?
if fs.IsPipe(inode.StableAttr) || fs.IsSocket(inode.StableAttr) {
- return current, syserror.ESPIPE
+ return current, linuxerr.ESPIPE
}
// Does the Inode represent a character device?
@@ -63,12 +64,12 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence,
switch inode.StableAttr.Type {
case fs.RegularFile, fs.SpecialFile, fs.BlockDevice:
if offset < 0 {
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
return offset, nil
case fs.Directory, fs.SpecialDirectory:
if offset != 0 {
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
// SEEK_SET to 0 moves the directory "cursor" to the beginning.
if dirCursor != nil {
@@ -76,22 +77,22 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence,
}
return 0, nil
default:
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
case fs.SeekCurrent:
switch inode.StableAttr.Type {
case fs.RegularFile, fs.SpecialFile, fs.BlockDevice:
if current+offset < 0 {
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
return current + offset, nil
case fs.Directory, fs.SpecialDirectory:
if offset != 0 {
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
return current, nil
default:
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
case fs.SeekEnd:
switch inode.StableAttr.Type {
@@ -103,14 +104,14 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence,
}
sz := uattr.Size
if sz+offset < 0 {
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
return sz + offset, nil
// FIXME(b/34778850): This is not universally correct.
// Remove SpecialDirectory.
case fs.SpecialDirectory:
if offset != 0 {
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
// SEEK_END to 0 moves the directory "cursor" to the end.
//
@@ -121,12 +122,12 @@ func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence,
// futile (EOF will always be the result).
return fs.FileMaxOffset, nil
default:
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
}
// Not a valid seek request.
- return current, syserror.EINVAL
+ return current, linuxerr.EINVAL
}
// FileGenericSeek implements fs.FileOperations.Seek for files that use a
@@ -152,7 +153,7 @@ type FileNoSeek struct{}
// Seek implements fs.FileOperations.Seek.
func (FileNoSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// FilePipeSeek implements fs.FileOperations.Seek and can be used for files
@@ -161,7 +162,7 @@ type FilePipeSeek struct{}
// Seek implements fs.FileOperations.Seek.
func (FilePipeSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// FileNotDirReaddir implements fs.FileOperations.Readdir for non-directories.
@@ -178,7 +179,7 @@ type FileNoFsync struct{}
// Fsync implements fs.FileOperations.Fsync.
func (FileNoFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// FileNoopFsync implements fs.FileOperations.Fsync for files that don't need
@@ -204,7 +205,7 @@ type FileNoMMap struct{}
// ConfigureMMap implements fs.FileOperations.ConfigureMMap.
func (FileNoMMap) ConfigureMMap(context.Context, *fs.File, *memmap.MMapOpts) error {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
// GenericConfigureMMap implements fs.FileOperations.ConfigureMMap for most
@@ -345,7 +346,7 @@ func NewFileStaticContentReader(b []byte) FileStaticContentReader {
// Read implements fs.FileOperations.Read.
func (scr *FileStaticContentReader) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset >= int64(len(scr.content)) {
return 0, nil
@@ -367,7 +368,7 @@ type FileNoRead struct{}
// Read implements fs.FileOperations.Read.
func (FileNoRead) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// FileNoWrite implements fs.FileOperations.Write to return EINVAL.
@@ -375,7 +376,7 @@ type FileNoWrite struct{}
// Write implements fs.FileOperations.Write.
func (FileNoWrite) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// FileNoopRead implement fs.FileOperations.Read as a noop.
diff --git a/pkg/sentry/fs/fsutil/inode.go b/pkg/sentry/fs/fsutil/inode.go
index 85e7e35db..d2b769007 100644
--- a/pkg/sentry/fs/fsutil/inode.go
+++ b/pkg/sentry/fs/fsutil/inode.go
@@ -17,6 +17,7 @@ package fsutil
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -218,7 +219,7 @@ func (i *InodeSimpleExtendedAttributes) GetXattr(_ context.Context, _ *fs.Inode,
value, ok := i.xattrs[name]
i.mu.RUnlock()
if !ok {
- return "", syserror.ENOATTR
+ return "", linuxerr.ENOATTR
}
return value, nil
}
@@ -229,7 +230,7 @@ func (i *InodeSimpleExtendedAttributes) SetXattr(_ context.Context, _ *fs.Inode,
defer i.mu.Unlock()
if i.xattrs == nil {
if flags&linux.XATTR_REPLACE != 0 {
- return syserror.ENODATA
+ return linuxerr.ENODATA
}
i.xattrs = make(map[string]string)
}
@@ -239,7 +240,7 @@ func (i *InodeSimpleExtendedAttributes) SetXattr(_ context.Context, _ *fs.Inode,
return syserror.EEXIST
}
if !ok && flags&linux.XATTR_REPLACE != 0 {
- return syserror.ENODATA
+ return linuxerr.ENODATA
}
i.xattrs[name] = value
@@ -265,7 +266,7 @@ func (i *InodeSimpleExtendedAttributes) RemoveXattr(_ context.Context, _ *fs.Ino
delete(i.xattrs, name)
return nil
}
- return syserror.ENOATTR
+ return linuxerr.ENOATTR
}
// staticFile is a file with static contents. It is returned by
@@ -376,7 +377,7 @@ func (InodeNotDirectory) RemoveDirectory(context.Context, *fs.Inode, string) err
// Rename implements fs.FileOperations.Rename.
func (InodeNotDirectory) Rename(context.Context, *fs.Inode, *fs.Inode, string, *fs.Inode, string, bool) error {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// InodeNotSocket can be used by Inodes that are not sockets.
@@ -392,7 +393,7 @@ type InodeNotTruncatable struct{}
// Truncate implements fs.InodeOperations.Truncate.
func (InodeNotTruncatable) Truncate(context.Context, *fs.Inode, int64) error {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// InodeIsDirTruncate implements fs.InodeOperations.Truncate for directories.
@@ -416,7 +417,7 @@ type InodeNotRenameable struct{}
// Rename implements fs.InodeOperations.Rename.
func (InodeNotRenameable) Rename(context.Context, *fs.Inode, *fs.Inode, string, *fs.Inode, string, bool) error {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// InodeNotOpenable can be used by Inodes that cannot be opened.
@@ -448,12 +449,12 @@ type InodeNotSymlink struct{}
// Readlink implements fs.InodeOperations.Readlink.
func (InodeNotSymlink) Readlink(context.Context, *fs.Inode) (string, error) {
- return "", syserror.ENOLINK
+ return "", linuxerr.ENOLINK
}
// Getlink implements fs.InodeOperations.Getlink.
func (InodeNotSymlink) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
- return nil, syserror.ENOLINK
+ return nil, linuxerr.ENOLINK
}
// InodeNoExtendedAttributes can be used by Inodes that do not support
diff --git a/pkg/sentry/fs/gofer/BUILD b/pkg/sentry/fs/gofer/BUILD
index 94cb05246..c08301d19 100644
--- a/pkg/sentry/fs/gofer/BUILD
+++ b/pkg/sentry/fs/gofer/BUILD
@@ -26,6 +26,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/hostarch",
"//pkg/log",
diff --git a/pkg/sentry/fs/gofer/cache_policy.go b/pkg/sentry/fs/gofer/cache_policy.go
index 07a564e92..f8b7a60fc 100644
--- a/pkg/sentry/fs/gofer/cache_policy.go
+++ b/pkg/sentry/fs/gofer/cache_policy.go
@@ -139,7 +139,7 @@ func (cp cachePolicy) revalidate(ctx context.Context, name string, parent, child
// Walk from parent to child again.
//
- // TODO(b/112031682): If we have a directory FD in the parent
+ // NOTE(b/112031682): If we have a directory FD in the parent
// inodeOperations, then we can use fstatat(2) to get the inode
// attributes instead of making this RPC.
qids, f, mask, attr, err := parentIops.fileState.file.walkGetAttr(ctx, []string{name})
diff --git a/pkg/sentry/fs/gofer/inode.go b/pkg/sentry/fs/gofer/inode.go
index da3178527..9ff64a8b6 100644
--- a/pkg/sentry/fs/gofer/inode.go
+++ b/pkg/sentry/fs/gofer/inode.go
@@ -20,6 +20,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/p9"
@@ -476,7 +477,7 @@ func (i *inodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.Fi
switch d.Inode.StableAttr.Type {
case fs.Socket:
if i.session().overrides != nil {
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
return i.getFileSocket(ctx, d, flags)
case fs.Pipe:
@@ -676,7 +677,7 @@ func (i *inodeOperations) Readlink(ctx context.Context, inode *fs.Inode) (string
// Getlink implementfs fs.InodeOperations.Getlink.
func (i *inodeOperations) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
if !fs.IsSymlink(i.fileState.sattr) {
- return nil, syserror.ENOLINK
+ return nil, linuxerr.ENOLINK
}
return nil, fs.ErrResolveViaReadlink
}
@@ -714,7 +715,7 @@ func (i *inodeOperations) configureMMap(file *fs.File, opts *memmap.MMapOpts) er
if i.fileState.hostMappable != nil {
return fsutil.GenericConfigureMMap(file, i.fileState.hostMappable, opts)
}
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
func init() {
diff --git a/pkg/sentry/fs/gofer/inode_state.go b/pkg/sentry/fs/gofer/inode_state.go
index e2af1d2ae..19f91f010 100644
--- a/pkg/sentry/fs/gofer/inode_state.go
+++ b/pkg/sentry/fs/gofer/inode_state.go
@@ -112,13 +112,6 @@ func (i *inodeFileState) loadLoading(_ struct{}) {
// +checklocks:i.loading
func (i *inodeFileState) afterLoad() {
load := func() (err error) {
- // See comment on i.loading().
- defer func() {
- if err == nil {
- i.loading.Unlock()
- }
- }()
-
// Manually restore the p9.File.
name, ok := i.s.inodeMappings[i.sattr.InodeID]
if !ok {
@@ -167,6 +160,9 @@ func (i *inodeFileState) afterLoad() {
i.savedUAttr = nil
}
+ // See comment on i.loading(). This only unlocks on the
+ // non-error path.
+ i.loading.Unlock() // +checklocksforce: per comment.
return nil
}
diff --git a/pkg/sentry/fs/gofer/path.go b/pkg/sentry/fs/gofer/path.go
index 940838a44..958f46bd6 100644
--- a/pkg/sentry/fs/gofer/path.go
+++ b/pkg/sentry/fs/gofer/path.go
@@ -18,6 +18,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/p9"
"gvisor.dev/gvisor/pkg/sentry/device"
@@ -43,10 +44,11 @@ func changeType(mode p9.FileMode, newType p9.FileMode) p9.FileMode {
// policy.
func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string) (*fs.Dirent, error) {
if len(name) > maxFilenameLen {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
- cp := i.session().cachePolicy
+ s := i.session()
+ cp := s.cachePolicy
if cp.cacheReaddir() {
// Check to see if we have readdirCache that indicates the
// child does not exist. Avoid holding readdirMu longer than
@@ -66,7 +68,7 @@ func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string
// Get a p9.File for name.
qids, newFile, mask, p9attr, err := i.fileState.file.walkGetAttr(ctx, []string{name})
if err != nil {
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
if cp.cacheNegativeDirents() {
// Return a negative Dirent. It will stay cached until something
// is created over it.
@@ -77,7 +79,7 @@ func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string
return nil, err
}
- if i.session().overrides != nil {
+ if s.overrides != nil {
// Check if file belongs to a internal named pipe. Note that it doesn't need
// to check for sockets because it's done in newInodeOperations below.
deviceKey := device.MultiDeviceKey{
@@ -85,13 +87,13 @@ func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string
SecondaryDevice: i.session().connID,
Inode: qids[0].Path,
}
- unlock := i.session().overrides.lock()
- if pipeInode := i.session().overrides.getPipe(deviceKey); pipeInode != nil {
- unlock()
+ s.overrides.lock()
+ if pipeInode := s.overrides.getPipe(deviceKey); pipeInode != nil {
+ s.overrides.unlock()
pipeInode.IncRef()
return fs.NewDirent(ctx, pipeInode, name), nil
}
- unlock()
+ s.overrides.unlock()
}
// Construct the Inode operations.
@@ -106,7 +108,7 @@ func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string
// Ownership is currently ignored.
func (i *inodeOperations) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perm fs.FilePermissions) (*fs.File, error) {
if len(name) > maxFilenameLen {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
// Create replaces the directory fid with the newly created/opened
@@ -195,7 +197,7 @@ func (i *inodeOperations) Create(ctx context.Context, dir *fs.Inode, name string
// CreateLink uses Create to create a symlink between oldname and newname.
func (i *inodeOperations) CreateLink(ctx context.Context, dir *fs.Inode, oldname string, newname string) error {
if len(newname) > maxFilenameLen {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
owner := fs.FileOwnerFromContext(ctx)
@@ -209,29 +211,32 @@ func (i *inodeOperations) CreateLink(ctx context.Context, dir *fs.Inode, oldname
// CreateHardLink implements InodeOperations.CreateHardLink.
func (i *inodeOperations) CreateHardLink(ctx context.Context, inode *fs.Inode, target *fs.Inode, newName string) error {
if len(newName) > maxFilenameLen {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
targetOpts, ok := target.InodeOperations.(*inodeOperations)
if !ok {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if err := i.fileState.file.link(ctx, &targetOpts.fileState.file, newName); err != nil {
return err
}
- if i.session().cachePolicy.cacheUAttrs(inode) {
+
+ s := i.session()
+ if s.cachePolicy.cacheUAttrs(inode) {
// Increase link count.
targetOpts.cachingInodeOps.IncLinks(ctx)
}
+
i.touchModificationAndStatusChangeTime(ctx, inode)
return nil
}
// CreateDirectory uses Create to create a directory named s under inodeOperations.
-func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, s string, perm fs.FilePermissions) error {
- if len(s) > maxFilenameLen {
- return syserror.ENAMETOOLONG
+func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, perm fs.FilePermissions) error {
+ if len(name) > maxFilenameLen {
+ return linuxerr.ENAMETOOLONG
}
// If the parent directory has setgid enabled, change the new directory's
@@ -246,16 +251,18 @@ func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, s
perm.SetGID = true
}
- if _, err := i.fileState.file.mkdir(ctx, s, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
+ if _, err := i.fileState.file.mkdir(ctx, name, p9.FileMode(perm.LinuxMode()), p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
return err
}
- if i.session().cachePolicy.cacheUAttrs(dir) {
+
+ s := i.session()
+ if s.cachePolicy.cacheUAttrs(dir) {
// Increase link count.
//
// N.B. This will update the modification time.
i.cachingInodeOps.IncLinks(ctx)
}
- if i.session().cachePolicy.cacheReaddir() {
+ if s.cachePolicy.cacheReaddir() {
// Invalidate readdir cache.
i.markDirectoryDirty()
}
@@ -265,16 +272,17 @@ func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, s
// Bind implements InodeOperations.Bind.
func (i *inodeOperations) Bind(ctx context.Context, dir *fs.Inode, name string, ep transport.BoundEndpoint, perm fs.FilePermissions) (*fs.Dirent, error) {
if len(name) > maxFilenameLen {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
- if i.session().overrides == nil {
+ s := i.session()
+ if s.overrides == nil {
return nil, syserror.EOPNOTSUPP
}
// Stabilize the override map while creation is in progress.
- unlock := i.session().overrides.lock()
- defer unlock()
+ s.overrides.lock()
+ defer s.overrides.unlock()
sattr, iops, err := i.createEndpointFile(ctx, dir, name, perm, p9.ModeSocket)
if err != nil {
@@ -283,22 +291,23 @@ func (i *inodeOperations) Bind(ctx context.Context, dir *fs.Inode, name string,
// Construct the positive Dirent.
childDir := fs.NewDirent(ctx, fs.NewInode(ctx, iops, dir.MountSource, sattr), name)
- i.session().overrides.addBoundEndpoint(iops.fileState.key, childDir, ep)
+ s.overrides.addBoundEndpoint(iops.fileState.key, childDir, ep)
return childDir, nil
}
// CreateFifo implements fs.InodeOperations.CreateFifo.
func (i *inodeOperations) CreateFifo(ctx context.Context, dir *fs.Inode, name string, perm fs.FilePermissions) error {
if len(name) > maxFilenameLen {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
owner := fs.FileOwnerFromContext(ctx)
mode := p9.FileMode(perm.LinuxMode()) | p9.ModeNamedPipe
// N.B. FIFOs use major/minor numbers 0.
+ s := i.session()
if _, err := i.fileState.file.mknod(ctx, name, mode, 0, 0, p9.UID(owner.UID), p9.GID(owner.GID)); err != nil {
- if i.session().overrides == nil || err != syserror.EPERM {
+ if s.overrides == nil || !linuxerr.Equals(linuxerr.EPERM, err) {
return err
}
// If gofer doesn't support mknod, check if we can create an internal fifo.
@@ -310,13 +319,14 @@ func (i *inodeOperations) CreateFifo(ctx context.Context, dir *fs.Inode, name st
}
func (i *inodeOperations) createInternalFifo(ctx context.Context, dir *fs.Inode, name string, owner fs.FileOwner, perm fs.FilePermissions) error {
- if i.session().overrides == nil {
- return syserror.EPERM
+ s := i.session()
+ if s.overrides == nil {
+ return linuxerr.EPERM
}
// Stabilize the override map while creation is in progress.
- unlock := i.session().overrides.lock()
- defer unlock()
+ s.overrides.lock()
+ defer s.overrides.unlock()
sattr, fileOps, err := i.createEndpointFile(ctx, dir, name, perm, p9.ModeNamedPipe)
if err != nil {
@@ -335,7 +345,7 @@ func (i *inodeOperations) createInternalFifo(ctx context.Context, dir *fs.Inode,
// Construct the positive Dirent.
childDir := fs.NewDirent(ctx, fs.NewInode(ctx, iops, dir.MountSource, sattr), name)
- i.session().overrides.addPipe(fileOps.fileState.key, childDir, inode)
+ s.overrides.addPipe(fileOps.fileState.key, childDir, inode)
return nil
}
@@ -382,11 +392,12 @@ func (i *inodeOperations) createEndpointFile(ctx context.Context, dir *fs.Inode,
// Remove implements InodeOperations.Remove.
func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string) error {
if len(name) > maxFilenameLen {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
+ s := i.session()
var key *device.MultiDeviceKey
- if i.session().overrides != nil {
+ if s.overrides != nil {
// Find out if file being deleted is a socket or pipe that needs to be
// removed from endpoint map.
if d, err := i.Lookup(ctx, dir, name); err == nil {
@@ -401,8 +412,8 @@ func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string
}
// Stabilize the override map while deletion is in progress.
- unlock := i.session().overrides.lock()
- defer unlock()
+ s.overrides.lock()
+ defer s.overrides.unlock()
}
}
}
@@ -411,7 +422,7 @@ func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string
return err
}
if key != nil {
- i.session().overrides.remove(ctx, *key)
+ s.overrides.remove(ctx, *key)
}
i.touchModificationAndStatusChangeTime(ctx, dir)
@@ -421,18 +432,20 @@ func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string
// Remove implements InodeOperations.RemoveDirectory.
func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) error {
if len(name) > maxFilenameLen {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
// 0x200 = AT_REMOVEDIR.
if err := i.fileState.file.unlinkAt(ctx, name, 0x200); err != nil {
return err
}
- if i.session().cachePolicy.cacheUAttrs(dir) {
+
+ s := i.session()
+ if s.cachePolicy.cacheUAttrs(dir) {
// Decrease link count and updates atime.
i.cachingInodeOps.DecLinks(ctx)
}
- if i.session().cachePolicy.cacheReaddir() {
+ if s.cachePolicy.cacheReaddir() {
// Invalidate readdir cache.
i.markDirectoryDirty()
}
@@ -442,12 +455,12 @@ func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, na
// Rename renames this node.
func (i *inodeOperations) Rename(ctx context.Context, inode *fs.Inode, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
if len(newName) > maxFilenameLen {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
// Don't allow renames across different mounts.
if newParent.MountSource != oldParent.MountSource {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
// Unwrap the new parent to a *inodeOperations.
@@ -462,12 +475,13 @@ func (i *inodeOperations) Rename(ctx context.Context, inode *fs.Inode, oldParent
}
// Is the renamed entity a directory? Fix link counts.
+ s := i.session()
if fs.IsDir(i.fileState.sattr) {
// Update cached state.
- if i.session().cachePolicy.cacheUAttrs(oldParent) {
+ if s.cachePolicy.cacheUAttrs(oldParent) {
oldParentInodeOperations.cachingInodeOps.DecLinks(ctx)
}
- if i.session().cachePolicy.cacheUAttrs(newParent) {
+ if s.cachePolicy.cacheUAttrs(newParent) {
// Only IncLinks if there is a new addition to
// newParent. If this is replacement, then the total
// count remains the same.
@@ -476,7 +490,7 @@ func (i *inodeOperations) Rename(ctx context.Context, inode *fs.Inode, oldParent
}
}
}
- if i.session().cachePolicy.cacheReaddir() {
+ if s.cachePolicy.cacheReaddir() {
// Mark old directory dirty.
oldParentInodeOperations.markDirectoryDirty()
if oldParent != newParent {
@@ -486,17 +500,18 @@ func (i *inodeOperations) Rename(ctx context.Context, inode *fs.Inode, oldParent
}
// Rename always updates ctime.
- if i.session().cachePolicy.cacheUAttrs(inode) {
+ if s.cachePolicy.cacheUAttrs(inode) {
i.cachingInodeOps.TouchStatusChangeTime(ctx)
}
return nil
}
func (i *inodeOperations) touchModificationAndStatusChangeTime(ctx context.Context, inode *fs.Inode) {
- if i.session().cachePolicy.cacheUAttrs(inode) {
+ s := i.session()
+ if s.cachePolicy.cacheUAttrs(inode) {
i.cachingInodeOps.TouchModificationAndStatusChangeTime(ctx)
}
- if i.session().cachePolicy.cacheReaddir() {
+ if s.cachePolicy.cacheReaddir() {
// Invalidate readdir cache.
i.markDirectoryDirty()
}
diff --git a/pkg/sentry/fs/gofer/session.go b/pkg/sentry/fs/gofer/session.go
index 7cf3522ff..b7debeecb 100644
--- a/pkg/sentry/fs/gofer/session.go
+++ b/pkg/sentry/fs/gofer/session.go
@@ -98,9 +98,14 @@ func (e *overrideMaps) remove(ctx context.Context, key device.MultiDeviceKey) {
// lock blocks other addition and removal operations from happening while
// the backing file is being created or deleted. Returns a function that unlocks
// the endpoint map.
-func (e *overrideMaps) lock() func() {
+// +checklocksacquire:e.mu
+func (e *overrideMaps) lock() {
e.mu.Lock()
- return func() { e.mu.Unlock() }
+}
+
+// +checklocksrelease:e.mu
+func (e *overrideMaps) unlock() {
+ e.mu.Unlock()
}
// getBoundEndpoint returns the bound endpoint mapped to the given key.
@@ -366,8 +371,8 @@ func newOverrideMaps() *overrideMaps {
// fillKeyMap populates key and dirent maps upon restore from saved pathmap.
func (s *session) fillKeyMap(ctx context.Context) error {
- unlock := s.overrides.lock()
- defer unlock()
+ s.overrides.lock()
+ defer s.overrides.unlock()
for ep, dirPath := range s.overrides.pathMap {
_, file, err := s.attach.walk(ctx, splitAbsolutePath(dirPath))
@@ -394,8 +399,8 @@ func (s *session) fillKeyMap(ctx context.Context) error {
// fillPathMap populates paths for overrides from dirents in direntMap
// before save.
func (s *session) fillPathMap(ctx context.Context) error {
- unlock := s.overrides.lock()
- defer unlock()
+ s.overrides.lock()
+ defer s.overrides.unlock()
for _, endpoint := range s.overrides.keyMap {
mountRoot := endpoint.dirent.MountRoot()
diff --git a/pkg/sentry/fs/gofer/socket.go b/pkg/sentry/fs/gofer/socket.go
index 8a1c69ac2..1fd8a0910 100644
--- a/pkg/sentry/fs/gofer/socket.go
+++ b/pkg/sentry/fs/gofer/socket.go
@@ -32,10 +32,11 @@ func (i *inodeOperations) BoundEndpoint(inode *fs.Inode, path string) transport.
return nil
}
- if i.session().overrides != nil {
- unlock := i.session().overrides.lock()
- defer unlock()
- ep := i.session().overrides.getBoundEndpoint(i.fileState.key)
+ s := i.session()
+ if s.overrides != nil {
+ s.overrides.lock()
+ defer s.overrides.unlock()
+ ep := s.overrides.getBoundEndpoint(i.fileState.key)
if ep != nil {
return ep
}
diff --git a/pkg/sentry/fs/host/BUILD b/pkg/sentry/fs/host/BUILD
index 3c45f6cc5..24fc6305c 100644
--- a/pkg/sentry/fs/host/BUILD
+++ b/pkg/sentry/fs/host/BUILD
@@ -28,9 +28,9 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdnotifier",
- "//pkg/iovec",
"//pkg/log",
"//pkg/marshal/primitive",
"//pkg/refs",
@@ -40,6 +40,7 @@ go_library(
"//pkg/sentry/device",
"//pkg/sentry/fs",
"//pkg/sentry/fs/fsutil",
+ "//pkg/sentry/hostfd",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/time",
diff --git a/pkg/sentry/fs/host/file.go b/pkg/sentry/fs/host/file.go
index 07bd078b7..77c08a7ce 100644
--- a/pkg/sentry/fs/host/file.go
+++ b/pkg/sentry/fs/host/file.go
@@ -19,6 +19,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/log"
@@ -268,7 +269,7 @@ func (f *fileOperations) Flush(context.Context, *fs.File) error {
// ConfigureMMap implements fs.FileOperations.ConfigureMMap.
func (f *fileOperations) ConfigureMMap(ctx context.Context, file *fs.File, opts *memmap.MMapOpts) error {
if !canMap(file.Dirent.Inode) {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
return fsutil.GenericConfigureMMap(file, f.iops.cachingInodeOps, opts)
}
diff --git a/pkg/sentry/fs/host/host.go b/pkg/sentry/fs/host/host.go
index 081ba1dd8..9f6dbd7e9 100644
--- a/pkg/sentry/fs/host/host.go
+++ b/pkg/sentry/fs/host/host.go
@@ -17,8 +17,8 @@ package host
import (
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// filesystem is a host filesystem.
@@ -40,7 +40,7 @@ func (*filesystem) Name() string {
// Mount returns an error. Mounting hostfs is not allowed.
func (*filesystem) Mount(ctx context.Context, device string, flags fs.MountSourceFlags, data string, dataObj interface{}) (*fs.Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// AllowUserMount prohibits users from using mount(2) with this file system.
diff --git a/pkg/sentry/fs/host/inode.go b/pkg/sentry/fs/host/inode.go
index e299b532c..77bdf4925 100644
--- a/pkg/sentry/fs/host/inode.go
+++ b/pkg/sentry/fs/host/inode.go
@@ -17,6 +17,7 @@ package host
import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/secio"
@@ -113,7 +114,7 @@ func (i *inodeFileState) SetMaskedAttributes(ctx context.Context, mask fs.AttrMa
return nil
}
if mask.UID || mask.GID {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if mask.Perms {
if err := unix.Fchmod(i.FD(), uint32(attr.Perms.LinuxMode())); err != nil {
@@ -224,43 +225,43 @@ func (i *inodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name string
// Create implements fs.InodeOperations.Create.
func (i *inodeOperations) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perm fs.FilePermissions) (*fs.File, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// CreateDirectory implements fs.InodeOperations.CreateDirectory.
func (i *inodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, perm fs.FilePermissions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// CreateLink implements fs.InodeOperations.CreateLink.
func (i *inodeOperations) CreateLink(ctx context.Context, dir *fs.Inode, oldname string, newname string) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// CreateHardLink implements fs.InodeOperations.CreateHardLink.
func (*inodeOperations) CreateHardLink(context.Context, *fs.Inode, *fs.Inode, string) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// CreateFifo implements fs.InodeOperations.CreateFifo.
func (*inodeOperations) CreateFifo(context.Context, *fs.Inode, string, fs.FilePermissions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Remove implements fs.InodeOperations.Remove.
func (i *inodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// RemoveDirectory implements fs.InodeOperations.RemoveDirectory.
func (i *inodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Rename implements fs.InodeOperations.Rename.
func (i *inodeOperations) Rename(ctx context.Context, inode *fs.Inode, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Bind implements fs.InodeOperations.Bind.
@@ -276,7 +277,7 @@ func (i *inodeOperations) BoundEndpoint(inode *fs.Inode, path string) transport.
// GetFile implements fs.InodeOperations.GetFile.
func (i *inodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
if fs.IsSocket(d.Inode.StableAttr) {
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
return newFile(ctx, d, flags, i), nil
@@ -313,7 +314,7 @@ func (i *inodeOperations) Check(ctx context.Context, inode *fs.Inode, p fs.PermM
// SetOwner implements fs.InodeOperations.SetOwner.
func (i *inodeOperations) SetOwner(context.Context, *fs.Inode, fs.FileOwner) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// SetPermissions implements fs.InodeOperations.SetPermissions.
@@ -392,7 +393,7 @@ func (i *inodeOperations) Readlink(ctx context.Context, inode *fs.Inode) (string
// Getlink implements fs.InodeOperations.Getlink.
func (i *inodeOperations) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
if !fs.IsSymlink(i.fileState.sattr) {
- return nil, syserror.ENOLINK
+ return nil, linuxerr.ENOLINK
}
return nil, fs.ErrResolveViaReadlink
}
diff --git a/pkg/sentry/fs/host/socket.go b/pkg/sentry/fs/host/socket.go
index 46a2dc47d..54c421775 100644
--- a/pkg/sentry/fs/host/socket.go
+++ b/pkg/sentry/fs/host/socket.go
@@ -21,6 +21,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/refs"
@@ -31,7 +32,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/uniqueid"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserr"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/unet"
"gvisor.dev/gvisor/pkg/waiter"
@@ -211,9 +211,9 @@ func (c *ConnectedEndpoint) Send(ctx context.Context, data [][]byte, controlMess
if n < totalLen && err == nil {
// The host only returns a short write if it would otherwise
// block (and only for stream sockets).
- err = syserror.EAGAIN
+ err = linuxerr.EAGAIN
}
- if n > 0 && err != syserror.EAGAIN {
+ if n > 0 && !linuxerr.Equals(linuxerr.EAGAIN, err) {
// The caller may need to block to send more data, but
// otherwise there isn't anything that can be done about an
// error with a partial write.
diff --git a/pkg/sentry/fs/host/socket_iovec.go b/pkg/sentry/fs/host/socket_iovec.go
index 7380d75e7..d98e3c6d1 100644
--- a/pkg/sentry/fs/host/socket_iovec.go
+++ b/pkg/sentry/fs/host/socket_iovec.go
@@ -16,8 +16,8 @@ package host
import (
"golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/iovec"
- "gvisor.dev/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/sentry/hostfd"
)
// LINT.IfChange
@@ -66,13 +66,13 @@ func buildIovec(bufs [][]byte, maxlen int64, truncate bool) (length int64, iovec
if length > maxlen {
if truncate {
stopLen = maxlen
- err = syserror.EAGAIN
+ err = linuxerr.EAGAIN
} else {
- return 0, nil, nil, syserror.EMSGSIZE
+ return 0, nil, nil, linuxerr.EMSGSIZE
}
}
- if iovsRequired > iovec.MaxIovs {
+ if iovsRequired > hostfd.MaxSendRecvMsgIov {
// The kernel will reject our call if we pass this many iovs.
// Use a single intermediate buffer instead.
b := make([]byte, stopLen)
diff --git a/pkg/sentry/fs/host/tty.go b/pkg/sentry/fs/host/tty.go
index 1183727ab..c7010f35c 100644
--- a/pkg/sentry/fs/host/tty.go
+++ b/pkg/sentry/fs/host/tty.go
@@ -17,6 +17,7 @@ package host
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -191,7 +192,7 @@ func (t *TTYFileOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem.IO
if err := t.checkChange(ctx, linux.SIGTTOU); err != nil {
// drivers/tty/tty_io.c:tiocspgrp() converts -EIO from
// tty_check_change() to -ENOTTY.
- if err == syserror.EIO {
+ if linuxerr.Equals(linuxerr.EIO, err) {
return 0, syserror.ENOTTY
}
return 0, err
@@ -211,7 +212,7 @@ func (t *TTYFileOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem.IO
// pgID must be non-negative.
if pgID < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Process group with pgID must exist in this PID namespace.
@@ -223,7 +224,7 @@ func (t *TTYFileOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem.IO
// Check that new process group is in the TTY session.
if pg.Session() != t.session {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
t.fgProcessGroup = pg
diff --git a/pkg/sentry/fs/host/util.go b/pkg/sentry/fs/host/util.go
index ab74724a3..e7db79189 100644
--- a/pkg/sentry/fs/host/util.go
+++ b/pkg/sentry/fs/host/util.go
@@ -19,12 +19,12 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/device"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
- "gvisor.dev/gvisor/pkg/syserror"
)
func nodeType(s *unix.Stat_t) fs.InodeType {
@@ -98,7 +98,7 @@ type dirInfo struct {
// isBlockError unwraps os errors and checks if they are caused by EAGAIN or
// EWOULDBLOCK. This is so they can be transformed into syserror.ErrWouldBlock.
func isBlockError(err error) bool {
- if err == syserror.EAGAIN || err == syserror.EWOULDBLOCK {
+ if linuxerr.Equals(linuxerr.EAGAIN, err) || linuxerr.Equals(linuxerr.EWOULDBLOCK, err) {
return true
}
if pe, ok := err.(*os.PathError); ok {
diff --git a/pkg/sentry/fs/inode.go b/pkg/sentry/fs/inode.go
index 41a3c2047..ec204e5cf 100644
--- a/pkg/sentry/fs/inode.go
+++ b/pkg/sentry/fs/inode.go
@@ -17,6 +17,7 @@ package fs
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
@@ -298,7 +299,7 @@ func (i *Inode) RemoveXattr(ctx context.Context, d *Dirent, name string) error {
func (i *Inode) CheckPermission(ctx context.Context, p PermMask) error {
// First check the outer-most mounted filesystem.
if p.Write && i.MountSource.Flags.ReadOnly {
- return syserror.EROFS
+ return linuxerr.EROFS
}
if i.overlay != nil {
@@ -312,7 +313,7 @@ func (i *Inode) CheckPermission(ctx context.Context, p PermMask) error {
// we should not attempt to modify the writable layer if it
// is mounted read-only.
if p.Write && overlayUpperMountSource(i.MountSource).Flags.ReadOnly {
- return syserror.EROFS
+ return linuxerr.EROFS
}
}
@@ -324,7 +325,7 @@ func (i *Inode) check(ctx context.Context, p PermMask) error {
return overlayCheck(ctx, i.overlay, p)
}
if !i.InodeOperations.Check(ctx, i, p) {
- return syserror.EACCES
+ return linuxerr.EACCES
}
return nil
}
diff --git a/pkg/sentry/fs/inode_operations.go b/pkg/sentry/fs/inode_operations.go
index 2bbfb72ef..98e9fb2b1 100644
--- a/pkg/sentry/fs/inode_operations.go
+++ b/pkg/sentry/fs/inode_operations.go
@@ -283,7 +283,7 @@ type InodeOperations interface {
//
// Any error returned from Getlink other than ErrResolveViaReadlink
// indicates the caller's inability to traverse this Inode as a link
- // (e.g. syserror.ENOLINK indicates that the Inode is not a link,
+ // (e.g. linuxerr.ENOLINK indicates that the Inode is not a link,
// syscall.EPERM indicates that traversing the link is not allowed, etc).
Getlink(context.Context, *Inode) (*Dirent, error)
diff --git a/pkg/sentry/fs/inode_overlay.go b/pkg/sentry/fs/inode_overlay.go
index e97afc626..c47b9ce58 100644
--- a/pkg/sentry/fs/inode_overlay.go
+++ b/pkg/sentry/fs/inode_overlay.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.dev/gvisor/pkg/syserror"
@@ -71,7 +72,7 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name
// A file could have been created over a whiteout, so we need to
// check if something exists in the upper file system first.
child, err := parent.upper.Lookup(ctx, name)
- if err != nil && err != syserror.ENOENT {
+ if err != nil && !linuxerr.Equals(linuxerr.ENOENT, err) {
// We encountered an error that an overlay cannot handle,
// we must propagate it to the caller.
parent.copyMu.RUnlock()
@@ -125,7 +126,7 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name
// Check the lower file system.
child, err := parent.lower.Lookup(ctx, name)
// Same song and dance as above.
- if err != nil && err != syserror.ENOENT {
+ if err != nil && !linuxerr.Equals(linuxerr.ENOENT, err) {
// Don't leak resources.
if upperInode != nil {
upperInode.DecRef(ctx)
@@ -343,7 +344,7 @@ func overlayRemove(ctx context.Context, o *overlayEntry, parent *Dirent, child *
return err
}
if ser.Written() != 0 {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
}
if child.Inode.overlay.upper != nil {
@@ -374,7 +375,7 @@ func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, rena
// Maybe some day we can allow the more complicated case of
// non-overlay X overlay renames, but that's not necessary right now.
if renamed.Inode.overlay == nil || newParent.Inode.overlay == nil || oldParent.Inode.overlay == nil {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if replacement {
@@ -396,7 +397,7 @@ func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, rena
// newName has been removed out from under us. That's fine;
// filesystems where that can happen must handle stale
// 'replaced'.
- if err != nil && err != syserror.ENOENT {
+ if err != nil && !linuxerr.Equals(linuxerr.ENOENT, err) {
return err
}
if err == nil {
@@ -420,7 +421,7 @@ func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, rena
// need to bother checking for them.
if len(children) > 0 {
replaced.DecRef(ctx)
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
}
@@ -552,7 +553,7 @@ func overlayGetXattr(ctx context.Context, o *overlayEntry, name string, size uin
// Don't forward the value of the extended attribute if it would
// unexpectedly change the behavior of a wrapping overlay layer.
if isXattrOverlay(name) {
- return "", syserror.ENODATA
+ return "", linuxerr.ENODATA
}
o.copyMu.RLock()
@@ -568,7 +569,7 @@ func overlayGetXattr(ctx context.Context, o *overlayEntry, name string, size uin
func overlaySetXattr(ctx context.Context, o *overlayEntry, d *Dirent, name, value string, flags uint32) error {
// Don't allow changes to overlay xattrs through a setxattr syscall.
if isXattrOverlay(name) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := copyUp(ctx, d); err != nil {
@@ -600,7 +601,7 @@ func overlayListXattr(ctx context.Context, o *overlayEntry, size uint64) (map[st
func overlayRemoveXattr(ctx context.Context, o *overlayEntry, d *Dirent, name string) error {
// Don't allow changes to overlay xattrs through a removexattr syscall.
if isXattrOverlay(name) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := copyUp(ctx, d); err != nil {
@@ -687,7 +688,7 @@ func overlayGetlink(ctx context.Context, o *overlayEntry) (*Dirent, error) {
dirent.DecRef(ctx)
// Claim that the path is not accessible.
- err = syserror.EACCES
+ err = linuxerr.EACCES
log.Warningf("Getlink not supported in overlay for %q", name)
}
return nil, err
diff --git a/pkg/sentry/fs/inode_overlay_test.go b/pkg/sentry/fs/inode_overlay_test.go
index aa9851b26..a3800d700 100644
--- a/pkg/sentry/fs/inode_overlay_test.go
+++ b/pkg/sentry/fs/inode_overlay_test.go
@@ -18,11 +18,11 @@ import (
"testing"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/fs/ramfs"
"gvisor.dev/gvisor/pkg/sentry/kernel/contexttest"
- "gvisor.dev/gvisor/pkg/syserror"
)
func TestLookup(t *testing.T) {
@@ -191,11 +191,11 @@ func TestLookup(t *testing.T) {
} {
t.Run(test.desc, func(t *testing.T) {
dirent, err := test.dir.Lookup(ctx, test.name)
- if test.found && (err == syserror.ENOENT || dirent.IsNegative()) {
+ if test.found && (linuxerr.Equals(linuxerr.ENOENT, err) || dirent.IsNegative()) {
t.Fatalf("lookup %q expected to find positive dirent, got dirent %v err %v", test.name, dirent, err)
}
if !test.found {
- if err != syserror.ENOENT && !dirent.IsNegative() {
+ if !linuxerr.Equals(linuxerr.ENOENT, err) && !dirent.IsNegative() {
t.Errorf("lookup %q expected to return ENOENT or negative dirent, got dirent %v err %v", test.name, dirent, err)
}
// Nothing more to check.
@@ -389,7 +389,7 @@ func (d *dir) GetXattr(_ context.Context, _ *fs.Inode, name string, _ uint64) (s
return "y", nil
}
}
- return "", syserror.ENOATTR
+ return "", linuxerr.ENOATTR
}
// GetFile implements InodeOperations.GetFile.
diff --git a/pkg/sentry/fs/inotify.go b/pkg/sentry/fs/inotify.go
index 1b83643db..cf4d6d118 100644
--- a/pkg/sentry/fs/inotify.go
+++ b/pkg/sentry/fs/inotify.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -116,7 +117,7 @@ func (i *Inotify) Readiness(mask waiter.EventMask) waiter.EventMask {
// Seek implements FileOperations.Seek.
func (*Inotify) Seek(context.Context, *File, SeekWhence, int64) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Readdir implements FileOperatons.Readdir.
@@ -126,13 +127,13 @@ func (*Inotify) Readdir(context.Context, *File, DentrySerializer) (int64, error)
// Write implements FileOperations.Write.
func (*Inotify) Write(context.Context, *File, usermem.IOSequence, int64) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Read implements FileOperations.Read.
func (i *Inotify) Read(ctx context.Context, _ *File, dst usermem.IOSequence, _ int64) (int64, error) {
if dst.NumBytes() < inotifyEventBaseSize {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
i.evMu.Lock()
@@ -156,7 +157,7 @@ func (i *Inotify) Read(ctx context.Context, _ *File, dst usermem.IOSequence, _ i
// write some events out.
return writeLen, nil
}
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Linux always dequeues an available event as long as there's enough
@@ -183,7 +184,7 @@ func (*Inotify) WriteTo(context.Context, *File, io.Writer, int64, bool) (int64,
// Fsync implements FileOperations.Fsync.
func (*Inotify) Fsync(context.Context, *File, int64, int64, SyncType) error {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// ReadFrom implements FileOperations.ReadFrom.
@@ -198,7 +199,7 @@ func (*Inotify) Flush(context.Context, *File) error {
// ConfigureMMap implements FileOperations.ConfigureMMap.
func (*Inotify) ConfigureMMap(context.Context, *File, *memmap.MMapOpts) error {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
// UnstableAttr implements FileOperations.UnstableAttr.
@@ -329,7 +330,7 @@ func (i *Inotify) RmWatch(ctx context.Context, wd int32) error {
watch, ok := i.watches[wd]
if !ok {
i.mu.Unlock()
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Remove the watch from this instance.
diff --git a/pkg/sentry/fs/mock.go b/pkg/sentry/fs/mock.go
index 1d6ea5736..fba7b961b 100644
--- a/pkg/sentry/fs/mock.go
+++ b/pkg/sentry/fs/mock.go
@@ -16,7 +16,7 @@ package fs
import (
"gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
)
// MockInodeOperations implements InodeOperations for testing Inodes.
@@ -109,7 +109,7 @@ func (n *MockInodeOperations) SetPermissions(context.Context, *Inode, FilePermis
// SetOwner implements fs.InodeOperations.SetOwner.
func (*MockInodeOperations) SetOwner(context.Context, *Inode, FileOwner) error {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// SetTimestamps implements fs.InodeOperations.SetTimestamps.
@@ -172,5 +172,5 @@ func (n *MockInodeOperations) RemoveDirectory(context.Context, *Inode, string) e
// Getlink implements fs.InodeOperations.Getlink.
func (n *MockInodeOperations) Getlink(context.Context, *Inode) (*Dirent, error) {
- return nil, syserror.ENOLINK
+ return nil, linuxerr.ENOLINK
}
diff --git a/pkg/sentry/fs/mounts.go b/pkg/sentry/fs/mounts.go
index 243098a09..3de1c5561 100644
--- a/pkg/sentry/fs/mounts.go
+++ b/pkg/sentry/fs/mounts.go
@@ -20,6 +20,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sync"
@@ -281,7 +282,7 @@ func (mns *MountNamespace) withMountLocked(node *Dirent, fn func() error) error
// Linux allows mounting over the root (?). It comes with a strange set
// of semantics. We'll just not do this for now.
if node.parent == nil {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
// For both mount and unmount, we take this lock so we can swap out the
@@ -357,7 +358,7 @@ func (mns *MountNamespace) Unmount(ctx context.Context, node *Dirent, detachOnly
orig, ok := mns.mounts[node]
if !ok {
// node is not a mount point.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if orig.previous == nil {
@@ -380,7 +381,7 @@ func (mns *MountNamespace) Unmount(ctx context.Context, node *Dirent, detachOnly
if refs := m.DirentRefs(); refs < 2 {
panic(fmt.Sprintf("have %d refs on unmount, expect 2 or more", refs))
} else if refs != 2 {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
}
@@ -566,8 +567,8 @@ func (mns *MountNamespace) resolve(ctx context.Context, root, node *Dirent, rema
// Resolve the path.
target, err := node.Inode.Getlink(ctx)
- switch err {
- case nil:
+ switch {
+ case err == nil:
// Make sure we didn't exhaust the traversal budget.
if *remainingTraversals == 0 {
target.DecRef(ctx)
@@ -577,11 +578,11 @@ func (mns *MountNamespace) resolve(ctx context.Context, root, node *Dirent, rema
node.DecRef(ctx) // Drop the original reference.
return target, nil
- case unix.ENOLINK:
+ case linuxerr.Equals(linuxerr.ENOLINK, err):
// Not a symlink.
return node, nil
- case ErrResolveViaReadlink:
+ case err == ErrResolveViaReadlink:
defer node.DecRef(ctx) // See above.
// First, check if we should traverse.
diff --git a/pkg/sentry/fs/overlay.go b/pkg/sentry/fs/overlay.go
index f96f5a3e5..7e72e47b5 100644
--- a/pkg/sentry/fs/overlay.go
+++ b/pkg/sentry/fs/overlay.go
@@ -19,11 +19,11 @@ import (
"strings"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
// The virtual filesystem implements an overlay configuration. For a high-level
@@ -218,7 +218,7 @@ func newOverlayEntry(ctx context.Context, upper *Inode, lower *Inode, lowerExist
// We don't support copying up from character devices,
// named pipes, or anything weird (like proc files).
log.Warningf("%s not supported in lower filesytem", lower.StableAttr.Type)
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
}
return &overlayEntry{
diff --git a/pkg/sentry/fs/proc/BUILD b/pkg/sentry/fs/proc/BUILD
index 7af7e0b45..e6d74b949 100644
--- a/pkg/sentry/fs/proc/BUILD
+++ b/pkg/sentry/fs/proc/BUILD
@@ -30,6 +30,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/sentry/fs",
diff --git a/pkg/sentry/fs/proc/exec_args.go b/pkg/sentry/fs/proc/exec_args.go
index 24426b225..379429ab2 100644
--- a/pkg/sentry/fs/proc/exec_args.go
+++ b/pkg/sentry/fs/proc/exec_args.go
@@ -21,11 +21,11 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -104,7 +104,7 @@ var _ fs.FileOperations = (*execArgFile)(nil)
// Read reads the exec arg from the process's address space..
func (f *execArgFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
m, err := getTaskMM(f.t)
diff --git a/pkg/sentry/fs/proc/net.go b/pkg/sentry/fs/proc/net.go
index 91c35eea9..187e9a921 100644
--- a/pkg/sentry/fs/proc/net.go
+++ b/pkg/sentry/fs/proc/net.go
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -34,7 +35,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/socket"
"gvisor.dev/gvisor/pkg/sentry/socket/unix"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
@@ -291,7 +291,7 @@ func (n *netSnmp) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([]s
continue
}
if err := n.s.Statistics(stat, line.prefix); err != nil {
- if err == syserror.EOPNOTSUPP {
+ if linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
log.Infof("Failed to retrieve %s of /proc/net/snmp: %v", line.prefix, err)
} else {
log.Warningf("Failed to retrieve %s of /proc/net/snmp: %v", line.prefix, err)
diff --git a/pkg/sentry/fs/proc/proc.go b/pkg/sentry/fs/proc/proc.go
index 2f2a9f920..546b57287 100644
--- a/pkg/sentry/fs/proc/proc.go
+++ b/pkg/sentry/fs/proc/proc.go
@@ -21,6 +21,7 @@ import (
"strconv"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/fs/proc/device"
@@ -130,7 +131,7 @@ func (s *self) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
}
// Who is reading this link?
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
// threadSelf is more magical than "self" link.
@@ -154,7 +155,7 @@ func (s *threadSelf) Readlink(ctx context.Context, inode *fs.Inode) (string, err
}
// Who is reading this link?
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
// Lookup loads an Inode at name into a Dirent.
diff --git a/pkg/sentry/fs/proc/seqfile/BUILD b/pkg/sentry/fs/proc/seqfile/BUILD
index 713b81e08..90bd32345 100644
--- a/pkg/sentry/fs/proc/seqfile/BUILD
+++ b/pkg/sentry/fs/proc/seqfile/BUILD
@@ -9,13 +9,13 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/sentry/fs",
"//pkg/sentry/fs/fsutil",
"//pkg/sentry/fs/proc/device",
"//pkg/sentry/kernel/time",
"//pkg/sync",
- "//pkg/syserror",
"//pkg/usermem",
"//pkg/waiter",
],
diff --git a/pkg/sentry/fs/proc/seqfile/seqfile.go b/pkg/sentry/fs/proc/seqfile/seqfile.go
index b01688b1d..77270814e 100644
--- a/pkg/sentry/fs/proc/seqfile/seqfile.go
+++ b/pkg/sentry/fs/proc/seqfile/seqfile.go
@@ -20,13 +20,13 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/fs/proc/device"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -204,7 +204,7 @@ var _ fs.FileOperations = (*seqFileOperations)(nil)
// Write implements fs.FileOperations.Write.
func (*seqFileOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
- return 0, syserror.EACCES
+ return 0, linuxerr.EACCES
}
// Read implements fs.FileOperations.Read.
diff --git a/pkg/sentry/fs/proc/sys.go b/pkg/sentry/fs/proc/sys.go
index b998fb75d..085aa6d61 100644
--- a/pkg/sentry/fs/proc/sys.go
+++ b/pkg/sentry/fs/proc/sys.go
@@ -77,6 +77,27 @@ func (*overcommitMemory) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandl
}, 0
}
+// +stateify savable
+type maxMapCount struct{}
+
+// NeedsUpdate implements seqfile.SeqSource.
+func (*maxMapCount) NeedsUpdate(int64) bool {
+ return true
+}
+
+// ReadSeqFileData implements seqfile.SeqSource.
+func (*maxMapCount) ReadSeqFileData(ctx context.Context, h seqfile.SeqHandle) ([]seqfile.SeqData, int64) {
+ if h != nil {
+ return nil, 0
+ }
+ return []seqfile.SeqData{
+ {
+ Buf: []byte("2147483647\n"),
+ Handle: (*maxMapCount)(nil),
+ },
+ }, 0
+}
+
func (p *proc) newKernelDir(ctx context.Context, msrc *fs.MountSource) *fs.Inode {
h := hostname{
SimpleFileInode: *fsutil.NewSimpleFileInode(ctx, fs.RootOwner, fs.FilePermsFromMode(0444), linux.PROC_SUPER_MAGIC),
@@ -96,6 +117,7 @@ func (p *proc) newKernelDir(ctx context.Context, msrc *fs.MountSource) *fs.Inode
func (p *proc) newVMDir(ctx context.Context, msrc *fs.MountSource) *fs.Inode {
children := map[string]*fs.Inode{
+ "max_map_count": seqfile.NewSeqFileInode(ctx, &maxMapCount{}, msrc),
"mmap_min_addr": seqfile.NewSeqFileInode(ctx, &mmapMinAddrData{p.k}, msrc),
"overcommit_memory": seqfile.NewSeqFileInode(ctx, &overcommitMemory{}, msrc),
}
diff --git a/pkg/sentry/fs/proc/sys_net.go b/pkg/sentry/fs/proc/sys_net.go
index 4893af56b..71f37d582 100644
--- a/pkg/sentry/fs/proc/sys_net.go
+++ b/pkg/sentry/fs/proc/sys_net.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
@@ -28,7 +29,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/fs/ramfs"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
@@ -592,7 +592,7 @@ func (pf *portRangeFile) Write(ctx context.Context, _ *fs.File, src usermem.IOSe
// Port numbers must be uint16s.
if ports[0] < 0 || ports[1] < 0 || ports[0] > math.MaxUint16 || ports[1] > math.MaxUint16 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if err := pf.inode.stack.SetPortRange(uint16(ports[0]), uint16(ports[1])); err != nil {
diff --git a/pkg/sentry/fs/proc/task.go b/pkg/sentry/fs/proc/task.go
index ae5ed25f9..7a7af31e4 100644
--- a/pkg/sentry/fs/proc/task.go
+++ b/pkg/sentry/fs/proc/task.go
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
@@ -61,7 +62,7 @@ func getTaskMM(t *kernel.Task) (*mm.MemoryManager, error) {
func checkTaskState(t *kernel.Task) error {
switch t.ExitState() {
case kernel.TaskExitZombie:
- return syserror.EACCES
+ return linuxerr.EACCES
case kernel.TaskExitDead:
return syserror.ESRCH
}
@@ -272,7 +273,7 @@ func (e *exe) executable() (file fsbridge.File, err error) {
e.t.WithMuLocked(func(t *kernel.Task) {
mm := t.MemoryManager()
if mm == nil {
- err = syserror.EACCES
+ err = linuxerr.EACCES
return
}
@@ -290,7 +291,7 @@ func (e *exe) executable() (file fsbridge.File, err error) {
// Readlink implements fs.InodeOperations.
func (e *exe) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
if !kernel.ContextCanTrace(ctx, e.t, false) {
- return "", syserror.EACCES
+ return "", linuxerr.EACCES
}
// Pull out the executable for /proc/TID/exe.
@@ -323,7 +324,7 @@ func newCwd(ctx context.Context, t *kernel.Task, msrc *fs.MountSource) *fs.Inode
// Readlink implements fs.InodeOperations.
func (e *cwd) Readlink(ctx context.Context, inode *fs.Inode) (string, error) {
if !kernel.ContextCanTrace(ctx, e.t, false) {
- return "", syserror.EACCES
+ return "", linuxerr.EACCES
}
if err := checkTaskState(e.t); err != nil {
return "", err
@@ -380,7 +381,7 @@ func (n *namespaceSymlink) Readlink(ctx context.Context, inode *fs.Inode) (strin
// Getlink implements fs.InodeOperations.Getlink.
func (n *namespaceSymlink) Getlink(ctx context.Context, inode *fs.Inode) (*fs.Dirent, error) {
if !kernel.ContextCanTrace(ctx, n.t, false) {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
if err := checkTaskState(n.t); err != nil {
return nil, err
@@ -448,7 +449,7 @@ func (m *memData) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileF
// Permission to read this file is governed by PTRACE_MODE_ATTACH_FSCREDS
// Since we dont implement setfsuid/setfsgid we can just use PTRACE_MODE_ATTACH
if !kernel.ContextCanTrace(ctx, m.t, true) {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
if err := checkTaskState(m.t); err != nil {
return nil, err
@@ -867,7 +868,7 @@ var _ fs.FileOperations = (*commFile)(nil)
// Read implements fs.FileOperations.Read.
func (f *commFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
buf := []byte(f.t.Name() + "\n")
@@ -922,7 +923,7 @@ type auxvecFile struct {
// Read implements fs.FileOperations.Read.
func (f *auxvecFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
m, err := getTaskMM(f.t)
diff --git a/pkg/sentry/fs/proc/uid_gid_map.go b/pkg/sentry/fs/proc/uid_gid_map.go
index 30d5ad4cf..fcdc1e7bd 100644
--- a/pkg/sentry/fs/proc/uid_gid_map.go
+++ b/pkg/sentry/fs/proc/uid_gid_map.go
@@ -21,12 +21,12 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -108,7 +108,7 @@ const maxIDMapLines = 5
// Read implements fs.FileOperations.Read.
func (imfo *idMapFileOperations) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
var entries []auth.IDMapEntry
if imfo.iops.gids {
@@ -134,7 +134,7 @@ func (imfo *idMapFileOperations) Write(ctx context.Context, file *fs.File, src u
// the file ..." - user_namespaces(7)
srclen := src.NumBytes()
if srclen >= hostarch.PageSize || offset != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
b := make([]byte, srclen)
if _, err := src.CopyIn(ctx, b); err != nil {
@@ -154,7 +154,7 @@ func (imfo *idMapFileOperations) Write(ctx context.Context, file *fs.File, src u
}
lines := bytes.SplitN(b, []byte("\n"), maxIDMapLines+1)
if len(lines) > maxIDMapLines {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
entries := make([]auth.IDMapEntry, len(lines))
@@ -162,7 +162,7 @@ func (imfo *idMapFileOperations) Write(ctx context.Context, file *fs.File, src u
var e auth.IDMapEntry
_, err := fmt.Sscan(string(l), &e.FirstID, &e.FirstParentID, &e.Length)
if err != nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
entries[i] = e
}
diff --git a/pkg/sentry/fs/proc/uptime.go b/pkg/sentry/fs/proc/uptime.go
index c0f6fb802..ac896f963 100644
--- a/pkg/sentry/fs/proc/uptime.go
+++ b/pkg/sentry/fs/proc/uptime.go
@@ -20,10 +20,10 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -74,7 +74,7 @@ type uptimeFile struct {
// Read implements fs.FileOperations.Read.
func (f *uptimeFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
now := ktime.NowFromContext(ctx)
diff --git a/pkg/sentry/fs/ramfs/BUILD b/pkg/sentry/fs/ramfs/BUILD
index 4a3d9636b..b46567cf8 100644
--- a/pkg/sentry/fs/ramfs/BUILD
+++ b/pkg/sentry/fs/ramfs/BUILD
@@ -14,6 +14,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/sentry/fs",
"//pkg/sentry/fs/anon",
diff --git a/pkg/sentry/fs/ramfs/dir.go b/pkg/sentry/fs/ramfs/dir.go
index 19990f9db..33023af77 100644
--- a/pkg/sentry/fs/ramfs/dir.go
+++ b/pkg/sentry/fs/ramfs/dir.go
@@ -21,6 +21,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
@@ -178,7 +179,7 @@ func (d *Dir) Children() ([]string, map[string]fs.DentAttr) {
func (d *Dir) removeChildLocked(ctx context.Context, name string) (*fs.Inode, error) {
inode, ok := d.children[name]
if !ok {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
delete(d.children, name)
@@ -208,7 +209,7 @@ func (d *Dir) removeChildLocked(ctx context.Context, name string) (*fs.Inode, er
// Remove removes the named non-directory.
func (d *Dir) Remove(ctx context.Context, _ *fs.Inode, name string) error {
if len(name) > linux.NAME_MAX {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
d.mu.Lock()
@@ -226,7 +227,7 @@ func (d *Dir) Remove(ctx context.Context, _ *fs.Inode, name string) error {
// RemoveDirectory removes the named directory.
func (d *Dir) RemoveDirectory(ctx context.Context, _ *fs.Inode, name string) error {
if len(name) > linux.NAME_MAX {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
d.mu.Lock()
@@ -240,7 +241,7 @@ func (d *Dir) RemoveDirectory(ctx context.Context, _ *fs.Inode, name string) err
if ok, err := hasChildren(ctx, childInode); err != nil {
return err
} else if ok {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
// Child was empty. Proceed with removal.
@@ -259,7 +260,7 @@ func (d *Dir) RemoveDirectory(ctx context.Context, _ *fs.Inode, name string) err
// with a reference.
func (d *Dir) Lookup(ctx context.Context, _ *fs.Inode, p string) (*fs.Dirent, error) {
if len(p) > linux.NAME_MAX {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
d.mu.Lock()
@@ -292,7 +293,7 @@ func (d *Dir) walkLocked(ctx context.Context, p string) (*fs.Inode, error) {
// makeInodeOperations. It is the common logic for creating a new child.
func (d *Dir) createInodeOperationsCommon(ctx context.Context, name string, makeInodeOperations func() (*fs.Inode, error)) (*fs.Inode, error) {
if len(name) > linux.NAME_MAX {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
d.mu.Lock()
@@ -311,7 +312,7 @@ func (d *Dir) createInodeOperationsCommon(ctx context.Context, name string, make
// Create creates a new Inode with the given name and returns its File.
func (d *Dir) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perms fs.FilePermissions) (*fs.File, error) {
if d.CreateOps == nil || d.CreateOps.NewFile == nil {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
inode, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) {
@@ -333,7 +334,7 @@ func (d *Dir) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.F
// CreateLink returns a new link.
func (d *Dir) CreateLink(ctx context.Context, dir *fs.Inode, oldname, newname string) error {
if d.CreateOps == nil || d.CreateOps.NewSymlink == nil {
- return syserror.EACCES
+ return linuxerr.EACCES
}
_, err := d.createInodeOperationsCommon(ctx, newname, func() (*fs.Inode, error) {
return d.NewSymlink(ctx, dir, oldname)
@@ -344,7 +345,7 @@ func (d *Dir) CreateLink(ctx context.Context, dir *fs.Inode, oldname, newname st
// CreateHardLink creates a new hard link.
func (d *Dir) CreateHardLink(ctx context.Context, dir *fs.Inode, target *fs.Inode, name string) error {
if len(name) > linux.NAME_MAX {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
d.mu.Lock()
@@ -362,7 +363,7 @@ func (d *Dir) CreateHardLink(ctx context.Context, dir *fs.Inode, target *fs.Inod
// CreateDirectory returns a new subdirectory.
func (d *Dir) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, perms fs.FilePermissions) error {
if d.CreateOps == nil || d.CreateOps.NewDir == nil {
- return syserror.EACCES
+ return linuxerr.EACCES
}
_, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) {
return d.NewDir(ctx, dir, perms)
@@ -373,7 +374,7 @@ func (d *Dir) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, p
// Bind implements fs.InodeOperations.Bind.
func (d *Dir) Bind(ctx context.Context, dir *fs.Inode, name string, ep transport.BoundEndpoint, perms fs.FilePermissions) (*fs.Dirent, error) {
if d.CreateOps == nil || d.CreateOps.NewBoundEndpoint == nil {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
inode, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) {
return d.NewBoundEndpoint(ctx, dir, ep, perms)
@@ -392,7 +393,7 @@ func (d *Dir) Bind(ctx context.Context, dir *fs.Inode, name string, ep transport
// CreateFifo implements fs.InodeOperations.CreateFifo.
func (d *Dir) CreateFifo(ctx context.Context, dir *fs.Inode, name string, perms fs.FilePermissions) error {
if d.CreateOps == nil || d.CreateOps.NewFifo == nil {
- return syserror.EACCES
+ return linuxerr.EACCES
}
_, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) {
return d.NewFifo(ctx, dir, perms)
@@ -496,14 +497,14 @@ func hasChildren(ctx context.Context, inode *fs.Inode) (bool, error) {
func Rename(ctx context.Context, oldParent fs.InodeOperations, oldName string, newParent fs.InodeOperations, newName string, replacement bool) error {
op, ok := oldParent.(*Dir)
if !ok {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
np, ok := newParent.(*Dir)
if !ok {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if len(newName) > linux.NAME_MAX {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
np.mu.Lock()
@@ -521,7 +522,7 @@ func Rename(ctx context.Context, oldParent fs.InodeOperations, oldName string, n
if ok, err := hasChildren(ctx, replaced); err != nil {
return err
} else if ok {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
}
diff --git a/pkg/sentry/fs/ramfs/socket.go b/pkg/sentry/fs/ramfs/socket.go
index d0c565879..dc9d27bb3 100644
--- a/pkg/sentry/fs/ramfs/socket.go
+++ b/pkg/sentry/fs/ramfs/socket.go
@@ -17,10 +17,10 @@ package ramfs
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -64,7 +64,7 @@ func (s *Socket) BoundEndpoint(*fs.Inode, string) transport.BoundEndpoint {
// GetFile implements fs.FileOperations.GetFile.
func (s *Socket) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
// +stateify savable
diff --git a/pkg/sentry/fs/splice.go b/pkg/sentry/fs/splice.go
index 33da82868..fff4befb2 100644
--- a/pkg/sentry/fs/splice.go
+++ b/pkg/sentry/fs/splice.go
@@ -19,6 +19,7 @@ import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -28,7 +29,7 @@ import (
func Splice(ctx context.Context, dst *File, src *File, opts SpliceOpts) (int64, error) {
// Verify basic file flag permissions.
if !dst.Flags().Write || !src.Flags().Read {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Check whether or not the objects being sliced are stream-oriented
@@ -139,7 +140,7 @@ func Splice(ctx context.Context, dst *File, src *File, opts SpliceOpts) (int64,
// Attempt to do a WriteTo; this is likely the most efficient.
n, err := src.FileOperations.WriteTo(ctx, src, w, opts.Length, opts.Dup)
- if n == 0 && err == syserror.ENOSYS && !opts.Dup {
+ if n == 0 && linuxerr.Equals(linuxerr.ENOSYS, err) && !opts.Dup {
// Attempt as a ReadFrom. If a WriteTo, a ReadFrom may also be
// more efficient than a copy if buffers are cached or readily
// available. (It's unlikely that they can actually be donated).
@@ -151,7 +152,7 @@ func Splice(ctx context.Context, dst *File, src *File, opts SpliceOpts) (int64,
// if we block at some point, we could lose data. If the source is
// not a pipe then reading is not destructive; if the destination
// is a regular file, then it is guaranteed not to block writing.
- if n == 0 && err == syserror.ENOSYS && !opts.Dup && (!dstPipe || !srcPipe) {
+ if n == 0 && linuxerr.Equals(linuxerr.ENOSYS, err) && !opts.Dup && (!dstPipe || !srcPipe) {
// Fallback to an in-kernel copy.
n, err = io.Copy(w, &io.LimitedReader{
R: r,
diff --git a/pkg/sentry/fs/timerfd/BUILD b/pkg/sentry/fs/timerfd/BUILD
index c7977a217..0148b33cf 100644
--- a/pkg/sentry/fs/timerfd/BUILD
+++ b/pkg/sentry/fs/timerfd/BUILD
@@ -8,6 +8,7 @@ go_library(
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/sentry/fs",
"//pkg/sentry/fs/anon",
diff --git a/pkg/sentry/fs/timerfd/timerfd.go b/pkg/sentry/fs/timerfd/timerfd.go
index c8ebe256c..093a14c1f 100644
--- a/pkg/sentry/fs/timerfd/timerfd.go
+++ b/pkg/sentry/fs/timerfd/timerfd.go
@@ -20,6 +20,7 @@ import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/anon"
@@ -121,7 +122,7 @@ func (t *TimerOperations) EventUnregister(e *waiter.Entry) {
func (t *TimerOperations) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
const sizeofUint64 = 8
if dst.NumBytes() < sizeofUint64 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if val := atomic.SwapUint64(&t.val, 0); val != 0 {
var buf [sizeofUint64]byte
@@ -138,7 +139,7 @@ func (t *TimerOperations) Read(ctx context.Context, file *fs.File, dst usermem.I
// Write implements fs.FileOperations.Write.
func (t *TimerOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Notify implements ktime.TimerListener.Notify.
diff --git a/pkg/sentry/fs/tmpfs/BUILD b/pkg/sentry/fs/tmpfs/BUILD
index 90398376a..511fffb43 100644
--- a/pkg/sentry/fs/tmpfs/BUILD
+++ b/pkg/sentry/fs/tmpfs/BUILD
@@ -15,6 +15,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/safemem",
"//pkg/sentry/device",
@@ -30,7 +31,6 @@ go_library(
"//pkg/sentry/socket/unix/transport",
"//pkg/sentry/usage",
"//pkg/sync",
- "//pkg/syserror",
"//pkg/usermem",
"//pkg/waiter",
],
diff --git a/pkg/sentry/fs/tmpfs/inode_file.go b/pkg/sentry/fs/tmpfs/inode_file.go
index 7faa822f0..1974523bf 100644
--- a/pkg/sentry/fs/tmpfs/inode_file.go
+++ b/pkg/sentry/fs/tmpfs/inode_file.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -31,7 +32,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/usage"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -150,7 +150,7 @@ func (*fileInodeOperations) Rename(ctx context.Context, inode *fs.Inode, oldPare
// GetFile implements fs.InodeOperations.GetFile.
func (f *fileInodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
if fs.IsSocket(d.Inode.StableAttr) {
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
if flags.Write {
@@ -217,7 +217,7 @@ func (f *fileInodeOperations) Truncate(ctx context.Context, _ *fs.Inode, size in
fallthrough
case oldSize > size && f.seals&linux.F_SEAL_SHRINK != 0: // Shrink sealed
f.dataMu.Unlock()
- return syserror.EPERM
+ return linuxerr.EPERM
}
if oldSize != size {
@@ -278,7 +278,7 @@ func (f *fileInodeOperations) Allocate(ctx context.Context, _ *fs.Inode, offset,
// Check if current seals allow growth.
if f.seals&linux.F_SEAL_GROW != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
f.attr.Size = newSize
@@ -455,13 +455,13 @@ func (rw *fileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error)
end := fs.WriteEndOffset(rw.offset, int64(srcs.NumBytes()))
if end == math.MaxInt64 {
// Overflow.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Check if seals prevent either file growth or all writes.
switch {
case rw.f.seals&linux.F_SEAL_WRITE != 0: // Write sealed
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
case end > rw.f.attr.Size && rw.f.seals&linux.F_SEAL_GROW != 0: // Grow sealed
// When growth is sealed, Linux effectively allows writes which would
// normally grow the file to partially succeed up to the current EOF,
@@ -482,7 +482,7 @@ func (rw *fileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error)
}
if end <= rw.offset {
// Truncation would result in no data being written.
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
}
@@ -550,7 +550,7 @@ func (f *fileInodeOperations) AddMapping(ctx context.Context, ms memmap.MappingS
// Reject writable mapping if F_SEAL_WRITE is set.
if f.seals&linux.F_SEAL_WRITE != 0 && writable {
- return syserror.EPERM
+ return linuxerr.EPERM
}
f.mappings.AddMapping(ms, ar, offset, writable)
@@ -655,7 +655,7 @@ func GetSeals(inode *fs.Inode) (uint32, error) {
return f.seals, nil
}
// Not a memfd inode.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// AddSeals adds new file seals to a memfd inode.
@@ -668,13 +668,13 @@ func AddSeals(inode *fs.Inode, val uint32) error {
if f.seals&linux.F_SEAL_SEAL != 0 {
// Seal applied which prevents addition of any new seals.
- return syserror.EPERM
+ return linuxerr.EPERM
}
// F_SEAL_WRITE can only be added if there are no active writable maps.
if f.seals&linux.F_SEAL_WRITE == 0 && val&linux.F_SEAL_WRITE != 0 {
if f.writableMappingPages > 0 {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
}
@@ -683,5 +683,5 @@ func AddSeals(inode *fs.Inode, val uint32) error {
return nil
}
// Not a memfd inode.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
diff --git a/pkg/sentry/fs/tmpfs/tmpfs.go b/pkg/sentry/fs/tmpfs/tmpfs.go
index 6aa8ff331..9a835b556 100644
--- a/pkg/sentry/fs/tmpfs/tmpfs.go
+++ b/pkg/sentry/fs/tmpfs/tmpfs.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
@@ -28,7 +29,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.dev/gvisor/pkg/sentry/usage"
- "gvisor.dev/gvisor/pkg/syserror"
)
var fsInfo = fs.Info{
@@ -49,7 +49,7 @@ var fsInfo = fs.Info{
func rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string, replacement bool) error {
// Don't allow renames across different mounts.
if newParent.MountSource != oldParent.MountSource {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
op := oldParent.InodeOperations.(*Dir)
diff --git a/pkg/sentry/fs/tty/BUILD b/pkg/sentry/fs/tty/BUILD
index 86ada820e..5933cb67b 100644
--- a/pkg/sentry/fs/tty/BUILD
+++ b/pkg/sentry/fs/tty/BUILD
@@ -17,6 +17,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/marshal/primitive",
"//pkg/refs",
diff --git a/pkg/sentry/fs/tty/dir.go b/pkg/sentry/fs/tty/dir.go
index 13c9dbe7d..3242dcb6a 100644
--- a/pkg/sentry/fs/tty/dir.go
+++ b/pkg/sentry/fs/tty/dir.go
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
@@ -170,54 +171,54 @@ func (d *dirInodeOperations) Lookup(ctx context.Context, dir *fs.Inode, name str
//
// Creation is never allowed.
func (d *dirInodeOperations) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perm fs.FilePermissions) (*fs.File, error) {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
// CreateDirectory implements fs.InodeOperations.CreateDirectory.
//
// Creation is never allowed.
func (d *dirInodeOperations) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, perm fs.FilePermissions) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// CreateLink implements fs.InodeOperations.CreateLink.
//
// Creation is never allowed.
func (d *dirInodeOperations) CreateLink(ctx context.Context, dir *fs.Inode, oldname, newname string) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// CreateHardLink implements fs.InodeOperations.CreateHardLink.
//
// Creation is never allowed.
func (d *dirInodeOperations) CreateHardLink(ctx context.Context, dir *fs.Inode, target *fs.Inode, name string) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// CreateFifo implements fs.InodeOperations.CreateFifo.
//
// Creation is never allowed.
func (d *dirInodeOperations) CreateFifo(ctx context.Context, dir *fs.Inode, name string, perm fs.FilePermissions) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// Remove implements fs.InodeOperations.Remove.
//
// Removal is never allowed.
func (d *dirInodeOperations) Remove(ctx context.Context, dir *fs.Inode, name string) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// RemoveDirectory implements fs.InodeOperations.RemoveDirectory.
//
// Removal is never allowed.
func (d *dirInodeOperations) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Bind implements fs.InodeOperations.Bind.
func (d *dirInodeOperations) Bind(ctx context.Context, dir *fs.Inode, name string, data transport.BoundEndpoint, perm fs.FilePermissions) (*fs.Dirent, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// GetFile implements fs.InodeOperations.GetFile.
diff --git a/pkg/sentry/fs/tty/fs.go b/pkg/sentry/fs/tty/fs.go
index 13f4901db..0e5916380 100644
--- a/pkg/sentry/fs/tty/fs.go
+++ b/pkg/sentry/fs/tty/fs.go
@@ -16,9 +16,9 @@ package tty
import (
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/device"
"gvisor.dev/gvisor/pkg/sentry/fs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// ptsDevice is the pseudo-filesystem device.
@@ -64,7 +64,7 @@ func (f *filesystem) Mount(ctx context.Context, device string, flags fs.MountSou
// No options are supported.
if data != "" {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
return newDir(ctx, fs.NewMountSource(ctx, &superOperations{}, f, flags)), nil
diff --git a/pkg/sentry/fs/user/BUILD b/pkg/sentry/fs/user/BUILD
index 66e949c95..4acc73ee0 100644
--- a/pkg/sentry/fs/user/BUILD
+++ b/pkg/sentry/fs/user/BUILD
@@ -12,6 +12,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/log",
"//pkg/sentry/fs",
diff --git a/pkg/sentry/fs/user/path.go b/pkg/sentry/fs/user/path.go
index 124bc95ed..f6eaab2bd 100644
--- a/pkg/sentry/fs/user/path.go
+++ b/pkg/sentry/fs/user/path.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -93,7 +94,7 @@ func resolve(ctx context.Context, mns *fs.MountNamespace, paths []string, name s
binPath := path.Join(p, name)
traversals := uint(linux.MaxSymlinkTraversals)
d, err := mns.FindInode(ctx, root, nil, binPath, &traversals)
- if err == syserror.ENOENT || err == syserror.EACCES {
+ if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.EACCES, err) {
// Didn't find it here.
continue
}
@@ -142,7 +143,7 @@ func resolveVFS2(ctx context.Context, creds *auth.Credentials, mns *vfs.MountNam
Flags: linux.O_RDONLY,
}
dentry, err := root.Mount().Filesystem().VirtualFilesystem().OpenAt(ctx, creds, pop, opts)
- if err == syserror.ENOENT || err == syserror.EACCES {
+ if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.EACCES, err) {
// Didn't find it here.
continue
}
diff --git a/pkg/sentry/fsbridge/BUILD b/pkg/sentry/fsbridge/BUILD
index 6c798f0bd..6a5dda6ac 100644
--- a/pkg/sentry/fsbridge/BUILD
+++ b/pkg/sentry/fsbridge/BUILD
@@ -13,6 +13,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/sentry/fs",
"//pkg/sentry/kernel/auth",
diff --git a/pkg/sentry/fsbridge/fs.go b/pkg/sentry/fsbridge/fs.go
index 9785fd62a..36d7c110a 100644
--- a/pkg/sentry/fsbridge/fs.go
+++ b/pkg/sentry/fsbridge/fs.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -127,7 +128,7 @@ func (l *fsLookup) OpenPath(ctx context.Context, path string, opts vfs.OpenOptio
defer d.DecRef(ctx)
if !resolveFinal && fs.IsSymlink(d.Inode.StableAttr) {
- return nil, syserror.ELOOP
+ return nil, linuxerr.ELOOP
}
fsPerm := openOptionsToPermMask(&opts)
@@ -144,7 +145,7 @@ func (l *fsLookup) OpenPath(ctx context.Context, path string, opts vfs.OpenOptio
if opts.FileExec && d.Inode.StableAttr.Type != fs.RegularFile {
ctx.Infof("%q is not a regular file: %v", path, d.Inode.StableAttr.Type)
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
f, err := d.Inode.GetFile(ctx, d, flagsToFileFlags(opts.Flags))
diff --git a/pkg/sentry/fsimpl/cgroupfs/BUILD b/pkg/sentry/fsimpl/cgroupfs/BUILD
index 37efb641a..4c9c5b344 100644
--- a/pkg/sentry/fsimpl/cgroupfs/BUILD
+++ b/pkg/sentry/fsimpl/cgroupfs/BUILD
@@ -31,6 +31,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/context",
"//pkg/coverage",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/refs",
"//pkg/refsvfs2",
diff --git a/pkg/sentry/fsimpl/cgroupfs/base.go b/pkg/sentry/fsimpl/cgroupfs/base.go
index 6512e9cdb..4290ffe0d 100644
--- a/pkg/sentry/fsimpl/cgroupfs/base.go
+++ b/pkg/sentry/fsimpl/cgroupfs/base.go
@@ -23,10 +23,10 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -133,6 +133,17 @@ func (c *cgroupInode) Controllers() []kernel.CgroupController {
return c.fs.kcontrollers
}
+// tasks returns a snapshot of the tasks inside the cgroup.
+func (c *cgroupInode) tasks() []*kernel.Task {
+ c.fs.tasksMu.RLock()
+ defer c.fs.tasksMu.RUnlock()
+ ts := make([]*kernel.Task, 0, len(c.ts))
+ for t := range c.ts {
+ ts = append(ts, t)
+ }
+ return ts
+}
+
// Enter implements kernel.CgroupImpl.Enter.
func (c *cgroupInode) Enter(t *kernel.Task) {
c.fs.tasksMu.Lock()
@@ -163,10 +174,7 @@ func (d *cgroupProcsData) Generate(ctx context.Context, buf *bytes.Buffer) error
pgids := make(map[kernel.ThreadID]struct{})
- d.fs.tasksMu.RLock()
- defer d.fs.tasksMu.RUnlock()
-
- for task := range d.ts {
+ for _, task := range d.tasks() {
// Map dedups pgid, since iterating over all tasks produces multiple
// entries for the group leaders.
if pgid := currPidns.IDOfThreadGroup(task.ThreadGroup()); pgid != 0 {
@@ -205,10 +213,7 @@ func (d *tasksData) Generate(ctx context.Context, buf *bytes.Buffer) error {
var pids []kernel.ThreadID
- d.fs.tasksMu.RLock()
- defer d.fs.tasksMu.RUnlock()
-
- for task := range d.ts {
+ for _, task := range d.tasks() {
if pid := currPidns.IDOfTask(task); pid != 0 {
pids = append(pids, pid)
}
@@ -248,7 +253,7 @@ func parseInt64FromString(ctx context.Context, src usermem.IOSequence, offset in
// Note: This also handles zero-len writes if offset is beyond the end
// of src, or src is empty.
ctx.Warningf("cgroupfs.parseInt64FromString: failed to parse %q: %v", string(buf), err)
- return 0, int64(n), syserror.EINVAL
+ return 0, int64(n), linuxerr.EINVAL
}
return val, int64(n), nil
diff --git a/pkg/sentry/fsimpl/cgroupfs/cgroupfs.go b/pkg/sentry/fsimpl/cgroupfs/cgroupfs.go
index 54050de3c..24e28a51f 100644
--- a/pkg/sentry/fsimpl/cgroupfs/cgroupfs.go
+++ b/pkg/sentry/fsimpl/cgroupfs/cgroupfs.go
@@ -49,8 +49,9 @@
//
// kernel.CgroupRegistry.mu
// cgroupfs.filesystem.mu
-// Task.mu
-// cgroupfs.filesystem.tasksMu.
+// kernel.TaskSet.mu
+// kernel.Task.mu
+// cgroupfs.filesystem.tasksMu.
package cgroupfs
import (
@@ -61,12 +62,12 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
const (
@@ -166,7 +167,7 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
maxCachedDentries, err = strconv.ParseUint(str, 10, 64)
if err != nil {
ctx.Warningf("sys.FilesystemType.GetFilesystem: invalid dentry cache limit: dentry_cache_limit=%s", str)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
}
@@ -194,7 +195,7 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
if _, ok := mopts["all"]; ok {
if len(wantControllers) > 0 {
ctx.Debugf("cgroupfs.FilesystemType.GetFilesystem: other controllers specified with all: %v", wantControllers)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
delete(mopts, "all")
@@ -208,7 +209,7 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
if len(mopts) != 0 {
ctx.Debugf("cgroupfs.FilesystemType.GetFilesystem: unknown options: %v", mopts)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
k := kernel.KernelFromContext(ctx)
@@ -293,7 +294,7 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
ctx.Infof("cgroupfs.FilesystemType.GetFilesystem: failed to register new hierarchy with controllers %v: %v", wantControllers, err)
rootD.DecRef(ctx)
fs.VFSFilesystem().DecRef(ctx)
- return nil, nil, syserror.EBUSY
+ return nil, nil, linuxerr.EBUSY
}
// Move all existing tasks to the root of the new hierarchy.
@@ -363,7 +364,7 @@ func (*dir) Keep() bool {
// SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
func (*dir) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Open implements kernfs.Inode.Open.
diff --git a/pkg/sentry/fsimpl/devpts/BUILD b/pkg/sentry/fsimpl/devpts/BUILD
index 6af3c3781..50b4c02ef 100644
--- a/pkg/sentry/fsimpl/devpts/BUILD
+++ b/pkg/sentry/fsimpl/devpts/BUILD
@@ -29,6 +29,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/marshal",
"//pkg/marshal/primitive",
diff --git a/pkg/sentry/fsimpl/devpts/devpts.go b/pkg/sentry/fsimpl/devpts/devpts.go
index e75954105..7a488e9fd 100644
--- a/pkg/sentry/fsimpl/devpts/devpts.go
+++ b/pkg/sentry/fsimpl/devpts/devpts.go
@@ -25,6 +25,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -56,7 +57,7 @@ func (*FilesystemType) Name() string {
func (fstype *FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
// No data allowed.
if opts.Data != "" {
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fstype.initOnce.Do(func() {
diff --git a/pkg/sentry/fsimpl/devpts/master.go b/pkg/sentry/fsimpl/devpts/master.go
index 93c031c89..1374fd3be 100644
--- a/pkg/sentry/fsimpl/devpts/master.go
+++ b/pkg/sentry/fsimpl/devpts/master.go
@@ -17,6 +17,7 @@ package devpts
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
@@ -80,7 +81,7 @@ func (mi *masterInode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs
// SetStat implements kernfs.Inode.SetStat
func (mi *masterInode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
if opts.Stat.Mask&linux.STATX_SIZE != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
return mi.InodeAttrs.SetStat(ctx, vfsfs, creds, opts)
}
diff --git a/pkg/sentry/fsimpl/devpts/replica.go b/pkg/sentry/fsimpl/devpts/replica.go
index 96d2054cb..81572b991 100644
--- a/pkg/sentry/fsimpl/devpts/replica.go
+++ b/pkg/sentry/fsimpl/devpts/replica.go
@@ -17,6 +17,7 @@ package devpts
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
@@ -92,7 +93,7 @@ func (ri *replicaInode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vf
// SetStat implements kernfs.Inode.SetStat
func (ri *replicaInode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
if opts.Stat.Mask&linux.STATX_SIZE != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
return ri.InodeAttrs.SetStat(ctx, vfsfs, creds, opts)
}
diff --git a/pkg/sentry/fsimpl/ext/BUILD b/pkg/sentry/fsimpl/ext/BUILD
index 2dbc6bfd5..5e8b464a0 100644
--- a/pkg/sentry/fsimpl/ext/BUILD
+++ b/pkg/sentry/fsimpl/ext/BUILD
@@ -47,6 +47,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fspath",
"//pkg/log",
@@ -88,13 +89,13 @@ go_test(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/marshal/primitive",
"//pkg/sentry/contexttest",
"//pkg/sentry/fsimpl/ext/disklayout",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
- "//pkg/syserror",
"//pkg/test/testutil",
"//pkg/usermem",
"@com_github_google_go_cmp//cmp:go_default_library",
diff --git a/pkg/sentry/fsimpl/ext/block_map_file.go b/pkg/sentry/fsimpl/ext/block_map_file.go
index 1165234f9..79719faed 100644
--- a/pkg/sentry/fsimpl/ext/block_map_file.go
+++ b/pkg/sentry/fsimpl/ext/block_map_file.go
@@ -18,6 +18,7 @@ import (
"io"
"math"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -84,7 +85,7 @@ func (f *blockMapFile) ReadAt(dst []byte, off int64) (int, error) {
}
if off < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
offset := uint64(off)
diff --git a/pkg/sentry/fsimpl/ext/directory.go b/pkg/sentry/fsimpl/ext/directory.go
index 512b70ede..cc067c20e 100644
--- a/pkg/sentry/fsimpl/ext/directory.go
+++ b/pkg/sentry/fsimpl/ext/directory.go
@@ -17,12 +17,12 @@ package ext
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/ext/disklayout"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
// directory represents a directory inode. It holds the childList in memory.
@@ -218,7 +218,7 @@ func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallba
// Seek implements vfs.FileDescriptionImpl.Seek.
func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
if whence != linux.SEEK_SET && whence != linux.SEEK_CUR {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
dir := fd.inode().impl.(*directory)
@@ -234,7 +234,7 @@ func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (in
if offset < 0 {
// lseek(2) specifies that EINVAL should be returned if the resulting offset
// is negative.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
n := int64(len(dir.childMap))
diff --git a/pkg/sentry/fsimpl/ext/ext.go b/pkg/sentry/fsimpl/ext/ext.go
index 38fb7962b..80854b501 100644
--- a/pkg/sentry/fsimpl/ext/ext.go
+++ b/pkg/sentry/fsimpl/ext/ext.go
@@ -22,12 +22,12 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/ext/disklayout"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Name is the name of this filesystem.
@@ -133,13 +133,13 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
// mount(2) specifies that EINVAL should be returned if the superblock is
// invalid.
fs.vfsfs.DecRef(ctx)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// Refuse to mount if the filesystem is incompatible.
if !isCompatible(fs.sb) {
fs.vfsfs.DecRef(ctx)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fs.bgs, err = readBlockGroups(dev, fs.sb)
diff --git a/pkg/sentry/fsimpl/ext/ext_test.go b/pkg/sentry/fsimpl/ext/ext_test.go
index d9fd4590c..db712e71f 100644
--- a/pkg/sentry/fsimpl/ext/ext_test.go
+++ b/pkg/sentry/fsimpl/ext/ext_test.go
@@ -26,12 +26,12 @@ import (
"github.com/google/go-cmp/cmp/cmpopts"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/ext/disklayout"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/test/testutil"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -173,7 +173,7 @@ func TestSeek(t *testing.T) {
}
// EINVAL should be returned if the resulting offset is negative.
- if _, err := fd.Seek(ctx, -1, linux.SEEK_SET); err != syserror.EINVAL {
+ if _, err := fd.Seek(ctx, -1, linux.SEEK_SET); !linuxerr.Equals(linuxerr.EINVAL, err) {
t.Errorf("expected error EINVAL but got %v", err)
}
@@ -187,7 +187,7 @@ func TestSeek(t *testing.T) {
}
// EINVAL should be returned if the resulting offset is negative.
- if _, err := fd.Seek(ctx, -(size + 2), linux.SEEK_CUR); err != syserror.EINVAL {
+ if _, err := fd.Seek(ctx, -(size + 2), linux.SEEK_CUR); !linuxerr.Equals(linuxerr.EINVAL, err) {
t.Errorf("expected error EINVAL but got %v", err)
}
@@ -204,7 +204,7 @@ func TestSeek(t *testing.T) {
}
// EINVAL should be returned if the resulting offset is negative.
- if _, err := fd.Seek(ctx, -(size + 1), linux.SEEK_END); err != syserror.EINVAL {
+ if _, err := fd.Seek(ctx, -(size + 1), linux.SEEK_END); !linuxerr.Equals(linuxerr.EINVAL, err) {
t.Errorf("expected error EINVAL but got %v", err)
}
}
diff --git a/pkg/sentry/fsimpl/ext/extent_file.go b/pkg/sentry/fsimpl/ext/extent_file.go
index 778460107..f449bc8bd 100644
--- a/pkg/sentry/fsimpl/ext/extent_file.go
+++ b/pkg/sentry/fsimpl/ext/extent_file.go
@@ -18,6 +18,7 @@ import (
"io"
"sort"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/ext/disklayout"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -65,7 +66,7 @@ func (f *extentFile) buildExtTree() error {
if f.root.Header.NumEntries > 4 {
// read(2) specifies that EINVAL should be returned if the file is unsuitable
// for reading.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
f.root.Entries = make([]disklayout.ExtentEntryPair, f.root.Header.NumEntries)
@@ -145,7 +146,7 @@ func (f *extentFile) ReadAt(dst []byte, off int64) (int, error) {
}
if off < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if uint64(off) >= f.regFile.inode.diskInode.Size() {
diff --git a/pkg/sentry/fsimpl/ext/file_description.go b/pkg/sentry/fsimpl/ext/file_description.go
index 90b086468..2e9033c1d 100644
--- a/pkg/sentry/fsimpl/ext/file_description.go
+++ b/pkg/sentry/fsimpl/ext/file_description.go
@@ -17,8 +17,8 @@ package ext
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// fileDescription is embedded by ext implementations of
@@ -49,7 +49,7 @@ func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions)
if opts.Stat.Mask == 0 {
return nil
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// SetStat implements vfs.FileDescriptionImpl.StatFS.
diff --git a/pkg/sentry/fsimpl/ext/filesystem.go b/pkg/sentry/fsimpl/ext/filesystem.go
index d4fc484a2..599552fb9 100644
--- a/pkg/sentry/fsimpl/ext/filesystem.go
+++ b/pkg/sentry/fsimpl/ext/filesystem.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/ext/disklayout"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -331,7 +332,7 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
// EROFS is returned if write access is needed.
if vfs.MayWriteFileWithOpenFlags(opts.Flags) || opts.Flags&(linux.O_CREAT|linux.O_EXCL|linux.O_TMPFILE) != 0 {
- return nil, syserror.EROFS
+ return nil, linuxerr.EROFS
}
return inode.open(rp, vfsd, &opts)
}
@@ -344,7 +345,7 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
}
symlink, ok := inode.impl.(*symlink)
if !ok {
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
return symlink.target, nil
}
@@ -396,7 +397,7 @@ func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
return err
}
- return syserror.EROFS
+ return linuxerr.EROFS
}
// MkdirAt implements vfs.FilesystemImpl.MkdirAt.
@@ -409,7 +410,7 @@ func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
return err
}
- return syserror.EROFS
+ return linuxerr.EROFS
}
// MknodAt implements vfs.FilesystemImpl.MknodAt.
@@ -423,7 +424,7 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
return err
}
- return syserror.EROFS
+ return linuxerr.EROFS
}
// RenameAt implements vfs.FilesystemImpl.RenameAt.
@@ -437,7 +438,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
return err
}
- return syserror.EROFS
+ return linuxerr.EROFS
}
// RmdirAt implements vfs.FilesystemImpl.RmdirAt.
@@ -451,7 +452,7 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
return syserror.ENOTDIR
}
- return syserror.EROFS
+ return linuxerr.EROFS
}
// SetStatAt implements vfs.FilesystemImpl.SetStatAt.
@@ -461,7 +462,7 @@ func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts
return err
}
- return syserror.EROFS
+ return linuxerr.EROFS
}
// SymlinkAt implements vfs.FilesystemImpl.SymlinkAt.
@@ -475,7 +476,7 @@ func (fs *filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, targ
return err
}
- return syserror.EROFS
+ return linuxerr.EROFS
}
// UnlinkAt implements vfs.FilesystemImpl.UnlinkAt.
@@ -489,7 +490,7 @@ func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
return syserror.EISDIR
}
- return syserror.EROFS
+ return linuxerr.EROFS
}
// BoundEndpointAt implements vfs.FilesystemImpl.BoundEndpointAt.
@@ -503,7 +504,7 @@ func (fs *filesystem) BoundEndpointAt(ctx context.Context, rp *vfs.ResolvingPath
}
// TODO(b/134676337): Support sockets.
- return nil, syserror.ECONNREFUSED
+ return nil, linuxerr.ECONNREFUSED
}
// ListXattrAt implements vfs.FilesystemImpl.ListXattrAt.
@@ -512,7 +513,7 @@ func (fs *filesystem) ListXattrAt(ctx context.Context, rp *vfs.ResolvingPath, si
if err != nil {
return nil, err
}
- return nil, syserror.ENOTSUP
+ return nil, linuxerr.ENOTSUP
}
// GetXattrAt implements vfs.FilesystemImpl.GetXattrAt.
@@ -521,7 +522,7 @@ func (fs *filesystem) GetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
if err != nil {
return "", err
}
- return "", syserror.ENOTSUP
+ return "", linuxerr.ENOTSUP
}
// SetXattrAt implements vfs.FilesystemImpl.SetXattrAt.
@@ -530,7 +531,7 @@ func (fs *filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
if err != nil {
return err
}
- return syserror.ENOTSUP
+ return linuxerr.ENOTSUP
}
// RemoveXattrAt implements vfs.FilesystemImpl.RemoveXattrAt.
@@ -539,7 +540,7 @@ func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
if err != nil {
return err
}
- return syserror.ENOTSUP
+ return linuxerr.ENOTSUP
}
// PrependPath implements vfs.FilesystemImpl.PrependPath.
diff --git a/pkg/sentry/fsimpl/ext/inode.go b/pkg/sentry/fsimpl/ext/inode.go
index 4a555bf72..46658f855 100644
--- a/pkg/sentry/fsimpl/ext/inode.go
+++ b/pkg/sentry/fsimpl/ext/inode.go
@@ -19,6 +19,7 @@ import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/ext/disklayout"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -147,7 +148,7 @@ func newInode(fs *filesystem, inodeNum uint32) (*inode, error) {
return &f.inode, nil
default:
// TODO(b/134676337): Return appropriate errors for sockets, pipes and devices.
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
}
@@ -196,7 +197,7 @@ func (in *inode) open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts *vfs.OpenOpt
case *symlink:
if opts.Flags&linux.O_PATH == 0 {
// Can't open symlinks without O_PATH.
- return nil, syserror.ELOOP
+ return nil, linuxerr.ELOOP
}
var fd symlinkFD
fd.LockFD.Init(&in.locks)
diff --git a/pkg/sentry/fsimpl/ext/regular_file.go b/pkg/sentry/fsimpl/ext/regular_file.go
index 5ad9befcd..370d7bfdb 100644
--- a/pkg/sentry/fsimpl/ext/regular_file.go
+++ b/pkg/sentry/fsimpl/ext/regular_file.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -110,7 +111,7 @@ func (fd *regularFileFD) Read(ctx context.Context, dst usermem.IOSequence, opts
func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
// write(2) specifies that EBADF must be returned if the fd is not open for
// writing.
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Write implements vfs.FileDescriptionImpl.Write.
@@ -139,10 +140,10 @@ func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (
case linux.SEEK_END:
offset += int64(fd.inode().diskInode.Size())
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
fd.off = offset
return offset, nil
@@ -151,5 +152,5 @@ func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
// TODO(b/134676337): Implement mmap(2).
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
diff --git a/pkg/sentry/fsimpl/ext/symlink.go b/pkg/sentry/fsimpl/ext/symlink.go
index 5e2bcc837..c01017203 100644
--- a/pkg/sentry/fsimpl/ext/symlink.go
+++ b/pkg/sentry/fsimpl/ext/symlink.go
@@ -16,6 +16,7 @@ package ext
import (
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
@@ -81,22 +82,22 @@ func (fd *symlinkFD) Release(context.Context) {}
// PRead implements vfs.FileDescriptionImpl.PRead.
func (fd *symlinkFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Read implements vfs.FileDescriptionImpl.Read.
func (fd *symlinkFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// PWrite implements vfs.FileDescriptionImpl.PWrite.
func (fd *symlinkFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Write implements vfs.FileDescriptionImpl.Write.
func (fd *symlinkFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// IterDirents implements vfs.FileDescriptionImpl.IterDirents.
@@ -106,10 +107,10 @@ func (fd *symlinkFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback
// Seek implements vfs.FileDescriptionImpl.Seek.
func (fd *symlinkFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
func (fd *symlinkFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
diff --git a/pkg/sentry/fsimpl/fuse/BUILD b/pkg/sentry/fsimpl/fuse/BUILD
index 3a4777fbe..871df5984 100644
--- a/pkg/sentry/fsimpl/fuse/BUILD
+++ b/pkg/sentry/fsimpl/fuse/BUILD
@@ -46,6 +46,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/marshal",
@@ -76,6 +77,7 @@ go_test(
library = ":fuse",
deps = [
"//pkg/abi/linux",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/marshal",
"//pkg/sentry/fsimpl/testutil",
diff --git a/pkg/sentry/fsimpl/fuse/connection.go b/pkg/sentry/fsimpl/fuse/connection.go
index 077bf9307..d404edaf0 100644
--- a/pkg/sentry/fsimpl/fuse/connection.go
+++ b/pkg/sentry/fsimpl/fuse/connection.go
@@ -19,9 +19,9 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -252,11 +252,11 @@ func (conn *connection) Call(t *kernel.Task, r *Request) (*Response, error) {
}
if !conn.connected {
- return nil, syserror.ENOTCONN
+ return nil, linuxerr.ENOTCONN
}
if conn.connInitError {
- return nil, syserror.ECONNREFUSED
+ return nil, linuxerr.ECONNREFUSED
}
fut, err := conn.callFuture(t, r)
@@ -306,7 +306,7 @@ func (conn *connection) callFutureLocked(t *kernel.Task, r *Request) (*futureRes
conn.mu.Unlock()
// we checked connected before,
// this must be due to aborted connection.
- return nil, syserror.ECONNABORTED
+ return nil, linuxerr.ECONNABORTED
}
conn.mu.Unlock()
diff --git a/pkg/sentry/fsimpl/fuse/connection_test.go b/pkg/sentry/fsimpl/fuse/connection_test.go
index 78ea6a31e..1fddd858e 100644
--- a/pkg/sentry/fsimpl/fuse/connection_test.go
+++ b/pkg/sentry/fsimpl/fuse/connection_test.go
@@ -19,9 +19,9 @@ import (
"testing"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/syserror"
)
// TestConnectionInitBlock tests if initialization
@@ -104,7 +104,7 @@ func TestConnectionAbort(t *testing.T) {
// After abort, Call() should return directly with ENOTCONN.
req := conn.NewRequest(creds, 0, 0, 0, testObj)
_, err = conn.Call(task, req)
- if err != syserror.ENOTCONN {
+ if !linuxerr.Equals(linuxerr.ENOTCONN, err) {
t.Fatalf("Incorrect error code received for Call() after connection aborted")
}
diff --git a/pkg/sentry/fsimpl/fuse/dev.go b/pkg/sentry/fsimpl/fuse/dev.go
index 5d2bae14e..dab1e779d 100644
--- a/pkg/sentry/fsimpl/fuse/dev.go
+++ b/pkg/sentry/fsimpl/fuse/dev.go
@@ -18,6 +18,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -122,7 +123,7 @@ func (fd *DeviceFD) Release(ctx context.Context) {
func (fd *DeviceFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
// Operations on /dev/fuse don't make sense until a FUSE filesystem is mounted.
if fd.fs == nil {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
return 0, syserror.ENOSYS
@@ -132,7 +133,7 @@ func (fd *DeviceFD) PRead(ctx context.Context, dst usermem.IOSequence, offset in
func (fd *DeviceFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
// Operations on /dev/fuse don't make sense until a FUSE filesystem is mounted.
if fd.fs == nil {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
// We require that any Read done on this filesystem have a sane minimum
@@ -149,7 +150,7 @@ func (fd *DeviceFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.R
// If the read buffer is too small, error out.
if dst.NumBytes() < int64(minBuffSize) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
fd.mu.Lock()
@@ -234,7 +235,7 @@ func (fd *DeviceFD) readLocked(ctx context.Context, dst usermem.IOSequence, opts
func (fd *DeviceFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
// Operations on /dev/fuse don't make sense until a FUSE filesystem is mounted.
if fd.fs == nil {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
return 0, syserror.ENOSYS
@@ -251,12 +252,12 @@ func (fd *DeviceFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.
func (fd *DeviceFD) writeLocked(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
// Operations on /dev/fuse don't make sense until a FUSE filesystem is mounted.
if fd.fs == nil {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
// Return ENODEV if the filesystem is umounted.
if fd.fs.umounted {
- return 0, syserror.ENODEV
+ return 0, linuxerr.ENODEV
}
var cn, n int64
@@ -293,7 +294,7 @@ func (fd *DeviceFD) writeLocked(ctx context.Context, src usermem.IOSequence, opt
// Assert that the header isn't read into the writeBuf yet.
if fd.writeCursor >= hdrLen {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// We don't have the full common response header yet.
@@ -322,7 +323,7 @@ func (fd *DeviceFD) writeLocked(ctx context.Context, src usermem.IOSequence, opt
if !ok {
// Server sent us a response for a request we never sent,
// or for which we already received a reply (e.g. aborted), an unlikely event.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
delete(fd.completions, hdr.Unique)
@@ -391,7 +392,7 @@ func (fd *DeviceFD) EventUnregister(e *waiter.Entry) {
func (fd *DeviceFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
// Operations on /dev/fuse don't make sense until a FUSE filesystem is mounted.
if fd.fs == nil {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
return 0, syserror.ENOSYS
@@ -434,7 +435,7 @@ func (fd *DeviceFD) sendError(ctx context.Context, errno int32, unique linux.FUS
if !ok {
// A response for a request we never sent,
// or for which we already received a reply (e.g. aborted).
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
delete(fd.completions, respHdr.Unique)
diff --git a/pkg/sentry/fsimpl/fuse/fusefs.go b/pkg/sentry/fsimpl/fuse/fusefs.go
index 167c899e2..e9379e350 100644
--- a/pkg/sentry/fsimpl/fuse/fusefs.go
+++ b/pkg/sentry/fsimpl/fuse/fusefs.go
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
@@ -121,30 +122,30 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
deviceDescriptorStr, ok := mopts["fd"]
if !ok {
ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option fd missing")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
delete(mopts, "fd")
deviceDescriptor, err := strconv.ParseInt(deviceDescriptorStr, 10 /* base */, 32 /* bitSize */)
if err != nil {
ctx.Debugf("fusefs.FilesystemType.GetFilesystem: invalid fd: %q (%v)", deviceDescriptorStr, err)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
kernelTask := kernel.TaskFromContext(ctx)
if kernelTask == nil {
log.Warningf("%s.GetFilesystem: couldn't get kernel task from context", fsType.Name())
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fuseFDGeneric := kernelTask.GetFileVFS2(int32(deviceDescriptor))
if fuseFDGeneric == nil {
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
defer fuseFDGeneric.DecRef(ctx)
fuseFD, ok := fuseFDGeneric.Impl().(*DeviceFD)
if !ok {
log.Warningf("%s.GetFilesystem: device FD is %T, not a FUSE device", fsType.Name, fuseFDGeneric)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// Parse and set all the other supported FUSE mount options.
@@ -154,17 +155,17 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
uid, err := strconv.ParseUint(uidStr, 10, 32)
if err != nil {
log.Warningf("%s.GetFilesystem: invalid user_id: user_id=%s", fsType.Name(), uidStr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
kuid := creds.UserNamespace.MapToKUID(auth.UID(uid))
if !kuid.Ok() {
ctx.Warningf("fusefs.FilesystemType.GetFilesystem: unmapped uid: %d", uid)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fsopts.uid = kuid
} else {
ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option user_id missing")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
if gidStr, ok := mopts["group_id"]; ok {
@@ -172,17 +173,17 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
gid, err := strconv.ParseUint(gidStr, 10, 32)
if err != nil {
log.Warningf("%s.GetFilesystem: invalid group_id: group_id=%s", fsType.Name(), gidStr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
kgid := creds.UserNamespace.MapToKGID(auth.GID(gid))
if !kgid.Ok() {
ctx.Warningf("fusefs.FilesystemType.GetFilesystem: unmapped gid: %d", gid)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fsopts.gid = kgid
} else {
ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option group_id missing")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
if modeStr, ok := mopts["rootmode"]; ok {
@@ -190,12 +191,12 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
mode, err := strconv.ParseUint(modeStr, 8, 32)
if err != nil {
log.Warningf("%s.GetFilesystem: invalid mode: %q", fsType.Name(), modeStr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fsopts.rootMode = linux.FileMode(mode)
} else {
ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option rootmode missing")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// Set the maxInFlightRequests option.
@@ -206,7 +207,7 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
maxRead, err := strconv.ParseUint(maxReadStr, 10, 32)
if err != nil {
log.Warningf("%s.GetFilesystem: invalid max_read: max_read=%s", fsType.Name(), maxReadStr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
if maxRead < fuseMinMaxRead {
maxRead = fuseMinMaxRead
@@ -229,7 +230,7 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
// Check for unparsed options.
if len(mopts) != 0 {
log.Warningf("%s.GetFilesystem: unsupported or unknown options: %v", fsType.Name(), mopts)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// Create a new FUSE filesystem.
@@ -258,7 +259,7 @@ func newFUSEFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, fsTyp
conn, err := newFUSEConnection(ctx, fuseFD, opts)
if err != nil {
log.Warningf("fuse.NewFUSEFilesystem: NewFUSEConnection failed with error: %v", err)
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
fs := &filesystem{
@@ -375,7 +376,7 @@ func (i *inode) CheckPermissions(ctx context.Context, creds *auth.Credentials, a
creds.RealKGID != i.fs.opts.gid ||
creds.EffectiveKGID != i.fs.opts.gid ||
creds.SavedKGID != i.fs.opts.gid {
- return syserror.EACCES
+ return linuxerr.EACCES
}
}
@@ -396,7 +397,7 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr
return nil, syserror.ENOTDIR
}
if opts.Flags&linux.O_LARGEFILE == 0 && atomic.LoadUint64(&i.size) > linux.MAX_NON_LFS {
- return nil, syserror.EOVERFLOW
+ return nil, linuxerr.EOVERFLOW
}
var fd *fileDescription
@@ -418,7 +419,7 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr
kernelTask := kernel.TaskFromContext(ctx)
if kernelTask == nil {
log.Warningf("fusefs.Inode.Open: couldn't get kernel task from context")
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
// Build the request.
@@ -440,7 +441,7 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr
if err != nil {
return nil, err
}
- if err := res.Error(); err == syserror.ENOSYS && !isDir {
+ if err := res.Error(); linuxerr.Equals(linuxerr.ENOSYS, err) && !isDir {
i.fs.conn.noOpen = true
} else if err != nil {
return nil, err
@@ -512,7 +513,7 @@ func (i *inode) NewFile(ctx context.Context, name string, opts vfs.OpenOptions)
kernelTask := kernel.TaskFromContext(ctx)
if kernelTask == nil {
log.Warningf("fusefs.Inode.NewFile: couldn't get kernel task from context", i.nodeID)
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
in := linux.FUSECreateIn{
CreateMeta: linux.FUSECreateMeta{
@@ -552,7 +553,7 @@ func (i *inode) Unlink(ctx context.Context, name string, child kernfs.Inode) err
kernelTask := kernel.TaskFromContext(ctx)
if kernelTask == nil {
log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.nodeID)
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
in := linux.FUSEUnlinkIn{Name: name}
req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_UNLINK, &in)
@@ -596,7 +597,7 @@ func (i *inode) newEntry(ctx context.Context, name string, fileType linux.FileMo
kernelTask := kernel.TaskFromContext(ctx)
if kernelTask == nil {
log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.nodeID)
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, payload)
res, err := i.fs.conn.Call(kernelTask, req)
@@ -626,13 +627,13 @@ func (i *inode) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry,
// Readlink implements kernfs.Inode.Readlink.
func (i *inode) Readlink(ctx context.Context, mnt *vfs.Mount) (string, error) {
if i.Mode().FileType()&linux.S_IFLNK == 0 {
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
if len(i.link) == 0 {
kernelTask := kernel.TaskFromContext(ctx)
if kernelTask == nil {
log.Warningf("fusefs.Inode.Readlink: couldn't get kernel task from context")
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_READLINK, &linux.FUSEEmptyIn{})
res, err := i.fs.conn.Call(kernelTask, req)
@@ -728,7 +729,7 @@ func (i *inode) getAttr(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOp
task := kernel.TaskFromContext(ctx)
if task == nil {
log.Warningf("couldn't get kernel task from context")
- return linux.FUSEAttr{}, syserror.EINVAL
+ return linux.FUSEAttr{}, linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(ctx)
@@ -833,7 +834,7 @@ func (i *inode) setAttr(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
task := kernel.TaskFromContext(ctx)
if task == nil {
log.Warningf("couldn't get kernel task from context")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// We should retain the original file type when assigning new mode.
diff --git a/pkg/sentry/fsimpl/fuse/read_write.go b/pkg/sentry/fsimpl/fuse/read_write.go
index 66ea889f9..35d0ab6f4 100644
--- a/pkg/sentry/fsimpl/fuse/read_write.go
+++ b/pkg/sentry/fsimpl/fuse/read_write.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -39,7 +40,7 @@ func (fs *filesystem) ReadInPages(ctx context.Context, fd *regularFileFD, off ui
t := kernel.TaskFromContext(ctx)
if t == nil {
log.Warningf("fusefs.Read: couldn't get kernel task from context")
- return nil, 0, syserror.EINVAL
+ return nil, 0, linuxerr.EINVAL
}
// Round up to a multiple of page size.
@@ -155,7 +156,7 @@ func (fs *filesystem) Write(ctx context.Context, fd *regularFileFD, off uint64,
t := kernel.TaskFromContext(ctx)
if t == nil {
log.Warningf("fusefs.Read: couldn't get kernel task from context")
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// One request cannnot exceed either maxWrite or maxPages.
diff --git a/pkg/sentry/fsimpl/fuse/regular_file.go b/pkg/sentry/fsimpl/fuse/regular_file.go
index 5bdd096c3..a0802cd32 100644
--- a/pkg/sentry/fsimpl/fuse/regular_file.go
+++ b/pkg/sentry/fsimpl/fuse/regular_file.go
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
@@ -39,7 +40,7 @@ type regularFileFD struct {
// PRead implements vfs.FileDescriptionImpl.PRead.
func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Check that flags are supported.
@@ -56,7 +57,7 @@ func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
} else if size > math.MaxUint32 {
// FUSE only supports uint32 for size.
// Overflow.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// TODO(gvisor.dev/issue/3678): Add direct IO support.
@@ -143,7 +144,7 @@ func (fd *regularFileFD) Write(ctx context.Context, src usermem.IOSequence, opts
// final offset should be ignored by PWrite.
func (fd *regularFileFD) pwrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (written, finalOff int64, err error) {
if offset < 0 {
- return 0, offset, syserror.EINVAL
+ return 0, offset, linuxerr.EINVAL
}
// Check that flags are supported.
@@ -171,11 +172,11 @@ func (fd *regularFileFD) pwrite(ctx context.Context, src usermem.IOSequence, off
if srclen > math.MaxUint32 {
// FUSE only supports uint32 for size.
// Overflow.
- return 0, offset, syserror.EINVAL
+ return 0, offset, linuxerr.EINVAL
}
if end := offset + srclen; end < offset {
// Overflow.
- return 0, offset, syserror.EINVAL
+ return 0, offset, linuxerr.EINVAL
}
srclen, err = vfs.CheckLimit(ctx, offset, srclen)
diff --git a/pkg/sentry/fsimpl/gofer/BUILD b/pkg/sentry/fsimpl/gofer/BUILD
index 368272f12..752060044 100644
--- a/pkg/sentry/fsimpl/gofer/BUILD
+++ b/pkg/sentry/fsimpl/gofer/BUILD
@@ -49,6 +49,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdnotifier",
"//pkg/fspath",
diff --git a/pkg/sentry/fsimpl/gofer/directory.go b/pkg/sentry/fsimpl/gofer/directory.go
index 177e42649..5c48a9fee 100644
--- a/pkg/sentry/fsimpl/gofer/directory.go
+++ b/pkg/sentry/fsimpl/gofer/directory.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/p9"
"gvisor.dev/gvisor/pkg/refsvfs2"
@@ -28,7 +29,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
func (d *dentry) isDir() bool {
@@ -297,7 +297,7 @@ func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (in
switch whence {
case linux.SEEK_SET:
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset == 0 {
// Ensure that the next call to fd.IterDirents() calls
@@ -309,13 +309,13 @@ func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (in
case linux.SEEK_CUR:
offset += fd.off
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Don't clear fd.dirents in this case, even if offset == 0.
fd.off = offset
return fd.off, nil
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/fsimpl/gofer/filesystem.go b/pkg/sentry/fsimpl/gofer/filesystem.go
index 91ec4a142..652e5fe77 100644
--- a/pkg/sentry/fsimpl/gofer/filesystem.go
+++ b/pkg/sentry/fsimpl/gofer/filesystem.go
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/p9"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/host"
@@ -146,6 +147,7 @@ func putDentrySlice(ds *[]*dentry) {
// but dentry slices are allocated lazily, and it's much easier to say "defer
// fs.renameMuRUnlockAndCheckCaching(&ds)" than "defer func() {
// fs.renameMuRUnlockAndCheckCaching(ds) }()" to work around this.
+// +checklocksrelease:fs.renameMu
func (fs *filesystem) renameMuRUnlockAndCheckCaching(ctx context.Context, dsp **[]*dentry) {
fs.renameMu.RUnlock()
if *dsp == nil {
@@ -158,6 +160,7 @@ func (fs *filesystem) renameMuRUnlockAndCheckCaching(ctx context.Context, dsp **
putDentrySlice(*dsp)
}
+// +checklocksrelease:fs.renameMu
func (fs *filesystem) renameMuUnlockAndCheckCaching(ctx context.Context, ds **[]*dentry) {
if *ds == nil {
fs.renameMu.Unlock()
@@ -244,7 +247,7 @@ afterSymlink:
// * dentry at name has been revalidated
func (fs *filesystem) getChildLocked(ctx context.Context, parent *dentry, name string, ds **[]*dentry) (*dentry, error) {
if len(name) > maxFilenameLen {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
if child, ok := parent.children[name]; ok || parent.isSynthetic() {
if child == nil {
@@ -255,7 +258,7 @@ func (fs *filesystem) getChildLocked(ctx context.Context, parent *dentry, name s
qid, file, attrMask, attr, err := parent.file.walkGetAttrOne(ctx, name)
if err != nil {
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
parent.cacheNegativeLookupLocked(name)
}
return nil, err
@@ -372,7 +375,7 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir
defer parent.dirMu.Unlock()
if len(name) > maxFilenameLen {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
// Check for existence only if caching information is available. Otherwise,
// don't check for existence just yet. We will check for existence if the
@@ -382,7 +385,7 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir
return syserror.EEXIST
}
checkExistence := func() error {
- if child, err := fs.getChildLocked(ctx, parent, name, &ds); err != nil && err != syserror.ENOENT {
+ if child, err := fs.getChildLocked(ctx, parent, name, &ds); err != nil && !linuxerr.Equals(linuxerr.ENOENT, err) {
return err
} else if child != nil {
return syserror.EEXIST
@@ -412,7 +415,7 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir
}
if parent.isSynthetic() {
if createInSyntheticDir == nil {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := createInSyntheticDir(parent, name); err != nil {
return err
@@ -469,10 +472,10 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
name := rp.Component()
if dir {
if name == "." {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if name == ".." {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
} else {
if name == "." || name == ".." {
@@ -539,8 +542,8 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
if child.syntheticChildren != 0 {
// This is definitely not an empty directory, irrespective of
// fs.opts.interop.
- vfsObj.AbortDeleteDentry(&child.vfsd)
- return syserror.ENOTEMPTY
+ vfsObj.AbortDeleteDentry(&child.vfsd) // +checklocksforce: PrepareDeleteDentry called if child != nil.
+ return linuxerr.ENOTEMPTY
}
// If InteropModeShared is in effect and the first call to
// PrepareDeleteDentry above succeeded, then child wasn't
@@ -549,13 +552,13 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
// still exist) would be a waste of time.
if child.cachedMetadataAuthoritative() {
if !child.isDir() {
- vfsObj.AbortDeleteDentry(&child.vfsd)
+ vfsObj.AbortDeleteDentry(&child.vfsd) // +checklocksforce: see above.
return syserror.ENOTDIR
}
for _, grandchild := range child.children {
if grandchild != nil {
- vfsObj.AbortDeleteDentry(&child.vfsd)
- return syserror.ENOTEMPTY
+ vfsObj.AbortDeleteDentry(&child.vfsd) // +checklocksforce: see above.
+ return linuxerr.ENOTEMPTY
}
}
}
@@ -564,12 +567,12 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
} else {
// child must be a non-directory file.
if child != nil && child.isDir() {
- vfsObj.AbortDeleteDentry(&child.vfsd)
+ vfsObj.AbortDeleteDentry(&child.vfsd) // +checklocksforce: see above.
return syserror.EISDIR
}
if rp.MustBeDir() {
if child != nil {
- vfsObj.AbortDeleteDentry(&child.vfsd)
+ vfsObj.AbortDeleteDentry(&child.vfsd) // +checklocksforce: see above.
}
return syserror.ENOTDIR
}
@@ -582,7 +585,7 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
err = parent.file.unlinkAt(ctx, name, flags)
if err != nil {
if child != nil {
- vfsObj.AbortDeleteDentry(&child.vfsd)
+ vfsObj.AbortDeleteDentry(&child.vfsd) // +checklocksforce: see above.
}
return err
}
@@ -600,7 +603,7 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
}
if child != nil {
- vfsObj.CommitDeleteDentry(ctx, &child.vfsd)
+ vfsObj.CommitDeleteDentry(ctx, &child.vfsd) // +checklocksforce: see above.
child.setDeleted()
if child.isSynthetic() {
parent.syntheticChildren--
@@ -674,11 +677,11 @@ func (fs *filesystem) GetParentDentryAt(ctx context.Context, rp *vfs.ResolvingPa
func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error {
return fs.doCreateAt(ctx, rp, false /* dir */, func(parent *dentry, childName string, _ **[]*dentry) error {
if rp.Mount() != vd.Mount() {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
d := vd.Dentry().Impl().(*dentry)
if d.isDir() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
gid := auth.KGID(atomic.LoadUint32(&d.gid))
uid := auth.KUID(atomic.LoadUint32(&d.uid))
@@ -690,7 +693,7 @@ func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
return syserror.ENOENT
}
if d.nlink == math.MaxUint32 {
- return syserror.EMLINK
+ return linuxerr.EMLINK
}
if err := parent.file.link(ctx, d.file, childName); err != nil {
return err
@@ -715,7 +718,7 @@ func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
mode |= linux.S_ISGID
}
if _, err := parent.file.mkdir(ctx, name, p9.FileMode(mode), (p9.UID)(creds.EffectiveKUID), p9.GID(kgid)); err != nil {
- if !opts.ForSyntheticMountpoint || err == syserror.EEXIST {
+ if !opts.ForSyntheticMountpoint || linuxerr.Equals(linuxerr.EEXIST, err) {
return err
}
ctx.Infof("Failed to create remote directory %q: %v; falling back to synthetic directory", name, err)
@@ -734,7 +737,7 @@ func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
}, func(parent *dentry, name string) error {
if !opts.ForSyntheticMountpoint {
// Can't create non-synthetic files in synthetic directories.
- return syserror.EPERM
+ return linuxerr.EPERM
}
parent.createSyntheticChildLocked(&createSyntheticOpts{
name: name,
@@ -752,7 +755,7 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
return fs.doCreateAt(ctx, rp, false /* dir */, func(parent *dentry, name string, ds **[]*dentry) error {
creds := rp.Credentials()
_, err := parent.file.mknod(ctx, name, (p9.FileMode)(opts.Mode), opts.DevMajor, opts.DevMinor, (p9.UID)(creds.EffectiveKUID), (p9.GID)(creds.EffectiveKGID))
- if err != syserror.EPERM {
+ if !linuxerr.Equals(linuxerr.EPERM, err) {
return err
}
@@ -765,7 +768,7 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
case err == nil:
// Step succeeded, another file exists.
return syserror.EEXIST
- case err != syserror.ENOENT:
+ case !linuxerr.Equals(linuxerr.ENOENT, err):
// Unexpected error.
return err
}
@@ -793,7 +796,7 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
return nil
}
// Retain error from gofer if synthetic file cannot be created internally.
- return syserror.EPERM
+ return linuxerr.EPERM
}, nil)
}
@@ -862,10 +865,10 @@ afterTrailingSymlink:
// Determine whether or not we need to create a file.
parent.dirMu.Lock()
child, _, err := fs.stepLocked(ctx, rp, parent, false /* mayFollowSymlinks */, &ds)
- if err == syserror.ENOENT && mayCreate {
+ if linuxerr.Equals(linuxerr.ENOENT, err) && mayCreate {
if parent.isSynthetic() {
parent.dirMu.Unlock()
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
fd, err := parent.createAndOpenChildLocked(ctx, rp, &opts, &ds)
parent.dirMu.Unlock()
@@ -942,7 +945,7 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
return nil, syserror.EISDIR
}
if opts.Flags&linux.O_DIRECT != 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
if !d.isSynthetic() {
if err := d.ensureSharedHandle(ctx, ats&vfs.MayRead != 0, false /* write */, false /* trunc */); err != nil {
@@ -962,10 +965,10 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
return &fd.vfsfd, nil
case linux.S_IFLNK:
// Can't open symlinks without O_PATH, which is handled at the VFS layer.
- return nil, syserror.ELOOP
+ return nil, linuxerr.ELOOP
case linux.S_IFSOCK:
if d.isSynthetic() {
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
if d.fs.iopts.OpenSocketsByConnecting {
return d.openSocketByConnecting(ctx, opts)
@@ -998,7 +1001,7 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
func (d *dentry) openSocketByConnecting(ctx context.Context, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
if opts.Flags&linux.O_DIRECT != 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
fdObj, err := d.file.connect(ctx, p9.AnonymousSocket)
if err != nil {
@@ -1019,7 +1022,7 @@ func (d *dentry) openSocketByConnecting(ctx context.Context, opts *vfs.OpenOptio
func (d *dentry) openSpecialFile(ctx context.Context, mnt *vfs.Mount, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
ats := vfs.AccessTypesForOpenFlags(opts)
if opts.Flags&linux.O_DIRECT != 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
// We assume that the server silently inserts O_NONBLOCK in the open flags
// for all named pipes (because all existing gofers do this).
@@ -1033,7 +1036,7 @@ func (d *dentry) openSpecialFile(ctx context.Context, mnt *vfs.Mount, opts *vfs.
retry:
h, err := openHandle(ctx, d.file, ats.MayRead(), ats.MayWrite(), opts.Flags&linux.O_TRUNC != 0)
if err != nil {
- if isBlockingOpenOfNamedPipe && ats == vfs.MayWrite && err == syserror.ENXIO {
+ if isBlockingOpenOfNamedPipe && ats == vfs.MayWrite && linuxerr.Equals(linuxerr.ENXIO, err) {
// An attempt to open a named pipe with O_WRONLY|O_NONBLOCK fails
// with ENXIO if opening the same named pipe with O_WRONLY would
// block because there are no readers of the pipe.
@@ -1187,18 +1190,14 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
return "", err
}
if !d.isSymlink() {
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
return d.readlink(ctx, rp.Mount())
}
// RenameAt implements vfs.FilesystemImpl.RenameAt.
func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
- if opts.Flags != 0 {
- // Requires 9P support.
- return syserror.EINVAL
- }
-
+ // Resolve newParent first to verify that it's on this Mount.
var ds *[]*dentry
fs.renameMu.Lock()
defer fs.renameMuUnlockAndCheckCaching(ctx, &ds)
@@ -1206,13 +1205,26 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
if err != nil {
return err
}
+
+ if opts.Flags&^linux.RENAME_NOREPLACE != 0 {
+ return linuxerr.EINVAL
+ }
+ if fs.opts.interop == InteropModeShared && opts.Flags&linux.RENAME_NOREPLACE != 0 {
+ // Requires 9P support to synchronize with other remote filesystem
+ // users.
+ return linuxerr.EINVAL
+ }
+
newName := rp.Component()
if newName == "." || newName == ".." {
- return syserror.EBUSY
+ if opts.Flags&linux.RENAME_NOREPLACE != 0 {
+ return syserror.EEXIST
+ }
+ return linuxerr.EBUSY
}
mnt := rp.Mount()
if mnt != oldParentVD.Mount() {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if err := mnt.CheckBeginWrite(); err != nil {
return err
@@ -1251,7 +1263,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
}
if renamed.isDir() {
if renamed == newParent || genericIsAncestorDentry(renamed, newParent) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if oldParent != newParent {
if err := renamed.checkPermissions(creds, vfs.MayWrite); err != nil {
@@ -1275,18 +1287,21 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
return syserror.ENOENT
}
replaced, err := fs.getChildLocked(ctx, newParent, newName, &ds)
- if err != nil && err != syserror.ENOENT {
+ if err != nil && !linuxerr.Equals(linuxerr.ENOENT, err) {
return err
}
var replacedVFSD *vfs.Dentry
if replaced != nil {
+ if opts.Flags&linux.RENAME_NOREPLACE != 0 {
+ return syserror.EEXIST
+ }
replacedVFSD = &replaced.vfsd
if replaced.isDir() {
if !renamed.isDir() {
return syserror.EISDIR
}
if genericIsAncestorDentry(replaced, renamed) {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
} else {
if rp.MustBeDir() || renamed.isDir() {
@@ -1495,7 +1510,7 @@ func (fs *filesystem) BoundEndpointAt(ctx context.Context, rp *vfs.ResolvingPath
return d.endpoint, nil
}
}
- return nil, syserror.ECONNREFUSED
+ return nil, linuxerr.ECONNREFUSED
}
// ListXattrAt implements vfs.FilesystemImpl.ListXattrAt.
diff --git a/pkg/sentry/fsimpl/gofer/gofer.go b/pkg/sentry/fsimpl/gofer/gofer.go
index 21692d2ac..2f85215d9 100644
--- a/pkg/sentry/fsimpl/gofer/gofer.go
+++ b/pkg/sentry/fsimpl/gofer/gofer.go
@@ -46,6 +46,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/p9"
@@ -318,7 +319,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
mfp := pgalloc.MemoryFileProviderFromContext(ctx)
if mfp == nil {
ctx.Warningf("gofer.FilesystemType.GetFilesystem: context does not provide a pgalloc.MemoryFileProvider")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
mopts := vfs.GenericParseMountOptions(opts.Data)
@@ -354,7 +355,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
fsopts.interop = InteropModeShared
default:
ctx.Warningf("gofer.FilesystemType.GetFilesystem: invalid cache policy: %s=%s", moptCache, cache)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
}
@@ -365,7 +366,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
dfltuid, err := strconv.ParseUint(dfltuidstr, 10, 32)
if err != nil {
ctx.Warningf("gofer.FilesystemType.GetFilesystem: invalid default UID: %s=%s", moptDfltUID, dfltuidstr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// In Linux, dfltuid is interpreted as a UID and is converted to a KUID
// in the caller's user namespace, but goferfs isn't
@@ -378,7 +379,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
dfltgid, err := strconv.ParseUint(dfltgidstr, 10, 32)
if err != nil {
ctx.Warningf("gofer.FilesystemType.GetFilesystem: invalid default UID: %s=%s", moptDfltGID, dfltgidstr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fsopts.dfltgid = auth.KGID(dfltgid)
}
@@ -390,7 +391,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
msize, err := strconv.ParseUint(msizestr, 10, 32)
if err != nil {
ctx.Warningf("gofer.FilesystemType.GetFilesystem: invalid message size: %s=%s", moptMsize, msizestr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fsopts.msize = uint32(msize)
}
@@ -409,7 +410,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
maxCachedDentries, err := strconv.ParseUint(str, 10, 64)
if err != nil {
ctx.Warningf("gofer.FilesystemType.GetFilesystem: invalid dentry cache limit: %s=%s", moptDentryCacheLimit, str)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
fsopts.maxCachedDentries = maxCachedDentries
}
@@ -433,14 +434,14 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
// Check for unparsed options.
if len(mopts) != 0 {
ctx.Warningf("gofer.FilesystemType.GetFilesystem: unknown options: %v", mopts)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// Handle internal options.
iopts, ok := opts.InternalData.(InternalFilesystemOptions)
if opts.InternalData != nil && !ok {
ctx.Warningf("gofer.FilesystemType.GetFilesystem: GetFilesystemOptions.InternalData has type %T, wanted gofer.InternalFilesystemOptions", opts.InternalData)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// If !ok, iopts being the zero value is correct.
@@ -503,7 +504,7 @@ func getFDFromMountOptionsMap(ctx context.Context, mopts map[string]string) (int
trans, ok := mopts[moptTransport]
if !ok || trans != transportModeFD {
ctx.Warningf("gofer.getFDFromMountOptionsMap: transport must be specified as '%s=%s'", moptTransport, transportModeFD)
- return -1, syserror.EINVAL
+ return -1, linuxerr.EINVAL
}
delete(mopts, moptTransport)
@@ -511,28 +512,28 @@ func getFDFromMountOptionsMap(ctx context.Context, mopts map[string]string) (int
rfdstr, ok := mopts[moptReadFD]
if !ok {
ctx.Warningf("gofer.getFDFromMountOptionsMap: read FD must be specified as '%s=<file descriptor>'", moptReadFD)
- return -1, syserror.EINVAL
+ return -1, linuxerr.EINVAL
}
delete(mopts, moptReadFD)
rfd, err := strconv.Atoi(rfdstr)
if err != nil {
ctx.Warningf("gofer.getFDFromMountOptionsMap: invalid read FD: %s=%s", moptReadFD, rfdstr)
- return -1, syserror.EINVAL
+ return -1, linuxerr.EINVAL
}
wfdstr, ok := mopts[moptWriteFD]
if !ok {
ctx.Warningf("gofer.getFDFromMountOptionsMap: write FD must be specified as '%s=<file descriptor>'", moptWriteFD)
- return -1, syserror.EINVAL
+ return -1, linuxerr.EINVAL
}
delete(mopts, moptWriteFD)
wfd, err := strconv.Atoi(wfdstr)
if err != nil {
ctx.Warningf("gofer.getFDFromMountOptionsMap: invalid write FD: %s=%s", moptWriteFD, wfdstr)
- return -1, syserror.EINVAL
+ return -1, linuxerr.EINVAL
}
if rfd != wfd {
ctx.Warningf("gofer.getFDFromMountOptionsMap: read FD (%d) and write FD (%d) must be equal", rfd, wfd)
- return -1, syserror.EINVAL
+ return -1, linuxerr.EINVAL
}
return rfd, nil
}
@@ -946,10 +947,10 @@ func (d *dentry) cachedMetadataAuthoritative() bool {
// updateFromP9Attrs is called to update d's metadata after an update from the
// remote filesystem.
// Precondition: d.metadataMu must be locked.
+// +checklocks:d.metadataMu
func (d *dentry) updateFromP9AttrsLocked(mask p9.AttrMask, attr *p9.Attr) {
if mask.Mode {
if got, want := uint32(attr.Mode.FileType()), d.fileType(); got != want {
- d.metadataMu.Unlock()
panic(fmt.Sprintf("gofer.dentry file type changed from %#o to %#o", want, got))
}
atomic.StoreUint32(&d.mode, uint32(attr.Mode))
@@ -988,6 +989,7 @@ func (d *dentry) updateFromP9AttrsLocked(mask p9.AttrMask, attr *p9.Attr) {
// Preconditions: !d.isSynthetic().
// Preconditions: d.metadataMu is locked.
+// +checklocks:d.metadataMu
func (d *dentry) refreshSizeLocked(ctx context.Context) error {
d.handleMu.RLock()
@@ -1019,6 +1021,7 @@ func (d *dentry) updateFromGetattr(ctx context.Context) error {
// Preconditions:
// * !d.isSynthetic().
// * d.metadataMu is locked.
+// +checklocks:d.metadataMu
func (d *dentry) updateFromGetattrLocked(ctx context.Context) error {
// Use d.readFile or d.writeFile, which represent 9P FIDs that have been
// opened, in preference to d.file, which represents a 9P fid that has not.
@@ -1043,7 +1046,8 @@ func (d *dentry) updateFromGetattrLocked(ctx context.Context) error {
_, attrMask, attr, err := file.getAttr(ctx, dentryAttrMask())
if handleMuRLocked {
- d.handleMu.RUnlock() // must be released before updateFromP9AttrsLocked()
+ // handleMu must be released before updateFromP9AttrsLocked().
+ d.handleMu.RUnlock() // +checklocksforce: complex case.
}
if err != nil {
return err
@@ -1090,7 +1094,7 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, opts *vfs
return nil
}
if stat.Mask&^(linux.STATX_MODE|linux.STATX_UID|linux.STATX_GID|linux.STATX_ATIME|linux.STATX_MTIME|linux.STATX_SIZE) != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
mode := linux.FileMode(atomic.LoadUint32(&d.mode))
if err := vfs.CheckSetStat(ctx, creds, opts, mode, auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid))); err != nil {
@@ -1110,7 +1114,7 @@ func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, opts *vfs
case linux.S_IFDIR:
return syserror.EISDIR
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
@@ -1282,9 +1286,12 @@ func (d *dentry) checkPermissions(creds *auth.Credentials, ats vfs.AccessTypes)
}
func (d *dentry) checkXattrPermissions(creds *auth.Credentials, name string, ats vfs.AccessTypes) error {
- // We only support xattrs prefixed with "user." (see b/148380782). Currently,
- // there is no need to expose any other xattrs through a gofer.
- if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
+ // Deny access to the "security" and "system" namespaces since applications
+ // may expect these to affect kernel behavior in unimplemented ways
+ // (b/148380782). Allow all other extended attributes to be passed through
+ // to the remote filesystem. This is inconsistent with Linux's 9p client,
+ // but consistent with other filesystems (e.g. FUSE).
+ if strings.HasPrefix(name, linux.XATTR_SECURITY_PREFIX) || strings.HasPrefix(name, linux.XATTR_SYSTEM_PREFIX) {
return syserror.EOPNOTSUPP
}
mode := linux.FileMode(atomic.LoadUint32(&d.mode))
@@ -1466,7 +1473,7 @@ func (d *dentry) checkCachingLocked(ctx context.Context, renameMuWriteLocked boo
if d.isDeleted() {
d.watches.HandleDeletion(ctx)
}
- d.destroyLocked(ctx)
+ d.destroyLocked(ctx) // +checklocksforce: renameMu must be acquired at this point.
return
}
// If d still has inotify watches and it is not deleted or invalidated, it
@@ -1494,7 +1501,7 @@ func (d *dentry) checkCachingLocked(ctx context.Context, renameMuWriteLocked boo
delete(d.parent.children, d.name)
d.parent.dirMu.Unlock()
}
- d.destroyLocked(ctx)
+ d.destroyLocked(ctx) // +checklocksforce: see above.
return
}
@@ -1523,7 +1530,7 @@ func (d *dentry) checkCachingLocked(ctx context.Context, renameMuWriteLocked boo
d.fs.renameMu.Lock()
defer d.fs.renameMu.Unlock()
}
- d.fs.evictCachedDentryLocked(ctx)
+ d.fs.evictCachedDentryLocked(ctx) // +checklocksforce: see above.
}
}
@@ -1540,6 +1547,7 @@ func (d *dentry) removeFromCacheLocked() {
// Precondition: fs.renameMu must be locked for writing; it may be temporarily
// unlocked.
+// +checklocks:fs.renameMu
func (fs *filesystem) evictAllCachedDentriesLocked(ctx context.Context) {
for fs.cachedDentriesLen != 0 {
fs.evictCachedDentryLocked(ctx)
@@ -1548,6 +1556,7 @@ func (fs *filesystem) evictAllCachedDentriesLocked(ctx context.Context) {
// Preconditions:
// * fs.renameMu must be locked for writing; it may be temporarily unlocked.
+// +checklocks:fs.renameMu
func (fs *filesystem) evictCachedDentryLocked(ctx context.Context) {
fs.cacheMu.Lock()
victim := fs.cachedDentries.Back()
@@ -1584,7 +1593,7 @@ func (fs *filesystem) evictCachedDentryLocked(ctx context.Context) {
// will try to acquire fs.renameMu (which we have already acquired). Hence,
// fs.renameMu will synchronize the destroy attempts.
victim.cachingMu.Unlock()
- victim.destroyLocked(ctx)
+ victim.destroyLocked(ctx) // +checklocksforce: owned as precondition, victim.fs == fs.
}
// destroyLocked destroys the dentry.
@@ -1594,6 +1603,7 @@ func (fs *filesystem) evictCachedDentryLocked(ctx context.Context) {
// * d.refs == 0.
// * d.parent.children[d.name] != d, i.e. d is not reachable by path traversal
// from its former parent dentry.
+// +checklocks:d.fs.renameMu
func (d *dentry) destroyLocked(ctx context.Context) {
switch atomic.LoadInt64(&d.refs) {
case 0:
@@ -1684,7 +1694,7 @@ func (d *dentry) setDeleted() {
}
func (d *dentry) listXattr(ctx context.Context, creds *auth.Credentials, size uint64) ([]string, error) {
- if d.file.isNil() || !d.userXattrSupported() {
+ if d.file.isNil() {
return nil, nil
}
xattrMap, err := d.file.listXattr(ctx, size)
@@ -1693,17 +1703,14 @@ func (d *dentry) listXattr(ctx context.Context, creds *auth.Credentials, size ui
}
xattrs := make([]string, 0, len(xattrMap))
for x := range xattrMap {
- // We only support xattrs in the user.* namespace.
- if strings.HasPrefix(x, linux.XATTR_USER_PREFIX) {
- xattrs = append(xattrs, x)
- }
+ xattrs = append(xattrs, x)
}
return xattrs, nil
}
func (d *dentry) getXattr(ctx context.Context, creds *auth.Credentials, opts *vfs.GetXattrOptions) (string, error) {
if d.file.isNil() {
- return "", syserror.ENODATA
+ return "", linuxerr.ENODATA
}
if err := d.checkXattrPermissions(creds, opts.Name, vfs.MayRead); err != nil {
return "", err
@@ -1713,7 +1720,7 @@ func (d *dentry) getXattr(ctx context.Context, creds *auth.Credentials, opts *vf
func (d *dentry) setXattr(ctx context.Context, creds *auth.Credentials, opts *vfs.SetXattrOptions) error {
if d.file.isNil() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := d.checkXattrPermissions(creds, opts.Name, vfs.MayWrite); err != nil {
return err
@@ -1723,7 +1730,7 @@ func (d *dentry) setXattr(ctx context.Context, creds *auth.Credentials, opts *vf
func (d *dentry) removeXattr(ctx context.Context, creds *auth.Credentials, name string) error {
if d.file.isNil() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := d.checkXattrPermissions(creds, name, vfs.MayWrite); err != nil {
return err
@@ -1731,13 +1738,6 @@ func (d *dentry) removeXattr(ctx context.Context, creds *auth.Credentials, name
return d.file.removeXattr(ctx, name)
}
-// Extended attributes in the user.* namespace are only supported for regular
-// files and directories.
-func (d *dentry) userXattrSupported() bool {
- filetype := linux.FileMode(atomic.LoadUint32(&d.mode)).FileType()
- return filetype == linux.ModeRegular || filetype == linux.ModeDirectory
-}
-
// Preconditions:
// * !d.isSynthetic().
// * d.isRegularFile() || d.isDir().
@@ -1770,7 +1770,7 @@ func (d *dentry) ensureSharedHandle(ctx context.Context, read, write, trunc bool
openReadable := !d.readFile.isNil() || read
openWritable := !d.writeFile.isNil() || write
h, err := openHandle(ctx, d.file, openReadable, openWritable, trunc)
- if err == syserror.EACCES && (openReadable != read || openWritable != write) {
+ if linuxerr.Equals(linuxerr.EACCES, err) && (openReadable != read || openWritable != write) {
// It may not be possible to use a single handle for both
// reading and writing, since permissions on the file may have
// changed to e.g. disallow reading after previously being
diff --git a/pkg/sentry/fsimpl/gofer/host_named_pipe.go b/pkg/sentry/fsimpl/gofer/host_named_pipe.go
index c7bf10007..398288ee3 100644
--- a/pkg/sentry/fsimpl/gofer/host_named_pipe.go
+++ b/pkg/sentry/fsimpl/gofer/host_named_pipe.go
@@ -21,6 +21,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -78,7 +79,7 @@ func nonblockingPipeHasWriter(fd int32) (bool, error) {
defer tempPipeMu.Unlock()
// Copy 1 byte from fd into the temporary pipe.
n, err := unix.Tee(int(fd), tempPipeWriteFD, 1, unix.SPLICE_F_NONBLOCK)
- if err == syserror.EAGAIN {
+ if linuxerr.Equals(linuxerr.EAGAIN, err) {
// The pipe represented by fd is empty, but has a writer.
return true, nil
}
diff --git a/pkg/sentry/fsimpl/gofer/regular_file.go b/pkg/sentry/fsimpl/gofer/regular_file.go
index eed05e369..7f458dd05 100644
--- a/pkg/sentry/fsimpl/gofer/regular_file.go
+++ b/pkg/sentry/fsimpl/gofer/regular_file.go
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/metric"
@@ -124,7 +125,7 @@ func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
}()
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Check that flags are supported.
@@ -194,7 +195,7 @@ func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, off
// offset should be ignored by PWrite.
func (fd *regularFileFD) pwrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (written, finalOff int64, err error) {
if offset < 0 {
- return 0, offset, syserror.EINVAL
+ return 0, offset, linuxerr.EINVAL
}
// Check that flags are supported.
@@ -297,7 +298,7 @@ func (fd *regularFileFD) writeCache(ctx context.Context, d *dentry, offset int64
pgstart := hostarch.PageRoundDown(uint64(offset))
pgend, ok := hostarch.PageRoundUp(uint64(offset + src.NumBytes()))
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
mr := memmap.MappableRange{pgstart, pgend}
var freed []memmap.FileRange
@@ -652,20 +653,20 @@ func regularFileSeekLocked(ctx context.Context, d *dentry, fdOffset, offset int6
offset += size
case linux.SEEK_DATA:
if offset > size {
- return 0, syserror.ENXIO
+ return 0, linuxerr.ENXIO
}
// Use offset as specified.
case linux.SEEK_HOLE:
if offset > size {
- return 0, syserror.ENXIO
+ return 0, linuxerr.ENXIO
}
offset = size
}
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
return offset, nil
}
@@ -678,28 +679,28 @@ func (fd *regularFileFD) Sync(ctx context.Context) error {
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
d := fd.dentry()
- switch d.fs.opts.interop {
- case InteropModeExclusive:
- // Any mapping is fine.
- case InteropModeWritethrough:
- // Shared writable mappings require a host FD, since otherwise we can't
- // synchronously flush memory-mapped writes to the remote file.
- if opts.Private || !opts.MaxPerms.Write {
- break
- }
- fallthrough
- case InteropModeShared:
- // All mappings require a host FD to be coherent with other filesystem
- // users.
- if d.fs.opts.forcePageCache {
- // Whether or not we have a host FD, we're not allowed to use it.
- return syserror.ENODEV
- }
- if atomic.LoadInt32(&d.mmapFD) < 0 {
- return syserror.ENODEV
+ // Force sentry page caching at your own risk.
+ if !d.fs.opts.forcePageCache {
+ switch d.fs.opts.interop {
+ case InteropModeExclusive:
+ // Any mapping is fine.
+ case InteropModeWritethrough:
+ // Shared writable mappings require a host FD, since otherwise we
+ // can't synchronously flush memory-mapped writes to the remote
+ // file.
+ if opts.Private || !opts.MaxPerms.Write {
+ break
+ }
+ fallthrough
+ case InteropModeShared:
+ // All mappings require a host FD to be coherent with other
+ // filesystem users.
+ if atomic.LoadInt32(&d.mmapFD) < 0 {
+ return linuxerr.ENODEV
+ }
+ default:
+ panic(fmt.Sprintf("unknown InteropMode %v", d.fs.opts.interop))
}
- default:
- panic(fmt.Sprintf("unknown InteropMode %v", d.fs.opts.interop))
}
// After this point, d may be used as a memmap.Mappable.
d.pf.hostFileMapperInitOnce.Do(d.pf.hostFileMapper.Init)
@@ -708,12 +709,12 @@ func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpt
}
func (d *dentry) mayCachePages() bool {
- if d.fs.opts.interop == InteropModeShared {
- return false
- }
if d.fs.opts.forcePageCache {
return true
}
+ if d.fs.opts.interop == InteropModeShared {
+ return false
+ }
return atomic.LoadInt32(&d.mmapFD) >= 0
}
diff --git a/pkg/sentry/fsimpl/gofer/revalidate.go b/pkg/sentry/fsimpl/gofer/revalidate.go
index 8f81f0822..226790a11 100644
--- a/pkg/sentry/fsimpl/gofer/revalidate.go
+++ b/pkg/sentry/fsimpl/gofer/revalidate.go
@@ -247,16 +247,16 @@ func (fs *filesystem) revalidateHelper(ctx context.Context, vfsObj *vfs.VirtualF
if found && !d.isSynthetic() {
// First dentry is where the search is starting, just update attributes
// since it cannot be replaced.
- d.updateFromP9AttrsLocked(stats[i].Valid, &stats[i].Attr)
+ d.updateFromP9AttrsLocked(stats[i].Valid, &stats[i].Attr) // +checklocksforce: acquired by lockAllMetadata.
}
- d.metadataMu.Unlock()
+ d.metadataMu.Unlock() // +checklocksforce: see above.
continue
}
// Note that synthetic dentries will always fails the comparison check
// below.
if !found || d.qidPath != stats[i].QID.Path {
- d.metadataMu.Unlock()
+ d.metadataMu.Unlock() // +checklocksforce: see above.
if !found && d.isSynthetic() {
// We have a synthetic file, and no remote file has arisen to replace
// it.
@@ -298,7 +298,7 @@ func (fs *filesystem) revalidateHelper(ctx context.Context, vfsObj *vfs.VirtualF
}
// The file at this path hasn't changed. Just update cached metadata.
- d.updateFromP9AttrsLocked(stats[i].Valid, &stats[i].Attr)
+ d.updateFromP9AttrsLocked(stats[i].Valid, &stats[i].Attr) // +checklocksforce: see above.
d.metadataMu.Unlock()
}
@@ -354,6 +354,7 @@ func (r *revalidateState) add(name string, d *dentry) {
r.dentries = append(r.dentries, d)
}
+// +checklocksignore
func (r *revalidateState) lockAllMetadata() {
for _, d := range r.dentries {
d.metadataMu.Lock()
@@ -372,6 +373,7 @@ func (r *revalidateState) popFront() *dentry {
// reset releases all metadata locks and resets all fields to allow this
// instance to be reused.
+// +checklocksignore
func (r *revalidateState) reset() {
if r.locked {
// Unlock any remaining dentries.
diff --git a/pkg/sentry/fsimpl/gofer/save_restore.go b/pkg/sentry/fsimpl/gofer/save_restore.go
index 83e841a51..e67422a2f 100644
--- a/pkg/sentry/fsimpl/gofer/save_restore.go
+++ b/pkg/sentry/fsimpl/gofer/save_restore.go
@@ -21,13 +21,13 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/p9"
"gvisor.dev/gvisor/pkg/refsvfs2"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
type saveRestoreContextID int
@@ -92,7 +92,7 @@ func (fd *specialFileFD) savePipeData(ctx context.Context) error {
fd.buf = append(fd.buf, buf[:n]...)
}
if err != nil {
- if err == io.EOF || err == syserror.EAGAIN {
+ if err == io.EOF || linuxerr.Equals(linuxerr.EAGAIN, err) {
break
}
return err
diff --git a/pkg/sentry/fsimpl/gofer/special_file.go b/pkg/sentry/fsimpl/gofer/special_file.go
index c12444b7e..690cde707 100644
--- a/pkg/sentry/fsimpl/gofer/special_file.go
+++ b/pkg/sentry/fsimpl/gofer/special_file.go
@@ -20,6 +20,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/p9"
@@ -183,7 +184,7 @@ func (fd *specialFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
}()
if fd.seekable && offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Check that flags are supported.
@@ -228,7 +229,7 @@ func (fd *specialFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
// Just buffer the read instead.
buf := make([]byte, dst.NumBytes())
n, err := fd.handle.readToBlocksAt(ctx, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf)), uint64(offset))
- if err == syserror.EAGAIN {
+ if linuxerr.Equals(linuxerr.EAGAIN, err) {
err = syserror.ErrWouldBlock
}
if n == 0 {
@@ -263,7 +264,7 @@ func (fd *specialFileFD) PWrite(ctx context.Context, src usermem.IOSequence, off
// offset should be ignored by PWrite.
func (fd *specialFileFD) pwrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (written, finalOff int64, err error) {
if fd.seekable && offset < 0 {
- return 0, offset, syserror.EINVAL
+ return 0, offset, linuxerr.EINVAL
}
// Check that flags are supported.
@@ -316,7 +317,7 @@ func (fd *specialFileFD) pwrite(ctx context.Context, src usermem.IOSequence, off
return 0, offset, copyErr
}
n, err := fd.handle.writeFromBlocksAt(ctx, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf[:copied])), uint64(offset))
- if err == syserror.EAGAIN {
+ if linuxerr.Equals(linuxerr.EAGAIN, err) {
err = syserror.ErrWouldBlock
}
// Update offset if the offset is valid.
@@ -354,7 +355,7 @@ func (fd *specialFileFD) Write(ctx context.Context, src usermem.IOSequence, opts
// Seek implements vfs.FileDescriptionImpl.Seek.
func (fd *specialFileFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
if !fd.seekable {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
fd.mu.Lock()
defer fd.mu.Unlock()
diff --git a/pkg/sentry/fsimpl/gofer/symlink.go b/pkg/sentry/fsimpl/gofer/symlink.go
index 2ec819f86..dbd834c67 100644
--- a/pkg/sentry/fsimpl/gofer/symlink.go
+++ b/pkg/sentry/fsimpl/gofer/symlink.go
@@ -41,7 +41,7 @@ func (d *dentry) readlink(ctx context.Context, mnt *vfs.Mount) (string, error) {
d.haveTarget = true
d.target = target
}
- d.dataMu.Unlock()
+ d.dataMu.Unlock() // +checklocksforce: guaranteed locked from above.
}
return target, err
}
diff --git a/pkg/sentry/fsimpl/host/BUILD b/pkg/sentry/fsimpl/host/BUILD
index b94dfeb7f..476545d00 100644
--- a/pkg/sentry/fsimpl/host/BUILD
+++ b/pkg/sentry/fsimpl/host/BUILD
@@ -45,10 +45,10 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fdnotifier",
"//pkg/fspath",
"//pkg/hostarch",
- "//pkg/iovec",
"//pkg/log",
"//pkg/marshal/primitive",
"//pkg/refs",
diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go
index a81f550b1..cdd33e0fe 100644
--- a/pkg/sentry/fsimpl/host/host.go
+++ b/pkg/sentry/fsimpl/host/host.go
@@ -24,6 +24,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/hostarch"
@@ -109,12 +110,12 @@ type inode struct {
func newInode(ctx context.Context, fs *filesystem, hostFD int, savable bool, fileType linux.FileMode, isTTY bool) (*inode, error) {
// Determine if hostFD is seekable.
_, err := unix.Seek(hostFD, 0, linux.SEEK_CUR)
- seekable := err != syserror.ESPIPE
+ seekable := !linuxerr.Equals(linuxerr.ESPIPE, err)
// We expect regular files to be seekable, as this is required for them to
// be memory-mappable.
if !seekable && fileType == unix.S_IFREG {
ctx.Infof("host.newInode: host FD %d is a non-seekable regular file", hostFD)
- return nil, syserror.ESPIPE
+ return nil, linuxerr.ESPIPE
}
i := &inode{
@@ -289,10 +290,10 @@ func (i *inode) Mode() linux.FileMode {
// Stat implements kernfs.Inode.Stat.
func (i *inode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) {
if opts.Mask&linux.STATX__RESERVED != 0 {
- return linux.Statx{}, syserror.EINVAL
+ return linux.Statx{}, linuxerr.EINVAL
}
if opts.Sync&linux.AT_STATX_SYNC_TYPE == linux.AT_STATX_SYNC_TYPE {
- return linux.Statx{}, syserror.EINVAL
+ return linux.Statx{}, linuxerr.EINVAL
}
fs := vfsfs.Impl().(*filesystem)
@@ -301,7 +302,7 @@ func (i *inode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOp
mask := opts.Mask & linux.STATX_ALL
var s unix.Statx_t
err := unix.Statx(i.hostFD, "", int(unix.AT_EMPTY_PATH|opts.Sync), int(mask), &s)
- if err == syserror.ENOSYS {
+ if linuxerr.Equals(linuxerr.ENOSYS, err) {
// Fallback to fstat(2), if statx(2) is not supported on the host.
//
// TODO(b/151263641): Remove fallback.
@@ -408,7 +409,7 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
return nil
}
if m&^(linux.STATX_MODE|linux.STATX_SIZE|linux.STATX_ATIME|linux.STATX_MTIME) != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
var hostStat unix.Stat_t
if err := unix.Fstat(i.hostFD, &hostStat); err != nil {
@@ -425,7 +426,7 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
}
if m&linux.STATX_SIZE != 0 {
if hostStat.Mode&linux.S_IFMT != linux.S_IFREG {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if err := unix.Ftruncate(i.hostFD, int64(s.Size)); err != nil {
return err
@@ -470,7 +471,7 @@ func (i *inode) DecRef(ctx context.Context) {
func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
// Once created, we cannot re-open a socket fd through /proc/[pid]/fd/.
if i.Mode().FileType() == linux.S_IFSOCK {
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
return i.open(ctx, d, rp.Mount(), opts.Flags)
}
@@ -529,7 +530,7 @@ func (i *inode) open(ctx context.Context, d *kernfs.Dentry, mnt *vfs.Mount, flag
default:
log.Warningf("cannot import host fd %d with file type %o", i.hostFD, fileType)
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
}
@@ -589,7 +590,7 @@ func (f *fileDescription) PRead(ctx context.Context, dst usermem.IOSequence, off
i := f.inode
if !i.seekable {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
return readFromHostFD(ctx, i.hostFD, dst, offset, opts.Flags)
@@ -660,7 +661,7 @@ func readFromHostFD(ctx context.Context, hostFD int, dst usermem.IOSequence, off
// PWrite implements vfs.FileDescriptionImpl.PWrite.
func (f *fileDescription) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
if !f.inode.seekable {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
return f.writeToHostFD(ctx, src, offset, opts.Flags)
@@ -721,7 +722,7 @@ func (f *fileDescription) writeToHostFD(ctx context.Context, src usermem.IOSeque
func (f *fileDescription) Seek(_ context.Context, offset int64, whence int32) (int64, error) {
i := f.inode
if !i.seekable {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
f.offsetMu.Lock()
@@ -730,17 +731,17 @@ func (f *fileDescription) Seek(_ context.Context, offset int64, whence int32) (i
switch whence {
case linux.SEEK_SET:
if offset < 0 {
- return f.offset, syserror.EINVAL
+ return f.offset, linuxerr.EINVAL
}
f.offset = offset
case linux.SEEK_CUR:
// Check for overflow. Note that underflow cannot occur, since f.offset >= 0.
if offset > math.MaxInt64-f.offset {
- return f.offset, syserror.EOVERFLOW
+ return f.offset, linuxerr.EOVERFLOW
}
if f.offset+offset < 0 {
- return f.offset, syserror.EINVAL
+ return f.offset, linuxerr.EINVAL
}
f.offset += offset
@@ -753,10 +754,10 @@ func (f *fileDescription) Seek(_ context.Context, offset int64, whence int32) (i
// Check for overflow. Note that underflow cannot occur, since size >= 0.
if offset > math.MaxInt64-size {
- return f.offset, syserror.EOVERFLOW
+ return f.offset, linuxerr.EOVERFLOW
}
if size+offset < 0 {
- return f.offset, syserror.EINVAL
+ return f.offset, linuxerr.EINVAL
}
f.offset = size + offset
@@ -773,7 +774,7 @@ func (f *fileDescription) Seek(_ context.Context, offset int64, whence int32) (i
default:
// Invalid whence.
- return f.offset, syserror.EINVAL
+ return f.offset, linuxerr.EINVAL
}
return f.offset, nil
@@ -790,7 +791,7 @@ func (f *fileDescription) ConfigureMMap(_ context.Context, opts *memmap.MMapOpts
// NOTE(b/38213152): Technically, some obscure char devices can be memory
// mapped, but we only allow regular files.
if f.inode.ftype != unix.S_IFREG {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
i := f.inode
i.CachedMappable.InitFileMapperOnce()
diff --git a/pkg/sentry/fsimpl/host/socket.go b/pkg/sentry/fsimpl/host/socket.go
index ca85f5601..709d5747d 100644
--- a/pkg/sentry/fsimpl/host/socket.go
+++ b/pkg/sentry/fsimpl/host/socket.go
@@ -21,6 +21,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/socket/control"
@@ -28,7 +29,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/uniqueid"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserr"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/unet"
"gvisor.dev/gvisor/pkg/waiter"
@@ -158,9 +158,9 @@ func (c *ConnectedEndpoint) Send(ctx context.Context, data [][]byte, controlMess
if n < totalLen && err == nil {
// The host only returns a short write if it would otherwise
// block (and only for stream sockets).
- err = syserror.EAGAIN
+ err = linuxerr.EAGAIN
}
- if n > 0 && err != syserror.EAGAIN {
+ if n > 0 && !linuxerr.Equals(linuxerr.EAGAIN, err) {
// The caller may need to block to send more data, but
// otherwise there isn't anything that can be done about an
// error with a partial write.
diff --git a/pkg/sentry/fsimpl/host/socket_iovec.go b/pkg/sentry/fsimpl/host/socket_iovec.go
index b123a63ee..292b44c43 100644
--- a/pkg/sentry/fsimpl/host/socket_iovec.go
+++ b/pkg/sentry/fsimpl/host/socket_iovec.go
@@ -16,8 +16,8 @@ package host
import (
"golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/iovec"
- "gvisor.dev/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/sentry/hostfd"
)
// copyToMulti copies as many bytes from src to dst as possible.
@@ -64,13 +64,13 @@ func buildIovec(bufs [][]byte, maxlen int64, truncate bool) (length int64, iovec
if length > maxlen {
if truncate {
stopLen = maxlen
- err = syserror.EAGAIN
+ err = linuxerr.EAGAIN
} else {
- return 0, nil, nil, syserror.EMSGSIZE
+ return 0, nil, nil, linuxerr.EMSGSIZE
}
}
- if iovsRequired > iovec.MaxIovs {
+ if iovsRequired > hostfd.MaxSendRecvMsgIov {
// The kernel will reject our call if we pass this many iovs.
// Use a single intermediate buffer instead.
b := make([]byte, stopLen)
diff --git a/pkg/sentry/fsimpl/host/tty.go b/pkg/sentry/fsimpl/host/tty.go
index 0f9e20a84..5974dce64 100644
--- a/pkg/sentry/fsimpl/host/tty.go
+++ b/pkg/sentry/fsimpl/host/tty.go
@@ -17,6 +17,7 @@ package host
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -211,7 +212,7 @@ func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch
if err := t.checkChange(ctx, linux.SIGTTOU); err != nil {
// drivers/tty/tty_io.c:tiocspgrp() converts -EIO from tty_check_change()
// to -ENOTTY.
- if err == syserror.EIO {
+ if linuxerr.Equals(linuxerr.EIO, err) {
return 0, syserror.ENOTTY
}
return 0, err
@@ -230,7 +231,7 @@ func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch
// pgID must be non-negative.
if pgID < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Process group with pgID must exist in this PID namespace.
@@ -242,7 +243,7 @@ func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch
// Check that new process group is in the TTY session.
if pg.Session() != t.session {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
t.fgProcessGroup = pg
diff --git a/pkg/sentry/fsimpl/host/util.go b/pkg/sentry/fsimpl/host/util.go
index 63b465859..95d7ebe2e 100644
--- a/pkg/sentry/fsimpl/host/util.go
+++ b/pkg/sentry/fsimpl/host/util.go
@@ -17,7 +17,7 @@ package host
import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
)
func toTimespec(ts linux.StatxTimestamp, omit bool) unix.Timespec {
@@ -44,5 +44,5 @@ func timespecToStatxTimestamp(ts unix.Timespec) linux.StatxTimestamp {
// isBlockError checks if an error is EAGAIN or EWOULDBLOCK.
// If so, they can be transformed into syserror.ErrWouldBlock.
func isBlockError(err error) bool {
- return err == syserror.EAGAIN || err == syserror.EWOULDBLOCK
+ return linuxerr.Equals(linuxerr.EAGAIN, err) || linuxerr.Equals(linuxerr.EWOULDBLOCK, err)
}
diff --git a/pkg/sentry/fsimpl/kernfs/BUILD b/pkg/sentry/fsimpl/kernfs/BUILD
index b7d13cced..d53937db6 100644
--- a/pkg/sentry/fsimpl/kernfs/BUILD
+++ b/pkg/sentry/fsimpl/kernfs/BUILD
@@ -104,6 +104,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/hostarch",
"//pkg/log",
@@ -135,6 +136,7 @@ go_test(
":kernfs",
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/refs",
"//pkg/refsvfs2",
diff --git a/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go b/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go
index 84b1c3745..9d7526e47 100644
--- a/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go
+++ b/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go
@@ -19,9 +19,9 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -71,7 +71,7 @@ func (f *DynamicBytesFile) Open(ctx context.Context, rp *vfs.ResolvingPath, d *D
// inode attributes to be changed. Override SetStat() making it call
// f.InodeAttrs to allow it.
func (*DynamicBytesFile) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// DynamicBytesFD implements vfs.FileDescriptionImpl for an FD backed by a
@@ -137,5 +137,5 @@ func (fd *DynamicBytesFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux
// SetStat implements vfs.FileDescriptionImpl.SetStat.
func (fd *DynamicBytesFD) SetStat(context.Context, vfs.SetStatOptions) error {
// DynamicBytesFiles are immutable.
- return syserror.EPERM
+ return linuxerr.EPERM
}
diff --git a/pkg/sentry/fsimpl/kernfs/fd_impl_util.go b/pkg/sentry/fsimpl/kernfs/fd_impl_util.go
index e55111af0..8b008dc10 100644
--- a/pkg/sentry/fsimpl/kernfs/fd_impl_util.go
+++ b/pkg/sentry/fsimpl/kernfs/fd_impl_util.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -248,10 +249,10 @@ func (fd *GenericDirectoryFD) Seek(ctx context.Context, offset int64, whence int
panic(fmt.Sprintf("Invalid GenericDirectoryFD.seekEnd = %v", fd.seekEnd))
}
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
fd.off = offset
return offset, nil
diff --git a/pkg/sentry/fsimpl/kernfs/filesystem.go b/pkg/sentry/fsimpl/kernfs/filesystem.go
index f50b0fb08..20d2526ad 100644
--- a/pkg/sentry/fsimpl/kernfs/filesystem.go
+++ b/pkg/sentry/fsimpl/kernfs/filesystem.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
@@ -70,7 +71,7 @@ afterSymlink:
return d.parent, nil
}
if len(name) > linux.NAME_MAX {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
d.dirMu.Lock()
next, err := fs.revalidateChildLocked(ctx, rp.VirtualFilesystem(), d, name, d.children[name])
@@ -217,7 +218,7 @@ func checkCreateLocked(ctx context.Context, creds *auth.Credentials, name string
return syserror.EEXIST
}
if len(name) > linux.NAME_MAX {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
if _, ok := parent.children[name]; ok {
return syserror.EEXIST
@@ -237,7 +238,7 @@ func checkCreateLocked(ctx context.Context, creds *auth.Credentials, name string
func checkDeleteLocked(ctx context.Context, rp *vfs.ResolvingPath, d *Dentry) error {
parent := d.parent
if parent == nil {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
if parent.vfsd.IsDead() {
return syserror.ENOENT
@@ -364,7 +365,7 @@ func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
return syserror.ENOENT
}
if rp.Mount() != vd.Mount() {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if err := rp.Mount().CheckBeginWrite(); err != nil {
return err
@@ -373,7 +374,7 @@ func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
d := vd.Dentry().Impl().(*Dentry)
if d.isDir() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
childI, err := parent.inode.NewLink(ctx, pc, d.inode)
@@ -411,7 +412,7 @@ func (fs *Filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
defer rp.Mount().EndWrite()
childI, err := parent.inode.NewDir(ctx, pc, opts)
if err != nil {
- if !opts.ForSyntheticMountpoint || err == syserror.EEXIST {
+ if !opts.ForSyntheticMountpoint || linuxerr.Equals(linuxerr.EEXIST, err) {
return err
}
childI = newSyntheticDirectory(ctx, rp.Credentials(), opts.Mode)
@@ -542,11 +543,11 @@ afterTrailingSymlink:
return nil, syserror.EISDIR
}
if len(pc) > linux.NAME_MAX {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
// Determine whether or not we need to create a file.
child, err := fs.stepExistingLocked(ctx, rp, parent, false /* mayFollowSymlinks */)
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
// Already checked for searchability above; now check for writability.
if err := parent.inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayWrite); err != nil {
return nil, err
@@ -622,7 +623,7 @@ func (fs *Filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
}
if !d.isSymlink() {
fs.mu.RUnlock()
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
// Inode.Readlink() cannot be called holding fs locks.
@@ -635,12 +636,6 @@ func (fs *Filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
// RenameAt implements vfs.FilesystemImpl.RenameAt.
func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
- // Only RENAME_NOREPLACE is supported.
- if opts.Flags&^linux.RENAME_NOREPLACE != 0 {
- return syserror.EINVAL
- }
- noReplace := opts.Flags&linux.RENAME_NOREPLACE != 0
-
fs.mu.Lock()
defer fs.processDeferredDecRefs(ctx)
defer fs.mu.Unlock()
@@ -651,9 +646,16 @@ func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
if err != nil {
return err
}
+
+ // Only RENAME_NOREPLACE is supported.
+ if opts.Flags&^linux.RENAME_NOREPLACE != 0 {
+ return linuxerr.EINVAL
+ }
+ noReplace := opts.Flags&linux.RENAME_NOREPLACE != 0
+
mnt := rp.Mount()
if mnt != oldParentVD.Mount() {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if err := mnt.CheckBeginWrite(); err != nil {
return err
@@ -681,12 +683,14 @@ func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
if noReplace {
return syserror.EEXIST
}
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
- switch err := checkCreateLocked(ctx, rp.Credentials(), newName, dstDir); err {
- case nil:
+
+ err = checkCreateLocked(ctx, rp.Credentials(), newName, dstDir)
+ switch {
+ case err == nil:
// Ok, continue with rename as replacement.
- case syserror.EEXIST:
+ case linuxerr.Equals(linuxerr.EEXIST, err):
if noReplace {
// Won't overwrite existing node since RENAME_NOREPLACE was requested.
return syserror.EEXIST
@@ -748,7 +752,7 @@ func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
fs.deferDecRef(replaced)
replaceVFSD = replaced.VFSDentry()
}
- virtfs.CommitRenameReplaceDentry(ctx, srcVFSD, replaceVFSD)
+ virtfs.CommitRenameReplaceDentry(ctx, srcVFSD, replaceVFSD) // +checklocksforce: to may be nil, that's okay.
return nil
}
@@ -773,7 +777,7 @@ func (fs *Filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
return syserror.ENOTDIR
}
if d.inode.HasChildren() {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
virtfs := rp.VirtualFilesystem()
parentDentry := d.parent
@@ -784,7 +788,7 @@ func (fs *Filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
defer mntns.DecRef(ctx)
vfsd := d.VFSDentry()
if err := virtfs.PrepareDeleteDentry(mntns, vfsd); err != nil {
- return err
+ return err // +checklocksforce: vfsd is not locked.
}
if err := parentDentry.inode.RmDir(ctx, d.name, d.inode); err != nil {
@@ -926,7 +930,7 @@ func (fs *Filesystem) BoundEndpointAt(ctx context.Context, rp *vfs.ResolvingPath
if err := d.inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayWrite); err != nil {
return nil, err
}
- return nil, syserror.ECONNREFUSED
+ return nil, linuxerr.ECONNREFUSED
}
// ListXattrAt implements vfs.FilesystemImpl.ListXattrAt.
@@ -939,7 +943,7 @@ func (fs *Filesystem) ListXattrAt(ctx context.Context, rp *vfs.ResolvingPath, si
return nil, err
}
// kernfs currently does not support extended attributes.
- return nil, syserror.ENOTSUP
+ return nil, linuxerr.ENOTSUP
}
// GetXattrAt implements vfs.FilesystemImpl.GetXattrAt.
@@ -952,7 +956,7 @@ func (fs *Filesystem) GetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
return "", err
}
// kernfs currently does not support extended attributes.
- return "", syserror.ENOTSUP
+ return "", linuxerr.ENOTSUP
}
// SetXattrAt implements vfs.FilesystemImpl.SetXattrAt.
@@ -965,7 +969,7 @@ func (fs *Filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
return err
}
// kernfs currently does not support extended attributes.
- return syserror.ENOTSUP
+ return linuxerr.ENOTSUP
}
// RemoveXattrAt implements vfs.FilesystemImpl.RemoveXattrAt.
@@ -978,7 +982,7 @@ func (fs *Filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
return err
}
// kernfs currently does not support extended attributes.
- return syserror.ENOTSUP
+ return linuxerr.ENOTSUP
}
// PrependPath implements vfs.FilesystemImpl.PrependPath.
diff --git a/pkg/sentry/fsimpl/kernfs/inode_impl_util.go b/pkg/sentry/fsimpl/kernfs/inode_impl_util.go
index 3d0866ecf..df29c8838 100644
--- a/pkg/sentry/fsimpl/kernfs/inode_impl_util.go
+++ b/pkg/sentry/fsimpl/kernfs/inode_impl_util.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
@@ -61,27 +62,27 @@ type InodeDirectoryNoNewChildren struct{}
// NewFile implements Inode.NewFile.
func (InodeDirectoryNoNewChildren) NewFile(context.Context, string, vfs.OpenOptions) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// NewDir implements Inode.NewDir.
func (InodeDirectoryNoNewChildren) NewDir(context.Context, string, vfs.MkdirOptions) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// NewLink implements Inode.NewLink.
func (InodeDirectoryNoNewChildren) NewLink(context.Context, string, Inode) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// NewSymlink implements Inode.NewSymlink.
func (InodeDirectoryNoNewChildren) NewSymlink(context.Context, string, string) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// NewNode implements Inode.NewNode.
func (InodeDirectoryNoNewChildren) NewNode(context.Context, string, vfs.MknodOptions) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// InodeNotDirectory partially implements the Inode interface, specifically the
@@ -158,12 +159,12 @@ type InodeNotSymlink struct{}
// Readlink implements Inode.Readlink.
func (InodeNotSymlink) Readlink(context.Context, *vfs.Mount) (string, error) {
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
// Getlink implements Inode.Getlink.
func (InodeNotSymlink) Getlink(context.Context, *vfs.Mount) (vfs.VirtualDentry, string, error) {
- return vfs.VirtualDentry{}, "", syserror.EINVAL
+ return vfs.VirtualDentry{}, "", linuxerr.EINVAL
}
// InodeAttrs partially implements the Inode interface, specifically the
@@ -285,7 +286,7 @@ func (a *InodeAttrs) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *aut
// allowed by kernfs files but does not do anything. If some other behavior is
// needed, the embedder should consider extending SetStat.
if opts.Stat.Mask&^(linux.STATX_MODE|linux.STATX_UID|linux.STATX_GID|linux.STATX_ATIME|linux.STATX_MTIME|linux.STATX_SIZE) != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if opts.Stat.Mask&linux.STATX_SIZE != 0 && a.Mode().IsDir() {
return syserror.EISDIR
@@ -569,7 +570,7 @@ func (o *OrderedChildren) checkExistingLocked(name string, child Inode) error {
// Unlink implements Inode.Unlink.
func (o *OrderedChildren) Unlink(ctx context.Context, name string, child Inode) error {
if !o.writable {
- return syserror.EPERM
+ return linuxerr.EPERM
}
o.mu.Lock()
defer o.mu.Unlock()
@@ -599,15 +600,15 @@ func (o *OrderedChildren) RmDir(ctx context.Context, name string, child Inode) e
// Postcondition: reference on any replaced dentry transferred to caller.
func (o *OrderedChildren) Rename(ctx context.Context, oldname, newname string, child, dstDir Inode) error {
if !o.writable {
- return syserror.EPERM
+ return linuxerr.EPERM
}
dst, ok := dstDir.(interface{}).(*OrderedChildren)
if !ok {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if !dst.writable {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Note: There's a potential deadlock below if concurrent calls to Rename
@@ -653,7 +654,7 @@ type InodeSymlink struct {
// Open implements Inode.Open.
func (InodeSymlink) Open(ctx context.Context, rp *vfs.ResolvingPath, d *Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
- return nil, syserror.ELOOP
+ return nil, linuxerr.ELOOP
}
// StaticDirectory is a standard implementation of a directory with static
@@ -709,7 +710,7 @@ func (s *StaticDirectory) Open(ctx context.Context, rp *vfs.ResolvingPath, d *De
// SetStat implements Inode.SetStat not allowing inode attributes to be changed.
func (*StaticDirectory) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// DecRef implements Inode.DecRef.
diff --git a/pkg/sentry/fsimpl/kernfs/kernfs_test.go b/pkg/sentry/fsimpl/kernfs/kernfs_test.go
index 1cd3137e6..609887943 100644
--- a/pkg/sentry/fsimpl/kernfs/kernfs_test.go
+++ b/pkg/sentry/fsimpl/kernfs/kernfs_test.go
@@ -22,12 +22,12 @@ import (
"github.com/google/go-cmp/cmp"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/testutil"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -94,7 +94,7 @@ type attrs struct {
}
func (*attrs) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
type readonlyDir struct {
@@ -196,15 +196,15 @@ func (d *dir) NewFile(ctx context.Context, name string, opts vfs.OpenOptions) (k
}
func (*dir) NewLink(context.Context, string, kernfs.Inode) (kernfs.Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
func (*dir) NewSymlink(context.Context, string, string) (kernfs.Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
func (*dir) NewNode(context.Context, string, vfs.MknodOptions) (kernfs.Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
func (fsType) Name() string {
@@ -318,10 +318,10 @@ func TestDirFDReadWrite(t *testing.T) {
defer fd.DecRef(sys.Ctx)
// Read/Write should fail for directory FDs.
- if _, err := fd.Read(sys.Ctx, usermem.BytesIOSequence([]byte{}), vfs.ReadOptions{}); err != syserror.EISDIR {
+ if _, err := fd.Read(sys.Ctx, usermem.BytesIOSequence([]byte{}), vfs.ReadOptions{}); !linuxerr.Equals(linuxerr.EISDIR, err) {
t.Fatalf("Read for directory FD failed with unexpected error: %v", err)
}
- if _, err := fd.Write(sys.Ctx, usermem.BytesIOSequence([]byte{}), vfs.WriteOptions{}); err != syserror.EBADF {
+ if _, err := fd.Write(sys.Ctx, usermem.BytesIOSequence([]byte{}), vfs.WriteOptions{}); !linuxerr.Equals(linuxerr.EBADF, err) {
t.Fatalf("Write for directory FD failed with unexpected error: %v", err)
}
}
diff --git a/pkg/sentry/fsimpl/kernfs/symlink.go b/pkg/sentry/fsimpl/kernfs/symlink.go
index a0736c0d6..4adf76ce6 100644
--- a/pkg/sentry/fsimpl/kernfs/symlink.go
+++ b/pkg/sentry/fsimpl/kernfs/symlink.go
@@ -17,9 +17,9 @@ package kernfs
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// StaticSymlink provides an Inode implementation for symlinks that point to
@@ -62,5 +62,5 @@ func (s *StaticSymlink) Getlink(context.Context, *vfs.Mount) (vfs.VirtualDentry,
// SetStat implements Inode.SetStat not allowing inode attributes to be changed.
func (*StaticSymlink) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
diff --git a/pkg/sentry/fsimpl/kernfs/synthetic_directory.go b/pkg/sentry/fsimpl/kernfs/synthetic_directory.go
index 11694c392..c91d23b56 100644
--- a/pkg/sentry/fsimpl/kernfs/synthetic_directory.go
+++ b/pkg/sentry/fsimpl/kernfs/synthetic_directory.go
@@ -19,9 +19,9 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// syntheticDirectory implements kernfs.Inode for a directory created by
@@ -65,13 +65,13 @@ func (dir *syntheticDirectory) Open(ctx context.Context, rp *vfs.ResolvingPath,
// NewFile implements Inode.NewFile.
func (dir *syntheticDirectory) NewFile(ctx context.Context, name string, opts vfs.OpenOptions) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// NewDir implements Inode.NewDir.
func (dir *syntheticDirectory) NewDir(ctx context.Context, name string, opts vfs.MkdirOptions) (Inode, error) {
if !opts.ForSyntheticMountpoint {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
subdirI := newSyntheticDirectory(ctx, auth.CredentialsFromContext(ctx), opts.Mode&linux.PermissionsMask)
if err := dir.OrderedChildren.Insert(name, subdirI); err != nil {
@@ -84,17 +84,17 @@ func (dir *syntheticDirectory) NewDir(ctx context.Context, name string, opts vfs
// NewLink implements Inode.NewLink.
func (dir *syntheticDirectory) NewLink(ctx context.Context, name string, target Inode) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// NewSymlink implements Inode.NewSymlink.
func (dir *syntheticDirectory) NewSymlink(ctx context.Context, name, target string) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// NewNode implements Inode.NewNode.
func (dir *syntheticDirectory) NewNode(ctx context.Context, name string, opts vfs.MknodOptions) (Inode, error) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
// DecRef implements Inode.DecRef.
diff --git a/pkg/sentry/fsimpl/overlay/BUILD b/pkg/sentry/fsimpl/overlay/BUILD
index 5504476c8..ed730e215 100644
--- a/pkg/sentry/fsimpl/overlay/BUILD
+++ b/pkg/sentry/fsimpl/overlay/BUILD
@@ -29,6 +29,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/hostarch",
"//pkg/log",
diff --git a/pkg/sentry/fsimpl/overlay/copy_up.go b/pkg/sentry/fsimpl/overlay/copy_up.go
index 45aa5a494..1f85a1f0d 100644
--- a/pkg/sentry/fsimpl/overlay/copy_up.go
+++ b/pkg/sentry/fsimpl/overlay/copy_up.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -51,13 +52,13 @@ func (d *dentry) copyUpLocked(ctx context.Context) error {
// Can be copied-up.
default:
// Can't be copied-up.
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Ensure that our parent directory is copied-up.
if d.parent == nil {
// d is a filesystem root with no upper layer.
- return syserror.EROFS
+ return linuxerr.EROFS
}
if err := d.parent.copyUpLocked(ctx); err != nil {
return err
@@ -271,7 +272,7 @@ func (d *dentry) copyUpLocked(ctx context.Context) error {
}
if upperStat.Mask&linux.STATX_INO == 0 {
cleanupUndoCopyUp()
- return syserror.EREMOTE
+ return linuxerr.EREMOTE
}
atomic.StoreUint32(&d.devMajor, upperStat.DevMajor)
atomic.StoreUint32(&d.devMinor, upperStat.DevMinor)
@@ -349,7 +350,7 @@ func (d *dentry) copyXattrsLocked(ctx context.Context) error {
lowerXattrs, err := vfsObj.ListXattrAt(ctx, d.fs.creds, lowerPop, 0)
if err != nil {
- if err == syserror.EOPNOTSUPP {
+ if linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
// There are no guarantees as to the contents of lowerXattrs.
return nil
}
diff --git a/pkg/sentry/fsimpl/overlay/directory.go b/pkg/sentry/fsimpl/overlay/directory.go
index df4492346..ad3cdbb56 100644
--- a/pkg/sentry/fsimpl/overlay/directory.go
+++ b/pkg/sentry/fsimpl/overlay/directory.go
@@ -19,10 +19,10 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
func (d *dentry) isDir() bool {
@@ -69,7 +69,7 @@ func (d *dentry) collectWhiteoutsForRmdirLocked(ctx context.Context) (map[string
return nil
}
// Non-whiteout file in the directory prevents rmdir.
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}))
if err != nil {
readdirErr = err
@@ -88,7 +88,7 @@ func (d *dentry) collectWhiteoutsForRmdirLocked(ctx context.Context) (map[string
}
if stat.RdevMajor != 0 || stat.RdevMinor != 0 {
// This file is a real character device, not a whiteout.
- readdirErr = syserror.ENOTEMPTY
+ readdirErr = linuxerr.ENOTEMPTY
return false
}
whiteouts[maybeWhiteoutName] = isUpper
@@ -256,7 +256,7 @@ func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (in
switch whence {
case linux.SEEK_SET:
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset == 0 {
// Ensure that the next call to fd.IterDirents() calls
@@ -268,13 +268,13 @@ func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (in
case linux.SEEK_CUR:
offset += fd.off
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Don't clear fd.dirents in this case, even if offset == 0.
fd.off = offset
return fd.off, nil
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/fsimpl/overlay/filesystem.go b/pkg/sentry/fsimpl/overlay/filesystem.go
index 46c500427..77f9affc1 100644
--- a/pkg/sentry/fsimpl/overlay/filesystem.go
+++ b/pkg/sentry/fsimpl/overlay/filesystem.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -86,7 +87,7 @@ func putDentrySlice(ds *[]*dentry) {
// fs.renameMuRUnlockAndCheckDrop(&ds)" than "defer func() {
// fs.renameMuRUnlockAndCheckDrop(ds) }()" to work around this.
//
-// +checklocks:fs.renameMu
+// +checklocksrelease:fs.renameMu
func (fs *filesystem) renameMuRUnlockAndCheckDrop(ctx context.Context, dsp **[]*dentry) {
fs.renameMu.RUnlock()
if *dsp == nil {
@@ -112,7 +113,7 @@ func (fs *filesystem) renameMuRUnlockAndCheckDrop(ctx context.Context, dsp **[]*
putDentrySlice(*dsp)
}
-// +checklocks:fs.renameMu
+// +checklocksrelease:fs.renameMu
func (fs *filesystem) renameMuUnlockAndCheckDrop(ctx context.Context, ds **[]*dentry) {
if *ds == nil {
fs.renameMu.Unlock()
@@ -218,7 +219,7 @@ func (fs *filesystem) lookupLocked(ctx context.Context, parent *dentry, name str
Start: parentVD,
Path: childPath,
}, &vfs.GetDentryOptions{})
- if err == syserror.ENOENT || err == syserror.ENAMETOOLONG {
+ if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.ENAMETOOLONG, err) {
// The file doesn't exist on this layer. Proceed to the next one.
return true
}
@@ -245,7 +246,7 @@ func (fs *filesystem) lookupLocked(ctx context.Context, parent *dentry, name str
return false
}
if stat.Mask&mask != mask {
- lookupErr = syserror.EREMOTE
+ lookupErr = linuxerr.EREMOTE
return false
}
@@ -352,7 +353,7 @@ func (fs *filesystem) lookupLayerLocked(ctx context.Context, parent *dentry, nam
}, &vfs.StatOptions{
Mask: linux.STATX_TYPE,
})
- if err == syserror.ENOENT || err == syserror.ENAMETOOLONG {
+ if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.ENAMETOOLONG, err) {
// The file doesn't exist on this layer. Proceed to the next
// one.
return true
@@ -365,7 +366,7 @@ func (fs *filesystem) lookupLayerLocked(ctx context.Context, parent *dentry, nam
// Linux's overlayfs tends to return EREMOTE in cases where a file
// is unusable for reasons that are not better captured by another
// errno.
- lookupErr = syserror.EREMOTE
+ lookupErr = linuxerr.EREMOTE
return false
}
if isWhiteout(&stat) {
@@ -620,11 +621,11 @@ func (fs *filesystem) GetParentDentryAt(ctx context.Context, rp *vfs.ResolvingPa
func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error {
return fs.doCreateAt(ctx, rp, false /* dir */, func(parent *dentry, childName string, haveUpperWhiteout bool) error {
if rp.Mount() != vd.Mount() {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
old := vd.Dentry().Impl().(*dentry)
if old.isDir() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := old.copyUpLocked(ctx); err != nil {
return err
@@ -725,7 +726,7 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
return fs.doCreateAt(ctx, rp, false /* dir */, func(parent *dentry, childName string, haveUpperWhiteout bool) error {
// Disallow attempts to create whiteouts.
if opts.Mode&linux.S_IFMT == linux.S_IFCHR && opts.DevMajor == 0 && opts.DevMinor == 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
vfsObj := fs.vfsfs.VirtualFilesystem()
pop := vfs.PathOperation{
@@ -811,7 +812,7 @@ afterTrailingSymlink:
// Determine whether or not we need to create a file.
parent.dirMu.Lock()
child, topLookupLayer, err := fs.stepLocked(ctx, rp, parent, false /* mayFollowSymlinks */, &ds)
- if err == syserror.ENOENT && mayCreate {
+ if linuxerr.Equals(linuxerr.ENOENT, err) && mayCreate {
fd, err := fs.createAndOpenLocked(ctx, rp, parent, &opts, &ds, topLookupLayer == lookupLayerUpperWhiteout)
parent.dirMu.Unlock()
return fd, err
@@ -871,7 +872,7 @@ func (d *dentry) openCopiedUp(ctx context.Context, rp *vfs.ResolvingPath, opts *
return nil, syserror.EISDIR
}
if opts.Flags&linux.O_DIRECT != 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
fd := &directoryFD{}
fd.LockFD.Init(&d.locks)
@@ -1017,10 +1018,7 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
// RenameAt implements vfs.FilesystemImpl.RenameAt.
func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
- if opts.Flags != 0 {
- return syserror.EINVAL
- }
-
+ // Resolve newParent first to verify that it's on this Mount.
var ds *[]*dentry
fs.renameMu.Lock()
defer fs.renameMuUnlockAndCheckDrop(ctx, &ds)
@@ -1028,13 +1026,21 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
if err != nil {
return err
}
+
+ if opts.Flags&^linux.RENAME_NOREPLACE != 0 {
+ return linuxerr.EINVAL
+ }
+
newName := rp.Component()
if newName == "." || newName == ".." {
- return syserror.EBUSY
+ if opts.Flags&linux.RENAME_NOREPLACE != 0 {
+ return syserror.EEXIST
+ }
+ return linuxerr.EBUSY
}
mnt := rp.Mount()
if mnt != oldParentVD.Mount() {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if err := mnt.CheckBeginWrite(); err != nil {
return err
@@ -1059,7 +1065,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
}
if renamed.isDir() {
if renamed == newParent || genericIsAncestorDentry(renamed, newParent) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if oldParent != newParent {
if err := renamed.checkPermissions(creds, vfs.MayWrite); err != nil {
@@ -1089,17 +1095,20 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
whiteouts map[string]bool
)
replaced, replacedLayer, err = fs.getChildLocked(ctx, newParent, newName, &ds)
- if err != nil && err != syserror.ENOENT {
+ if err != nil && !linuxerr.Equals(linuxerr.ENOENT, err) {
return err
}
if replaced != nil {
+ if opts.Flags&linux.RENAME_NOREPLACE != 0 {
+ return syserror.EEXIST
+ }
replacedVFSD = &replaced.vfsd
if replaced.isDir() {
if !renamed.isDir() {
return syserror.EISDIR
}
if genericIsAncestorDentry(replaced, renamed) {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
replaced.dirMu.Lock()
defer replaced.dirMu.Unlock()
@@ -1169,7 +1178,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
Root: replaced.upperVD,
Start: replaced.upperVD,
Path: fspath.Parse(whiteoutName),
- }); err != nil && err != syserror.EEXIST {
+ }); err != nil && !linuxerr.Equals(linuxerr.EEXIST, err) {
panic(fmt.Sprintf("unrecoverable overlayfs inconsistency: failed to recreate deleted whiteout after RenameAt failure: %v", err))
}
}
@@ -1277,10 +1286,10 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
defer rp.Mount().EndWrite()
name := rp.Component()
if name == "." {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if name == ".." {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
vfsObj := rp.VirtualFilesystem()
mntns := vfs.MountNamespaceFromContext(ctx)
@@ -1336,7 +1345,7 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
Root: child.upperVD,
Start: child.upperVD,
Path: fspath.Parse(whiteoutName),
- }); err != nil && err != syserror.EEXIST {
+ }); err != nil && !linuxerr.Equals(linuxerr.EEXIST, err) {
panic(fmt.Sprintf("unrecoverable overlayfs inconsistency: failed to recreate deleted whiteout after RmdirAt failure: %v", err))
}
}
diff --git a/pkg/sentry/fsimpl/overlay/overlay.go b/pkg/sentry/fsimpl/overlay/overlay.go
index 454c20d4f..46d9f1f1d 100644
--- a/pkg/sentry/fsimpl/overlay/overlay.go
+++ b/pkg/sentry/fsimpl/overlay/overlay.go
@@ -40,13 +40,13 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/refsvfs2"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Name is the default filesystem name.
@@ -135,7 +135,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
fsopts, ok := fsoptsRaw.(FilesystemOptions)
if fsoptsRaw != nil && !ok {
ctx.Infof("overlay.FilesystemType.GetFilesystem: GetFilesystemOptions.InternalData has type %T, wanted overlay.FilesystemOptions or nil", fsoptsRaw)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
vfsroot := vfs.RootFromContext(ctx)
if vfsroot.Ok() {
@@ -145,7 +145,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
if upperPathname, ok := mopts["upperdir"]; ok {
if fsopts.UpperRoot.Ok() {
ctx.Infof("overlay.FilesystemType.GetFilesystem: both upperdir and FilesystemOptions.UpperRoot are specified")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
delete(mopts, "upperdir")
// Linux overlayfs also requires a workdir when upperdir is
@@ -154,7 +154,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
upperPath := fspath.Parse(upperPathname)
if !upperPath.Absolute {
ctx.Infof("overlay.FilesystemType.GetFilesystem: upperdir %q must be absolute", upperPathname)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
upperRoot, err := vfsObj.GetDentryAt(ctx, creds, &vfs.PathOperation{
Root: vfsroot,
@@ -181,7 +181,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
if lowerPathnamesStr, ok := mopts["lowerdir"]; ok {
if len(fsopts.LowerRoots) != 0 {
ctx.Infof("overlay.FilesystemType.GetFilesystem: both lowerdir and FilesystemOptions.LowerRoots are specified")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
delete(mopts, "lowerdir")
lowerPathnames := strings.Split(lowerPathnamesStr, ":")
@@ -189,7 +189,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
lowerPath := fspath.Parse(lowerPathname)
if !lowerPath.Absolute {
ctx.Infof("overlay.FilesystemType.GetFilesystem: lowerdir %q must be absolute", lowerPathname)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
lowerRoot, err := vfsObj.GetDentryAt(ctx, creds, &vfs.PathOperation{
Root: vfsroot,
@@ -216,21 +216,21 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
if len(mopts) != 0 {
ctx.Infof("overlay.FilesystemType.GetFilesystem: unused options: %v", mopts)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
if len(fsopts.LowerRoots) == 0 {
ctx.Infof("overlay.FilesystemType.GetFilesystem: at least one lower layer is required")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
if len(fsopts.LowerRoots) < 2 && !fsopts.UpperRoot.Ok() {
ctx.Infof("overlay.FilesystemType.GetFilesystem: at least two lower layers are required when no upper layer is present")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
const maxLowerLayers = 500 // Linux: fs/overlay/super.c:OVL_MAX_STACK
if len(fsopts.LowerRoots) > maxLowerLayers {
ctx.Infof("overlay.FilesystemType.GetFilesystem: %d lower layers specified, maximum %d", len(fsopts.LowerRoots), maxLowerLayers)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// Take extra references held by the filesystem.
@@ -277,13 +277,13 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
if rootStat.Mask&rootStatMask != rootStatMask {
root.destroyLocked(ctx)
fs.vfsfs.DecRef(ctx)
- return nil, nil, syserror.EREMOTE
+ return nil, nil, linuxerr.EREMOTE
}
if isWhiteout(&rootStat) {
ctx.Infof("overlay.FilesystemType.GetFilesystem: filesystem root is a whiteout")
root.destroyLocked(ctx)
fs.vfsfs.DecRef(ctx)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
root.mode = uint32(rootStat.Mode)
root.uid = rootStat.UID
diff --git a/pkg/sentry/fsimpl/overlay/regular_file.go b/pkg/sentry/fsimpl/overlay/regular_file.go
index 82491a0f8..156ffeaeb 100644
--- a/pkg/sentry/fsimpl/overlay/regular_file.go
+++ b/pkg/sentry/fsimpl/overlay/regular_file.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -26,7 +27,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -415,7 +415,7 @@ func (fd *regularFileFD) ensureMappable(ctx context.Context, opts *memmap.MMapOp
// Only permit mmap of regular files, since other file types may have
// unpredictable behavior when mmapped (e.g. /dev/zero).
if atomic.LoadUint32(&d.mode)&linux.S_IFMT != linux.S_IFREG {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
// Get a Mappable for the current top layer.
diff --git a/pkg/sentry/fsimpl/pipefs/BUILD b/pkg/sentry/fsimpl/pipefs/BUILD
index 278ee3c92..a50510031 100644
--- a/pkg/sentry/fsimpl/pipefs/BUILD
+++ b/pkg/sentry/fsimpl/pipefs/BUILD
@@ -9,6 +9,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/hostarch",
"//pkg/sentry/fsimpl/kernfs",
@@ -16,6 +17,5 @@ go_library(
"//pkg/sentry/kernel/pipe",
"//pkg/sentry/kernel/time",
"//pkg/sentry/vfs",
- "//pkg/syserror",
],
)
diff --git a/pkg/sentry/fsimpl/pipefs/pipefs.go b/pkg/sentry/fsimpl/pipefs/pipefs.go
index 08aedc2ad..af09195a7 100644
--- a/pkg/sentry/fsimpl/pipefs/pipefs.go
+++ b/pkg/sentry/fsimpl/pipefs/pipefs.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
@@ -28,7 +29,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// +stateify savable
@@ -152,7 +152,7 @@ func (i *inode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.
if opts.Stat.Mask == 0 {
return nil
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Open implements kernfs.Inode.Open.
diff --git a/pkg/sentry/fsimpl/proc/BUILD b/pkg/sentry/fsimpl/proc/BUILD
index 2b628bd55..1d3d2d95f 100644
--- a/pkg/sentry/fsimpl/proc/BUILD
+++ b/pkg/sentry/fsimpl/proc/BUILD
@@ -81,6 +81,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/refs",
@@ -119,6 +120,7 @@ go_test(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/sentry/contexttest",
"//pkg/sentry/fsimpl/testutil",
@@ -127,7 +129,6 @@ go_test(
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
- "//pkg/syserror",
"//pkg/usermem",
],
)
diff --git a/pkg/sentry/fsimpl/proc/filesystem.go b/pkg/sentry/fsimpl/proc/filesystem.go
index ce8f55b1f..f2697c12d 100644
--- a/pkg/sentry/fsimpl/proc/filesystem.go
+++ b/pkg/sentry/fsimpl/proc/filesystem.go
@@ -21,11 +21,11 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
const (
@@ -76,7 +76,7 @@ func (ft FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualF
maxCachedDentries, err = strconv.ParseUint(str, 10, 64)
if err != nil {
ctx.Warningf("proc.FilesystemType.GetFilesystem: invalid dentry cache limit: dentry_cache_limit=%s", str)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/fsimpl/proc/subtasks.go b/pkg/sentry/fsimpl/proc/subtasks.go
index c53cc0122..d99f90b36 100644
--- a/pkg/sentry/fsimpl/proc/subtasks.go
+++ b/pkg/sentry/fsimpl/proc/subtasks.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -180,7 +181,7 @@ func (i *subtasksInode) Stat(ctx context.Context, vsfs *vfs.Filesystem, opts vfs
// SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
func (*subtasksInode) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// DecRef implements kernfs.Inode.DecRef.
diff --git a/pkg/sentry/fsimpl/proc/task.go b/pkg/sentry/fsimpl/proc/task.go
index d05cc1508..4577bfe2e 100644
--- a/pkg/sentry/fsimpl/proc/task.go
+++ b/pkg/sentry/fsimpl/proc/task.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -124,7 +125,7 @@ func (i *taskInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.D
// SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
func (*taskInode) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// DecRef implements kernfs.Inode.DecRef.
diff --git a/pkg/sentry/fsimpl/proc/task_files.go b/pkg/sentry/fsimpl/proc/task_files.go
index b294dfd6a..3cd9628c7 100644
--- a/pkg/sentry/fsimpl/proc/task_files.go
+++ b/pkg/sentry/fsimpl/proc/task_files.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
@@ -70,7 +71,7 @@ func getMMIncRef(task *kernel.Task) (*mm.MemoryManager, error) {
func checkTaskState(t *kernel.Task) error {
switch t.ExitState() {
case kernel.TaskExitZombie:
- return syserror.EACCES
+ return linuxerr.EACCES
case kernel.TaskExitDead:
return syserror.ESRCH
}
@@ -325,7 +326,7 @@ func (d *idMapData) Write(ctx context.Context, src usermem.IOSequence, offset in
// the file ..." - user_namespaces(7)
srclen := src.NumBytes()
if srclen >= hostarch.PageSize || offset != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
b := make([]byte, srclen)
if _, err := src.CopyIn(ctx, b); err != nil {
@@ -345,7 +346,7 @@ func (d *idMapData) Write(ctx context.Context, src usermem.IOSequence, offset in
}
lines := bytes.SplitN(b, []byte("\n"), maxIDMapLines+1)
if len(lines) > maxIDMapLines {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
entries := make([]auth.IDMapEntry, len(lines))
@@ -353,7 +354,7 @@ func (d *idMapData) Write(ctx context.Context, src usermem.IOSequence, offset in
var e auth.IDMapEntry
_, err := fmt.Sscan(string(l), &e.FirstID, &e.FirstParentID, &e.Length)
if err != nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
entries[i] = e
}
@@ -408,7 +409,7 @@ func (f *memInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.De
// Permission to read this file is governed by PTRACE_MODE_ATTACH_FSCREDS
// Since we dont implement setfsuid/setfsgid we can just use PTRACE_MODE_ATTACH
if !kernel.ContextCanTrace(ctx, f.task, true) {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
if err := checkTaskState(f.task); err != nil {
return nil, err
@@ -422,7 +423,7 @@ func (f *memInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.De
// SetStat implements kernfs.Inode.SetStat.
func (*memInode) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
var _ vfs.FileDescriptionImpl = (*memFD)(nil)
@@ -461,10 +462,10 @@ func (fd *memFD) Seek(ctx context.Context, offset int64, whence int32) (int64, e
case linux.SEEK_CUR:
offset += fd.offset
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
fd.offset = offset
return offset, nil
@@ -512,7 +513,7 @@ func (fd *memFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, e
// SetStat implements vfs.FileDescriptionImpl.SetStat.
func (fd *memFD) SetStat(context.Context, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Release implements vfs.FileDescriptionImpl.Release.
@@ -836,7 +837,7 @@ func (s *exeSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error)
// Getlink implements kernfs.Inode.Getlink.
func (s *exeSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDentry, string, error) {
if !kernel.ContextCanTrace(ctx, s.task, false) {
- return vfs.VirtualDentry{}, "", syserror.EACCES
+ return vfs.VirtualDentry{}, "", linuxerr.EACCES
}
if err := checkTaskState(s.task); err != nil {
return vfs.VirtualDentry{}, "", err
@@ -847,7 +848,7 @@ func (s *exeSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDent
s.task.WithMuLocked(func(t *kernel.Task) {
mm := t.MemoryManager()
if mm == nil {
- err = syserror.EACCES
+ err = linuxerr.EACCES
return
}
@@ -912,7 +913,7 @@ func (s *cwdSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error)
// Getlink implements kernfs.Inode.Getlink.
func (s *cwdSymlink) Getlink(ctx context.Context, _ *vfs.Mount) (vfs.VirtualDentry, string, error) {
if !kernel.ContextCanTrace(ctx, s.task, false) {
- return vfs.VirtualDentry{}, "", syserror.EACCES
+ return vfs.VirtualDentry{}, "", linuxerr.EACCES
}
if err := checkTaskState(s.task); err != nil {
return vfs.VirtualDentry{}, "", err
diff --git a/pkg/sentry/fsimpl/proc/task_net.go b/pkg/sentry/fsimpl/proc/task_net.go
index 177cb828f..ab47ea5a7 100644
--- a/pkg/sentry/fsimpl/proc/task_net.go
+++ b/pkg/sentry/fsimpl/proc/task_net.go
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
@@ -33,7 +34,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/socket/unix"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
@@ -679,7 +679,7 @@ func (d *netSnmpData) Generate(ctx context.Context, buf *bytes.Buffer) error {
continue
}
if err := d.stack.Statistics(stat, line.prefix); err != nil {
- if err == syserror.EOPNOTSUPP {
+ if linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
log.Infof("Failed to retrieve %s of /proc/net/snmp: %v", line.prefix, err)
} else {
log.Warningf("Failed to retrieve %s of /proc/net/snmp: %v", line.prefix, err)
diff --git a/pkg/sentry/fsimpl/proc/tasks_files.go b/pkg/sentry/fsimpl/proc/tasks_files.go
index 045ed7a2d..03bed22a3 100644
--- a/pkg/sentry/fsimpl/proc/tasks_files.go
+++ b/pkg/sentry/fsimpl/proc/tasks_files.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -53,7 +54,7 @@ func (s *selfSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error
t := kernel.TaskFromContext(ctx)
if t == nil {
// Who is reading this link?
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
tgid := s.pidns.IDOfThreadGroup(t.ThreadGroup())
if tgid == 0 {
@@ -69,7 +70,7 @@ func (s *selfSymlink) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualD
// SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
func (*selfSymlink) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// +stateify savable
@@ -94,7 +95,7 @@ func (s *threadSelfSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string,
t := kernel.TaskFromContext(ctx)
if t == nil {
// Who is reading this link?
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
tgid := s.pidns.IDOfThreadGroup(t.ThreadGroup())
tid := s.pidns.IDOfTask(t)
@@ -111,7 +112,7 @@ func (s *threadSelfSymlink) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.Vi
// SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
func (*threadSelfSymlink) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// dynamicBytesFileSetAttr implements a special file that allows inode
diff --git a/pkg/sentry/fsimpl/proc/tasks_sys.go b/pkg/sentry/fsimpl/proc/tasks_sys.go
index 88ab49048..99f64a9d8 100644
--- a/pkg/sentry/fsimpl/proc/tasks_sys.go
+++ b/pkg/sentry/fsimpl/proc/tasks_sys.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/inet"
@@ -28,7 +29,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -55,6 +55,7 @@ func (fs *filesystem) newSysDir(ctx context.Context, root *auth.Credentials, k *
}),
}),
"vm": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{
+ "max_map_count": fs.newInode(ctx, root, 0444, newStaticFile("2147483647\n")),
"mmap_min_addr": fs.newInode(ctx, root, 0444, &mmapMinAddrData{k: k}),
"overcommit_memory": fs.newInode(ctx, root, 0444, newStaticFile("0\n")),
}),
@@ -208,7 +209,7 @@ func (d *tcpSackData) Generate(ctx context.Context, buf *bytes.Buffer) error {
func (d *tcpSackData) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// No need to handle partial writes thus far.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if src.NumBytes() == 0 {
return 0, nil
@@ -256,7 +257,7 @@ func (d *tcpRecoveryData) Generate(ctx context.Context, buf *bytes.Buffer) error
func (d *tcpRecoveryData) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// No need to handle partial writes thus far.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if src.NumBytes() == 0 {
return 0, nil
@@ -310,7 +311,7 @@ func (d *tcpMemData) Generate(ctx context.Context, buf *bytes.Buffer) error {
func (d *tcpMemData) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// No need to handle partial writes thus far.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if src.NumBytes() == 0 {
return 0, nil
@@ -395,7 +396,7 @@ func (ipf *ipForwarding) Generate(ctx context.Context, buf *bytes.Buffer) error
func (ipf *ipForwarding) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// No need to handle partial writes thus far.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if src.NumBytes() == 0 {
return 0, nil
@@ -448,7 +449,7 @@ func (pr *portRange) Generate(ctx context.Context, buf *bytes.Buffer) error {
func (pr *portRange) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// No need to handle partial writes thus far.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if src.NumBytes() == 0 {
return 0, nil
@@ -466,7 +467,7 @@ func (pr *portRange) Write(ctx context.Context, src usermem.IOSequence, offset i
// Port numbers must be uint16s.
if ports[0] < 0 || ports[1] < 0 || ports[0] > math.MaxUint16 || ports[1] > math.MaxUint16 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if err := pr.stack.SetPortRange(uint16(ports[0]), uint16(ports[1])); err != nil {
diff --git a/pkg/sentry/fsimpl/proc/tasks_test.go b/pkg/sentry/fsimpl/proc/tasks_test.go
index e534fbca8..14f806c3c 100644
--- a/pkg/sentry/fsimpl/proc/tasks_test.go
+++ b/pkg/sentry/fsimpl/proc/tasks_test.go
@@ -23,13 +23,13 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/testutil"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -227,7 +227,7 @@ func TestTasks(t *testing.T) {
defer fd.DecRef(s.Ctx)
buf := make([]byte, 1)
bufIOSeq := usermem.BytesIOSequence(buf)
- if _, err := fd.Read(s.Ctx, bufIOSeq, vfs.ReadOptions{}); err != syserror.EISDIR {
+ if _, err := fd.Read(s.Ctx, bufIOSeq, vfs.ReadOptions{}); !linuxerr.Equals(linuxerr.EISDIR, err) {
t.Errorf("wrong error reading directory: %v", err)
}
}
@@ -237,7 +237,7 @@ func TestTasks(t *testing.T) {
s.Creds,
s.PathOpAtRoot("/proc/9999"),
&vfs.OpenOptions{},
- ); err != syserror.ENOENT {
+ ); !linuxerr.Equals(linuxerr.ENOENT, err) {
t.Fatalf("wrong error from vfsfs.OpenAt(/proc/9999): %v", err)
}
}
diff --git a/pkg/sentry/fsimpl/proc/yama.go b/pkg/sentry/fsimpl/proc/yama.go
index e039ec45e..7240563d7 100644
--- a/pkg/sentry/fsimpl/proc/yama.go
+++ b/pkg/sentry/fsimpl/proc/yama.go
@@ -21,11 +21,11 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -56,7 +56,7 @@ func (s *yamaPtraceScope) Generate(ctx context.Context, buf *bytes.Buffer) error
func (s *yamaPtraceScope) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if offset != 0 {
// Ignore partial writes.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if src.NumBytes() == 0 {
return 0, nil
@@ -73,7 +73,7 @@ func (s *yamaPtraceScope) Write(ctx context.Context, src usermem.IOSequence, off
// We do not support YAMA levels > YAMA_SCOPE_RELATIONAL.
if v < linux.YAMA_SCOPE_DISABLED || v > linux.YAMA_SCOPE_RELATIONAL {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
atomic.StoreInt32(s.level, v)
diff --git a/pkg/sentry/fsimpl/sockfs/BUILD b/pkg/sentry/fsimpl/sockfs/BUILD
index 9453277b8..9defca936 100644
--- a/pkg/sentry/fsimpl/sockfs/BUILD
+++ b/pkg/sentry/fsimpl/sockfs/BUILD
@@ -9,10 +9,10 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/sentry/fsimpl/kernfs",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
- "//pkg/syserror",
],
)
diff --git a/pkg/sentry/fsimpl/sockfs/sockfs.go b/pkg/sentry/fsimpl/sockfs/sockfs.go
index 735756280..75934ecd0 100644
--- a/pkg/sentry/fsimpl/sockfs/sockfs.go
+++ b/pkg/sentry/fsimpl/sockfs/sockfs.go
@@ -20,11 +20,11 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// filesystemType implements vfs.FilesystemType.
@@ -102,7 +102,7 @@ type inode struct {
// Open implements kernfs.Inode.Open.
func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
// StatFS implements kernfs.Inode.StatFS.
diff --git a/pkg/sentry/fsimpl/sys/BUILD b/pkg/sentry/fsimpl/sys/BUILD
index 09043b572..1af0a5cbc 100644
--- a/pkg/sentry/fsimpl/sys/BUILD
+++ b/pkg/sentry/fsimpl/sys/BUILD
@@ -26,6 +26,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/context",
"//pkg/coverage",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/refs",
"//pkg/refsvfs2",
diff --git a/pkg/sentry/fsimpl/sys/kcov.go b/pkg/sentry/fsimpl/sys/kcov.go
index b13f141a8..d06aea162 100644
--- a/pkg/sentry/fsimpl/sys/kcov.go
+++ b/pkg/sentry/fsimpl/sys/kcov.go
@@ -17,6 +17,7 @@ package sys
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -85,7 +86,7 @@ func (fd *kcovFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallAr
case linux.KCOV_DISABLE:
if arg != 0 {
// This arg is unused; it should be 0.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
return 0, fd.kcov.DisableTrace(ctx)
default:
diff --git a/pkg/sentry/fsimpl/sys/sys.go b/pkg/sentry/fsimpl/sys/sys.go
index 14eb10dcd..f322d2747 100644
--- a/pkg/sentry/fsimpl/sys/sys.go
+++ b/pkg/sentry/fsimpl/sys/sys.go
@@ -23,12 +23,12 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/coverage"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
const (
@@ -74,7 +74,7 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
maxCachedDentries, err = strconv.ParseUint(str, 10, 64)
if err != nil {
ctx.Warningf("sys.FilesystemType.GetFilesystem: invalid dentry cache limit: dentry_cache_limit=%s", str)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
}
@@ -174,7 +174,7 @@ func (fs *filesystem) newDir(ctx context.Context, creds *auth.Credentials, mode
// SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
func (*dir) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Open implements kernfs.Inode.Open.
diff --git a/pkg/sentry/fsimpl/testutil/BUILD b/pkg/sentry/fsimpl/testutil/BUILD
index c766164c7..b3f9d1010 100644
--- a/pkg/sentry/fsimpl/testutil/BUILD
+++ b/pkg/sentry/fsimpl/testutil/BUILD
@@ -17,7 +17,6 @@ go_library(
"//pkg/fspath",
"//pkg/hostarch",
"//pkg/memutil",
- "//pkg/metric",
"//pkg/sentry/fsbridge",
"//pkg/sentry/fsimpl/tmpfs",
"//pkg/sentry/kernel",
diff --git a/pkg/sentry/fsimpl/testutil/kernel.go b/pkg/sentry/fsimpl/testutil/kernel.go
index 438840ae2..473b41cff 100644
--- a/pkg/sentry/fsimpl/testutil/kernel.go
+++ b/pkg/sentry/fsimpl/testutil/kernel.go
@@ -25,7 +25,6 @@ import (
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/memutil"
- "gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -63,8 +62,6 @@ func Boot() (*kernel.Kernel, error) {
return nil, fmt.Errorf("creating platform: %v", err)
}
- metric.CreateSentryMetrics()
-
kernel.VFS2Enabled = true
k := &kernel.Kernel{
Platform: plat,
@@ -83,12 +80,8 @@ func Boot() (*kernel.Kernel, error) {
}
// Create timekeeper.
- tk, err := kernel.NewTimekeeper(k, vdso.ParamPage.FileRange())
- if err != nil {
- return nil, fmt.Errorf("creating timekeeper: %v", err)
- }
+ tk := kernel.NewTimekeeper(k, vdso.ParamPage.FileRange())
tk.SetClocks(time.NewCalibratedClocks())
- k.SetTimekeeper(tk)
creds := auth.NewRootCredentials(auth.NewRootUserNamespace())
@@ -97,6 +90,7 @@ func Boot() (*kernel.Kernel, error) {
if err = k.Init(kernel.InitKernelArgs{
ApplicationCores: uint(runtime.GOMAXPROCS(-1)),
FeatureSet: cpuid.HostFeatureSet(),
+ Timekeeper: tk,
RootUserNamespace: creds.UserNamespace,
Vdso: vdso,
RootUTSNamespace: kernel.NewUTSNamespace("hostname", "domain", creds.UserNamespace),
diff --git a/pkg/sentry/fsimpl/timerfd/BUILD b/pkg/sentry/fsimpl/timerfd/BUILD
index 7ce7dc429..e6980a314 100644
--- a/pkg/sentry/fsimpl/timerfd/BUILD
+++ b/pkg/sentry/fsimpl/timerfd/BUILD
@@ -8,6 +8,7 @@ go_library(
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/sentry/kernel/time",
"//pkg/sentry/vfs",
diff --git a/pkg/sentry/fsimpl/timerfd/timerfd.go b/pkg/sentry/fsimpl/timerfd/timerfd.go
index cbb8b67c5..655a1c76a 100644
--- a/pkg/sentry/fsimpl/timerfd/timerfd.go
+++ b/pkg/sentry/fsimpl/timerfd/timerfd.go
@@ -19,6 +19,7 @@ import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -69,7 +70,7 @@ func New(ctx context.Context, vfsObj *vfs.VirtualFilesystem, clock ktime.Clock,
func (tfd *TimerFileDescription) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
const sizeofUint64 = 8
if dst.NumBytes() < sizeofUint64 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if val := atomic.SwapUint64(&tfd.val, 0); val != 0 {
var buf [sizeofUint64]byte
diff --git a/pkg/sentry/fsimpl/tmpfs/BUILD b/pkg/sentry/fsimpl/tmpfs/BUILD
index e21fddd7f..0de43f67f 100644
--- a/pkg/sentry/fsimpl/tmpfs/BUILD
+++ b/pkg/sentry/fsimpl/tmpfs/BUILD
@@ -58,6 +58,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/amutex",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/hostarch",
"//pkg/log",
@@ -94,6 +95,7 @@ go_test(
":tmpfs",
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/refs",
"//pkg/sentry/contexttest",
@@ -118,6 +120,7 @@ go_test(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/sentry/contexttest",
"//pkg/sentry/fs/lock",
diff --git a/pkg/sentry/fsimpl/tmpfs/benchmark_test.go b/pkg/sentry/fsimpl/tmpfs/benchmark_test.go
index 3cc63e732..4bc7e08d6 100644
--- a/pkg/sentry/fsimpl/tmpfs/benchmark_test.go
+++ b/pkg/sentry/fsimpl/tmpfs/benchmark_test.go
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
@@ -68,7 +69,7 @@ func fileOpOn(ctx context.Context, mntns *fs.MountNamespace, root, wd *fs.Dirent
rel = wd
} else {
// Need to extract the given FD.
- return syserror.EBADF
+ return linuxerr.EBADF
}
// Lookup the node.
diff --git a/pkg/sentry/fsimpl/tmpfs/directory.go b/pkg/sentry/fsimpl/tmpfs/directory.go
index e8d256495..c25494c0b 100644
--- a/pkg/sentry/fsimpl/tmpfs/directory.go
+++ b/pkg/sentry/fsimpl/tmpfs/directory.go
@@ -19,10 +19,10 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
// +stateify savable
@@ -196,10 +196,10 @@ func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (in
case linux.SEEK_CUR:
offset += fd.off
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// If the offset isn't changing (e.g. due to lseek(0, SEEK_CUR)), don't
diff --git a/pkg/sentry/fsimpl/tmpfs/filesystem.go b/pkg/sentry/fsimpl/tmpfs/filesystem.go
index 766289e60..2370fc95e 100644
--- a/pkg/sentry/fsimpl/tmpfs/filesystem.go
+++ b/pkg/sentry/fsimpl/tmpfs/filesystem.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -70,7 +71,7 @@ afterSymlink:
return d.parent, nil
}
if len(name) > linux.NAME_MAX {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
child, ok := dir.childMap[name]
if !ok {
@@ -164,7 +165,7 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir
return syserror.EEXIST
}
if len(name) > linux.NAME_MAX {
- return syserror.ENAMETOOLONG
+ return linuxerr.ENAMETOOLONG
}
if _, ok := parentDir.childMap[name]; ok {
return syserror.EEXIST
@@ -246,12 +247,12 @@ func (fs *filesystem) GetParentDentryAt(ctx context.Context, rp *vfs.ResolvingPa
func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error {
return fs.doCreateAt(ctx, rp, false /* dir */, func(parentDir *directory, name string) error {
if rp.Mount() != vd.Mount() {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
d := vd.Dentry().Impl().(*dentry)
i := d.inode
if i.isDir() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := vfs.MayLink(auth.CredentialsFromContext(ctx), linux.FileMode(atomic.LoadUint32(&i.mode)), auth.KUID(atomic.LoadUint32(&i.uid)), auth.KGID(atomic.LoadUint32(&i.gid))); err != nil {
return err
@@ -260,7 +261,7 @@ func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
return syserror.ENOENT
}
if i.nlink == maxLinks {
- return syserror.EMLINK
+ return linuxerr.EMLINK
}
i.incLinksLocked()
i.watches.Notify(ctx, "", linux.IN_ATTRIB, 0, vfs.InodeEvent, false /* unlinked */)
@@ -274,7 +275,7 @@ func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
return fs.doCreateAt(ctx, rp, true /* dir */, func(parentDir *directory, name string) error {
creds := rp.Credentials()
if parentDir.inode.nlink == maxLinks {
- return syserror.EMLINK
+ return linuxerr.EMLINK
}
parentDir.inode.incLinksLocked() // from child's ".."
childDir := fs.newDirectory(creds.EffectiveKUID, creds.EffectiveKGID, opts.Mode, parentDir)
@@ -300,7 +301,7 @@ func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
case linux.S_IFSOCK:
childInode = fs.newSocketFile(creds.EffectiveKUID, creds.EffectiveKGID, opts.Mode, opts.Endpoint, parentDir)
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
child := fs.newDentry(childInode)
parentDir.insertChildLocked(child, name)
@@ -372,7 +373,7 @@ afterTrailingSymlink:
return nil, syserror.EISDIR
}
if len(name) > linux.NAME_MAX {
- return nil, syserror.ENAMETOOLONG
+ return nil, linuxerr.ENAMETOOLONG
}
// Determine whether or not we need to create a file.
child, ok := parentDir.childMap[name]
@@ -466,13 +467,13 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.Open
return &fd.vfsfd, nil
case *symlink:
// Can't open symlinks without O_PATH, which is handled at the VFS layer.
- return nil, syserror.ELOOP
+ return nil, linuxerr.ELOOP
case *namedPipe:
return impl.pipe.Open(ctx, rp.Mount(), &d.vfsd, opts.Flags, &d.inode.locks)
case *deviceFile:
return rp.VirtualFilesystem().OpenDeviceSpecialFile(ctx, rp.Mount(), &d.vfsd, impl.kind, impl.major, impl.minor, opts)
case *socketFile:
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
default:
panic(fmt.Sprintf("unknown inode type: %T", d.inode.impl))
}
@@ -488,7 +489,7 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
}
symlink, ok := d.inode.impl.(*symlink)
if !ok {
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
symlink.inode.touchAtime(rp.Mount())
return symlink.target, nil
@@ -496,25 +497,29 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
// RenameAt implements vfs.FilesystemImpl.RenameAt.
func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
- if opts.Flags != 0 {
- // TODO(b/145974740): Support renameat2 flags.
- return syserror.EINVAL
- }
-
- // Resolve newParent first to verify that it's on this Mount.
+ // Resolve newParentDir first to verify that it's on this Mount.
fs.mu.Lock()
defer fs.mu.Unlock()
newParentDir, err := walkParentDirLocked(ctx, rp, rp.Start().Impl().(*dentry))
if err != nil {
return err
}
+
+ if opts.Flags&^linux.RENAME_NOREPLACE != 0 {
+ // TODO(b/145974740): Support other renameat2 flags.
+ return linuxerr.EINVAL
+ }
+
newName := rp.Component()
if newName == "." || newName == ".." {
- return syserror.EBUSY
+ if opts.Flags&linux.RENAME_NOREPLACE != 0 {
+ return syserror.EEXIST
+ }
+ return linuxerr.EBUSY
}
mnt := rp.Mount()
if mnt != oldParentVD.Mount() {
- return syserror.EXDEV
+ return linuxerr.EXDEV
}
if err := mnt.CheckBeginWrite(); err != nil {
return err
@@ -537,7 +542,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
// mounted filesystem.
if renamed.inode.isDir() {
if renamed == &newParentDir.dentry || genericIsAncestorDentry(renamed, &newParentDir.dentry) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if oldParentDir != newParentDir {
// Writability is needed to change renamed's "..".
@@ -556,13 +561,16 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
}
replaced, ok := newParentDir.childMap[newName]
if ok {
+ if opts.Flags&linux.RENAME_NOREPLACE != 0 {
+ return syserror.EEXIST
+ }
replacedDir, ok := replaced.inode.impl.(*directory)
if ok {
if !renamed.inode.isDir() {
return syserror.EISDIR
}
if len(replacedDir.childMap) != 0 {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
} else {
if rp.MustBeDir() {
@@ -574,7 +582,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
}
} else {
if renamed.inode.isDir() && newParentDir.inode.nlink == maxLinks {
- return syserror.EMLINK
+ return linuxerr.EMLINK
}
}
// tmpfs never calls VFS.InvalidateDentry(), so newParentDir.dentry can
@@ -639,10 +647,10 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
}
name := rp.Component()
if name == "." {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if name == ".." {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
child, ok := parentDir.childMap[name]
if !ok {
@@ -656,7 +664,7 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
return syserror.ENOTDIR
}
if len(childDir.childMap) != 0 {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
mnt := rp.Mount()
if err := mnt.CheckBeginWrite(); err != nil {
@@ -799,11 +807,11 @@ func (fs *filesystem) BoundEndpointAt(ctx context.Context, rp *vfs.ResolvingPath
switch impl := d.inode.impl.(type) {
case *socketFile:
if impl.ep == nil {
- return nil, syserror.ECONNREFUSED
+ return nil, linuxerr.ECONNREFUSED
}
return impl.ep, nil
default:
- return nil, syserror.ECONNREFUSED
+ return nil, linuxerr.ECONNREFUSED
}
}
@@ -815,7 +823,7 @@ func (fs *filesystem) ListXattrAt(ctx context.Context, rp *vfs.ResolvingPath, si
if err != nil {
return nil, err
}
- return d.inode.listXattr(size)
+ return d.inode.listXattr(rp.Credentials(), size)
}
// GetXattrAt implements vfs.FilesystemImpl.GetXattrAt.
diff --git a/pkg/sentry/fsimpl/tmpfs/pipe_test.go b/pkg/sentry/fsimpl/tmpfs/pipe_test.go
index 2f856ce36..418c7994e 100644
--- a/pkg/sentry/fsimpl/tmpfs/pipe_test.go
+++ b/pkg/sentry/fsimpl/tmpfs/pipe_test.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -114,7 +115,7 @@ func TestNonblockingWriteError(t *testing.T) {
}
openOpts := vfs.OpenOptions{Flags: linux.O_WRONLY | linux.O_NONBLOCK}
_, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts)
- if err != syserror.ENXIO {
+ if !linuxerr.Equals(linuxerr.ENXIO, err) {
t.Fatalf("expected ENXIO, but got error: %v", err)
}
}
diff --git a/pkg/sentry/fsimpl/tmpfs/regular_file.go b/pkg/sentry/fsimpl/tmpfs/regular_file.go
index c45bddff6..ce595ce85 100644
--- a/pkg/sentry/fsimpl/tmpfs/regular_file.go
+++ b/pkg/sentry/fsimpl/tmpfs/regular_file.go
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -185,7 +186,7 @@ func (rf *regularFile) truncateLocked(newSize uint64) (bool, error) {
// Can we grow the file?
if rf.seals&linux.F_SEAL_GROW != 0 {
rf.dataMu.Unlock()
- return false, syserror.EPERM
+ return false, linuxerr.EPERM
}
// We only need to update the file size.
atomic.StoreUint64(&rf.size, newSize)
@@ -196,7 +197,7 @@ func (rf *regularFile) truncateLocked(newSize uint64) (bool, error) {
// We are shrinking the file. First check if this is allowed.
if rf.seals&linux.F_SEAL_SHRINK != 0 {
rf.dataMu.Unlock()
- return false, syserror.EPERM
+ return false, linuxerr.EPERM
}
// Update the file size.
@@ -233,7 +234,7 @@ func (rf *regularFile) AddMapping(ctx context.Context, ms memmap.MappingSpace, a
// Reject writable mapping if F_SEAL_WRITE is set.
if rf.seals&linux.F_SEAL_WRITE != 0 && writable {
- return syserror.EPERM
+ return linuxerr.EPERM
}
rf.mappings.AddMapping(ms, ar, offset, writable)
@@ -366,7 +367,7 @@ func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offs
fsmetric.TmpfsReads.Increment()
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Check that flags are supported. RWF_DSYNC/RWF_SYNC can be ignored since
@@ -407,7 +408,7 @@ func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, off
// final offset should be ignored by PWrite.
func (fd *regularFileFD) pwrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (written, finalOff int64, err error) {
if offset < 0 {
- return 0, offset, syserror.EINVAL
+ return 0, offset, linuxerr.EINVAL
}
// Check that flags are supported. RWF_DSYNC/RWF_SYNC can be ignored since
@@ -432,7 +433,7 @@ func (fd *regularFileFD) pwrite(ctx context.Context, src usermem.IOSequence, off
}
if end := offset + srclen; end < offset {
// Overflow.
- return 0, offset, syserror.EINVAL
+ return 0, offset, linuxerr.EINVAL
}
srclen, err = vfs.CheckLimit(ctx, offset, srclen)
@@ -476,10 +477,10 @@ func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (
case linux.SEEK_END:
offset += int64(atomic.LoadUint64(&fd.inode().impl.(*regularFile).size))
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
fd.off = offset
return offset, nil
@@ -594,7 +595,7 @@ func (rw *regularFileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64,
// Check if seals prevent either file growth or all writes.
switch {
case rw.file.seals&linux.F_SEAL_WRITE != 0: // Write sealed
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
case end > rw.file.size && rw.file.seals&linux.F_SEAL_GROW != 0: // Grow sealed
// When growth is sealed, Linux effectively allows writes which would
// normally grow the file to partially succeed up to the current EOF,
@@ -615,7 +616,7 @@ func (rw *regularFileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64,
}
if end <= rw.off {
// Truncation would result in no data being written.
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
}
@@ -684,7 +685,7 @@ exitLoop:
func GetSeals(fd *vfs.FileDescription) (uint32, error) {
f, ok := fd.Impl().(*regularFileFD)
if !ok {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
rf := f.inode().impl.(*regularFile)
rf.dataMu.RLock()
@@ -696,7 +697,7 @@ func GetSeals(fd *vfs.FileDescription) (uint32, error) {
func AddSeals(fd *vfs.FileDescription, val uint32) error {
f, ok := fd.Impl().(*regularFileFD)
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
rf := f.inode().impl.(*regularFile)
rf.mapsMu.Lock()
@@ -706,13 +707,13 @@ func AddSeals(fd *vfs.FileDescription, val uint32) error {
if rf.seals&linux.F_SEAL_SEAL != 0 {
// Seal applied which prevents addition of any new seals.
- return syserror.EPERM
+ return linuxerr.EPERM
}
// F_SEAL_WRITE can only be added if there are no active writable maps.
if rf.seals&linux.F_SEAL_WRITE == 0 && val&linux.F_SEAL_WRITE != 0 {
if rf.writableMappingPages > 0 {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
}
diff --git a/pkg/sentry/fsimpl/tmpfs/tmpfs.go b/pkg/sentry/fsimpl/tmpfs/tmpfs.go
index 9ae25ce9e..4d6731852 100644
--- a/pkg/sentry/fsimpl/tmpfs/tmpfs.go
+++ b/pkg/sentry/fsimpl/tmpfs/tmpfs.go
@@ -36,6 +36,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/kernel/time"
@@ -138,7 +139,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
mode, err := strconv.ParseUint(modeStr, 8, 32)
if err != nil {
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid mode: %q", modeStr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
rootMode = linux.FileMode(mode & 07777)
}
@@ -149,12 +150,12 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
uid, err := strconv.ParseUint(uidStr, 10, 32)
if err != nil {
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid uid: %q", uidStr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
kuid := creds.UserNamespace.MapToKUID(auth.UID(uid))
if !kuid.Ok() {
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped uid: %d", uid)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
rootKUID = kuid
}
@@ -165,18 +166,18 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
gid, err := strconv.ParseUint(gidStr, 10, 32)
if err != nil {
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: invalid gid: %q", gidStr)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
kgid := creds.UserNamespace.MapToKGID(auth.GID(gid))
if !kgid.Ok() {
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unmapped gid: %d", gid)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
rootKGID = kgid
}
if len(mopts) != 0 {
ctx.Warningf("tmpfs.FilesystemType.GetFilesystem: unknown options: %v", mopts)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
devMinor, err := vfsObj.GetAnonBlockDevMinor()
@@ -527,7 +528,7 @@ func (i *inode) setStat(ctx context.Context, creds *auth.Credentials, opts *vfs.
return nil
}
if stat.Mask&^(linux.STATX_MODE|linux.STATX_UID|linux.STATX_GID|linux.STATX_ATIME|linux.STATX_MTIME|linux.STATX_CTIME|linux.STATX_SIZE) != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
mode := linux.FileMode(atomic.LoadUint32(&i.mode))
if err := vfs.CheckSetStat(ctx, creds, opts, mode, auth.KUID(atomic.LoadUint32(&i.uid)), auth.KGID(atomic.LoadUint32(&i.gid))); err != nil {
@@ -557,7 +558,7 @@ func (i *inode) setStat(ctx context.Context, creds *auth.Credentials, opts *vfs.
case *directory:
return syserror.EISDIR
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
if mask&linux.STATX_UID != 0 {
@@ -717,44 +718,63 @@ func (i *inode) touchCMtimeLocked() {
atomic.StoreInt64(&i.ctime, now)
}
-func (i *inode) listXattr(size uint64) ([]string, error) {
- return i.xattrs.ListXattr(size)
+func checkXattrName(name string) error {
+ // Linux's tmpfs supports "security" and "trusted" xattr namespaces, and
+ // (depending on build configuration) POSIX ACL xattr namespaces
+ // ("system.posix_acl_access" and "system.posix_acl_default"). We don't
+ // support POSIX ACLs or the "security" namespace (b/148380782).
+ if strings.HasPrefix(name, linux.XATTR_TRUSTED_PREFIX) {
+ return nil
+ }
+ // We support the "user" namespace because we have tests that depend on
+ // this feature.
+ if strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
+ return nil
+ }
+ return syserror.EOPNOTSUPP
+}
+
+func (i *inode) listXattr(creds *auth.Credentials, size uint64) ([]string, error) {
+ return i.xattrs.ListXattr(creds, size)
}
func (i *inode) getXattr(creds *auth.Credentials, opts *vfs.GetXattrOptions) (string, error) {
- if err := i.checkXattrPermissions(creds, opts.Name, vfs.MayRead); err != nil {
+ if err := checkXattrName(opts.Name); err != nil {
return "", err
}
- return i.xattrs.GetXattr(opts)
+ mode := linux.FileMode(atomic.LoadUint32(&i.mode))
+ kuid := auth.KUID(atomic.LoadUint32(&i.uid))
+ kgid := auth.KGID(atomic.LoadUint32(&i.gid))
+ if err := vfs.GenericCheckPermissions(creds, vfs.MayRead, mode, kuid, kgid); err != nil {
+ return "", err
+ }
+ return i.xattrs.GetXattr(creds, mode, kuid, opts)
}
func (i *inode) setXattr(creds *auth.Credentials, opts *vfs.SetXattrOptions) error {
- if err := i.checkXattrPermissions(creds, opts.Name, vfs.MayWrite); err != nil {
+ if err := checkXattrName(opts.Name); err != nil {
return err
}
- return i.xattrs.SetXattr(opts)
-}
-
-func (i *inode) removeXattr(creds *auth.Credentials, name string) error {
- if err := i.checkXattrPermissions(creds, name, vfs.MayWrite); err != nil {
+ mode := linux.FileMode(atomic.LoadUint32(&i.mode))
+ kuid := auth.KUID(atomic.LoadUint32(&i.uid))
+ kgid := auth.KGID(atomic.LoadUint32(&i.gid))
+ if err := vfs.GenericCheckPermissions(creds, vfs.MayWrite, mode, kuid, kgid); err != nil {
return err
}
- return i.xattrs.RemoveXattr(name)
+ return i.xattrs.SetXattr(creds, mode, kuid, opts)
}
-func (i *inode) checkXattrPermissions(creds *auth.Credentials, name string, ats vfs.AccessTypes) error {
- // We currently only support extended attributes in the user.* and
- // trusted.* namespaces. See b/148380782.
- if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) && !strings.HasPrefix(name, linux.XATTR_TRUSTED_PREFIX) {
- return syserror.EOPNOTSUPP
+func (i *inode) removeXattr(creds *auth.Credentials, name string) error {
+ if err := checkXattrName(name); err != nil {
+ return err
}
mode := linux.FileMode(atomic.LoadUint32(&i.mode))
kuid := auth.KUID(atomic.LoadUint32(&i.uid))
kgid := auth.KGID(atomic.LoadUint32(&i.gid))
- if err := vfs.GenericCheckPermissions(creds, ats, mode, kuid, kgid); err != nil {
+ if err := vfs.GenericCheckPermissions(creds, vfs.MayWrite, mode, kuid, kgid); err != nil {
return err
}
- return vfs.CheckXattrPermissions(creds, ats, mode, kuid, name)
+ return i.xattrs.RemoveXattr(creds, mode, kuid, name)
}
// fileDescription is embedded by tmpfs implementations of
@@ -807,7 +827,7 @@ func (fd *fileDescription) StatFS(ctx context.Context) (linux.Statfs, error) {
// ListXattr implements vfs.FileDescriptionImpl.ListXattr.
func (fd *fileDescription) ListXattr(ctx context.Context, size uint64) ([]string, error) {
- return fd.inode().listXattr(size)
+ return fd.inode().listXattr(auth.CredentialsFromContext(ctx), size)
}
// GetXattr implements vfs.FileDescriptionImpl.GetXattr.
diff --git a/pkg/sentry/fsimpl/verity/BUILD b/pkg/sentry/fsimpl/verity/BUILD
index d473a922d..1d855234c 100644
--- a/pkg/sentry/fsimpl/verity/BUILD
+++ b/pkg/sentry/fsimpl/verity/BUILD
@@ -13,6 +13,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/hostarch",
"//pkg/marshal/primitive",
@@ -41,6 +42,7 @@ go_test(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/sentry/arch",
"//pkg/sentry/fsimpl/testutil",
@@ -48,7 +50,6 @@ go_test(
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
- "//pkg/syserror",
"//pkg/usermem",
],
)
diff --git a/pkg/sentry/fsimpl/verity/filesystem.go b/pkg/sentry/fsimpl/verity/filesystem.go
index 3582d14c9..358a66072 100644
--- a/pkg/sentry/fsimpl/verity/filesystem.go
+++ b/pkg/sentry/fsimpl/verity/filesystem.go
@@ -25,6 +25,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/merkletree"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -74,6 +75,7 @@ func putDentrySlice(ds *[]*dentry) {
// but dentry slices are allocated lazily, and it's much easier to say "defer
// fs.renameMuRUnlockAndCheckDrop(&ds)" than "defer func() {
// fs.renameMuRUnlockAndCheckDrop(ds) }()" to work around this.
+// +checklocksrelease:fs.renameMu
func (fs *filesystem) renameMuRUnlockAndCheckDrop(ctx context.Context, ds **[]*dentry) {
fs.renameMu.RUnlock()
if *ds == nil {
@@ -89,6 +91,7 @@ func (fs *filesystem) renameMuRUnlockAndCheckDrop(ctx context.Context, ds **[]*d
putDentrySlice(*ds)
}
+// +checklocksrelease:fs.renameMu
func (fs *filesystem) renameMuUnlockAndCheckDrop(ctx context.Context, ds **[]*dentry) {
if *ds == nil {
fs.renameMu.Unlock()
@@ -195,7 +198,7 @@ func (fs *filesystem) verifyChildLocked(ctx context.Context, parent *dentry, chi
// The Merkle tree file for the child should have been created and
// contains the expected xattrs. If the file or the xattr does not
// exist, it indicates unexpected modifications to the file system.
- if err == syserror.ENOENT || err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.ENODATA, err) {
return nil, fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s for %s: %v", merkleOffsetInParentXattr, childPath, err))
}
if err != nil {
@@ -218,7 +221,7 @@ func (fs *filesystem) verifyChildLocked(ctx context.Context, parent *dentry, chi
// The parent Merkle tree file should have been created. If it's
// missing, it indicates an unexpected modification to the file system.
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
return nil, fs.alertIntegrityViolation(fmt.Sprintf("Failed to open parent Merkle file for %s: %v", childPath, err))
}
if err != nil {
@@ -238,7 +241,7 @@ func (fs *filesystem) verifyChildLocked(ctx context.Context, parent *dentry, chi
// The Merkle tree file for the child should have been created and
// contains the expected xattrs. If the file or the xattr does not
// exist, it indicates unexpected modifications to the file system.
- if err == syserror.ENOENT || err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.ENODATA, err) {
return nil, fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s for %s: %v", merkleSizeXattr, childPath, err))
}
if err != nil {
@@ -261,7 +264,7 @@ func (fs *filesystem) verifyChildLocked(ctx context.Context, parent *dentry, chi
Root: parent.lowerVD,
Start: parent.lowerVD,
}, &vfs.StatOptions{})
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
return nil, fs.alertIntegrityViolation(fmt.Sprintf("Failed to get parent stat for %s: %v", childPath, err))
}
if err != nil {
@@ -282,7 +285,7 @@ func (fs *filesystem) verifyChildLocked(ctx context.Context, parent *dentry, chi
Mode: uint32(parentStat.Mode),
UID: parentStat.UID,
GID: parentStat.GID,
- Children: parent.childrenNames,
+ Children: parent.childrenList,
HashAlgorithms: fs.alg.toLinuxHashAlg(),
ReadOffset: int64(offset),
ReadSize: int64(merkletree.DigestSize(fs.alg.toLinuxHashAlg())),
@@ -327,7 +330,7 @@ func (fs *filesystem) verifyStatAndChildrenLocked(ctx context.Context, d *dentry
}, &vfs.OpenOptions{
Flags: linux.O_RDONLY,
})
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
return fs.alertIntegrityViolation(fmt.Sprintf("Failed to open merkle file for %s: %v", childPath, err))
}
if err != nil {
@@ -341,7 +344,7 @@ func (fs *filesystem) verifyStatAndChildrenLocked(ctx context.Context, d *dentry
Size: sizeOfStringInt32,
})
- if err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENODATA, err) {
return fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s for merkle file of %s: %v", merkleSizeXattr, childPath, err))
}
if err != nil {
@@ -359,7 +362,7 @@ func (fs *filesystem) verifyStatAndChildrenLocked(ctx context.Context, d *dentry
Size: sizeOfStringInt32,
})
- if err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENODATA, err) {
return fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s for merkle file of %s: %v", childrenOffsetXattr, childPath, err))
}
if err != nil {
@@ -375,7 +378,7 @@ func (fs *filesystem) verifyStatAndChildrenLocked(ctx context.Context, d *dentry
Size: sizeOfStringInt32,
})
- if err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENODATA, err) {
return fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s for merkle file of %s: %v", childrenSizeXattr, childPath, err))
}
if err != nil {
@@ -403,6 +406,9 @@ func (fs *filesystem) verifyStatAndChildrenLocked(ctx context.Context, d *dentry
var buf bytes.Buffer
d.hashMu.RLock()
+
+ d.generateChildrenList()
+
params := &merkletree.VerifyParams{
Out: &buf,
Tree: &fdReader,
@@ -411,7 +417,7 @@ func (fs *filesystem) verifyStatAndChildrenLocked(ctx context.Context, d *dentry
Mode: uint32(stat.Mode),
UID: stat.UID,
GID: stat.GID,
- Children: d.childrenNames,
+ Children: d.childrenList,
HashAlgorithms: fs.alg.toLinuxHashAlg(),
ReadOffset: 0,
// Set read size to 0 so only the metadata is verified.
@@ -465,7 +471,7 @@ func (fs *filesystem) getChildLocked(ctx context.Context, parent *dentry, name s
}
childVD, err := parent.getLowerAt(ctx, vfsObj, name)
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
// The file was previously accessed. If the
// file does not exist now, it indicates an
// unexpected modification to the file system.
@@ -480,7 +486,7 @@ func (fs *filesystem) getChildLocked(ctx context.Context, parent *dentry, name s
// The Merkle tree file was previous accessed. If it
// does not exist now, it indicates an unexpected
// modification to the file system.
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
return nil, fs.alertIntegrityViolation(fmt.Sprintf("Expected Merkle file for target %s but none found", path))
}
if err != nil {
@@ -551,7 +557,7 @@ func (fs *filesystem) lookupAndVerifyLocked(ctx context.Context, parent *dentry,
}
childVD, err := parent.getLowerAt(ctx, vfsObj, name)
- if parent.verityEnabled() && err == syserror.ENOENT {
+ if parent.verityEnabled() && linuxerr.Equals(linuxerr.ENOENT, err) {
return nil, fs.alertIntegrityViolation(fmt.Sprintf("file %s expected but not found", parentPath+"/"+name))
}
if err != nil {
@@ -564,7 +570,7 @@ func (fs *filesystem) lookupAndVerifyLocked(ctx context.Context, parent *dentry,
childMerkleVD, err := parent.getLowerAt(ctx, vfsObj, merklePrefix+name)
if err != nil {
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
if parent.verityEnabled() {
return nil, fs.alertIntegrityViolation(fmt.Sprintf("Merkle file for %s expected but not found", parentPath+"/"+name))
}
@@ -708,7 +714,7 @@ func (fs *filesystem) resolveLocked(ctx context.Context, rp *vfs.ResolvingPath,
func (fs *filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error {
// Verity file system is read-only.
if ats&vfs.MayWrite != 0 {
- return syserror.EROFS
+ return linuxerr.EROFS
}
var ds *[]*dentry
fs.renameMu.RLock()
@@ -758,26 +764,26 @@ func (fs *filesystem) GetParentDentryAt(ctx context.Context, rp *vfs.ResolvingPa
// LinkAt implements vfs.FilesystemImpl.LinkAt.
func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// MkdirAt implements vfs.FilesystemImpl.MkdirAt.
func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// MknodAt implements vfs.FilesystemImpl.MknodAt.
func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// OpenAt implements vfs.FilesystemImpl.OpenAt.
func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
// Verity fs is read-only.
if opts.Flags&(linux.O_WRONLY|linux.O_CREAT) != 0 {
- return nil, syserror.EROFS
+ return nil, linuxerr.EROFS
}
var ds *[]*dentry
@@ -826,7 +832,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
// Users should not open the Merkle tree files. Those are for verity fs
// use only.
if strings.Contains(d.name, merklePrefix) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
ats := vfs.AccessTypesForOpenFlags(opts)
if err := d.checkPermissions(rp.Credentials(), ats); err != nil {
@@ -835,7 +841,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
// Verity fs is read-only.
if ats&vfs.MayWrite != 0 {
- return nil, syserror.EROFS
+ return nil, linuxerr.EROFS
}
// Get the path to the target file. This is only used to provide path
@@ -854,7 +860,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
// The file should exist, as we succeeded in finding its dentry. If it's
// missing, it indicates an unexpected modification to the file system.
if err != nil {
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
return nil, d.fs.alertIntegrityViolation(fmt.Sprintf("File %s expected but not found", path))
}
return nil, err
@@ -877,7 +883,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
// dentry. If it's missing, it indicates an unexpected modification to
// the file system.
if err != nil {
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
return nil, d.fs.alertIntegrityViolation(fmt.Sprintf("Merkle file for %s expected but not found", path))
}
return nil, err
@@ -902,7 +908,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
Flags: linux.O_WRONLY | linux.O_APPEND,
})
if err != nil {
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
return nil, d.fs.alertIntegrityViolation(fmt.Sprintf("Merkle file for %s expected but not found", path))
}
return nil, err
@@ -919,7 +925,7 @@ func (d *dentry) openLocked(ctx context.Context, rp *vfs.ResolvingPath, opts *vf
Flags: linux.O_WRONLY | linux.O_APPEND,
})
if err != nil {
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
parentPath, _ := d.fs.vfsfs.VirtualFilesystem().PathnameWithDeleted(ctx, d.fs.rootDentry.lowerVD, d.parent.lowerVD)
return nil, d.fs.alertIntegrityViolation(fmt.Sprintf("Merkle file for %s expected but not found", parentPath))
}
@@ -969,19 +975,19 @@ func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st
// RenameAt implements vfs.FilesystemImpl.RenameAt.
func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// RmdirAt implements vfs.FilesystemImpl.RmdirAt.
func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// SetStatAt implements vfs.FilesystemImpl.SetStatAt.
func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// StatAt implements vfs.FilesystemImpl.StatAt.
@@ -1021,13 +1027,13 @@ func (fs *filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linu
// SymlinkAt implements vfs.FilesystemImpl.SymlinkAt.
func (fs *filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, target string) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// UnlinkAt implements vfs.FilesystemImpl.UnlinkAt.
func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// BoundEndpointAt implements vfs.FilesystemImpl.BoundEndpointAt.
@@ -1038,7 +1044,7 @@ func (fs *filesystem) BoundEndpointAt(ctx context.Context, rp *vfs.ResolvingPath
if _, err := fs.resolveLocked(ctx, rp, &ds); err != nil {
return nil, err
}
- return nil, syserror.ECONNREFUSED
+ return nil, linuxerr.ECONNREFUSED
}
// ListXattrAt implements vfs.FilesystemImpl.ListXattrAt.
@@ -1076,13 +1082,13 @@ func (fs *filesystem) GetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
// SetXattrAt implements vfs.FilesystemImpl.SetXattrAt.
func (fs *filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetXattrOptions) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// RemoveXattrAt implements vfs.FilesystemImpl.RemoveXattrAt.
func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath, name string) error {
// Verity file system is read-only.
- return syserror.EROFS
+ return linuxerr.EROFS
}
// PrependPath implements vfs.FilesystemImpl.PrependPath.
diff --git a/pkg/sentry/fsimpl/verity/verity.go b/pkg/sentry/fsimpl/verity/verity.go
index 969003613..104a28227 100644
--- a/pkg/sentry/fsimpl/verity/verity.go
+++ b/pkg/sentry/fsimpl/verity/verity.go
@@ -39,12 +39,14 @@ import (
"encoding/json"
"fmt"
"math"
+ "sort"
"strconv"
"strings"
"sync/atomic"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
@@ -251,7 +253,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
hash, err := hex.DecodeString(encodedRootHash)
if err != nil {
ctx.Warningf("verity.FilesystemType.GetFilesystem: Failed to decode root hash: %v", err)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
rootHash = hash
}
@@ -269,19 +271,19 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
// Check for unparsed options.
if len(mopts) != 0 {
ctx.Warningf("verity.FilesystemType.GetFilesystem: unknown options: %v", mopts)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
// Handle internal options.
iopts, ok := opts.InternalData.(InternalFilesystemOptions)
if len(lowerPathname) == 0 && !ok {
ctx.Warningf("verity.FilesystemType.GetFilesystem: missing verity configs")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
if len(lowerPathname) != 0 {
if ok {
ctx.Warningf("verity.FilesystemType.GetFilesystem: unexpected verity configs with specified lower path")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
iopts = InternalFilesystemOptions{
AllowRuntimeEnable: len(rootHash) == 0,
@@ -300,7 +302,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
lowerPath := fspath.Parse(lowerPathname)
if !lowerPath.Absolute {
ctx.Infof("verity.FilesystemType.GetFilesystem: lower_path %q must be absolute", lowerPathname)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
var err error
mountedLowerVD, err = vfsObj.GetDentryAt(ctx, creds, &vfs.PathOperation{
@@ -358,7 +360,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
// If runtime enable is allowed, the root merkle tree may be absent. We
// should create the tree file.
- if err == syserror.ENOENT && fs.allowRuntimeEnable {
+ if linuxerr.Equals(linuxerr.ENOENT, err) && fs.allowRuntimeEnable {
lowerMerkleFD, err := vfsObj.OpenAt(ctx, fs.creds, &vfs.PathOperation{
Root: lowerVD,
Start: lowerVD,
@@ -439,7 +441,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
if !d.isDir() {
ctx.Warningf("verity root must be a directory")
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
if !fs.allowRuntimeEnable {
@@ -451,7 +453,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
Name: childrenOffsetXattr,
Size: sizeOfStringInt32,
})
- if err == syserror.ENOENT || err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.ENODATA, err) {
return nil, nil, fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s: %v", childrenOffsetXattr, err))
}
if err != nil {
@@ -470,7 +472,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
Name: childrenSizeXattr,
Size: sizeOfStringInt32,
})
- if err == syserror.ENOENT || err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENOENT, err) || linuxerr.Equals(linuxerr.ENODATA, err) {
return nil, nil, fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s: %v", childrenSizeXattr, err))
}
if err != nil {
@@ -487,7 +489,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
}, &vfs.OpenOptions{
Flags: linux.O_RDONLY,
})
- if err == syserror.ENOENT {
+ if linuxerr.Equals(linuxerr.ENOENT, err) {
return nil, nil, fs.alertIntegrityViolation(fmt.Sprintf("Failed to open root Merkle file: %v", err))
}
if err != nil {
@@ -508,6 +510,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
if err := fs.verifyStatAndChildrenLocked(ctx, d, stat); err != nil {
return nil, nil, err
}
+ d.generateChildrenList()
}
d.vfsd.Init(d)
@@ -564,6 +567,11 @@ type dentry struct {
// populated by enableVerity. childrenNames is also protected by dirMu.
childrenNames map[string]struct{}
+ // childrenList is a complete sorted list of childrenNames. This list
+ // is generated when verity is enabled, or the first time the file is
+ // verified in non runtime enable mode.
+ childrenList []string
+
// lowerVD is the VirtualDentry in the underlying file system. It is
// never modified after initialized.
lowerVD vfs.VirtualDentry
@@ -749,6 +757,17 @@ func (d *dentry) verityEnabled() bool {
return !d.fs.allowRuntimeEnable || len(d.hash) != 0
}
+// generateChildrenList generates a sorted childrenList from childrenNames, and
+// cache it in d for hashing.
+func (d *dentry) generateChildrenList() {
+ if len(d.childrenList) == 0 && len(d.childrenNames) != 0 {
+ for child := range d.childrenNames {
+ d.childrenList = append(d.childrenList, child)
+ }
+ sort.Strings(d.childrenList)
+ }
+}
+
// getLowerAt returns the dentry in the underlying file system, which is
// represented by filename relative to d.
func (d *dentry) getLowerAt(ctx context.Context, vfsObj *vfs.VirtualFilesystem, filename string) (vfs.VirtualDentry, error) {
@@ -857,7 +876,7 @@ func (fd *fileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linu
// SetStat implements vfs.FileDescriptionImpl.SetStat.
func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
// Verity files are read-only.
- return syserror.EPERM
+ return linuxerr.EPERM
}
// IterDirents implements vfs.FileDescriptionImpl.IterDirents.
@@ -921,14 +940,14 @@ func (fd *fileDescription) Seek(ctx context.Context, offset int64, whence int32)
case linux.SEEK_END:
n = int64(fd.d.size)
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset > math.MaxInt64-n {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
offset += n
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
fd.off = offset
return offset, nil
@@ -962,10 +981,12 @@ func (fd *fileDescription) generateMerkleLocked(ctx context.Context) ([]byte, ui
return nil, 0, err
}
+ fd.d.generateChildrenList()
+
params := &merkletree.GenerateParams{
TreeReader: &merkleReader,
TreeWriter: &merkleWriter,
- Children: fd.d.childrenNames,
+ Children: fd.d.childrenList,
HashAlgorithms: fd.d.fs.alg.toLinuxHashAlg(),
Name: fd.d.name,
Mode: uint32(stat.Mode),
@@ -1007,7 +1028,7 @@ func (fd *fileDescription) generateMerkleLocked(ctx context.Context) ([]byte, ui
default:
// TODO(b/167728857): Investigate whether and how we should
// enable other types of file.
- return nil, 0, syserror.EINVAL
+ return nil, 0, linuxerr.EINVAL
}
hash, err := merkletree.Generate(params)
return hash, uint64(params.Size), err
@@ -1056,7 +1077,7 @@ func (fd *fileDescription) recordChildrenLocked(ctx context.Context) error {
// and stores its hash in its parent directory's Merkle tree.
func (fd *fileDescription) enableVerity(ctx context.Context) (uintptr, error) {
if !fd.d.fs.allowRuntimeEnable {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
fd.d.fs.verityMu.Lock()
@@ -1125,7 +1146,7 @@ func (fd *fileDescription) enableVerity(ctx context.Context) (uintptr, error) {
func (fd *fileDescription) measureVerity(ctx context.Context, verityDigest hostarch.Addr) (uintptr, error) {
t := kernel.TaskFromContext(ctx)
if t == nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
var metadata linux.DigestMetadata
@@ -1138,7 +1159,7 @@ func (fd *fileDescription) measureVerity(ctx context.Context, verityDigest hosta
// enabled, in which case fd.d.hash should be set.
if len(fd.d.hash) == 0 {
if fd.d.fs.allowRuntimeEnable {
- return 0, syserror.ENODATA
+ return 0, linuxerr.ENODATA
}
return 0, fd.d.fs.alertIntegrityViolation("Ioctl measureVerity: no hash found")
}
@@ -1148,7 +1169,7 @@ func (fd *fileDescription) measureVerity(ctx context.Context, verityDigest hosta
return 0, err
}
if metadata.DigestSize < uint16(len(fd.d.hash)) {
- return 0, syserror.EOVERFLOW
+ return 0, linuxerr.EOVERFLOW
}
// Populate the output digest size, since DigestSize is both input and
@@ -1178,7 +1199,7 @@ func (fd *fileDescription) verityFlags(ctx context.Context, flags hostarch.Addr)
t := kernel.TaskFromContext(ctx)
if t == nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
_, err := primitive.CopyInt32Out(t, flags, f)
return 0, err
@@ -1227,7 +1248,7 @@ func (fd *fileDescription) PRead(ctx context.Context, dst usermem.IOSequence, of
// The Merkle tree file for the child should have been created and
// contains the expected xattrs. If the xattr does not exist, it
// indicates unexpected modifications to the file system.
- if err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENODATA, err) {
return 0, fd.d.fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s: %v", merkleSizeXattr, err))
}
if err != nil {
@@ -1261,7 +1282,7 @@ func (fd *fileDescription) PRead(ctx context.Context, dst usermem.IOSequence, of
Mode: fd.d.mode,
UID: fd.d.uid,
GID: fd.d.gid,
- Children: fd.d.childrenNames,
+ Children: fd.d.childrenList,
HashAlgorithms: fd.d.fs.alg.toLinuxHashAlg(),
ReadOffset: offset,
ReadSize: dst.NumBytes(),
@@ -1277,12 +1298,12 @@ func (fd *fileDescription) PRead(ctx context.Context, dst usermem.IOSequence, of
// PWrite implements vfs.FileDescriptionImpl.PWrite.
func (fd *fileDescription) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
- return 0, syserror.EROFS
+ return 0, linuxerr.EROFS
}
// Write implements vfs.FileDescriptionImpl.Write.
func (fd *fileDescription) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
- return 0, syserror.EROFS
+ return 0, linuxerr.EROFS
}
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
@@ -1298,7 +1319,7 @@ func (fd *fileDescription) ConfigureMMap(ctx context.Context, opts *memmap.MMapO
// Check if mmap is allowed on the lower filesystem.
if !opts.SentryOwnedContent {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
return vfs.GenericConfigureMMap(&fd.vfsfd, fd, opts)
}
@@ -1349,7 +1370,7 @@ func (fd *fileDescription) Translate(ctx context.Context, required, optional mem
// The Merkle tree file for the child should have been created and
// contains the expected xattrs. If the xattr does not exist, it
// indicates unexpected modifications to the file system.
- if err == syserror.ENODATA {
+ if linuxerr.Equals(linuxerr.ENODATA, err) {
return nil, fd.d.fs.alertIntegrityViolation(fmt.Sprintf("Failed to get xattr %s: %v", merkleSizeXattr, err))
}
if err != nil {
@@ -1433,7 +1454,7 @@ func (r *mmapReadSeeker) ReadAt(p []byte, off int64) (int, error) {
// mapped region.
readOffset := off - int64(r.Offset)
if readOffset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
bs.DropFirst64(uint64(readOffset))
view := bs.TakeFirst64(uint64(len(p)))
diff --git a/pkg/sentry/fsimpl/verity/verity_test.go b/pkg/sentry/fsimpl/verity/verity_test.go
index 5c78a0019..65465b814 100644
--- a/pkg/sentry/fsimpl/verity/verity_test.go
+++ b/pkg/sentry/fsimpl/verity/verity_test.go
@@ -24,6 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/testutil"
@@ -31,7 +32,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -476,7 +476,7 @@ func TestOpenNonexistentFile(t *testing.T) {
// Ensure open an unexpected file in the parent directory fails with
// ENOENT rather than verification failure.
- if _, err = openVerityAt(ctx, vfsObj, root, filename+"abc", linux.O_RDONLY, linux.ModeRegular); err != syserror.ENOENT {
+ if _, err = openVerityAt(ctx, vfsObj, root, filename+"abc", linux.O_RDONLY, linux.ModeRegular); !linuxerr.Equals(linuxerr.ENOENT, err) {
t.Errorf("OpenAt unexpected error: %v", err)
}
}
@@ -767,7 +767,7 @@ func TestOpenDeletedFileFails(t *testing.T) {
}
// Ensure reopening the verity enabled file fails.
- if _, err = openVerityAt(ctx, vfsObj, root, filename, linux.O_RDONLY, linux.ModeRegular); err != syserror.EIO {
+ if _, err = openVerityAt(ctx, vfsObj, root, filename, linux.O_RDONLY, linux.ModeRegular); !linuxerr.Equals(linuxerr.EIO, err) {
t.Errorf("got OpenAt error: %v, expected EIO", err)
}
})
@@ -829,7 +829,7 @@ func TestOpenRenamedFileFails(t *testing.T) {
}
// Ensure reopening the verity enabled file fails.
- if _, err = openVerityAt(ctx, vfsObj, root, filename, linux.O_RDONLY, linux.ModeRegular); err != syserror.EIO {
+ if _, err = openVerityAt(ctx, vfsObj, root, filename, linux.O_RDONLY, linux.ModeRegular); !linuxerr.Equals(linuxerr.EIO, err) {
t.Errorf("got OpenAt error: %v, expected EIO", err)
}
})
@@ -1063,14 +1063,14 @@ func TestDeletedSymlinkFileReadFails(t *testing.T) {
Root: root,
Start: root,
Path: fspath.Parse(symlink),
- }); err != syserror.EIO {
+ }); !linuxerr.Equals(linuxerr.EIO, err) {
t.Fatalf("ReadlinkAt succeeded with modified symlink: %v", err)
}
if tc.testWalk {
fileInSymlinkDirectory := symlink + "/verity-test-file"
// Ensure opening the verity enabled file in the symlink directory fails.
- if _, err := openVerityAt(ctx, vfsObj, root, fileInSymlinkDirectory, linux.O_RDONLY, linux.ModeRegular); err != syserror.EIO {
+ if _, err := openVerityAt(ctx, vfsObj, root, fileInSymlinkDirectory, linux.O_RDONLY, linux.ModeRegular); !linuxerr.Equals(linuxerr.EIO, err) {
t.Errorf("Open succeeded with modified symlink: %v", err)
}
}
@@ -1195,14 +1195,14 @@ func TestModifiedSymlinkFileReadFails(t *testing.T) {
Root: root,
Start: root,
Path: fspath.Parse(symlink),
- }); err != syserror.EIO {
+ }); !linuxerr.Equals(linuxerr.EIO, err) {
t.Fatalf("ReadlinkAt succeeded with modified symlink: %v", err)
}
if tc.testWalk {
fileInSymlinkDirectory := symlink + "/verity-test-file"
// Ensure opening the verity enabled file in the symlink directory fails.
- if _, err := openVerityAt(ctx, vfsObj, root, fileInSymlinkDirectory, linux.O_RDONLY, linux.ModeRegular); err != syserror.EIO {
+ if _, err := openVerityAt(ctx, vfsObj, root, fileInSymlinkDirectory, linux.O_RDONLY, linux.ModeRegular); !linuxerr.Equals(linuxerr.EIO, err) {
t.Errorf("Open succeeded with modified symlink: %v", err)
}
}
diff --git a/pkg/sentry/hostfd/hostfd_linux.go b/pkg/sentry/hostfd/hostfd_linux.go
index 1cabc848f..e103e7296 100644
--- a/pkg/sentry/hostfd/hostfd_linux.go
+++ b/pkg/sentry/hostfd/hostfd_linux.go
@@ -14,5 +14,10 @@
package hostfd
-// maxIov is the maximum permitted size of a struct iovec array.
-const maxIov = 1024 // UIO_MAXIOV
+// MaxReadWriteIov is the maximum permitted size of a struct iovec array in a
+// readv, writev, preadv, or pwritev host syscall.
+const MaxReadWriteIov = 1024 // UIO_MAXIOV
+
+// MaxSendRecvMsgIov is the maximum permitted size of a struct iovec array in a
+// sendmsg or recvmsg host syscall.
+const MaxSendRecvMsgIov = 1024 // UIO_MAXIOV
diff --git a/pkg/sentry/hostfd/hostfd_unsafe.go b/pkg/sentry/hostfd/hostfd_unsafe.go
index 03c6d2a16..a43311eb4 100644
--- a/pkg/sentry/hostfd/hostfd_unsafe.go
+++ b/pkg/sentry/hostfd/hostfd_unsafe.go
@@ -23,6 +23,11 @@ import (
"gvisor.dev/gvisor/pkg/safemem"
)
+const (
+ sizeofIovec = unsafe.Sizeof(unix.Iovec{})
+ sizeofMsghdr = unsafe.Sizeof(unix.Msghdr{})
+)
+
// Preadv2 reads up to dsts.NumBytes() bytes from host file descriptor fd into
// dsts. offset and flags are interpreted as for preadv2(2).
//
@@ -44,9 +49,9 @@ func Preadv2(fd int32, dsts safemem.BlockSeq, offset int64, flags uint32) (uint6
}
} else {
iovs := safemem.IovecsFromBlockSeq(dsts)
- if len(iovs) > maxIov {
- log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), maxIov)
- iovs = iovs[:maxIov]
+ if len(iovs) > MaxReadWriteIov {
+ log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), MaxReadWriteIov)
+ iovs = iovs[:MaxReadWriteIov]
}
n, _, e = unix.Syscall6(unix.SYS_PREADV2, uintptr(fd), uintptr((unsafe.Pointer)(&iovs[0])), uintptr(len(iovs)), uintptr(offset), 0 /* pos_h */, uintptr(flags))
}
@@ -80,9 +85,9 @@ func Pwritev2(fd int32, srcs safemem.BlockSeq, offset int64, flags uint32) (uint
}
} else {
iovs := safemem.IovecsFromBlockSeq(srcs)
- if len(iovs) > maxIov {
- log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), maxIov)
- iovs = iovs[:maxIov]
+ if len(iovs) > MaxReadWriteIov {
+ log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), MaxReadWriteIov)
+ iovs = iovs[:MaxReadWriteIov]
}
n, _, e = unix.Syscall6(unix.SYS_PWRITEV2, uintptr(fd), uintptr((unsafe.Pointer)(&iovs[0])), uintptr(len(iovs)), uintptr(offset), 0 /* pos_h */, uintptr(flags))
}
diff --git a/pkg/sentry/kernel/BUILD b/pkg/sentry/kernel/BUILD
index a1ec6daab..26614b029 100644
--- a/pkg/sentry/kernel/BUILD
+++ b/pkg/sentry/kernel/BUILD
@@ -32,7 +32,7 @@ go_template_instance(
out = "seqatomic_taskgoroutineschedinfo_unsafe.go",
package = "kernel",
suffix = "TaskGoroutineSchedInfo",
- template = "//pkg/sync:generic_seqatomic",
+ template = "//pkg/sync/seqatomic:generic_seqatomic",
types = {
"Value": "TaskGoroutineSchedInfo",
},
@@ -218,6 +218,7 @@ go_library(
":uncaught_signal_go_proto",
"//pkg/abi",
"//pkg/abi/linux",
+ "//pkg/abi/linux/errno",
"//pkg/amutex",
"//pkg/bits",
"//pkg/bpf",
@@ -225,6 +226,8 @@ go_library(
"//pkg/context",
"//pkg/coverage",
"//pkg/cpuid",
+ "//pkg/errors",
+ "//pkg/errors/linuxerr",
"//pkg/eventchannel",
"//pkg/fspath",
"//pkg/goid",
@@ -298,6 +301,7 @@ go_test(
deps = [
"//pkg/abi",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/sentry/arch",
"//pkg/sentry/contexttest",
@@ -309,6 +313,5 @@ go_test(
"//pkg/sentry/time",
"//pkg/sentry/usage",
"//pkg/sync",
- "//pkg/syserror",
],
)
diff --git a/pkg/sentry/kernel/auth/BUILD b/pkg/sentry/kernel/auth/BUILD
index 869e49ebc..7a1a36454 100644
--- a/pkg/sentry/kernel/auth/BUILD
+++ b/pkg/sentry/kernel/auth/BUILD
@@ -8,7 +8,7 @@ go_template_instance(
out = "atomicptr_credentials_unsafe.go",
package = "auth",
suffix = "Credentials",
- template = "//pkg/sync:generic_atomicptr",
+ template = "//pkg/sync/atomicptr:generic_atomicptr",
types = {
"Value": "Credentials",
},
@@ -63,6 +63,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/bits",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/sync",
"//pkg/syserror",
diff --git a/pkg/sentry/kernel/auth/credentials.go b/pkg/sentry/kernel/auth/credentials.go
index 6862f2ef5..fc245c54b 100644
--- a/pkg/sentry/kernel/auth/credentials.go
+++ b/pkg/sentry/kernel/auth/credentials.go
@@ -16,7 +16,7 @@ package auth
import (
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
)
// Credentials contains information required to authorize privileged operations
@@ -125,7 +125,7 @@ func NewUserCredentials(kuid KUID, kgid KGID, extraKGIDs []KGID, capabilities *T
creds.EffectiveCaps = capabilities.EffectiveCaps
creds.BoundingCaps = capabilities.BoundingCaps
creds.InheritableCaps = capabilities.InheritableCaps
- // TODO(nlacasse): Support ambient capabilities.
+ // TODO(gvisor.dev/issue/3166): Support ambient capabilities.
} else {
// If no capabilities are specified, grant capabilities consistent with
// setresuid + setresgid from NewRootCredentials to the given uid and
@@ -203,7 +203,7 @@ func (c *Credentials) UseUID(uid UID) (KUID, error) {
// uid must be mapped.
kuid := c.UserNamespace.MapToKUID(uid)
if !kuid.Ok() {
- return NoID, syserror.EINVAL
+ return NoID, linuxerr.EINVAL
}
// If c has CAP_SETUID, then it can use any UID in its user namespace.
if c.HasCapability(linux.CAP_SETUID) {
@@ -214,7 +214,7 @@ func (c *Credentials) UseUID(uid UID) (KUID, error) {
if kuid == c.RealKUID || kuid == c.EffectiveKUID || kuid == c.SavedKUID {
return kuid, nil
}
- return NoID, syserror.EPERM
+ return NoID, linuxerr.EPERM
}
// UseGID checks that c can use gid in its user namespace, then translates it
@@ -222,7 +222,7 @@ func (c *Credentials) UseUID(uid UID) (KUID, error) {
func (c *Credentials) UseGID(gid GID) (KGID, error) {
kgid := c.UserNamespace.MapToKGID(gid)
if !kgid.Ok() {
- return NoID, syserror.EINVAL
+ return NoID, linuxerr.EINVAL
}
if c.HasCapability(linux.CAP_SETGID) {
return kgid, nil
@@ -230,7 +230,7 @@ func (c *Credentials) UseGID(gid GID) (KGID, error) {
if kgid == c.RealKGID || kgid == c.EffectiveKGID || kgid == c.SavedKGID {
return kgid, nil
}
- return NoID, syserror.EPERM
+ return NoID, linuxerr.EPERM
}
// SetUID translates the provided uid to the root user namespace and updates c's
@@ -239,7 +239,7 @@ func (c *Credentials) UseGID(gid GID) (KGID, error) {
func (c *Credentials) SetUID(uid UID) error {
kuid := c.UserNamespace.MapToKUID(uid)
if !kuid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
c.RealKUID = kuid
c.EffectiveKUID = kuid
@@ -253,7 +253,7 @@ func (c *Credentials) SetUID(uid UID) error {
func (c *Credentials) SetGID(gid GID) error {
kgid := c.UserNamespace.MapToKGID(gid)
if !kgid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
c.RealKGID = kgid
c.EffectiveKGID = kgid
diff --git a/pkg/sentry/kernel/auth/id_map.go b/pkg/sentry/kernel/auth/id_map.go
index 28cbe159d..f06a374a0 100644
--- a/pkg/sentry/kernel/auth/id_map.go
+++ b/pkg/sentry/kernel/auth/id_map.go
@@ -17,7 +17,7 @@ package auth
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
)
// MapFromKUID translates kuid, a UID in the root namespace, to a UID in ns.
@@ -106,11 +106,11 @@ func (ns *UserNamespace) SetUIDMap(ctx context.Context, entries []IDMapEntry) er
// than once to a uid_map file in a user namespace fails with the error
// EPERM. Similar rules apply for gid_map files." - user_namespaces(7)
if !ns.uidMapFromParent.IsEmpty() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// "At least one line must be written to the file."
if len(entries) == 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// """
// In order for a process to write to the /proc/[pid]/uid_map
@@ -121,12 +121,12 @@ func (ns *UserNamespace) SetUIDMap(ctx context.Context, entries []IDMapEntry) er
// in the user namespace of the process pid.
// """
if !c.HasCapabilityIn(linux.CAP_SETUID, ns) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// "2. The writing process must either be in the user namespace of the process
// pid or be in the parent user namespace of the process pid."
if c.UserNamespace != ns && c.UserNamespace != ns.parent {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// """
// 3. (see trySetUIDMap)
@@ -145,14 +145,14 @@ func (ns *UserNamespace) SetUIDMap(ctx context.Context, entries []IDMapEntry) er
// parent user namespace to a user ID (group ID) in the user namespace.
// """
if len(entries) != 1 || ns.parent.MapToKUID(UID(entries[0].FirstParentID)) != c.EffectiveKUID || entries[0].Length != 1 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// """
// + The writing process must have the same effective user ID as the
// process that created the user namespace.
// """
if c.EffectiveKUID != ns.owner {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
// trySetUIDMap leaves data in maps if it fails.
@@ -170,11 +170,11 @@ func (ns *UserNamespace) trySetUIDMap(entries []IDMapEntry) error {
// checks for NoID.
lastID := e.FirstID + e.Length
if lastID <= e.FirstID {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
lastParentID := e.FirstParentID + e.Length
if lastParentID <= e.FirstParentID {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// "3. The mapped user IDs (group IDs) must in turn have a mapping in
// the parent user namespace."
@@ -182,14 +182,14 @@ func (ns *UserNamespace) trySetUIDMap(entries []IDMapEntry) error {
// mappings when it's created, so SetUIDMap would have returned EPERM
// without reaching this point if ns is root.
if !ns.parent.allIDsMapped(&ns.parent.uidMapToParent, e.FirstParentID, lastParentID) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// If either of these Adds fail, we have an overlapping range.
if !ns.uidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if !ns.uidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
return nil
@@ -202,24 +202,24 @@ func (ns *UserNamespace) SetGIDMap(ctx context.Context, entries []IDMapEntry) er
ns.mu.Lock()
defer ns.mu.Unlock()
if !ns.gidMapFromParent.IsEmpty() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if len(entries) == 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if !c.HasCapabilityIn(linux.CAP_SETGID, ns) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if c.UserNamespace != ns && c.UserNamespace != ns.parent {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if !c.HasCapabilityIn(linux.CAP_SETGID, ns.parent) {
if len(entries) != 1 || ns.parent.MapToKGID(GID(entries[0].FirstParentID)) != c.EffectiveKGID || entries[0].Length != 1 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// It's correct for this to still be UID.
if c.EffectiveKUID != ns.owner {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// "In the case of gid_map, use of the setgroups(2) system call must
// first be denied by writing "deny" to the /proc/[pid]/setgroups file
@@ -239,20 +239,20 @@ func (ns *UserNamespace) trySetGIDMap(entries []IDMapEntry) error {
for _, e := range entries {
lastID := e.FirstID + e.Length
if lastID <= e.FirstID {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
lastParentID := e.FirstParentID + e.Length
if lastParentID <= e.FirstParentID {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if !ns.parent.allIDsMapped(&ns.parent.gidMapToParent, e.FirstParentID, lastParentID) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if !ns.gidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if !ns.gidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
return nil
diff --git a/pkg/sentry/kernel/auth/user_namespace.go b/pkg/sentry/kernel/auth/user_namespace.go
index 9dd52c860..40a406f9d 100644
--- a/pkg/sentry/kernel/auth/user_namespace.go
+++ b/pkg/sentry/kernel/auth/user_namespace.go
@@ -17,8 +17,8 @@ package auth
import (
"math"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
// A UserNamespace represents a user namespace. See user_namespaces(7) for
@@ -105,7 +105,7 @@ func (c *Credentials) NewChildUserNamespace() (*UserNamespace, error) {
if c.UserNamespace.depth() >= maxUserNamespaceDepth {
// "... Calls to unshare(2) or clone(2) that would cause this limit to
// be exceeded fail with the error EUSERS." - user_namespaces(7)
- return nil, syserror.EUSERS
+ return nil, linuxerr.EUSERS
}
// "EPERM: CLONE_NEWUSER was specified in flags, but either the effective
// user ID or the effective group ID of the caller does not have a mapping
@@ -114,10 +114,10 @@ func (c *Credentials) NewChildUserNamespace() (*UserNamespace, error) {
// process are mapped to user IDs and group IDs in the user namespace of
// the calling process at the time of the call." - unshare(2)
if !c.EffectiveKUID.In(c.UserNamespace).Ok() {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
if !c.EffectiveKGID.In(c.UserNamespace).Ok() {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
return &UserNamespace{
parent: c.UserNamespace,
diff --git a/pkg/sentry/kernel/fasync/BUILD b/pkg/sentry/kernel/fasync/BUILD
index f855f038b..6b2dd09da 100644
--- a/pkg/sentry/kernel/fasync/BUILD
+++ b/pkg/sentry/kernel/fasync/BUILD
@@ -8,13 +8,12 @@ go_library(
visibility = ["//:sandbox"],
deps = [
"//pkg/abi/linux",
- "//pkg/sentry/arch",
+ "//pkg/errors/linuxerr",
"//pkg/sentry/fs",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
"//pkg/sync",
- "//pkg/syserror",
"//pkg/waiter",
],
)
diff --git a/pkg/sentry/kernel/fasync/fasync.go b/pkg/sentry/kernel/fasync/fasync.go
index dbbbaeeb0..473987a79 100644
--- a/pkg/sentry/kernel/fasync/fasync.go
+++ b/pkg/sentry/kernel/fasync/fasync.go
@@ -17,13 +17,12 @@ package fasync
import (
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/sentry/arch"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -125,9 +124,9 @@ func (a *FileAsync) Callback(e *waiter.Entry, mask waiter.EventMask) {
if !permCheck {
return
}
- signalInfo := &arch.SignalInfo{
+ signalInfo := &linux.SignalInfo{
Signo: int32(linux.SIGIO),
- Code: arch.SignalInfoKernel,
+ Code: linux.SI_KERNEL,
}
if a.signal != 0 {
signalInfo.Signo = int32(a.signal)
@@ -249,7 +248,7 @@ func (a *FileAsync) Signal() linux.Signal {
// to send SIGIO.
func (a *FileAsync) SetSignal(signal linux.Signal) error {
if signal != 0 && !signal.IsValid() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
a.mu.Lock()
defer a.mu.Unlock()
diff --git a/pkg/sentry/kernel/fd_table.go b/pkg/sentry/kernel/fd_table.go
index 62777faa8..8786a70b5 100644
--- a/pkg/sentry/kernel/fd_table.go
+++ b/pkg/sentry/kernel/fd_table.go
@@ -23,12 +23,12 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
// FDFlags define flags for an individual descriptor.
@@ -156,7 +156,7 @@ func (f *FDTable) dropVFS2(ctx context.Context, file *vfs.FileDescription) {
// Release any POSIX lock possibly held by the FDTable.
if file.SupportsLocks() {
err := file.UnlockPOSIX(ctx, f, lock.LockRange{0, lock.LockEOF})
- if err != nil && err != syserror.ENOLCK {
+ if err != nil && !linuxerr.Equals(linuxerr.ENOLCK, err) {
panic(fmt.Sprintf("UnlockPOSIX failed: %v", err))
}
}
diff --git a/pkg/sentry/kernel/futex/BUILD b/pkg/sentry/kernel/futex/BUILD
index a75686cf3..cfdea5cf7 100644
--- a/pkg/sentry/kernel/futex/BUILD
+++ b/pkg/sentry/kernel/futex/BUILD
@@ -8,7 +8,7 @@ go_template_instance(
out = "atomicptr_bucket_unsafe.go",
package = "futex",
suffix = "Bucket",
- template = "//pkg/sync:generic_atomicptr",
+ template = "//pkg/sync/atomicptr:generic_atomicptr",
types = {
"Value": "bucket",
},
@@ -37,6 +37,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/sentry/memmap",
@@ -53,8 +54,8 @@ go_test(
library = ":futex",
deps = [
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/sync",
- "@org_golang_x_sys//unix:go_default_library",
],
)
diff --git a/pkg/sentry/kernel/futex/futex.go b/pkg/sentry/kernel/futex/futex.go
index 0427cf3f4..f5c364c96 100644
--- a/pkg/sentry/kernel/futex/futex.go
+++ b/pkg/sentry/kernel/futex/futex.go
@@ -20,6 +20,7 @@ package futex
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sync"
@@ -122,7 +123,7 @@ func check(t Target, addr hostarch.Addr, val uint32) error {
return err
}
if cur != val {
- return syserror.EAGAIN
+ return linuxerr.EAGAIN
}
return nil
}
@@ -332,7 +333,7 @@ func getKey(t Target, addr hostarch.Addr, private bool) (Key, error) {
// Ensure the address is aligned.
// It must be a DWORD boundary.
if addr&0x3 != 0 {
- return Key{}, syserror.EINVAL
+ return Key{}, linuxerr.EINVAL
}
if private {
return Key{Kind: KindPrivate, Offset: uint64(addr)}, nil
@@ -397,8 +398,8 @@ func (m *Manager) Fork() *Manager {
}
// lockBucket returns a locked bucket for the given key.
-func (m *Manager) lockBucket(k *Key) *bucket {
- var b *bucket
+// +checklocksacquire:b.mu
+func (m *Manager) lockBucket(k *Key) (b *bucket) {
if k.Kind == KindSharedMappable {
b = m.sharedBucket
} else {
@@ -409,7 +410,9 @@ func (m *Manager) lockBucket(k *Key) *bucket {
}
// lockBuckets returns locked buckets for the given keys.
-func (m *Manager) lockBuckets(k1, k2 *Key) (*bucket, *bucket) {
+// +checklocksacquire:b1.mu
+// +checklocksacquire:b2.mu
+func (m *Manager) lockBuckets(k1, k2 *Key) (b1 *bucket, b2 *bucket) {
// Buckets must be consistently ordered to avoid circular lock
// dependencies. We order buckets in m.privateBuckets by index (lowest
// index first), and all buckets in m.privateBuckets precede
@@ -419,8 +422,8 @@ func (m *Manager) lockBuckets(k1, k2 *Key) (*bucket, *bucket) {
if k1.Kind != KindSharedMappable && k2.Kind != KindSharedMappable {
i1 := bucketIndexForAddr(k1.addr())
i2 := bucketIndexForAddr(k2.addr())
- b1 := &m.privateBuckets[i1]
- b2 := &m.privateBuckets[i2]
+ b1 = &m.privateBuckets[i1]
+ b2 = &m.privateBuckets[i2]
switch {
case i1 < i2:
b1.mu.Lock()
@@ -431,19 +434,30 @@ func (m *Manager) lockBuckets(k1, k2 *Key) (*bucket, *bucket) {
default:
b1.mu.Lock()
}
- return b1, b2
+ return b1, b2 // +checklocksforce
}
// At least one of b1 or b2 should be m.sharedBucket.
- b1 := m.sharedBucket
- b2 := m.sharedBucket
+ b1 = m.sharedBucket
+ b2 = m.sharedBucket
if k1.Kind != KindSharedMappable {
b1 = m.lockBucket(k1)
} else if k2.Kind != KindSharedMappable {
b2 = m.lockBucket(k2)
}
m.sharedBucket.mu.Lock()
- return b1, b2
+ return b1, b2 // +checklocksforce
+}
+
+// unlockBuckets unlocks two buckets.
+// +checklocksrelease:b1.mu
+// +checklocksrelease:b2.mu
+func (m *Manager) unlockBuckets(b1, b2 *bucket) {
+ b1.mu.Unlock()
+ if b1 != b2 {
+ b2.mu.Unlock()
+ }
+ return // +checklocksforce
}
// Wake wakes up to n waiters matching the bitmask on the given addr.
@@ -476,10 +490,7 @@ func (m *Manager) doRequeue(t Target, addr, naddr hostarch.Addr, private bool, c
defer k2.release(t)
b1, b2 := m.lockBuckets(&k1, &k2)
- defer b1.mu.Unlock()
- if b2 != b1 {
- defer b2.mu.Unlock()
- }
+ defer m.unlockBuckets(b1, b2)
if checkval {
if err := check(t, addr, val); err != nil {
@@ -526,10 +537,7 @@ func (m *Manager) WakeOp(t Target, addr1, addr2 hostarch.Addr, private bool, nwa
defer k2.release(t)
b1, b2 := m.lockBuckets(&k1, &k2)
- defer b1.mu.Unlock()
- if b2 != b1 {
- defer b2.mu.Unlock()
- }
+ defer m.unlockBuckets(b1, b2)
done := 0
cond, err := atomicOp(t, addr2, op)
@@ -670,7 +678,7 @@ func (m *Manager) lockPILocked(w *Waiter, t Target, addr hostarch.Addr, tid uint
return false, err
}
if (cur & linux.FUTEX_TID_MASK) == tid {
- return false, syserror.EDEADLK
+ return false, linuxerr.EDEADLK
}
if (cur & linux.FUTEX_TID_MASK) == 0 {
@@ -745,7 +753,7 @@ func (m *Manager) unlockPILocked(t Target, addr hostarch.Addr, tid uint32, b *bu
}
if (cur & linux.FUTEX_TID_MASK) != tid {
- return syserror.EPERM
+ return linuxerr.EPERM
}
var next *Waiter // Who's the next owner?
@@ -773,7 +781,7 @@ func (m *Manager) unlockPILocked(t Target, addr hostarch.Addr, tid uint32, b *bu
if prev != cur {
// Let user mode handle CAS races. This is different than lock, which
// retries when CAS fails.
- return syserror.EAGAIN
+ return linuxerr.EAGAIN
}
return nil
}
@@ -790,7 +798,7 @@ func (m *Manager) unlockPILocked(t Target, addr hostarch.Addr, tid uint32, b *bu
return err
}
if prev != cur {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
b.wakeWaiterLocked(next)
diff --git a/pkg/sentry/kernel/futex/futex_test.go b/pkg/sentry/kernel/futex/futex_test.go
index deba44e5c..04c136f87 100644
--- a/pkg/sentry/kernel/futex/futex_test.go
+++ b/pkg/sentry/kernel/futex/futex_test.go
@@ -21,8 +21,8 @@ import (
"testing"
"unsafe"
- "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sync"
)
@@ -488,7 +488,7 @@ func (t *testMutex) Lock() {
// Wait for it to be "not locked".
w := NewWaiter()
err := t.m.WaitPrepare(w, t.d, t.a, true, testMutexLocked, ^uint32(0))
- if err == unix.EAGAIN {
+ if linuxerr.Equals(linuxerr.EAGAIN, err) {
continue
}
if err != nil {
diff --git a/pkg/sentry/kernel/kcov.go b/pkg/sentry/kernel/kcov.go
index 4b943106b..e8a71bec1 100644
--- a/pkg/sentry/kernel/kcov.go
+++ b/pkg/sentry/kernel/kcov.go
@@ -22,13 +22,13 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/coverage"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/mm"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
"gvisor.dev/gvisor/pkg/sentry/usage"
- "gvisor.dev/gvisor/pkg/syserror"
)
// kcovAreaSizeMax is the maximum number of uint64 entries allowed in the kcov
@@ -125,19 +125,19 @@ func (kcov *Kcov) InitTrace(size uint64) error {
defer kcov.mu.Unlock()
if kcov.mode != linux.KCOV_MODE_DISABLED {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
// To simplify all the logic around mapping, we require that the length of the
// shared region is a multiple of the system page size.
if (8*size)&(hostarch.PageSize-1) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// We need space for at least two uint64s to hold current position and a
// single PC.
if size < 2 || size > kcovAreaSizeMax {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
kcov.size = size
@@ -157,7 +157,7 @@ func (kcov *Kcov) EnableTrace(ctx context.Context, traceKind uint8) error {
// KCOV_ENABLE must be preceded by KCOV_INIT_TRACE and an mmap call.
if kcov.mode != linux.KCOV_MODE_INIT || kcov.mappable == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
switch traceKind {
@@ -165,13 +165,13 @@ func (kcov *Kcov) EnableTrace(ctx context.Context, traceKind uint8) error {
kcov.mode = linux.KCOV_MODE_TRACE_PC
case linux.KCOV_TRACE_CMP:
// We do not support KCOV_MODE_TRACE_CMP.
- return syserror.ENOTSUP
+ return linuxerr.ENOTSUP
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if kcov.owningTask != nil && kcov.owningTask != t {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
kcov.owningTask = t
@@ -195,7 +195,7 @@ func (kcov *Kcov) DisableTrace(ctx context.Context) error {
}
if t != kcov.owningTask {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
kcov.mode = linux.KCOV_MODE_INIT
kcov.owningTask = nil
@@ -237,7 +237,7 @@ func (kcov *Kcov) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) erro
defer kcov.mu.Unlock()
if kcov.mode != linux.KCOV_MODE_INIT {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if kcov.mappable == nil {
diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go
index febe7fe50..352c36ba9 100644
--- a/pkg/sentry/kernel/kernel.go
+++ b/pkg/sentry/kernel/kernel.go
@@ -143,12 +143,6 @@ type Kernel struct {
// to CreateProcess, and is protected by extMu.
globalInit *ThreadGroup
- // realtimeClock is a ktime.Clock based on timekeeper's Realtime.
- realtimeClock *timekeeperClock
-
- // monotonicClock is a ktime.Clock based on timekeeper's Monotonic.
- monotonicClock *timekeeperClock
-
// syslog is the kernel log.
syslog syslog
@@ -306,6 +300,9 @@ type InitKernelArgs struct {
// FeatureSet is the emulated CPU feature set.
FeatureSet *cpuid.FeatureSet
+ // Timekeeper manages time for all tasks in the system.
+ Timekeeper *Timekeeper
+
// RootUserNamespace is the root user namespace.
RootUserNamespace *auth.UserNamespace
@@ -345,24 +342,18 @@ type InitKernelArgs struct {
PIDNamespace *PIDNamespace
}
-// SetTimekeeper sets Kernel.timekeeper. SetTimekeeper must be called before
-// Init.
-func (k *Kernel) SetTimekeeper(tk *Timekeeper) {
- k.timekeeper = tk
-}
-
// Init initialize the Kernel with no tasks.
//
// Callers must manually set Kernel.Platform and call Kernel.SetMemoryFile
-// and Kernel.SetTimekeeper before calling Init.
+// before calling Init.
func (k *Kernel) Init(args InitKernelArgs) error {
if args.FeatureSet == nil {
return fmt.Errorf("args.FeatureSet is nil")
}
- if k.timekeeper == nil {
- return fmt.Errorf("timekeeper is nil")
+ if args.Timekeeper == nil {
+ return fmt.Errorf("args.Timekeeper is nil")
}
- if k.timekeeper.clocks == nil {
+ if args.Timekeeper.clocks == nil {
return fmt.Errorf("must call Timekeeper.SetClocks() before Kernel.Init()")
}
if args.RootUserNamespace == nil {
@@ -373,6 +364,7 @@ func (k *Kernel) Init(args InitKernelArgs) error {
}
k.featureSet = args.FeatureSet
+ k.timekeeper = args.Timekeeper
k.tasks = newTaskSet(args.PIDNamespace)
k.rootUserNamespace = args.RootUserNamespace
k.rootUTSNamespace = args.RootUTSNamespace
@@ -397,8 +389,6 @@ func (k *Kernel) Init(args InitKernelArgs) error {
}
k.extraAuxv = args.ExtraAuxv
k.vdso = args.Vdso
- k.realtimeClock = &timekeeperClock{tk: k.timekeeper, c: sentrytime.Realtime}
- k.monotonicClock = &timekeeperClock{tk: k.timekeeper, c: sentrytime.Monotonic}
k.futexes = futex.NewManager()
k.netlinkPorts = port.New()
k.ptraceExceptions = make(map[*Task]*Task)
@@ -531,6 +521,8 @@ func (k *Kernel) SaveTo(ctx context.Context, w wire.Writer) error {
}
log.Infof("CPUID save took [%s].", time.Since(cpuidStart))
+ // Save the timekeeper's state.
+
// Save the kernel state.
kernelStart := time.Now()
stats, err := state.Save(ctx, w, k)
@@ -675,7 +667,7 @@ func (k *Kernel) invalidateUnsavableMappings(ctx context.Context) error {
}
// LoadFrom returns a new Kernel loaded from args.
-func (k *Kernel) LoadFrom(ctx context.Context, r wire.Reader, net inet.Stack, clocks sentrytime.Clocks, vfsOpts *vfs.CompleteRestoreOptions) error {
+func (k *Kernel) LoadFrom(ctx context.Context, r wire.Reader, timeReady chan struct{}, net inet.Stack, clocks sentrytime.Clocks, vfsOpts *vfs.CompleteRestoreOptions) error {
loadStart := time.Now()
initAppCores := k.applicationCores
@@ -722,6 +714,11 @@ func (k *Kernel) LoadFrom(ctx context.Context, r wire.Reader, net inet.Stack, cl
log.Infof("Overall load took [%s]", time.Since(loadStart))
k.Timekeeper().SetClocks(clocks)
+
+ if timeReady != nil {
+ close(timeReady)
+ }
+
if net != nil {
net.Resume()
}
@@ -1103,7 +1100,7 @@ func (k *Kernel) Start() error {
}
k.started = true
- k.cpuClockTicker = ktime.NewTimer(k.monotonicClock, newKernelCPUClockTicker(k))
+ k.cpuClockTicker = ktime.NewTimer(k.timekeeper.monotonicClock, newKernelCPUClockTicker(k))
k.cpuClockTicker.Swap(ktime.Setting{
Enabled: true,
Period: linux.ClockTick,
@@ -1258,7 +1255,7 @@ func (k *Kernel) incRunningTasks() {
// These cause very different value of cpuClock. But again, since
// nothing was running while the ticker was disabled, those differences
// don't matter.
- setting, exp := k.cpuClockTickerSetting.At(k.monotonicClock.Now())
+ setting, exp := k.cpuClockTickerSetting.At(k.timekeeper.monotonicClock.Now())
if exp > 0 {
atomic.AddUint64(&k.cpuClock, exp)
}
@@ -1341,7 +1338,7 @@ func (k *Kernel) Unpause() {
// context is used only for debugging to describe how the signal was received.
//
// Preconditions: Kernel must have an init process.
-func (k *Kernel) SendExternalSignal(info *arch.SignalInfo, context string) {
+func (k *Kernel) SendExternalSignal(info *linux.SignalInfo, context string) {
k.extMu.Lock()
defer k.extMu.Unlock()
k.sendExternalSignal(info, context)
@@ -1349,7 +1346,7 @@ func (k *Kernel) SendExternalSignal(info *arch.SignalInfo, context string) {
// SendExternalSignalThreadGroup injects a signal into an specific ThreadGroup.
// This function doesn't skip signals like SendExternalSignal does.
-func (k *Kernel) SendExternalSignalThreadGroup(tg *ThreadGroup, info *arch.SignalInfo) error {
+func (k *Kernel) SendExternalSignalThreadGroup(tg *ThreadGroup, info *linux.SignalInfo) error {
k.extMu.Lock()
defer k.extMu.Unlock()
return tg.SendSignal(info)
@@ -1357,7 +1354,7 @@ func (k *Kernel) SendExternalSignalThreadGroup(tg *ThreadGroup, info *arch.Signa
// SendContainerSignal sends the given signal to all processes inside the
// namespace that match the given container ID.
-func (k *Kernel) SendContainerSignal(cid string, info *arch.SignalInfo) error {
+func (k *Kernel) SendContainerSignal(cid string, info *linux.SignalInfo) error {
k.extMu.Lock()
defer k.extMu.Unlock()
k.tasks.mu.RLock()
@@ -1468,12 +1465,12 @@ func (k *Kernel) ApplicationCores() uint {
// RealtimeClock returns the application CLOCK_REALTIME clock.
func (k *Kernel) RealtimeClock() ktime.Clock {
- return k.realtimeClock
+ return k.timekeeper.realtimeClock
}
// MonotonicClock returns the application CLOCK_MONOTONIC clock.
func (k *Kernel) MonotonicClock() ktime.Clock {
- return k.monotonicClock
+ return k.timekeeper.monotonicClock
}
// CPUClockNow returns the current value of k.cpuClock.
@@ -1553,32 +1550,6 @@ func (k *Kernel) SetSaveError(err error) {
}
}
-var _ tcpip.Clock = (*Kernel)(nil)
-
-// Now implements tcpip.Clock.NowNanoseconds.
-func (k *Kernel) Now() time.Time {
- nsec, err := k.timekeeper.GetTime(sentrytime.Realtime)
- if err != nil {
- panic("timekeeper.GetTime(sentrytime.Realtime): " + err.Error())
- }
- return time.Unix(0, nsec)
-}
-
-// NowMonotonic implements tcpip.Clock.NowMonotonic.
-func (k *Kernel) NowMonotonic() tcpip.MonotonicTime {
- nsec, err := k.timekeeper.GetTime(sentrytime.Monotonic)
- if err != nil {
- panic("timekeeper.GetTime(sentrytime.Monotonic): " + err.Error())
- }
- var mt tcpip.MonotonicTime
- return mt.Add(time.Duration(nsec) * time.Nanosecond)
-}
-
-// AfterFunc implements tcpip.Clock.AfterFunc.
-func (k *Kernel) AfterFunc(d time.Duration, f func()) tcpip.Timer {
- return ktime.TcpipAfterFunc(k.realtimeClock, d, f)
-}
-
// SetMemoryFile sets Kernel.mf. SetMemoryFile must be called before Init or
// LoadFrom.
func (k *Kernel) SetMemoryFile(mf *pgalloc.MemoryFile) {
@@ -1861,7 +1832,9 @@ func (k *Kernel) PopulateNewCgroupHierarchy(root Cgroup) {
return
}
t.mu.Lock()
- t.enterCgroupLocked(root)
+ // A task can be in the cgroup if it has been created after the
+ // cgroup hierarchy was registered.
+ t.enterCgroupIfNotYetLocked(root)
t.mu.Unlock()
})
k.tasks.mu.RUnlock()
diff --git a/pkg/sentry/kernel/pending_signals.go b/pkg/sentry/kernel/pending_signals.go
index 77a35b788..af455c434 100644
--- a/pkg/sentry/kernel/pending_signals.go
+++ b/pkg/sentry/kernel/pending_signals.go
@@ -17,7 +17,6 @@ package kernel
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bits"
- "gvisor.dev/gvisor/pkg/sentry/arch"
)
const (
@@ -65,7 +64,7 @@ type pendingSignalQueue struct {
type pendingSignal struct {
// pendingSignalEntry links into a pendingSignalList.
pendingSignalEntry
- *arch.SignalInfo
+ *linux.SignalInfo
// If timer is not nil, it is the IntervalTimer which sent this signal.
timer *IntervalTimer
@@ -75,7 +74,7 @@ type pendingSignal struct {
// on failure (if the given signal's queue is full).
//
// Preconditions: info represents a valid signal.
-func (p *pendingSignals) enqueue(info *arch.SignalInfo, timer *IntervalTimer) bool {
+func (p *pendingSignals) enqueue(info *linux.SignalInfo, timer *IntervalTimer) bool {
sig := linux.Signal(info.Signo)
q := &p.signals[sig.Index()]
if sig.IsStandard() {
@@ -93,7 +92,7 @@ func (p *pendingSignals) enqueue(info *arch.SignalInfo, timer *IntervalTimer) bo
// dequeue dequeues and returns any pending signal not masked by mask. If no
// unmasked signals are pending, dequeue returns nil.
-func (p *pendingSignals) dequeue(mask linux.SignalSet) *arch.SignalInfo {
+func (p *pendingSignals) dequeue(mask linux.SignalSet) *linux.SignalInfo {
// "Real-time signals are delivered in a guaranteed order. Multiple
// real-time signals of the same type are delivered in the order they were
// sent. If different real-time signals are sent to a process, they are
@@ -111,7 +110,7 @@ func (p *pendingSignals) dequeue(mask linux.SignalSet) *arch.SignalInfo {
return p.dequeueSpecific(linux.Signal(lowestPendingUnblockedBit + 1))
}
-func (p *pendingSignals) dequeueSpecific(sig linux.Signal) *arch.SignalInfo {
+func (p *pendingSignals) dequeueSpecific(sig linux.Signal) *linux.SignalInfo {
q := &p.signals[sig.Index()]
ps := q.pendingSignalList.Front()
if ps == nil {
diff --git a/pkg/sentry/kernel/pending_signals_state.go b/pkg/sentry/kernel/pending_signals_state.go
index ca8b4e164..e77f1a254 100644
--- a/pkg/sentry/kernel/pending_signals_state.go
+++ b/pkg/sentry/kernel/pending_signals_state.go
@@ -14,13 +14,11 @@
package kernel
-import (
- "gvisor.dev/gvisor/pkg/sentry/arch"
-)
+import "gvisor.dev/gvisor/pkg/abi/linux"
// +stateify savable
type savedPendingSignal struct {
- si *arch.SignalInfo
+ si *linux.SignalInfo
timer *IntervalTimer
}
diff --git a/pkg/sentry/kernel/pipe/BUILD b/pkg/sentry/kernel/pipe/BUILD
index 34c617b08..94ebac7c5 100644
--- a/pkg/sentry/kernel/pipe/BUILD
+++ b/pkg/sentry/kernel/pipe/BUILD
@@ -21,6 +21,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/amutex",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/marshal/primitive",
"//pkg/safemem",
@@ -47,6 +48,7 @@ go_test(
library = ":pipe",
deps = [
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/sentry/contexttest",
"//pkg/sentry/fs",
"//pkg/syserror",
diff --git a/pkg/sentry/kernel/pipe/node.go b/pkg/sentry/kernel/pipe/node.go
index 6497dc4ba..08786d704 100644
--- a/pkg/sentry/kernel/pipe/node.go
+++ b/pkg/sentry/kernel/pipe/node.go
@@ -17,6 +17,7 @@ package pipe
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sync"
@@ -112,7 +113,7 @@ func (i *inodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.Fi
// read side isn't open yet.
if flags.NonBlocking {
w.DecRef(ctx)
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
if !waitFor(&i.mu, &i.rWakeup, ctx) {
@@ -130,10 +131,10 @@ func (i *inodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.Fi
return rw, nil
default:
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
}
func (*inodeOperations) Allocate(_ context.Context, _ *fs.Inode, _, _ int64) error {
- return syserror.EPIPE
+ return linuxerr.EPIPE
}
diff --git a/pkg/sentry/kernel/pipe/node_test.go b/pkg/sentry/kernel/pipe/node_test.go
index d6fb0fdb8..d25cf658e 100644
--- a/pkg/sentry/kernel/pipe/node_test.go
+++ b/pkg/sentry/kernel/pipe/node_test.go
@@ -19,6 +19,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/syserror"
@@ -258,7 +259,7 @@ func TestNonblockingWriteOpenFileNoReaders(t *testing.T) {
ctx := newSleeperContext(t)
f := NewInodeOperations(ctx, perms, newNamedPipe(t))
- if _, err := testOpen(ctx, t, f, fs.FileFlags{Write: true, NonBlocking: true}, nil); err != syserror.ENXIO {
+ if _, err := testOpen(ctx, t, f, fs.FileFlags{Write: true, NonBlocking: true}, nil); !linuxerr.Equals(linuxerr.ENXIO, err) {
t.Fatalf("Nonblocking open for write failed unexpected error %v.", err)
}
}
diff --git a/pkg/sentry/kernel/pipe/pipe.go b/pkg/sentry/kernel/pipe/pipe.go
index 06769931a..85e3ce9f4 100644
--- a/pkg/sentry/kernel/pipe/pipe.go
+++ b/pkg/sentry/kernel/pipe/pipe.go
@@ -22,6 +22,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -428,18 +429,18 @@ func (p *Pipe) FifoSize(context.Context, *fs.File) (int64, error) {
// SetFifoSize implements fs.FifoSizer.SetFifoSize.
func (p *Pipe) SetFifoSize(size int64) (int64, error) {
if size < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if size < MinimumPipeSize {
size = MinimumPipeSize // Per spec.
}
if size > MaximumPipeSize {
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
p.mu.Lock()
defer p.mu.Unlock()
if size < p.size {
- return 0, syserror.EBUSY
+ return 0, linuxerr.EBUSY
}
p.max = size
return size, nil
diff --git a/pkg/sentry/kernel/pipe/pipe_unsafe.go b/pkg/sentry/kernel/pipe/pipe_unsafe.go
index dd60cba24..077c5d596 100644
--- a/pkg/sentry/kernel/pipe/pipe_unsafe.go
+++ b/pkg/sentry/kernel/pipe/pipe_unsafe.go
@@ -23,6 +23,8 @@ import (
// concurrent calls cannot deadlock.
//
// Preconditions: x != y.
+// +checklocksacquire:x.mu
+// +checklocksacquire:y.mu
func lockTwoPipes(x, y *Pipe) {
// Lock the two pipes in order of increasing address.
if uintptr(unsafe.Pointer(x)) < uintptr(unsafe.Pointer(y)) {
diff --git a/pkg/sentry/kernel/pipe/pipe_util.go b/pkg/sentry/kernel/pipe/pipe_util.go
index 24e467e93..c883a9014 100644
--- a/pkg/sentry/kernel/pipe/pipe_util.go
+++ b/pkg/sentry/kernel/pipe/pipe_util.go
@@ -22,6 +22,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/amutex"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -86,7 +87,7 @@ func (p *Pipe) Write(ctx context.Context, src usermem.IOSequence) (int64, error)
if n > 0 {
p.Notify(waiter.ReadableEvents)
}
- if err == unix.EPIPE {
+ if linuxerr.Equals(linuxerr.EPIPE, err) {
// If we are returning EPIPE send SIGPIPE to the task.
if sendSig := linux.SignalNoInfoFuncFromContext(ctx); sendSig != nil {
sendSig(linux.SIGPIPE)
@@ -135,7 +136,7 @@ func (p *Pipe) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArgume
v = math.MaxInt32 // Silently truncate.
}
// Copy result to userspace.
- iocc := primitive.IOCopyContext{
+ iocc := usermem.IOCopyContext{
IO: io,
Ctx: ctx,
Opts: usermem.IOOpts{
@@ -156,6 +157,7 @@ func (p *Pipe) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArgume
//
// mu must be held by the caller. waitFor returns with mu held, but it will
// drop mu before blocking for any reader/writers.
+// +checklocks:mu
func waitFor(mu *sync.Mutex, wakeupChan *chan struct{}, sleeper amutex.Sleeper) bool {
// Ideally this function would simply use a condition variable. However, the
// wait needs to be interruptible via 'sleeper', so we must sychronize via a
diff --git a/pkg/sentry/kernel/pipe/vfs.go b/pkg/sentry/kernel/pipe/vfs.go
index 95b948edb..077d5fd7f 100644
--- a/pkg/sentry/kernel/pipe/vfs.go
+++ b/pkg/sentry/kernel/pipe/vfs.go
@@ -17,6 +17,7 @@ package pipe
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -79,7 +80,7 @@ func (vp *VFSPipe) ReaderWriterPair(ctx context.Context, mnt *vfs.Mount, vfsd *v
// Allocate implements vfs.FileDescriptionImpl.Allocate.
func (*VFSPipe) Allocate(context.Context, uint64, uint64, uint64) error {
- return syserror.ESPIPE
+ return linuxerr.ESPIPE
}
// Open opens the pipe represented by vp.
@@ -90,7 +91,7 @@ func (vp *VFSPipe) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, s
readable := vfs.MayReadFileWithOpenFlags(statusFlags)
writable := vfs.MayWriteFileWithOpenFlags(statusFlags)
if !readable && !writable {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
fd, err := vp.newFD(mnt, vfsd, statusFlags, locks)
@@ -131,7 +132,7 @@ func (vp *VFSPipe) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, s
// side isn't open yet.
if statusFlags&linux.O_NONBLOCK != 0 {
fd.DecRef(ctx)
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
// Wait for a reader to open the other end.
if !waitFor(&vp.mu, &vp.rWakeup, ctx) {
@@ -224,7 +225,7 @@ func (fd *VFSPipeFD) Readiness(mask waiter.EventMask) waiter.EventMask {
// Allocate implements vfs.FileDescriptionImpl.Allocate.
func (fd *VFSPipeFD) Allocate(ctx context.Context, mode, offset, length uint64) error {
- return syserror.ESPIPE
+ return linuxerr.ESPIPE
}
// EventRegister implements waiter.Waitable.EventRegister.
@@ -415,7 +416,7 @@ func Tee(ctx context.Context, dst, src *VFSPipeFD, count int64) (int64, error) {
// Preconditions: count > 0.
func spliceOrTee(ctx context.Context, dst, src *VFSPipeFD, count int64, removeFromSrc bool) (int64, error) {
if dst.pipe == src.pipe {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
lockTwoPipes(dst.pipe, src.pipe)
diff --git a/pkg/sentry/kernel/posixtimer.go b/pkg/sentry/kernel/posixtimer.go
index 2e861a5a8..319754a42 100644
--- a/pkg/sentry/kernel/posixtimer.go
+++ b/pkg/sentry/kernel/posixtimer.go
@@ -18,9 +18,8 @@ import (
"math"
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/sentry/arch"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
- "gvisor.dev/gvisor/pkg/syserror"
)
// IntervalTimer represents a POSIX interval timer as described by
@@ -97,7 +96,7 @@ func (it *IntervalTimer) ResumeTimer() {
}
// Preconditions: it.target's signal mutex must be locked.
-func (it *IntervalTimer) updateDequeuedSignalLocked(si *arch.SignalInfo) {
+func (it *IntervalTimer) updateDequeuedSignalLocked(si *linux.SignalInfo) {
it.sigpending = false
if it.sigorphan {
return
@@ -138,9 +137,9 @@ func (it *IntervalTimer) Notify(exp uint64, setting ktime.Setting) (ktime.Settin
it.sigpending = true
it.sigorphan = false
it.overrunCur += exp - 1
- si := &arch.SignalInfo{
+ si := &linux.SignalInfo{
Signo: int32(it.signo),
- Code: arch.SignalInfoTimer,
+ Code: linux.SI_TIMER,
}
si.SetTimerID(it.id)
si.SetSigval(it.sigval)
@@ -176,7 +175,7 @@ func (t *Task) IntervalTimerCreate(c ktime.Clock, sigev *linux.Sigevent) (linux.
break
}
if t.tg.nextTimerID == end {
- return 0, syserror.EAGAIN
+ return 0, linuxerr.EAGAIN
}
}
@@ -215,16 +214,16 @@ func (t *Task) IntervalTimerCreate(c ktime.Clock, sigev *linux.Sigevent) (linux.
target, ok := t.tg.pidns.tasks[ThreadID(sigev.Tid)]
t.tg.pidns.owner.mu.RUnlock()
if !ok || target.tg != t.tg {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
it.target = target
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if sigev.Notify != linux.SIGEV_NONE {
it.signo = linux.Signal(sigev.Signo)
if !it.signo.IsValid() {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
it.timer = ktime.NewTimer(c, it)
@@ -239,7 +238,7 @@ func (t *Task) IntervalTimerDelete(id linux.TimerID) error {
defer t.tg.timerMu.Unlock()
it := t.tg.timers[id]
if it == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
delete(t.tg.timers, id)
it.DestroyTimer()
@@ -252,7 +251,7 @@ func (t *Task) IntervalTimerSettime(id linux.TimerID, its linux.Itimerspec, abs
defer t.tg.timerMu.Unlock()
it := t.tg.timers[id]
if it == nil {
- return linux.Itimerspec{}, syserror.EINVAL
+ return linux.Itimerspec{}, linuxerr.EINVAL
}
newS, err := ktime.SettingFromItimerspec(its, abs, it.timer.Clock())
@@ -270,7 +269,7 @@ func (t *Task) IntervalTimerGettime(id linux.TimerID) (linux.Itimerspec, error)
defer t.tg.timerMu.Unlock()
it := t.tg.timers[id]
if it == nil {
- return linux.Itimerspec{}, syserror.EINVAL
+ return linux.Itimerspec{}, linuxerr.EINVAL
}
tm, s := it.timer.Get()
@@ -286,7 +285,7 @@ func (t *Task) IntervalTimerGetoverrun(id linux.TimerID) (int32, error) {
defer t.tg.timerMu.Unlock()
it := t.tg.timers[id]
if it == nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// By timer_create(2) invariant, either it.target == nil (in which case
// it.overrunLast is immutably 0) or t.tg == it.target.tg; and the fact
diff --git a/pkg/sentry/kernel/ptrace.go b/pkg/sentry/kernel/ptrace.go
index 57c7659e7..161140980 100644
--- a/pkg/sentry/kernel/ptrace.go
+++ b/pkg/sentry/kernel/ptrace.go
@@ -19,9 +19,9 @@ import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/mm"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
@@ -295,7 +295,7 @@ func (t *Task) isYAMADescendantOfLocked(ancestor *Task) bool {
// Precondition: the TaskSet mutex must be locked (for reading or writing).
func (t *Task) hasYAMAExceptionForLocked(tracer *Task) bool {
- allowed, ok := t.k.ptraceExceptions[t]
+ allowed, ok := t.k.ptraceExceptions[t.tg.leader]
if !ok {
return false
}
@@ -394,7 +394,7 @@ func (t *Task) ptraceTrapLocked(code int32) {
t.trapStopPending = false
t.tg.signalHandlers.mu.Unlock()
t.ptraceCode = code
- t.ptraceSiginfo = &arch.SignalInfo{
+ t.ptraceSiginfo = &linux.SignalInfo{
Signo: int32(linux.SIGTRAP),
Code: code,
}
@@ -402,7 +402,7 @@ func (t *Task) ptraceTrapLocked(code int32) {
t.ptraceSiginfo.SetUID(int32(t.Credentials().RealKUID.In(t.UserNamespace()).OrOverflow()))
if t.beginPtraceStopLocked() {
tracer := t.Tracer()
- tracer.signalStop(t, arch.CLD_TRAPPED, int32(linux.SIGTRAP))
+ tracer.signalStop(t, linux.CLD_TRAPPED, int32(linux.SIGTRAP))
tracer.tg.eventQueue.Notify(EventTraceeStop)
}
}
@@ -482,7 +482,7 @@ func (t *Task) ptraceTraceme() error {
t.tg.pidns.owner.mu.Lock()
defer t.tg.pidns.owner.mu.Unlock()
if t.hasTracer() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if t.parent == nil {
// In Linux, only init can not have a parent, and init is assumed never
@@ -498,7 +498,7 @@ func (t *Task) ptraceTraceme() error {
return nil
}
if !t.parent.canTraceLocked(t, true) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if t.parent.exitState != TaskExitNone {
// Fail silently, as if we were successfully attached but then
@@ -514,21 +514,21 @@ func (t *Task) ptraceTraceme() error {
// ptrace(PTRACE_SEIZE, target, 0, opts) if seize is true. t is the caller.
func (t *Task) ptraceAttach(target *Task, seize bool, opts uintptr) error {
if t.tg == target.tg {
- return syserror.EPERM
+ return linuxerr.EPERM
}
t.tg.pidns.owner.mu.Lock()
defer t.tg.pidns.owner.mu.Unlock()
if !t.canTraceLocked(target, true) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if target.hasTracer() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Attaching to zombies and dead tasks is not permitted; the exit
// notification logic relies on this. Linux allows attaching to PF_EXITING
// tasks, though.
if target.exitState >= TaskExitZombie {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if seize {
if err := target.ptraceSetOptionsLocked(opts); err != nil {
@@ -542,9 +542,9 @@ func (t *Task) ptraceAttach(target *Task, seize bool, opts uintptr) error {
// "Unlike PTRACE_ATTACH, PTRACE_SEIZE does not stop the process." -
// ptrace(2)
if !seize {
- target.sendSignalLocked(&arch.SignalInfo{
+ target.sendSignalLocked(&linux.SignalInfo{
Signo: int32(linux.SIGSTOP),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
}, false /* group */)
}
// Undocumented Linux feature: If the tracee is already group-stopped (and
@@ -586,7 +586,7 @@ func (t *Task) exitPtrace() {
for target := range t.ptraceTracees {
if target.ptraceOpts.ExitKill {
target.tg.signalHandlers.mu.Lock()
- target.sendSignalLocked(&arch.SignalInfo{
+ target.sendSignalLocked(&linux.SignalInfo{
Signo: int32(linux.SIGKILL),
}, false /* group */)
target.tg.signalHandlers.mu.Unlock()
@@ -652,7 +652,8 @@ func (t *Task) forgetTracerLocked() {
// Preconditions:
// * The signal mutex must be locked.
// * The caller must be running on the task goroutine.
-func (t *Task) ptraceSignalLocked(info *arch.SignalInfo) bool {
+// +checklocks:t.tg.signalHandlers.mu
+func (t *Task) ptraceSignalLocked(info *linux.SignalInfo) bool {
if linux.Signal(info.Signo) == linux.SIGKILL {
return false
}
@@ -678,7 +679,7 @@ func (t *Task) ptraceSignalLocked(info *arch.SignalInfo) bool {
t.ptraceSiginfo = info
t.Debugf("Entering signal-delivery-stop for signal %d", info.Signo)
if t.beginPtraceStopLocked() {
- tracer.signalStop(t, arch.CLD_TRAPPED, info.Signo)
+ tracer.signalStop(t, linux.CLD_TRAPPED, info.Signo)
tracer.tg.eventQueue.Notify(EventTraceeStop)
}
return true
@@ -829,7 +830,7 @@ func (t *Task) ptraceClone(kind ptraceCloneKind, child *Task, opts *CloneOptions
if child.ptraceSeized {
child.trapStopPending = true
} else {
- child.pendingSignals.enqueue(&arch.SignalInfo{
+ child.pendingSignals.enqueue(&linux.SignalInfo{
Signo: int32(linux.SIGSTOP),
}, nil)
}
@@ -893,9 +894,9 @@ func (t *Task) ptraceExec(oldTID ThreadID) {
}
t.tg.signalHandlers.mu.Lock()
defer t.tg.signalHandlers.mu.Unlock()
- t.sendSignalLocked(&arch.SignalInfo{
+ t.sendSignalLocked(&linux.SignalInfo{
Signo: int32(linux.SIGTRAP),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
}, false /* group */)
}
@@ -995,7 +996,7 @@ func (t *Task) ptraceSetOptionsLocked(opts uintptr) error {
linux.PTRACE_O_TRACEVFORK |
linux.PTRACE_O_TRACEVFORKDONE)
if opts&^valid != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
t.ptraceOpts = ptraceOptions{
ExitKill: opts&linux.PTRACE_O_EXITKILL != 0,
@@ -1222,27 +1223,27 @@ func (t *Task) Ptrace(req int64, pid ThreadID, addr, data hostarch.Addr) error {
t.tg.pidns.owner.mu.RLock()
defer t.tg.pidns.owner.mu.RUnlock()
if target.ptraceSiginfo == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
_, err := target.ptraceSiginfo.CopyOut(t, data)
return err
case linux.PTRACE_SETSIGINFO:
- var info arch.SignalInfo
+ var info linux.SignalInfo
if _, err := info.CopyIn(t, data); err != nil {
return err
}
t.tg.pidns.owner.mu.RLock()
defer t.tg.pidns.owner.mu.RUnlock()
if target.ptraceSiginfo == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
target.ptraceSiginfo = &info
return nil
case linux.PTRACE_GETSIGMASK:
if addr != linux.SignalSetSize {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
mask := target.SignalMask()
_, err := mask.CopyOut(t, data)
@@ -1250,7 +1251,7 @@ func (t *Task) Ptrace(req int64, pid ThreadID, addr, data hostarch.Addr) error {
case linux.PTRACE_SETSIGMASK:
if addr != linux.SignalSetSize {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
var mask linux.SignalSet
if _, err := mask.CopyIn(t, data); err != nil {
diff --git a/pkg/sentry/kernel/rseq.go b/pkg/sentry/kernel/rseq.go
index 4bc5bca44..5e0618e44 100644
--- a/pkg/sentry/kernel/rseq.go
+++ b/pkg/sentry/kernel/rseq.go
@@ -18,6 +18,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/hostcpu"
"gvisor.dev/gvisor/pkg/syserror"
@@ -59,20 +60,20 @@ func (t *Task) RSeqAvailable() bool {
func (t *Task) SetRSeq(addr hostarch.Addr, length, signature uint32) error {
if t.rseqAddr != 0 {
if t.rseqAddr != addr {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if t.rseqSignature != signature {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
// rseq must be aligned and correctly sized.
if addr&(linux.AlignOfRSeq-1) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if length != linux.SizeOfRSeq {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if _, ok := t.MemoryManager().CheckIORange(addr, linux.SizeOfRSeq); !ok {
return syserror.EFAULT
@@ -103,16 +104,16 @@ func (t *Task) SetRSeq(addr hostarch.Addr, length, signature uint32) error {
// Preconditions: The caller must be running on the task goroutine.
func (t *Task) ClearRSeq(addr hostarch.Addr, length, signature uint32) error {
if t.rseqAddr == 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if t.rseqAddr != addr {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if length != linux.SizeOfRSeq {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if t.rseqSignature != signature {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := t.rseqClearCPU(); err != nil {
@@ -152,10 +153,10 @@ func (t *Task) SetOldRSeqCriticalRegion(r OldRSeqCriticalRegion) error {
return nil
}
if r.CriticalSection.Start >= r.CriticalSection.End {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if r.CriticalSection.Contains(r.Restart) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// TODO(jamieliu): check that r.CriticalSection and r.Restart are in
// the application address range, for consistency with Linux.
@@ -187,7 +188,7 @@ func (t *Task) SetOldRSeqCPUAddr(addr hostarch.Addr) error {
// unfortunate, but unlikely in a correct program.
if err := t.rseqUpdateCPU(); err != nil {
t.oldRSeqCPUAddr = 0
- return syserror.EINVAL // yes, EINVAL, not err or EFAULT
+ return linuxerr.EINVAL // yes, EINVAL, not err or EFAULT
}
return nil
}
diff --git a/pkg/sentry/kernel/seccomp.go b/pkg/sentry/kernel/seccomp.go
index a95e174a2..54ca43c2e 100644
--- a/pkg/sentry/kernel/seccomp.go
+++ b/pkg/sentry/kernel/seccomp.go
@@ -39,11 +39,11 @@ func dataAsBPFInput(t *Task, d *linux.SeccompData) bpf.Input {
}
}
-func seccompSiginfo(t *Task, errno, sysno int32, ip hostarch.Addr) *arch.SignalInfo {
- si := &arch.SignalInfo{
+func seccompSiginfo(t *Task, errno, sysno int32, ip hostarch.Addr) *linux.SignalInfo {
+ si := &linux.SignalInfo{
Signo: int32(linux.SIGSYS),
Errno: errno,
- Code: arch.SYS_SECCOMP,
+ Code: linux.SYS_SECCOMP,
}
si.SetCallAddr(uint64(ip))
si.SetSyscall(sysno)
diff --git a/pkg/sentry/kernel/semaphore/BUILD b/pkg/sentry/kernel/semaphore/BUILD
index 65e5427c1..a787c00a8 100644
--- a/pkg/sentry/kernel/semaphore/BUILD
+++ b/pkg/sentry/kernel/semaphore/BUILD
@@ -25,6 +25,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/sentry/fs",
"//pkg/sentry/kernel/auth",
diff --git a/pkg/sentry/kernel/semaphore/semaphore.go b/pkg/sentry/kernel/semaphore/semaphore.go
index 1d9edf118..067cc75c1 100644
--- a/pkg/sentry/kernel/semaphore/semaphore.go
+++ b/pkg/sentry/kernel/semaphore/semaphore.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -127,7 +128,7 @@ func NewRegistry(userNS *auth.UserNamespace) *Registry {
// exists.
func (r *Registry) FindOrCreate(ctx context.Context, key, nsems int32, mode linux.FileMode, private, create, exclusive bool) (*Set, error) {
if nsems < 0 || nsems > semsMax {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
r.mu.Lock()
@@ -142,12 +143,12 @@ func (r *Registry) FindOrCreate(ctx context.Context, key, nsems int32, mode linu
// Check that caller can access semaphore set.
creds := auth.CredentialsFromContext(ctx)
if !set.checkPerms(creds, fs.PermsFromMode(mode)) {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
// Validate parameters.
if nsems > int32(set.Size()) {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
if create && exclusive {
return nil, syserror.EEXIST
@@ -163,7 +164,7 @@ func (r *Registry) FindOrCreate(ctx context.Context, key, nsems int32, mode linu
// Zero is only valid if an existing set is found.
if nsems == 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
// Apply system limits.
@@ -171,10 +172,10 @@ func (r *Registry) FindOrCreate(ctx context.Context, key, nsems int32, mode linu
// Map semaphores and map indexes in a registry are of the same size,
// check map semaphores only here for the system limit.
if len(r.semaphores) >= setsMax {
- return nil, syserror.EINVAL
+ return nil, syserror.ENOSPC
}
if r.totalSems() > int(semsTotalMax-nsems) {
- return nil, syserror.EINVAL
+ return nil, syserror.ENOSPC
}
// Finally create a new set.
@@ -238,7 +239,7 @@ func (r *Registry) RemoveID(id int32, creds *auth.Credentials) error {
set := r.semaphores[id]
if set == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
index, found := r.findIndexByID(id)
if !found {
@@ -252,7 +253,7 @@ func (r *Registry) RemoveID(id int32, creds *auth.Credentials) error {
// "The effective user ID of the calling process must match the creator or
// owner of the semaphore set, or the caller must be privileged."
if !set.checkCredentials(creds) && !set.checkCapability(creds) {
- return syserror.EACCES
+ return linuxerr.EACCES
}
delete(r.semaphores, set.ID)
@@ -370,7 +371,7 @@ func (s *Set) Change(ctx context.Context, creds *auth.Credentials, owner fs.File
// "The effective UID of the calling process must match the owner or creator
// of the semaphore set, or the caller must be privileged."
if !s.checkCredentials(creds) && !s.checkCapability(creds) {
- return syserror.EACCES
+ return linuxerr.EACCES
}
s.owner = owner
@@ -395,7 +396,7 @@ func (s *Set) semStat(creds *auth.Credentials, permMask fs.PermMask) (*linux.Sem
defer s.mu.Unlock()
if !s.checkPerms(creds, permMask) {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
return &linux.SemidDS{
@@ -425,7 +426,7 @@ func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Cred
// "The calling process must have alter permission on the semaphore set."
if !s.checkPerms(creds, fs.PermMask{Write: true}) {
- return syserror.EACCES
+ return linuxerr.EACCES
}
sem := s.findSem(num)
@@ -461,7 +462,7 @@ func (s *Set) SetValAll(ctx context.Context, vals []uint16, creds *auth.Credenti
// "The calling process must have alter permission on the semaphore set."
if !s.checkPerms(creds, fs.PermMask{Write: true}) {
- return syserror.EACCES
+ return linuxerr.EACCES
}
for i, val := range vals {
@@ -483,7 +484,7 @@ func (s *Set) GetVal(num int32, creds *auth.Credentials) (int16, error) {
// "The calling process must have read permission on the semaphore set."
if !s.checkPerms(creds, fs.PermMask{Read: true}) {
- return 0, syserror.EACCES
+ return 0, linuxerr.EACCES
}
sem := s.findSem(num)
@@ -500,7 +501,7 @@ func (s *Set) GetValAll(creds *auth.Credentials) ([]uint16, error) {
// "The calling process must have read permission on the semaphore set."
if !s.checkPerms(creds, fs.PermMask{Read: true}) {
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
vals := make([]uint16, s.Size())
@@ -517,7 +518,7 @@ func (s *Set) GetPID(num int32, creds *auth.Credentials) (int32, error) {
// "The calling process must have read permission on the semaphore set."
if !s.checkPerms(creds, fs.PermMask{Read: true}) {
- return 0, syserror.EACCES
+ return 0, linuxerr.EACCES
}
sem := s.findSem(num)
@@ -533,7 +534,7 @@ func (s *Set) countWaiters(num int32, creds *auth.Credentials, pred func(w *wait
// The calling process must have read permission on the semaphore set.
if !s.checkPerms(creds, fs.PermMask{Read: true}) {
- return 0, syserror.EACCES
+ return 0, linuxerr.EACCES
}
sem := s.findSem(num)
@@ -581,7 +582,7 @@ func (s *Set) ExecuteOps(ctx context.Context, ops []linux.Sembuf, creds *auth.Cr
readOnly := true
for _, op := range ops {
if s.findSem(int32(op.SemNum)) == nil {
- return nil, 0, syserror.EFBIG
+ return nil, 0, linuxerr.EFBIG
}
if op.SemOp != 0 {
readOnly = false
@@ -589,7 +590,7 @@ func (s *Set) ExecuteOps(ctx context.Context, ops []linux.Sembuf, creds *auth.Cr
}
if !s.checkPerms(creds, fs.PermMask{Read: readOnly, Write: !readOnly}) {
- return nil, 0, syserror.EACCES
+ return nil, 0, linuxerr.EACCES
}
ch, num, err := s.executeOps(ctx, ops, pid)
diff --git a/pkg/sentry/kernel/sessions.go b/pkg/sentry/kernel/sessions.go
index 0cd9e2533..ae21a55da 100644
--- a/pkg/sentry/kernel/sessions.go
+++ b/pkg/sentry/kernel/sessions.go
@@ -16,7 +16,7 @@ package kernel
import (
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/sentry/arch"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -121,8 +121,9 @@ func (pg *ProcessGroup) Originator() *ThreadGroup {
// IsOrphan returns true if this process group is an orphan.
func (pg *ProcessGroup) IsOrphan() bool {
- pg.originator.TaskSet().mu.RLock()
- defer pg.originator.TaskSet().mu.RUnlock()
+ ts := pg.originator.TaskSet()
+ ts.mu.RLock()
+ defer ts.mu.RUnlock()
return pg.ancestors == 0
}
@@ -233,7 +234,7 @@ func (pg *ProcessGroup) Session() *Session {
// SendSignal sends a signal to all processes inside the process group. It is
// analagous to kernel/signal.c:kill_pgrp.
-func (pg *ProcessGroup) SendSignal(info *arch.SignalInfo) error {
+func (pg *ProcessGroup) SendSignal(info *linux.SignalInfo) error {
tasks := pg.originator.TaskSet()
tasks.mu.RLock()
defer tasks.mu.RUnlock()
@@ -278,14 +279,14 @@ func (tg *ThreadGroup) createSession() error {
continue
}
if s.leader == tg {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if s.id == SessionID(id) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
for pg := s.processGroups.Front(); pg != nil; pg = pg.Next() {
if pg.id == ProcessGroupID(id) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
}
@@ -370,17 +371,22 @@ func (tg *ThreadGroup) CreateProcessGroup() error {
// Get the ID for this thread in the current namespace.
id := tg.pidns.tgids[tg]
+ // Check whether a process still exists or not.
+ if id == 0 {
+ return syserror.ESRCH
+ }
+
// Per above, check for a Session leader or existing group.
for s := tg.pidns.owner.sessions.Front(); s != nil; s = s.Next() {
if s.leader.pidns != tg.pidns {
continue
}
if s.leader == tg {
- return syserror.EPERM
+ return linuxerr.EPERM
}
for pg := s.processGroups.Front(); pg != nil; pg = pg.Next() {
if pg.id == ProcessGroupID(id) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
}
@@ -438,17 +444,17 @@ func (tg *ThreadGroup) JoinProcessGroup(pidns *PIDNamespace, pgid ProcessGroupID
// Lookup the ProcessGroup.
pg := pidns.processGroups[pgid]
if pg == nil {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Disallow the join if an execve has performed, per POSIX.
if checkExec && tg.execed {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// See if it's in the same session as ours.
if pg.session != tg.processGroup.session {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Join the group; adjust children.
diff --git a/pkg/sentry/kernel/shm/BUILD b/pkg/sentry/kernel/shm/BUILD
index 1c3c0794f..5b69333fe 100644
--- a/pkg/sentry/kernel/shm/BUILD
+++ b/pkg/sentry/kernel/shm/BUILD
@@ -28,6 +28,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/refs",
diff --git a/pkg/sentry/kernel/shm/shm.go b/pkg/sentry/kernel/shm/shm.go
index a73f1bdca..9c66b90ef 100644
--- a/pkg/sentry/kernel/shm/shm.go
+++ b/pkg/sentry/kernel/shm/shm.go
@@ -38,6 +38,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -145,7 +146,7 @@ func (r *Registry) FindOrCreate(ctx context.Context, pid int32, key Key, size ui
//
// Note that 'private' always implies the creation of a new segment
// whether IPC_CREAT is specified or not.
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
r.mu.Lock()
@@ -169,13 +170,13 @@ func (r *Registry) FindOrCreate(ctx context.Context, pid int32, key Key, size ui
// memory segment, and does not have the CAP_IPC_OWNER
// capability in the user namespace that governs its IPC
// namespace." - man shmget(2)
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
if size > shm.size {
// "A segment for the given key exists, but size is greater than
// the size of that segment." - man shmget(2)
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
if create && exclusive {
@@ -200,7 +201,7 @@ func (r *Registry) FindOrCreate(ctx context.Context, pid int32, key Key, size ui
if val, ok := hostarch.Addr(size).RoundUp(); ok {
sizeAligned = uint64(val)
} else {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
if numPages := sizeAligned / hostarch.PageSize; r.totalPages+numPages > linux.SHMALL {
@@ -558,7 +559,7 @@ func (s *Shm) ConfigureAttach(ctx context.Context, addr hostarch.Addr, opts Atta
// "The calling process does not have the required permissions for the
// requested attach type, and does not have the CAP_IPC_OWNER capability
// in the user namespace that governs its IPC namespace." - man shmat(2)
- return memmap.MMapOpts{}, syserror.EACCES
+ return memmap.MMapOpts{}, linuxerr.EACCES
}
return memmap.MMapOpts{
Length: s.size,
@@ -595,7 +596,7 @@ func (s *Shm) IPCStat(ctx context.Context) (*linux.ShmidDS, error) {
// read access for shmid, and the calling process does not have the
// CAP_IPC_OWNER capability in the user namespace that governs its IPC
// namespace." - man shmctl(2)
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
var mode uint16
@@ -645,14 +646,14 @@ func (s *Shm) Set(ctx context.Context, ds *linux.ShmidDS) error {
defer s.mu.Unlock()
if !s.checkOwnership(ctx) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
creds := auth.CredentialsFromContext(ctx)
uid := creds.UserNamespace.MapToKUID(auth.UID(ds.ShmPerm.UID))
gid := creds.UserNamespace.MapToKGID(auth.GID(ds.ShmPerm.GID))
if !uid.Ok() || !gid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// User may only modify the lower 9 bits of the mode. All the other bits are
diff --git a/pkg/sentry/kernel/signal.go b/pkg/sentry/kernel/signal.go
index 2488ae7d5..e08474d25 100644
--- a/pkg/sentry/kernel/signal.go
+++ b/pkg/sentry/kernel/signal.go
@@ -19,7 +19,6 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/log"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/platform"
)
@@ -36,7 +35,7 @@ const SignalPanic = linux.SIGUSR2
// context is used only for debugging to differentiate these cases.
//
// Preconditions: Kernel must have an init process.
-func (k *Kernel) sendExternalSignal(info *arch.SignalInfo, context string) {
+func (k *Kernel) sendExternalSignal(info *linux.SignalInfo, context string) {
switch linux.Signal(info.Signo) {
case linux.SIGURG:
// Sent by the Go 1.14+ runtime for asynchronous goroutine preemption.
@@ -60,18 +59,18 @@ func (k *Kernel) sendExternalSignal(info *arch.SignalInfo, context string) {
}
// SignalInfoPriv returns a SignalInfo equivalent to Linux's SEND_SIG_PRIV.
-func SignalInfoPriv(sig linux.Signal) *arch.SignalInfo {
- return &arch.SignalInfo{
+func SignalInfoPriv(sig linux.Signal) *linux.SignalInfo {
+ return &linux.SignalInfo{
Signo: int32(sig),
- Code: arch.SignalInfoKernel,
+ Code: linux.SI_KERNEL,
}
}
// SignalInfoNoInfo returns a SignalInfo equivalent to Linux's SEND_SIG_NOINFO.
-func SignalInfoNoInfo(sig linux.Signal, sender, receiver *Task) *arch.SignalInfo {
- info := &arch.SignalInfo{
+func SignalInfoNoInfo(sig linux.Signal, sender, receiver *Task) *linux.SignalInfo {
+ info := &linux.SignalInfo{
Signo: int32(sig),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
}
info.SetPID(int32(receiver.tg.pidns.IDOfThreadGroup(sender.tg)))
info.SetUID(int32(sender.Credentials().RealKUID.In(receiver.UserNamespace()).OrOverflow()))
diff --git a/pkg/sentry/kernel/signal_handlers.go b/pkg/sentry/kernel/signal_handlers.go
index 768fda220..147cc41bb 100644
--- a/pkg/sentry/kernel/signal_handlers.go
+++ b/pkg/sentry/kernel/signal_handlers.go
@@ -16,7 +16,6 @@ package kernel
import (
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sync"
)
@@ -30,14 +29,14 @@ type SignalHandlers struct {
mu sync.Mutex `state:"nosave"`
// actions is the action to be taken upon receiving each signal.
- actions map[linux.Signal]arch.SignalAct
+ actions map[linux.Signal]linux.SigAction
}
// NewSignalHandlers returns a new SignalHandlers specifying all default
// actions.
func NewSignalHandlers() *SignalHandlers {
return &SignalHandlers{
- actions: make(map[linux.Signal]arch.SignalAct),
+ actions: make(map[linux.Signal]linux.SigAction),
}
}
@@ -59,9 +58,9 @@ func (sh *SignalHandlers) CopyForExec() *SignalHandlers {
sh.mu.Lock()
defer sh.mu.Unlock()
for sig, act := range sh.actions {
- if act.Handler == arch.SignalActIgnore {
- sh2.actions[sig] = arch.SignalAct{
- Handler: arch.SignalActIgnore,
+ if act.Handler == linux.SIG_IGN {
+ sh2.actions[sig] = linux.SigAction{
+ Handler: linux.SIG_IGN,
}
}
}
@@ -73,15 +72,15 @@ func (sh *SignalHandlers) IsIgnored(sig linux.Signal) bool {
sh.mu.Lock()
defer sh.mu.Unlock()
sa, ok := sh.actions[sig]
- return ok && sa.Handler == arch.SignalActIgnore
+ return ok && sa.Handler == linux.SIG_IGN
}
// dequeueActionLocked returns the SignalAct that should be used to handle sig.
//
// Preconditions: sh.mu must be locked.
-func (sh *SignalHandlers) dequeueAction(sig linux.Signal) arch.SignalAct {
+func (sh *SignalHandlers) dequeueAction(sig linux.Signal) linux.SigAction {
act := sh.actions[sig]
- if act.IsResetHandler() {
+ if act.Flags&linux.SA_RESETHAND != 0 {
delete(sh.actions, sig)
}
return act
diff --git a/pkg/sentry/kernel/signalfd/BUILD b/pkg/sentry/kernel/signalfd/BUILD
index 76d472292..1110ecca5 100644
--- a/pkg/sentry/kernel/signalfd/BUILD
+++ b/pkg/sentry/kernel/signalfd/BUILD
@@ -9,6 +9,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/sentry/fs",
"//pkg/sentry/fs/anon",
"//pkg/sentry/fs/fsutil",
diff --git a/pkg/sentry/kernel/signalfd/signalfd.go b/pkg/sentry/kernel/signalfd/signalfd.go
index f58ec4194..47958e2d4 100644
--- a/pkg/sentry/kernel/signalfd/signalfd.go
+++ b/pkg/sentry/kernel/signalfd/signalfd.go
@@ -18,6 +18,7 @@ package signalfd
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/anon"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
@@ -64,7 +65,7 @@ func New(ctx context.Context, mask linux.SignalSet) (*fs.File, error) {
t := kernel.TaskFromContext(ctx)
if t == nil {
// No task context? Not valid.
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
// name matches fs/signalfd.c:signalfd4.
dirent := fs.NewDirent(ctx, anon.NewInode(ctx), "anon_inode:[signalfd]")
diff --git a/pkg/sentry/kernel/task.go b/pkg/sentry/kernel/task.go
index be1371855..d211e4d82 100644
--- a/pkg/sentry/kernel/task.go
+++ b/pkg/sentry/kernel/task.go
@@ -21,8 +21,8 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bpf"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -33,7 +33,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/usage"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -151,7 +150,7 @@ type Task struct {
// which the SA_ONSTACK flag is set.
//
// signalStack is exclusive to the task goroutine.
- signalStack arch.SignalStack
+ signalStack linux.SignalStack
// signalQueue is a set of registered waiters for signal-related events.
//
@@ -395,7 +394,7 @@ type Task struct {
// ptraceSiginfo is analogous to Linux's task_struct::last_siginfo.
//
// ptraceSiginfo is protected by the TaskSet mutex.
- ptraceSiginfo *arch.SignalInfo
+ ptraceSiginfo *linux.SignalInfo
// ptraceEventMsg is the value set by PTRACE_EVENT stops and returned to
// the tracer by ptrace(PTRACE_GETEVENTMSG).
@@ -847,21 +846,19 @@ func (t *Task) OOMScoreAdj() int32 {
// value should be between -1000 and 1000 inclusive.
func (t *Task) SetOOMScoreAdj(adj int32) error {
if adj > 1000 || adj < -1000 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
atomic.StoreInt32(&t.tg.oomScoreAdj, adj)
return nil
}
-// UID returns t's uid.
-// TODO(gvisor.dev/issue/170): This method is not namespaced yet.
-func (t *Task) UID() uint32 {
+// KUID returns t's kuid.
+func (t *Task) KUID() uint32 {
return uint32(t.Credentials().EffectiveKUID)
}
-// GID returns t's gid.
-// TODO(gvisor.dev/issue/170): This method is not namespaced yet.
-func (t *Task) GID() uint32 {
+// KGID returns t's kgid.
+func (t *Task) KGID() uint32 {
return uint32(t.Credentials().EffectiveKGID)
}
diff --git a/pkg/sentry/kernel/task_acct.go b/pkg/sentry/kernel/task_acct.go
index e574997f7..dd364ae50 100644
--- a/pkg/sentry/kernel/task_acct.go
+++ b/pkg/sentry/kernel/task_acct.go
@@ -18,10 +18,10 @@ package kernel
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/sentry/usage"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Getitimer implements getitimer(2).
@@ -44,7 +44,7 @@ func (t *Task) Getitimer(id int32) (linux.ItimerVal, error) {
s, _ = t.tg.itimerProfSetting.At(tm)
t.tg.signalHandlers.mu.Unlock()
default:
- return linux.ItimerVal{}, syserror.EINVAL
+ return linux.ItimerVal{}, linuxerr.EINVAL
}
val, iv := ktime.SpecFromSetting(tm, s)
return linux.ItimerVal{
@@ -105,7 +105,7 @@ func (t *Task) Setitimer(id int32, newitv linux.ItimerVal) (linux.ItimerVal, err
return linux.ItimerVal{}, err
}
default:
- return linux.ItimerVal{}, syserror.EINVAL
+ return linux.ItimerVal{}, linuxerr.EINVAL
}
oldval, oldiv := ktime.SpecFromSetting(tm, olds)
return linux.ItimerVal{
diff --git a/pkg/sentry/kernel/task_block.go b/pkg/sentry/kernel/task_block.go
index ecbe8f920..b2520eecf 100644
--- a/pkg/sentry/kernel/task_block.go
+++ b/pkg/sentry/kernel/task_block.go
@@ -19,6 +19,7 @@ import (
"runtime/trace"
"time"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserror"
@@ -45,7 +46,7 @@ func (t *Task) BlockWithTimeout(C chan struct{}, haveTimeout bool, timeout time.
err := t.BlockWithDeadline(C, true, deadline)
// Timeout, explicitly return a remaining duration of 0.
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
return 0, err
}
@@ -162,7 +163,7 @@ func (t *Task) block(C <-chan struct{}, timerChan <-chan struct{}) error {
region.End()
t.SleepFinish(true)
// We've timed out.
- return syserror.ETIMEDOUT
+ return linuxerr.ETIMEDOUT
}
}
diff --git a/pkg/sentry/kernel/task_cgroup.go b/pkg/sentry/kernel/task_cgroup.go
index 25d2504fa..7dd6d3108 100644
--- a/pkg/sentry/kernel/task_cgroup.go
+++ b/pkg/sentry/kernel/task_cgroup.go
@@ -20,8 +20,8 @@ import (
"sort"
"strings"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
- "gvisor.dev/gvisor/pkg/syserror"
)
// EnterInitialCgroups moves t into an initial set of cgroups.
@@ -67,7 +67,7 @@ func (t *Task) EnterCgroup(c Cgroup) error {
//
// TODO(b/183137098): Implement cgroup migration.
log.Warningf("Cgroup migration is not implemented")
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
}
}
@@ -85,6 +85,14 @@ func (t *Task) enterCgroupLocked(c Cgroup) {
c.Enter(t)
}
+// +checklocks:t.mu
+func (t *Task) enterCgroupIfNotYetLocked(c Cgroup) {
+ if _, ok := t.cgroups[c]; ok {
+ return
+ }
+ t.enterCgroupLocked(c)
+}
+
// LeaveCgroups removes t out from all its cgroups.
func (t *Task) LeaveCgroups() {
t.mu.Lock()
diff --git a/pkg/sentry/kernel/task_clone.go b/pkg/sentry/kernel/task_clone.go
index 405771f3f..7e1347aa6 100644
--- a/pkg/sentry/kernel/task_clone.go
+++ b/pkg/sentry/kernel/task_clone.go
@@ -20,9 +20,9 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bpf"
"gvisor.dev/gvisor/pkg/cleanup"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/inet"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -142,25 +142,25 @@ func (t *Task) Clone(opts *CloneOptions) (ThreadID, *SyscallControl, error) {
// address, any set of signal handlers must refer to the same address
// space.
if !opts.NewSignalHandlers && opts.NewAddressSpace {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// In order for the behavior of thread-group-directed signals to be sane,
// all tasks in a thread group must share signal handlers.
if !opts.NewThreadGroup && opts.NewSignalHandlers {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// All tasks in a thread group must be in the same PID namespace.
if !opts.NewThreadGroup && (opts.NewPIDNamespace || t.childPIDNamespace != nil) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// The two different ways of specifying a new PID namespace are
// incompatible.
if opts.NewPIDNamespace && t.childPIDNamespace != nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Thread groups and FS contexts cannot span user namespaces.
if opts.NewUserNamespace && (!opts.NewThreadGroup || !opts.NewFSContext) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Pull task registers and FPU state, a cloned task will inherit the
@@ -182,7 +182,7 @@ func (t *Task) Clone(opts *CloneOptions) (ThreadID, *SyscallControl, error) {
// in which it resides)." - clone(2). Neither chroot(2) nor
// user_namespaces(7) document this.
if t.IsChrooted() {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
userns, err = creds.NewChildUserNamespace()
if err != nil {
@@ -190,7 +190,7 @@ func (t *Task) Clone(opts *CloneOptions) (ThreadID, *SyscallControl, error) {
}
}
if (opts.NewPIDNamespace || opts.NewNetworkNamespace || opts.NewUTSNamespace) && !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, userns) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
utsns := t.UTSNamespace()
@@ -241,7 +241,7 @@ func (t *Task) Clone(opts *CloneOptions) (ThreadID, *SyscallControl, error) {
}
if opts.SetTLS {
if !image.Arch.SetTLS(uintptr(opts.TLS)) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
}
@@ -463,14 +463,14 @@ func (t *Task) Unshare(opts *SharingOptions) error {
// sense that clone(2) allows a task to share signal handlers and address
// spaces with tasks in other thread groups.
if opts.NewAddressSpace || opts.NewSignalHandlers {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
creds := t.Credentials()
if opts.NewThreadGroup {
t.tg.signalHandlers.mu.Lock()
if t.tg.tasksCount != 1 {
t.tg.signalHandlers.mu.Unlock()
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
t.tg.signalHandlers.mu.Unlock()
// This isn't racy because we're the only living task, and therefore
@@ -478,7 +478,7 @@ func (t *Task) Unshare(opts *SharingOptions) error {
}
if opts.NewUserNamespace {
if t.IsChrooted() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
newUserNS, err := creds.NewChildUserNamespace()
if err != nil {
@@ -494,7 +494,7 @@ func (t *Task) Unshare(opts *SharingOptions) error {
haveCapSysAdmin := t.HasCapability(linux.CAP_SYS_ADMIN)
if opts.NewPIDNamespace {
if !haveCapSysAdmin {
- return syserror.EPERM
+ return linuxerr.EPERM
}
t.childPIDNamespace = t.tg.pidns.NewChild(t.UserNamespace())
}
@@ -503,14 +503,14 @@ func (t *Task) Unshare(opts *SharingOptions) error {
if opts.NewNetworkNamespace {
if !haveCapSysAdmin {
t.mu.Unlock()
- return syserror.EPERM
+ return linuxerr.EPERM
}
t.netns = inet.NewNamespace(t.netns)
}
if opts.NewUTSNamespace {
if !haveCapSysAdmin {
t.mu.Unlock()
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Note that this must happen after NewUserNamespace, so the
// new user namespace is used if there is one.
@@ -519,7 +519,7 @@ func (t *Task) Unshare(opts *SharingOptions) error {
if opts.NewIPCNamespace {
if !haveCapSysAdmin {
t.mu.Unlock()
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Note that "If CLONE_NEWIPC is set, then create the process in a new IPC
// namespace"
diff --git a/pkg/sentry/kernel/task_exec.go b/pkg/sentry/kernel/task_exec.go
index d9897e802..cf8571262 100644
--- a/pkg/sentry/kernel/task_exec.go
+++ b/pkg/sentry/kernel/task_exec.go
@@ -66,7 +66,6 @@ package kernel
import (
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/mm"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -181,7 +180,7 @@ func (r *runSyscallAfterExecStop) execute(t *Task) taskRunState {
t.tg.signalHandlers = t.tg.signalHandlers.CopyForExec()
t.endStopCond.L = &t.tg.signalHandlers.mu
// "Any alternate signal stack is not preserved (sigaltstack(2))." - execve(2)
- t.signalStack = arch.SignalStack{Flags: arch.SignalStackFlagDisable}
+ t.signalStack = linux.SignalStack{Flags: linux.SS_DISABLE}
// "The termination signal is reset to SIGCHLD (see clone(2))."
t.tg.terminationSignal = linux.SIGCHLD
// execed indicates that the process can no longer join a process group
diff --git a/pkg/sentry/kernel/task_exit.go b/pkg/sentry/kernel/task_exit.go
index b1af1a7ef..fe08c7519 100644
--- a/pkg/sentry/kernel/task_exit.go
+++ b/pkg/sentry/kernel/task_exit.go
@@ -28,9 +28,10 @@ import (
"errors"
"fmt"
"strconv"
+ "strings"
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/sentry/arch"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
@@ -50,6 +51,23 @@ type ExitStatus struct {
Signo int
}
+func (es ExitStatus) String() string {
+ var b strings.Builder
+ if code := es.Code; code != 0 {
+ if b.Len() != 0 {
+ b.WriteByte(' ')
+ }
+ _, _ = fmt.Fprintf(&b, "Code=%d", code)
+ }
+ if signal := es.Signo; signal != 0 {
+ if b.Len() != 0 {
+ b.WriteByte(' ')
+ }
+ _, _ = fmt.Fprintf(&b, "Signal=%d", signal)
+ }
+ return b.String()
+}
+
// Signaled returns true if the ExitStatus indicates that the exiting task or
// thread group was killed by a signal.
func (es ExitStatus) Signaled() bool {
@@ -122,12 +140,12 @@ func (t *Task) killLocked() {
if t.stop != nil && t.stop.Killable() {
t.endInternalStopLocked()
}
- t.pendingSignals.enqueue(&arch.SignalInfo{
+ t.pendingSignals.enqueue(&linux.SignalInfo{
Signo: int32(linux.SIGKILL),
// Linux just sets SIGKILL in the pending signal bitmask without
// enqueueing an actual siginfo, such that
// kernel/signal.c:collect_signal() initializes si_code to SI_USER.
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
}, nil)
t.interrupt()
}
@@ -332,7 +350,7 @@ func (t *Task) exitThreadGroup() bool {
// signalStop must be called with t's signal mutex unlocked.
t.tg.signalHandlers.mu.Unlock()
if notifyParent && t.tg.leader.parent != nil {
- t.tg.leader.parent.signalStop(t, arch.CLD_STOPPED, int32(sig))
+ t.tg.leader.parent.signalStop(t, linux.CLD_STOPPED, int32(sig))
t.tg.leader.parent.tg.eventQueue.Notify(EventChildGroupStop)
}
return last
@@ -353,7 +371,7 @@ func (t *Task) exitChildren() {
continue
}
other.signalHandlers.mu.Lock()
- other.leader.sendSignalLocked(&arch.SignalInfo{
+ other.leader.sendSignalLocked(&linux.SignalInfo{
Signo: int32(linux.SIGKILL),
}, true /* group */)
other.signalHandlers.mu.Unlock()
@@ -368,9 +386,9 @@ func (t *Task) exitChildren() {
// wait for a parent to reap them.)
for c := range t.children {
if sig := c.ParentDeathSignal(); sig != 0 {
- siginfo := &arch.SignalInfo{
+ siginfo := &linux.SignalInfo{
Signo: int32(sig),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
}
siginfo.SetPID(int32(c.tg.pidns.tids[t]))
siginfo.SetUID(int32(t.Credentials().RealKUID.In(c.UserNamespace()).OrOverflow()))
@@ -652,10 +670,10 @@ func (t *Task) exitNotifyLocked(fromPtraceDetach bool) {
t.parent.tg.signalHandlers.mu.Lock()
if t.tg.terminationSignal == linux.SIGCHLD || fromPtraceDetach {
if act, ok := t.parent.tg.signalHandlers.actions[linux.SIGCHLD]; ok {
- if act.Handler == arch.SignalActIgnore {
+ if act.Handler == linux.SIG_IGN {
t.exitParentAcked = true
signalParent = false
- } else if act.Flags&arch.SignalFlagNoCldWait != 0 {
+ } else if act.Flags&linux.SA_NOCLDWAIT != 0 {
t.exitParentAcked = true
}
}
@@ -705,17 +723,17 @@ func (t *Task) exitNotifyLocked(fromPtraceDetach bool) {
}
// Preconditions: The TaskSet mutex must be locked.
-func (t *Task) exitNotificationSignal(sig linux.Signal, receiver *Task) *arch.SignalInfo {
- info := &arch.SignalInfo{
+func (t *Task) exitNotificationSignal(sig linux.Signal, receiver *Task) *linux.SignalInfo {
+ info := &linux.SignalInfo{
Signo: int32(sig),
}
info.SetPID(int32(receiver.tg.pidns.tids[t]))
info.SetUID(int32(t.Credentials().RealKUID.In(receiver.UserNamespace()).OrOverflow()))
if t.exitStatus.Signaled() {
- info.Code = arch.CLD_KILLED
+ info.Code = linux.CLD_KILLED
info.SetStatus(int32(t.exitStatus.Signo))
} else {
- info.Code = arch.CLD_EXITED
+ info.Code = linux.CLD_EXITED
info.SetStatus(int32(t.exitStatus.Code))
}
// TODO(b/72102453): Set utime, stime.
@@ -925,7 +943,7 @@ func (t *Task) waitOnce(opts *WaitOptions) (*WaitResult, error) {
if anyWaitableTasks {
return nil, ErrNoWaitableEvent
}
- return nil, syserror.ECHILD
+ return nil, linuxerr.ECHILD
}
// Preconditions: The TaskSet mutex must be locked for writing.
diff --git a/pkg/sentry/kernel/task_identity.go b/pkg/sentry/kernel/task_identity.go
index 0325967e4..a9067b682 100644
--- a/pkg/sentry/kernel/task_identity.go
+++ b/pkg/sentry/kernel/task_identity.go
@@ -16,9 +16,9 @@ package kernel
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/mm"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Credentials returns t's credentials.
@@ -47,7 +47,7 @@ func (t *Task) HasCapability(cp linux.Capability) bool {
func (t *Task) SetUID(uid auth.UID) error {
// setuid considers -1 to be invalid.
if !uid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
t.mu.Lock()
@@ -56,7 +56,7 @@ func (t *Task) SetUID(uid auth.UID) error {
creds := t.Credentials()
kuid := creds.UserNamespace.MapToKUID(uid)
if !kuid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// "setuid() sets the effective user ID of the calling process. If the
// effective UID of the caller is root (more precisely: if the caller has
@@ -70,7 +70,7 @@ func (t *Task) SetUID(uid auth.UID) error {
// capability) and uid does not match the real UID or saved set-user-ID of
// the calling process."
if kuid != creds.RealKUID && kuid != creds.SavedKUID {
- return syserror.EPERM
+ return linuxerr.EPERM
}
t.setKUIDsUncheckedLocked(creds.RealKUID, kuid, creds.SavedKUID)
return nil
@@ -87,26 +87,26 @@ func (t *Task) SetREUID(r, e auth.UID) error {
if r.Ok() {
newR = creds.UserNamespace.MapToKUID(r)
if !newR.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
newE := creds.EffectiveKUID
if e.Ok() {
newE = creds.UserNamespace.MapToKUID(e)
if !newE.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
if !creds.HasCapability(linux.CAP_SETUID) {
// "Unprivileged processes may only set the effective user ID to the
// real user ID, the effective user ID, or the saved set-user-ID."
if newE != creds.RealKUID && newE != creds.EffectiveKUID && newE != creds.SavedKUID {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// "Unprivileged users may only set the real user ID to the real user
// ID or the effective user ID."
if newR != creds.RealKUID && newR != creds.EffectiveKUID {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
// "If the real user ID is set (i.e., ruid is not -1) or the effective user
@@ -223,7 +223,7 @@ func (t *Task) setKUIDsUncheckedLocked(newR, newE, newS auth.KUID) {
// SetGID implements the semantics of setgid(2).
func (t *Task) SetGID(gid auth.GID) error {
if !gid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
t.mu.Lock()
@@ -232,14 +232,14 @@ func (t *Task) SetGID(gid auth.GID) error {
creds := t.Credentials()
kgid := creds.UserNamespace.MapToKGID(gid)
if !kgid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if creds.HasCapability(linux.CAP_SETGID) {
t.setKGIDsUncheckedLocked(kgid, kgid, kgid)
return nil
}
if kgid != creds.RealKGID && kgid != creds.SavedKGID {
- return syserror.EPERM
+ return linuxerr.EPERM
}
t.setKGIDsUncheckedLocked(creds.RealKGID, kgid, creds.SavedKGID)
return nil
@@ -255,22 +255,22 @@ func (t *Task) SetREGID(r, e auth.GID) error {
if r.Ok() {
newR = creds.UserNamespace.MapToKGID(r)
if !newR.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
newE := creds.EffectiveKGID
if e.Ok() {
newE = creds.UserNamespace.MapToKGID(e)
if !newE.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
if !creds.HasCapability(linux.CAP_SETGID) {
if newE != creds.RealKGID && newE != creds.EffectiveKGID && newE != creds.SavedKGID {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if newR != creds.RealKGID && newR != creds.EffectiveKGID {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
newS := creds.SavedKGID
@@ -343,13 +343,13 @@ func (t *Task) SetExtraGIDs(gids []auth.GID) error {
defer t.mu.Unlock()
creds := t.Credentials()
if !creds.HasCapability(linux.CAP_SETGID) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
kgids := make([]auth.KGID, len(gids))
for i, gid := range gids {
kgid := creds.UserNamespace.MapToKGID(gid)
if !kgid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
kgids[i] = kgid
}
@@ -367,25 +367,25 @@ func (t *Task) SetCapabilitySets(permitted, inheritable, effective auth.Capabili
// "Permitted: This is a limiting superset for the effective capabilities
// that the thread may assume." - capabilities(7)
if effective & ^permitted != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
creds := t.Credentials()
// "It is also a limiting superset for the capabilities that may be added
// to the inheritable set by a thread that does not have the CAP_SETPCAP
// capability in its effective set."
if !creds.HasCapability(linux.CAP_SETPCAP) && (inheritable & ^(creds.InheritableCaps|creds.PermittedCaps) != 0) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// "If a thread drops a capability from its permitted set, it can never
// reacquire that capability (unless it execve(2)s ..."
if permitted & ^creds.PermittedCaps != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// "... if a capability is not in the bounding set, then a thread can't add
// this capability to its inheritable set, even if it was in its permitted
// capabilities ..."
if inheritable & ^(creds.InheritableCaps|creds.BoundingCaps) != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
creds = creds.Fork() // The credentials object is immutable. See doc for creds.
creds.PermittedCaps = permitted
@@ -402,7 +402,7 @@ func (t *Task) DropBoundingCapability(cp linux.Capability) error {
defer t.mu.Unlock()
creds := t.Credentials()
if !creds.HasCapability(linux.CAP_SETPCAP) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
creds = creds.Fork() // The credentials object is immutable. See doc for creds.
creds.BoundingCaps &^= auth.CapabilitySetOf(cp)
@@ -422,7 +422,7 @@ func (t *Task) SetUserNamespace(ns *auth.UserNamespace) error {
// If t just created ns, then t.creds is guaranteed to have CAP_SYS_ADMIN
// in ns (by rule 3 in auth.Credentials.HasCapability).
if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, ns) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
creds = creds.Fork() // The credentials object is immutable. See doc for creds.
diff --git a/pkg/sentry/kernel/task_image.go b/pkg/sentry/kernel/task_image.go
index bd5543d4e..c132c27ef 100644
--- a/pkg/sentry/kernel/task_image.go
+++ b/pkg/sentry/kernel/task_image.go
@@ -17,7 +17,7 @@ package kernel
import (
"fmt"
- "gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -27,7 +27,7 @@ import (
"gvisor.dev/gvisor/pkg/syserr"
)
-var errNoSyscalls = syserr.New("no syscall table found", linux.ENOEXEC)
+var errNoSyscalls = syserr.New("no syscall table found", errno.ENOEXEC)
// Auxmap contains miscellaneous data for the task.
type Auxmap map[string]interface{}
diff --git a/pkg/sentry/kernel/task_sched.go b/pkg/sentry/kernel/task_sched.go
index 9ba5f8d78..9d9fa76a6 100644
--- a/pkg/sentry/kernel/task_sched.go
+++ b/pkg/sentry/kernel/task_sched.go
@@ -23,12 +23,12 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/hostcpu"
"gvisor.dev/gvisor/pkg/sentry/kernel/sched"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/sentry/usage"
- "gvisor.dev/gvisor/pkg/syserror"
)
// TaskGoroutineState is a coarse representation of the current execution
@@ -536,7 +536,7 @@ func (tg *ThreadGroup) updateCPUTimersEnabledLocked() {
// appropriate for /proc/[pid]/status.
func (t *Task) StateStatus() string {
switch s := t.TaskGoroutineSchedInfo().State; s {
- case TaskGoroutineNonexistent:
+ case TaskGoroutineNonexistent, TaskGoroutineRunningSys:
t.tg.pidns.owner.mu.RLock()
defer t.tg.pidns.owner.mu.RUnlock()
switch t.exitState {
@@ -546,16 +546,16 @@ func (t *Task) StateStatus() string {
return "X (dead)"
default:
// The task goroutine can't exit before passing through
- // runExitNotify, so this indicates that the task has been created,
- // but the task goroutine hasn't yet started. The Linux equivalent
- // is struct task_struct::state == TASK_NEW
+ // runExitNotify, so if s == TaskGoroutineNonexistent, the task has
+ // been created but the task goroutine hasn't yet started. The
+ // Linux equivalent is struct task_struct::state == TASK_NEW
// (kernel/fork.c:copy_process() =>
// kernel/sched/core.c:sched_fork()), but the TASK_NEW bit is
// masked out by TASK_REPORT for /proc/[pid]/status, leaving only
// TASK_RUNNING.
return "R (running)"
}
- case TaskGoroutineRunningSys, TaskGoroutineRunningApp:
+ case TaskGoroutineRunningApp:
return "R (running)"
case TaskGoroutineBlockedInterruptible:
return "S (sleeping)"
@@ -601,7 +601,7 @@ func (t *Task) SetCPUMask(mask sched.CPUSet) error {
// Ensure that at least 1 CPU is still allowed.
if mask.NumCPUs() == 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if t.k.useHostCores {
diff --git a/pkg/sentry/kernel/task_signals.go b/pkg/sentry/kernel/task_signals.go
index c2b9fc08f..72dce7cd9 100644
--- a/pkg/sentry/kernel/task_signals.go
+++ b/pkg/sentry/kernel/task_signals.go
@@ -22,6 +22,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/eventchannel"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -86,7 +87,7 @@ var defaultActions = map[linux.Signal]SignalAction{
}
// computeAction figures out what to do given a signal number
-// and an arch.SignalAct. SIGSTOP always results in a SignalActionStop,
+// and an linux.SigAction. SIGSTOP always results in a SignalActionStop,
// and SIGKILL always results in a SignalActionTerm.
// Signal 0 is always ignored as many programs use it for various internal functions
// and don't expect it to do anything.
@@ -97,7 +98,7 @@ var defaultActions = map[linux.Signal]SignalAction{
// 0, the default action is taken;
// 1, the signal is ignored;
// anything else, the function returns SignalActionHandler.
-func computeAction(sig linux.Signal, act arch.SignalAct) SignalAction {
+func computeAction(sig linux.Signal, act linux.SigAction) SignalAction {
switch sig {
case linux.SIGSTOP:
return SignalActionStop
@@ -108,9 +109,9 @@ func computeAction(sig linux.Signal, act arch.SignalAct) SignalAction {
}
switch act.Handler {
- case arch.SignalActDefault:
+ case linux.SIG_DFL:
return defaultActions[sig]
- case arch.SignalActIgnore:
+ case linux.SIG_IGN:
return SignalActionIgnore
default:
return SignalActionHandler
@@ -127,7 +128,7 @@ var StopSignals = linux.MakeSignalSet(linux.SIGSTOP, linux.SIGTSTP, linux.SIGTTI
// If there are no pending unmasked signals, dequeueSignalLocked returns nil.
//
// Preconditions: t.tg.signalHandlers.mu must be locked.
-func (t *Task) dequeueSignalLocked(mask linux.SignalSet) *arch.SignalInfo {
+func (t *Task) dequeueSignalLocked(mask linux.SignalSet) *linux.SignalInfo {
if info := t.pendingSignals.dequeue(mask); info != nil {
return info
}
@@ -155,7 +156,7 @@ func (t *Task) PendingSignals() linux.SignalSet {
}
// deliverSignal delivers the given signal and returns the following run state.
-func (t *Task) deliverSignal(info *arch.SignalInfo, act arch.SignalAct) taskRunState {
+func (t *Task) deliverSignal(info *linux.SignalInfo, act linux.SigAction) taskRunState {
sigact := computeAction(linux.Signal(info.Signo), act)
if t.haveSyscallReturn {
@@ -172,7 +173,7 @@ func (t *Task) deliverSignal(info *arch.SignalInfo, act arch.SignalAct) taskRunS
fallthrough
case sre == syserror.ERESTART_RESTARTBLOCK:
fallthrough
- case (sre == syserror.ERESTARTSYS && !act.IsRestart()):
+ case (sre == syserror.ERESTARTSYS && act.Flags&linux.SA_RESTART == 0):
t.Debugf("Not restarting syscall %d after errno %d: interrupted by signal %d", t.Arch().SyscallNo(), sre, info.Signo)
t.Arch().SetReturn(uintptr(-ExtractErrno(syserror.EINTR, -1)))
default:
@@ -236,7 +237,7 @@ func (t *Task) deliverSignal(info *arch.SignalInfo, act arch.SignalAct) taskRunS
// deliverSignalToHandler changes the task's userspace state to enter the given
// user-configured handler for the given signal.
-func (t *Task) deliverSignalToHandler(info *arch.SignalInfo, act arch.SignalAct) error {
+func (t *Task) deliverSignalToHandler(info *linux.SignalInfo, act linux.SigAction) error {
// Signal delivery to an application handler interrupts restartable
// sequences.
t.rseqInterrupt()
@@ -248,8 +249,8 @@ func (t *Task) deliverSignalToHandler(info *arch.SignalInfo, act arch.SignalAct)
// N.B. This is a *copy* of the alternate stack that the user's signal
// handler expects to see in its ucontext (even if it's not in use).
alt := t.signalStack
- if act.IsOnStack() && alt.IsEnabled() {
- alt.SetOnStack()
+ if act.Flags&linux.SA_ONSTACK != 0 && alt.IsEnabled() {
+ alt.Flags |= linux.SS_ONSTACK
if !alt.Contains(sp) {
sp = hostarch.Addr(alt.Top())
}
@@ -289,7 +290,7 @@ func (t *Task) deliverSignalToHandler(info *arch.SignalInfo, act arch.SignalAct)
// Add our signal mask.
newMask := t.signalMask | act.Mask
- if !act.IsNoDefer() {
+ if act.Flags&linux.SA_NODEFER == 0 {
newMask |= linux.SignalSetOf(linux.Signal(info.Signo))
}
t.SetSignalMask(newMask)
@@ -326,7 +327,7 @@ func (t *Task) SignalReturn(rt bool) (*SyscallControl, error) {
// Preconditions:
// * The caller must be running on the task goroutine.
// * t.exitState < TaskExitZombie.
-func (t *Task) Sigtimedwait(set linux.SignalSet, timeout time.Duration) (*arch.SignalInfo, error) {
+func (t *Task) Sigtimedwait(set linux.SignalSet, timeout time.Duration) (*linux.SignalInfo, error) {
// set is the set of signals we're interested in; invert it to get the set
// of signals to block.
mask := ^(set &^ UnblockableSignals)
@@ -338,7 +339,7 @@ func (t *Task) Sigtimedwait(set linux.SignalSet, timeout time.Duration) (*arch.S
}
if timeout == 0 {
- return nil, syserror.EAGAIN
+ return nil, linuxerr.EAGAIN
}
// Unblock signals we're waiting for. Remember the original signal mask so
@@ -359,8 +360,8 @@ func (t *Task) Sigtimedwait(set linux.SignalSet, timeout time.Duration) (*arch.S
if info := t.dequeueSignalLocked(mask); info != nil {
return info, nil
}
- if err == syserror.ETIMEDOUT {
- return nil, syserror.EAGAIN
+ if err == linuxerr.ETIMEDOUT {
+ return nil, linuxerr.EAGAIN
}
return nil, err
}
@@ -370,10 +371,10 @@ func (t *Task) Sigtimedwait(set linux.SignalSet, timeout time.Duration) (*arch.S
// The following errors may be returned:
//
// syserror.ESRCH - The task has exited.
-// syserror.EINVAL - The signal is not valid.
-// syserror.EAGAIN - THe signal is realtime, and cannot be queued.
+// linuxerr.EINVAL - The signal is not valid.
+// linuxerr.EAGAIN - THe signal is realtime, and cannot be queued.
//
-func (t *Task) SendSignal(info *arch.SignalInfo) error {
+func (t *Task) SendSignal(info *linux.SignalInfo) error {
t.tg.pidns.owner.mu.RLock()
defer t.tg.pidns.owner.mu.RUnlock()
t.tg.signalHandlers.mu.Lock()
@@ -382,7 +383,7 @@ func (t *Task) SendSignal(info *arch.SignalInfo) error {
}
// SendGroupSignal sends the given signal to t's thread group.
-func (t *Task) SendGroupSignal(info *arch.SignalInfo) error {
+func (t *Task) SendGroupSignal(info *linux.SignalInfo) error {
t.tg.pidns.owner.mu.RLock()
defer t.tg.pidns.owner.mu.RUnlock()
t.tg.signalHandlers.mu.Lock()
@@ -392,7 +393,7 @@ func (t *Task) SendGroupSignal(info *arch.SignalInfo) error {
// SendSignal sends the given signal to tg, using tg's leader to determine if
// the signal is blocked.
-func (tg *ThreadGroup) SendSignal(info *arch.SignalInfo) error {
+func (tg *ThreadGroup) SendSignal(info *linux.SignalInfo) error {
tg.pidns.owner.mu.RLock()
defer tg.pidns.owner.mu.RUnlock()
tg.signalHandlers.mu.Lock()
@@ -400,11 +401,11 @@ func (tg *ThreadGroup) SendSignal(info *arch.SignalInfo) error {
return tg.leader.sendSignalLocked(info, true /* group */)
}
-func (t *Task) sendSignalLocked(info *arch.SignalInfo, group bool) error {
+func (t *Task) sendSignalLocked(info *linux.SignalInfo, group bool) error {
return t.sendSignalTimerLocked(info, group, nil)
}
-func (t *Task) sendSignalTimerLocked(info *arch.SignalInfo, group bool, timer *IntervalTimer) error {
+func (t *Task) sendSignalTimerLocked(info *linux.SignalInfo, group bool, timer *IntervalTimer) error {
if t.exitState == TaskExitDead {
return syserror.ESRCH
}
@@ -413,7 +414,7 @@ func (t *Task) sendSignalTimerLocked(info *arch.SignalInfo, group bool, timer *I
return nil
}
if !sig.IsValid() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Signal side effects apply even if the signal is ultimately discarded.
@@ -450,7 +451,7 @@ func (t *Task) sendSignalTimerLocked(info *arch.SignalInfo, group bool, timer *I
}
if !q.enqueue(info, timer) {
if sig.IsRealtime() {
- return syserror.EAGAIN
+ return linuxerr.EAGAIN
}
t.Debugf("Discarding duplicate signal %d", sig)
if timer != nil {
@@ -572,9 +573,9 @@ func (t *Task) forceSignal(sig linux.Signal, unconditional bool) {
func (t *Task) forceSignalLocked(sig linux.Signal, unconditional bool) {
blocked := linux.SignalSetOf(sig)&t.signalMask != 0
act := t.tg.signalHandlers.actions[sig]
- ignored := act.Handler == arch.SignalActIgnore
+ ignored := act.Handler == linux.SIG_IGN
if blocked || ignored || unconditional {
- act.Handler = arch.SignalActDefault
+ act.Handler = linux.SIG_DFL
t.tg.signalHandlers.actions[sig] = act
if blocked {
t.setSignalMaskLocked(t.signalMask &^ linux.SignalSetOf(sig))
@@ -641,17 +642,17 @@ func (t *Task) SetSavedSignalMask(mask linux.SignalSet) {
}
// SignalStack returns the task-private signal stack.
-func (t *Task) SignalStack() arch.SignalStack {
+func (t *Task) SignalStack() linux.SignalStack {
t.p.PullFullState(t.MemoryManager().AddressSpace(), t.Arch())
alt := t.signalStack
if t.onSignalStack(alt) {
- alt.Flags |= arch.SignalStackFlagOnStack
+ alt.Flags |= linux.SS_ONSTACK
}
return alt
}
// onSignalStack returns true if the task is executing on the given signal stack.
-func (t *Task) onSignalStack(alt arch.SignalStack) bool {
+func (t *Task) onSignalStack(alt linux.SignalStack) bool {
sp := hostarch.Addr(t.Arch().Stack())
return alt.Contains(sp)
}
@@ -661,30 +662,30 @@ func (t *Task) onSignalStack(alt arch.SignalStack) bool {
// This value may not be changed if the task is currently executing on the
// signal stack, i.e. if t.onSignalStack returns true. In this case, this
// function will return false. Otherwise, true is returned.
-func (t *Task) SetSignalStack(alt arch.SignalStack) bool {
+func (t *Task) SetSignalStack(alt linux.SignalStack) bool {
// Check that we're not executing on the stack.
if t.onSignalStack(t.signalStack) {
return false
}
- if alt.Flags&arch.SignalStackFlagDisable != 0 {
+ if alt.Flags&linux.SS_DISABLE != 0 {
// Don't record anything beyond the flags.
- t.signalStack = arch.SignalStack{
- Flags: arch.SignalStackFlagDisable,
+ t.signalStack = linux.SignalStack{
+ Flags: linux.SS_DISABLE,
}
} else {
// Mask out irrelevant parts: only disable matters.
- alt.Flags &= arch.SignalStackFlagDisable
+ alt.Flags &= linux.SS_DISABLE
t.signalStack = alt
}
return true
}
-// SetSignalAct atomically sets the thread group's signal action for signal sig
+// SetSigAction atomically sets the thread group's signal action for signal sig
// to *actptr (if actptr is not nil) and returns the old signal action.
-func (tg *ThreadGroup) SetSignalAct(sig linux.Signal, actptr *arch.SignalAct) (arch.SignalAct, error) {
+func (tg *ThreadGroup) SetSigAction(sig linux.Signal, actptr *linux.SigAction) (linux.SigAction, error) {
if !sig.IsValid() {
- return arch.SignalAct{}, syserror.EINVAL
+ return linux.SigAction{}, linuxerr.EINVAL
}
tg.pidns.owner.mu.RLock()
@@ -695,7 +696,7 @@ func (tg *ThreadGroup) SetSignalAct(sig linux.Signal, actptr *arch.SignalAct) (a
oldact := sh.actions[sig]
if actptr != nil {
if sig == linux.SIGKILL || sig == linux.SIGSTOP {
- return oldact, syserror.EINVAL
+ return oldact, linuxerr.EINVAL
}
act := *actptr
@@ -718,48 +719,6 @@ func (tg *ThreadGroup) SetSignalAct(sig linux.Signal, actptr *arch.SignalAct) (a
return oldact, nil
}
-// CopyOutSignalAct converts the given SignalAct into an architecture-specific
-// type and then copies it out to task memory.
-func (t *Task) CopyOutSignalAct(addr hostarch.Addr, s *arch.SignalAct) error {
- n := t.Arch().NewSignalAct()
- n.SerializeFrom(s)
- _, err := n.CopyOut(t, addr)
- return err
-}
-
-// CopyInSignalAct copies an architecture-specific sigaction type from task
-// memory and then converts it into a SignalAct.
-func (t *Task) CopyInSignalAct(addr hostarch.Addr) (arch.SignalAct, error) {
- n := t.Arch().NewSignalAct()
- var s arch.SignalAct
- if _, err := n.CopyIn(t, addr); err != nil {
- return s, err
- }
- n.DeserializeTo(&s)
- return s, nil
-}
-
-// CopyOutSignalStack converts the given SignalStack into an
-// architecture-specific type and then copies it out to task memory.
-func (t *Task) CopyOutSignalStack(addr hostarch.Addr, s *arch.SignalStack) error {
- n := t.Arch().NewSignalStack()
- n.SerializeFrom(s)
- _, err := n.CopyOut(t, addr)
- return err
-}
-
-// CopyInSignalStack copies an architecture-specific stack_t from task memory
-// and then converts it into a SignalStack.
-func (t *Task) CopyInSignalStack(addr hostarch.Addr) (arch.SignalStack, error) {
- n := t.Arch().NewSignalStack()
- var s arch.SignalStack
- if _, err := n.CopyIn(t, addr); err != nil {
- return s, err
- }
- n.DeserializeTo(&s)
- return s, nil
-}
-
// groupStop is a TaskStop placed on tasks that have received a stop signal
// (SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU). (The term "group-stop" originates from
// the ptrace man page.)
@@ -774,7 +733,7 @@ func (*groupStop) Killable() bool { return true }
// previously-dequeued stop signal.
//
// Preconditions: The caller must be running on the task goroutine.
-func (t *Task) initiateGroupStop(info *arch.SignalInfo) {
+func (t *Task) initiateGroupStop(info *linux.SignalInfo) {
t.tg.pidns.owner.mu.RLock()
defer t.tg.pidns.owner.mu.RUnlock()
t.tg.signalHandlers.mu.Lock()
@@ -909,8 +868,8 @@ func (t *Task) signalStop(target *Task, code int32, status int32) {
t.tg.signalHandlers.mu.Lock()
defer t.tg.signalHandlers.mu.Unlock()
act, ok := t.tg.signalHandlers.actions[linux.SIGCHLD]
- if !ok || (act.Handler != arch.SignalActIgnore && act.Flags&arch.SignalFlagNoCldStop == 0) {
- sigchld := &arch.SignalInfo{
+ if !ok || (act.Handler != linux.SIG_IGN && act.Flags&linux.SA_NOCLDSTOP == 0) {
+ sigchld := &linux.SignalInfo{
Signo: int32(linux.SIGCHLD),
Code: code,
}
@@ -955,14 +914,14 @@ func (*runInterrupt) execute(t *Task) taskRunState {
// notified its tracer accordingly. But it's consistent with
// Linux...
if intr {
- tracer.signalStop(t.tg.leader, arch.CLD_STOPPED, int32(sig))
+ tracer.signalStop(t.tg.leader, linux.CLD_STOPPED, int32(sig))
if !notifyParent {
tracer.tg.eventQueue.Notify(EventGroupContinue | EventTraceeStop | EventChildGroupStop)
} else {
tracer.tg.eventQueue.Notify(EventGroupContinue | EventTraceeStop)
}
} else {
- tracer.signalStop(t.tg.leader, arch.CLD_CONTINUED, int32(sig))
+ tracer.signalStop(t.tg.leader, linux.CLD_CONTINUED, int32(sig))
tracer.tg.eventQueue.Notify(EventGroupContinue)
}
}
@@ -974,10 +933,10 @@ func (*runInterrupt) execute(t *Task) taskRunState {
// SIGCHLD is a standard signal, so the latter would always be
// dropped. Hence sending only the former is equivalent.
if intr {
- t.tg.leader.parent.signalStop(t.tg.leader, arch.CLD_STOPPED, int32(sig))
+ t.tg.leader.parent.signalStop(t.tg.leader, linux.CLD_STOPPED, int32(sig))
t.tg.leader.parent.tg.eventQueue.Notify(EventGroupContinue | EventChildGroupStop)
} else {
- t.tg.leader.parent.signalStop(t.tg.leader, arch.CLD_CONTINUED, int32(sig))
+ t.tg.leader.parent.signalStop(t.tg.leader, linux.CLD_CONTINUED, int32(sig))
t.tg.leader.parent.tg.eventQueue.Notify(EventGroupContinue)
}
}
@@ -1018,7 +977,7 @@ func (*runInterrupt) execute(t *Task) taskRunState {
// without requiring an extra PTRACE_GETSIGINFO call." -
// "Group-stop", ptrace(2)
t.ptraceCode = int32(sig) | linux.PTRACE_EVENT_STOP<<8
- t.ptraceSiginfo = &arch.SignalInfo{
+ t.ptraceSiginfo = &linux.SignalInfo{
Signo: int32(sig),
Code: t.ptraceCode,
}
@@ -1029,7 +988,7 @@ func (*runInterrupt) execute(t *Task) taskRunState {
t.ptraceSiginfo = nil
}
if t.beginPtraceStopLocked() {
- tracer.signalStop(t, arch.CLD_STOPPED, int32(sig))
+ tracer.signalStop(t, linux.CLD_STOPPED, int32(sig))
// For consistency with Linux, if the parent and tracer are in the
// same thread group, deduplicate notification signals.
if notifyParent && tracer.tg == t.tg.leader.parent.tg {
@@ -1047,7 +1006,7 @@ func (*runInterrupt) execute(t *Task) taskRunState {
t.tg.signalHandlers.mu.Unlock()
}
if notifyParent {
- t.tg.leader.parent.signalStop(t.tg.leader, arch.CLD_STOPPED, int32(sig))
+ t.tg.leader.parent.signalStop(t.tg.leader, linux.CLD_STOPPED, int32(sig))
t.tg.leader.parent.tg.eventQueue.Notify(EventChildGroupStop)
}
t.tg.pidns.owner.mu.RUnlock()
@@ -1101,7 +1060,7 @@ func (*runInterruptAfterSignalDeliveryStop) execute(t *Task) taskRunState {
if sig != linux.Signal(info.Signo) {
info.Signo = int32(sig)
info.Errno = 0
- info.Code = arch.SignalInfoUser
+ info.Code = linux.SI_USER
// pid isn't a valid field for all signal numbers, but Linux
// doesn't care (kernel/signal.c:ptrace_signal()).
//
diff --git a/pkg/sentry/kernel/task_start.go b/pkg/sentry/kernel/task_start.go
index 32031cd70..0565059c1 100644
--- a/pkg/sentry/kernel/task_start.go
+++ b/pkg/sentry/kernel/task_start.go
@@ -17,8 +17,8 @@ package kernel
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/kernel/futex"
@@ -131,7 +131,7 @@ func (ts *TaskSet) newTask(cfg *TaskConfig) (*Task, error) {
runState: (*runApp)(nil),
interruptChan: make(chan struct{}, 1),
signalMask: cfg.SignalMask,
- signalStack: arch.SignalStack{Flags: arch.SignalStackFlagDisable},
+ signalStack: linux.SignalStack{Flags: linux.SS_DISABLE},
image: *image,
fsContext: cfg.FSContext,
fdTable: cfg.FDTable,
@@ -300,7 +300,7 @@ func (ns *PIDNamespace) allocateTID() (ThreadID, error) {
// Did we do a full cycle?
if tid == ns.last {
// No tid available.
- return 0, syserror.EAGAIN
+ return 0, linuxerr.EAGAIN
}
}
}
diff --git a/pkg/sentry/kernel/task_syscall.go b/pkg/sentry/kernel/task_syscall.go
index 601fc0d3a..409b712d8 100644
--- a/pkg/sentry/kernel/task_syscall.go
+++ b/pkg/sentry/kernel/task_syscall.go
@@ -22,6 +22,8 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bits"
+ "gvisor.dev/gvisor/pkg/errors"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/metric"
@@ -357,7 +359,7 @@ func (t *Task) doVsyscallInvoke(sysno uintptr, args arch.SyscallArguments, calle
t.Arch().SetReturn(uintptr(rval))
} else {
t.Debugf("vsyscall %d, caller %x: emulated syscall returned error: %v", sysno, t.Arch().Value(caller), err)
- if err == syserror.EFAULT {
+ if linuxerr.Equals(linuxerr.EFAULT, err) {
t.forceSignal(linux.SIGSEGV, false /* unconditional */)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
// A return is not emulated in this case.
@@ -379,6 +381,8 @@ func ExtractErrno(err error, sysno int) int {
return 0
case unix.Errno:
return int(err)
+ case *errors.Error:
+ return int(err.Errno())
case syserror.SyscallRestartErrno:
return int(err)
case *memmap.BusError:
diff --git a/pkg/sentry/kernel/task_usermem.go b/pkg/sentry/kernel/task_usermem.go
index fc6d9438a..7935d15a6 100644
--- a/pkg/sentry/kernel/task_usermem.go
+++ b/pkg/sentry/kernel/task_usermem.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/mm"
"gvisor.dev/gvisor/pkg/syserror"
@@ -202,7 +203,7 @@ func (t *Task) CopyInIovecs(addr hostarch.Addr, numIovecs int) (hostarch.AddrRan
base := hostarch.Addr(hostarch.ByteOrder.Uint64(b[0:8]))
length := hostarch.ByteOrder.Uint64(b[8:16])
if length > math.MaxInt64 {
- return hostarch.AddrRangeSeq{}, syserror.EINVAL
+ return hostarch.AddrRangeSeq{}, linuxerr.EINVAL
}
ar, ok := t.MemoryManager().CheckIORange(base, int64(length))
if !ok {
@@ -270,7 +271,7 @@ func (t *Task) SingleIOSequence(addr hostarch.Addr, length int, opts usermem.IOO
// Preconditions: Same as Task.CopyInIovecs.
func (t *Task) IovecsIOSequence(addr hostarch.Addr, iovcnt int, opts usermem.IOOpts) (usermem.IOSequence, error) {
if iovcnt < 0 || iovcnt > linux.UIO_MAXIOV {
- return usermem.IOSequence{}, syserror.EINVAL
+ return usermem.IOSequence{}, linuxerr.EINVAL
}
ars, err := t.CopyInIovecs(addr, iovcnt)
if err != nil {
diff --git a/pkg/sentry/kernel/thread_group.go b/pkg/sentry/kernel/thread_group.go
index b92e98fa1..3ce11f542 100644
--- a/pkg/sentry/kernel/thread_group.go
+++ b/pkg/sentry/kernel/thread_group.go
@@ -19,7 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/sentry/arch"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
@@ -279,7 +279,7 @@ func (k *Kernel) NewThreadGroup(mntns *fs.MountNamespace, pidns *PIDNamespace, s
limits: limits,
mounts: mntns,
}
- tg.itimerRealTimer = ktime.NewTimer(k.monotonicClock, &itimerRealListener{tg: tg})
+ tg.itimerRealTimer = ktime.NewTimer(k.timekeeper.monotonicClock, &itimerRealListener{tg: tg})
tg.timers = make(map[linux.TimerID]*IntervalTimer)
tg.oldRSeqCritical.Store(&OldRSeqCriticalRegion{})
return tg
@@ -358,7 +358,7 @@ func (tg *ThreadGroup) SetControllingTTY(tty *TTY, steal bool, isReadable bool)
// "The calling process must be a session leader and not have a
// controlling terminal already." - tty_ioctl(4)
if tg.processGroup.session.leader != tg || tg.tty != nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(tg.leader)
@@ -372,7 +372,7 @@ func (tg *ThreadGroup) SetControllingTTY(tty *TTY, steal bool, isReadable bool)
if tty.tg != nil && tg.processGroup.session != tty.tg.processGroup.session {
// Stealing requires CAP_SYS_ADMIN in the root user namespace.
if !hasAdmin || !steal {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Steal the TTY away. Unlike TIOCNOTTY, don't send signals.
for othertg := range tg.pidns.owner.Root.tgids {
@@ -392,7 +392,7 @@ func (tg *ThreadGroup) SetControllingTTY(tty *TTY, steal bool, isReadable bool)
}
if !isReadable && !hasAdmin {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Set the controlling terminal and foreground process group.
@@ -446,10 +446,10 @@ func (tg *ThreadGroup) ReleaseControllingTTY(tty *TTY) error {
othertg.signalHandlers.mu.Lock()
othertg.tty = nil
if othertg.processGroup == tg.processGroup.session.foreground {
- if err := othertg.leader.sendSignalLocked(&arch.SignalInfo{Signo: int32(linux.SIGHUP)}, true /* group */); err != nil {
+ if err := othertg.leader.sendSignalLocked(&linux.SignalInfo{Signo: int32(linux.SIGHUP)}, true /* group */); err != nil {
lastErr = err
}
- if err := othertg.leader.sendSignalLocked(&arch.SignalInfo{Signo: int32(linux.SIGCONT)}, true /* group */); err != nil {
+ if err := othertg.leader.sendSignalLocked(&linux.SignalInfo{Signo: int32(linux.SIGCONT)}, true /* group */); err != nil {
lastErr = err
}
}
@@ -490,10 +490,10 @@ func (tg *ThreadGroup) SetForegroundProcessGroup(tty *TTY, pgid ProcessGroupID)
tg.signalHandlers.mu.Lock()
defer tg.signalHandlers.mu.Unlock()
- // TODO(b/129283598): "If tcsetpgrp() is called by a member of a
- // background process group in its session, and the calling process is
- // not blocking or ignoring SIGTTOU, a SIGTTOU signal is sent to all
- // members of this background process group."
+ // TODO(gvisor.dev/issue/6148): "If tcsetpgrp() is called by a member of a
+ // background process group in its session, and the calling process is not
+ // blocking or ignoring SIGTTOU, a SIGTTOU signal is sent to all members of
+ // this background process group."
// tty must be the controlling terminal.
if tg.tty != tty {
@@ -502,7 +502,7 @@ func (tg *ThreadGroup) SetForegroundProcessGroup(tty *TTY, pgid ProcessGroupID)
// pgid must be positive.
if pgid < 0 {
- return -1, syserror.EINVAL
+ return -1, linuxerr.EINVAL
}
// pg must not be empty. Empty process groups are removed from their
@@ -514,7 +514,7 @@ func (tg *ThreadGroup) SetForegroundProcessGroup(tty *TTY, pgid ProcessGroupID)
// pg must be part of this process's session.
if tg.processGroup.session != pg.session {
- return -1, syserror.EPERM
+ return -1, linuxerr.EPERM
}
tg.processGroup.session.foreground.id = pgid
diff --git a/pkg/sentry/kernel/time/BUILD b/pkg/sentry/kernel/time/BUILD
index 2817aa3ba..e293d9a0f 100644
--- a/pkg/sentry/kernel/time/BUILD
+++ b/pkg/sentry/kernel/time/BUILD
@@ -13,8 +13,8 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/sync",
- "//pkg/syserror",
"//pkg/waiter",
],
)
diff --git a/pkg/sentry/kernel/time/time.go b/pkg/sentry/kernel/time/time.go
index 26aa34aa6..191b92811 100644
--- a/pkg/sentry/kernel/time/time.go
+++ b/pkg/sentry/kernel/time/time.go
@@ -22,8 +22,8 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -322,7 +322,7 @@ func SettingFromSpec(value time.Duration, interval time.Duration, c Clock) (Sett
// interpreted as a time relative to now.
func SettingFromSpecAt(value time.Duration, interval time.Duration, now Time) (Setting, error) {
if value < 0 {
- return Setting{}, syserror.EINVAL
+ return Setting{}, linuxerr.EINVAL
}
if value == 0 {
return Setting{Period: interval}, nil
@@ -338,7 +338,7 @@ func SettingFromSpecAt(value time.Duration, interval time.Duration, now Time) (S
// interpreted as an absolute time.
func SettingFromAbsSpec(value Time, interval time.Duration) (Setting, error) {
if value.Before(ZeroTime) {
- return Setting{}, syserror.EINVAL
+ return Setting{}, linuxerr.EINVAL
}
if value.IsZero() {
return Setting{Period: interval}, nil
diff --git a/pkg/sentry/kernel/timekeeper.go b/pkg/sentry/kernel/timekeeper.go
index 7c4fefb16..6255bae7a 100644
--- a/pkg/sentry/kernel/timekeeper.go
+++ b/pkg/sentry/kernel/timekeeper.go
@@ -25,6 +25,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
sentrytime "gvisor.dev/gvisor/pkg/sentry/time"
"gvisor.dev/gvisor/pkg/sync"
+ "gvisor.dev/gvisor/pkg/tcpip"
)
// Timekeeper manages all of the kernel clocks.
@@ -39,6 +40,12 @@ type Timekeeper struct {
// It is set only once, by SetClocks.
clocks sentrytime.Clocks `state:"nosave"`
+ // realtimeClock is a ktime.Clock based on timekeeper's Realtime.
+ realtimeClock *timekeeperClock
+
+ // monotonicClock is a ktime.Clock based on timekeeper's Monotonic.
+ monotonicClock *timekeeperClock
+
// bootTime is the realtime when the system "booted". i.e., when
// SetClocks was called in the initial (not restored) run.
bootTime ktime.Time
@@ -90,10 +97,13 @@ type Timekeeper struct {
// NewTimekeeper does not take ownership of paramPage.
//
// SetClocks must be called on the returned Timekeeper before it is usable.
-func NewTimekeeper(mfp pgalloc.MemoryFileProvider, paramPage memmap.FileRange) (*Timekeeper, error) {
- return &Timekeeper{
+func NewTimekeeper(mfp pgalloc.MemoryFileProvider, paramPage memmap.FileRange) *Timekeeper {
+ t := Timekeeper{
params: NewVDSOParamPage(mfp, paramPage),
- }, nil
+ }
+ t.realtimeClock = &timekeeperClock{tk: &t, c: sentrytime.Realtime}
+ t.monotonicClock = &timekeeperClock{tk: &t, c: sentrytime.Monotonic}
+ return &t
}
// SetClocks the backing clock source.
@@ -167,6 +177,32 @@ func (t *Timekeeper) SetClocks(c sentrytime.Clocks) {
}
}
+var _ tcpip.Clock = (*Timekeeper)(nil)
+
+// Now implements tcpip.Clock.
+func (t *Timekeeper) Now() time.Time {
+ nsec, err := t.GetTime(sentrytime.Realtime)
+ if err != nil {
+ panic("timekeeper.GetTime(sentrytime.Realtime): " + err.Error())
+ }
+ return time.Unix(0, nsec)
+}
+
+// NowMonotonic implements tcpip.Clock.
+func (t *Timekeeper) NowMonotonic() tcpip.MonotonicTime {
+ nsec, err := t.GetTime(sentrytime.Monotonic)
+ if err != nil {
+ panic("timekeeper.GetTime(sentrytime.Monotonic): " + err.Error())
+ }
+ var mt tcpip.MonotonicTime
+ return mt.Add(time.Duration(nsec) * time.Nanosecond)
+}
+
+// AfterFunc implements tcpip.Clock.
+func (t *Timekeeper) AfterFunc(d time.Duration, f func()) tcpip.Timer {
+ return ktime.TcpipAfterFunc(t.realtimeClock, d, f)
+}
+
// startUpdater starts an update goroutine that keeps the clocks updated.
//
// mu must be held.
diff --git a/pkg/sentry/kernel/timekeeper_test.go b/pkg/sentry/kernel/timekeeper_test.go
index dfc3c0719..b6039505a 100644
--- a/pkg/sentry/kernel/timekeeper_test.go
+++ b/pkg/sentry/kernel/timekeeper_test.go
@@ -17,12 +17,12 @@ package kernel
import (
"testing"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
sentrytime "gvisor.dev/gvisor/pkg/sentry/time"
"gvisor.dev/gvisor/pkg/sentry/usage"
- "gvisor.dev/gvisor/pkg/syserror"
)
// mockClocks is a sentrytime.Clocks that simply returns the times in the
@@ -45,7 +45,7 @@ func (c *mockClocks) GetTime(id sentrytime.ClockID) (int64, error) {
case sentrytime.Realtime:
return c.realtime, nil
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/loader/BUILD b/pkg/sentry/loader/BUILD
index 4c65215fa..54bfed644 100644
--- a/pkg/sentry/loader/BUILD
+++ b/pkg/sentry/loader/BUILD
@@ -17,8 +17,10 @@ go_library(
deps = [
"//pkg/abi",
"//pkg/abi/linux",
+ "//pkg/abi/linux/errno",
"//pkg/context",
"//pkg/cpuid",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/rand",
diff --git a/pkg/sentry/loader/elf.go b/pkg/sentry/loader/elf.go
index 8fc3e2a79..577374fa4 100644
--- a/pkg/sentry/loader/elf.go
+++ b/pkg/sentry/loader/elf.go
@@ -24,6 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/cpuid"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -476,7 +477,7 @@ func loadParsedELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, in
// the open path would return a different
// error.
ctx.Infof("PT_INTERP path is empty: %v", path)
- return loadedELF{}, syserror.EACCES
+ return loadedELF{}, linuxerr.EACCES
}
}
}
@@ -517,13 +518,13 @@ func loadParsedELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, in
start, ok = start.AddLength(uint64(offset))
if !ok {
ctx.Infof(fmt.Sprintf("Start %#x + offset %#x overflows?", start, offset))
- return loadedELF{}, syserror.EINVAL
+ return loadedELF{}, linuxerr.EINVAL
}
end, ok = end.AddLength(uint64(offset))
if !ok {
ctx.Infof(fmt.Sprintf("End %#x + offset %#x overflows?", end, offset))
- return loadedELF{}, syserror.EINVAL
+ return loadedELF{}, linuxerr.EINVAL
}
info.entry, ok = info.entry.AddLength(uint64(offset))
@@ -621,20 +622,20 @@ func loadInitialELF(ctx context.Context, m *mm.MemoryManager, fs *cpuid.FeatureS
func loadInterpreterELF(ctx context.Context, m *mm.MemoryManager, f fsbridge.File, initial loadedELF) (loadedELF, error) {
info, err := parseHeader(ctx, f)
if err != nil {
- if err == syserror.ENOEXEC {
+ if linuxerr.Equals(linuxerr.ENOEXEC, err) {
// Bad interpreter.
- err = syserror.ELIBBAD
+ err = linuxerr.ELIBBAD
}
return loadedELF{}, err
}
if info.os != initial.os {
ctx.Infof("Initial ELF OS %v and interpreter ELF OS %v differ", initial.os, info.os)
- return loadedELF{}, syserror.ELIBBAD
+ return loadedELF{}, linuxerr.ELIBBAD
}
if info.arch != initial.arch {
ctx.Infof("Initial ELF arch %v and interpreter ELF arch %v differ", initial.arch, info.arch)
- return loadedELF{}, syserror.ELIBBAD
+ return loadedELF{}, linuxerr.ELIBBAD
}
// The interpreter is not given a load offset, as its location does not
diff --git a/pkg/sentry/loader/interpreter.go b/pkg/sentry/loader/interpreter.go
index 3886b4d33..3e302d92c 100644
--- a/pkg/sentry/loader/interpreter.go
+++ b/pkg/sentry/loader/interpreter.go
@@ -59,7 +59,7 @@ func parseInterpreterScript(ctx context.Context, filename string, f fsbridge.Fil
// Linux silently truncates the remainder of the line if it exceeds
// interpMaxLineLength.
i := bytes.IndexByte(line, '\n')
- if i > 0 {
+ if i >= 0 {
line = line[:i]
}
diff --git a/pkg/sentry/loader/loader.go b/pkg/sentry/loader/loader.go
index 47e3775a3..86d0c54cd 100644
--- a/pkg/sentry/loader/loader.go
+++ b/pkg/sentry/loader/loader.go
@@ -23,8 +23,10 @@ import (
"gvisor.dev/gvisor/pkg/abi"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/cpuid"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/rand"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -112,7 +114,7 @@ func checkIsRegularFile(ctx context.Context, file fsbridge.File, filename string
}
if t != linux.ModeRegular {
ctx.Infof("%q is not a regular file: %v", filename, t)
- return syserror.EACCES
+ return linuxerr.EACCES
}
return nil
}
@@ -206,7 +208,7 @@ func loadExecutable(ctx context.Context, args LoadArgs) (loadedELF, arch.Context
args.File = nil
}
- return loadedELF{}, nil, nil, nil, syserror.ELOOP
+ return loadedELF{}, nil, nil, nil, linuxerr.ELOOP
}
// Load loads args.File into a MemoryManager. If args.File is nil, the path
@@ -237,7 +239,7 @@ func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *V
// loaded.end is available for its use.
e, ok := loaded.end.RoundUp()
if !ok {
- return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("brk overflows: %#x", loaded.end), linux.ENOEXEC)
+ return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("brk overflows: %#x", loaded.end), errno.ENOEXEC)
}
args.MemoryManager.BrkSetup(ctx, e)
diff --git a/pkg/sentry/loader/vdso.go b/pkg/sentry/loader/vdso.go
index fd54261fd..054ef1723 100644
--- a/pkg/sentry/loader/vdso.go
+++ b/pkg/sentry/loader/vdso.go
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/abi"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/safemem"
@@ -58,7 +59,7 @@ type byteFullReader struct {
func (b *byteFullReader) ReadFull(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset >= int64(len(b.data)) {
return 0, io.EOF
diff --git a/pkg/sentry/mm/BUILD b/pkg/sentry/mm/BUILD
index b417c2da7..69aff21b6 100644
--- a/pkg/sentry/mm/BUILD
+++ b/pkg/sentry/mm/BUILD
@@ -125,6 +125,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/atomicbitops",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/refs",
@@ -156,6 +157,7 @@ go_test(
library = ":mm",
deps = [
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/sentry/arch",
"//pkg/sentry/contexttest",
@@ -163,7 +165,6 @@ go_test(
"//pkg/sentry/memmap",
"//pkg/sentry/pgalloc",
"//pkg/sentry/platform",
- "//pkg/syserror",
"//pkg/usermem",
],
)
diff --git a/pkg/sentry/mm/aio_context.go b/pkg/sentry/mm/aio_context.go
index 346866d3c..23747daba 100644
--- a/pkg/sentry/mm/aio_context.go
+++ b/pkg/sentry/mm/aio_context.go
@@ -17,6 +17,7 @@ package mm
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
@@ -158,11 +159,11 @@ func (ctx *AIOContext) Prepare() error {
defer ctx.mu.Unlock()
if ctx.dead {
// Context died after the caller looked it up.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if ctx.outstanding >= ctx.maxOutstanding {
// Context is busy.
- return syserror.EAGAIN
+ return linuxerr.EAGAIN
}
ctx.outstanding++
return nil
@@ -297,7 +298,7 @@ func (m *aioMappable) InodeID() uint64 {
// Msync implements memmap.MappingIdentity.Msync.
func (m *aioMappable) Msync(ctx context.Context, mr memmap.MappableRange) error {
// Linux: aio_ring_fops.fsync == NULL
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// AddMapping implements memmap.Mappable.AddMapping.
@@ -325,7 +326,7 @@ func (m *aioMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, s
// Linux's fs/aio.c:aio_ring_mremap().
mm, ok := ms.(*MemoryManager)
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
am := &mm.aioManager
am.mu.Lock()
@@ -333,12 +334,12 @@ func (m *aioMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, s
oldID := uint64(srcAR.Start)
aioCtx, ok := am.contexts[oldID]
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
aioCtx.mu.Lock()
defer aioCtx.mu.Unlock()
if aioCtx.dead {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Use the new ID for the AIOContext.
am.contexts[uint64(dstAR.Start)] = aioCtx
@@ -399,7 +400,7 @@ func (mm *MemoryManager) NewAIOContext(ctx context.Context, events uint32) (uint
id := uint64(addr)
if !mm.aioManager.newAIOContext(events, id) {
mm.MUnmap(ctx, addr, aioRingBufferSize)
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
return id, nil
}
diff --git a/pkg/sentry/mm/mm_test.go b/pkg/sentry/mm/mm_test.go
index 1304b0a2f..84cb8158d 100644
--- a/pkg/sentry/mm/mm_test.go
+++ b/pkg/sentry/mm/mm_test.go
@@ -18,6 +18,7 @@ import (
"testing"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
@@ -25,7 +26,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
"gvisor.dev/gvisor/pkg/sentry/platform"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -171,7 +171,7 @@ func TestIOAfterUnmap(t *testing.T) {
}
n, err = mm.CopyIn(ctx, addr, b, usermem.IOOpts{})
- if err != syserror.EFAULT {
+ if !linuxerr.Equals(linuxerr.EFAULT, err) {
t.Errorf("CopyIn got err %v want EFAULT", err)
}
if n != 0 {
@@ -212,7 +212,7 @@ func TestIOAfterMProtect(t *testing.T) {
// Without IgnorePermissions, CopyOut should no longer succeed.
n, err = mm.CopyOut(ctx, addr, b, usermem.IOOpts{})
- if err != syserror.EFAULT {
+ if !linuxerr.Equals(linuxerr.EFAULT, err) {
t.Errorf("CopyOut got err %v want EFAULT", err)
}
if n != 0 {
@@ -249,7 +249,7 @@ func TestAIOPrepareAfterDestroy(t *testing.T) {
mm.DestroyAIOContext(ctx, id)
// Prepare should fail because aioCtx should be destroyed.
- if err := aioCtx.Prepare(); err != syserror.EINVAL {
+ if err := aioCtx.Prepare(); !linuxerr.Equals(linuxerr.EINVAL, err) {
t.Errorf("aioCtx.Prepare got err %v want nil", err)
} else if err == nil {
aioCtx.CancelPendingRequest()
diff --git a/pkg/sentry/mm/shm.go b/pkg/sentry/mm/shm.go
index 3130be80c..94d5112a1 100644
--- a/pkg/sentry/mm/shm.go
+++ b/pkg/sentry/mm/shm.go
@@ -16,16 +16,16 @@ package mm
import (
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel/shm"
- "gvisor.dev/gvisor/pkg/syserror"
)
// DetachShm unmaps a sysv shared memory segment.
func (mm *MemoryManager) DetachShm(ctx context.Context, addr hostarch.Addr) error {
if addr != addr.RoundDown() {
// "... shmaddr is not aligned on a page boundary." - man shmdt(2)
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
var detached *shm.Shm
@@ -48,7 +48,7 @@ func (mm *MemoryManager) DetachShm(ctx context.Context, addr hostarch.Addr) erro
if detached == nil {
// There is no shared memory segment attached at addr.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Remove all vmas that could have been created by the same attach.
diff --git a/pkg/sentry/mm/special_mappable.go b/pkg/sentry/mm/special_mappable.go
index e748b7ff8..feafe76c1 100644
--- a/pkg/sentry/mm/special_mappable.go
+++ b/pkg/sentry/mm/special_mappable.go
@@ -16,6 +16,7 @@ package mm
import (
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
@@ -144,11 +145,11 @@ func (m *SpecialMappable) Length() uint64 {
// leak (b/143656263). Delete this function along with VFS1.
func NewSharedAnonMappable(length uint64, mfp pgalloc.MemoryFileProvider) (*SpecialMappable, error) {
if length == 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
alignedLen, ok := hostarch.Addr(length).RoundUp()
if !ok {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
fr, err := mfp.MemoryFile().Allocate(uint64(alignedLen), usage.Anonymous)
if err != nil {
diff --git a/pkg/sentry/mm/syscalls.go b/pkg/sentry/mm/syscalls.go
index 7ad6b7c21..b51ec6aa7 100644
--- a/pkg/sentry/mm/syscalls.go
+++ b/pkg/sentry/mm/syscalls.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/kernel/futex"
@@ -74,7 +75,7 @@ func (mm *MemoryManager) HandleUserFault(ctx context.Context, addr hostarch.Addr
// MMap establishes a memory mapping.
func (mm *MemoryManager) MMap(ctx context.Context, opts memmap.MMapOpts) (hostarch.Addr, error) {
if opts.Length == 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
length, ok := hostarch.Addr(opts.Length).RoundUp()
if !ok {
@@ -85,7 +86,7 @@ func (mm *MemoryManager) MMap(ctx context.Context, opts memmap.MMapOpts) (hostar
if opts.Mappable != nil {
// Offset must be aligned.
if hostarch.Addr(opts.Offset).RoundDown() != hostarch.Addr(opts.Offset) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Offset + length must not overflow.
if end := opts.Offset + opts.Length; end < opts.Offset {
@@ -99,19 +100,19 @@ func (mm *MemoryManager) MMap(ctx context.Context, opts memmap.MMapOpts) (hostar
// MAP_FIXED requires addr to be page-aligned; non-fixed mappings
// don't.
if opts.Fixed {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
opts.Addr = opts.Addr.RoundDown()
}
if !opts.MaxPerms.SupersetOf(opts.Perms) {
- return 0, syserror.EACCES
+ return 0, linuxerr.EACCES
}
if opts.Unmap && !opts.Fixed {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if opts.GrowsDown && opts.Mappable != nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Get the new vma.
@@ -203,6 +204,7 @@ func (mm *MemoryManager) populateVMA(ctx context.Context, vseg vmaIterator, ar h
// * vseg.Range().IsSupersetOf(ar).
//
// Postconditions: mm.mappingMu will be unlocked.
+// +checklocksrelease:mm.mappingMu
func (mm *MemoryManager) populateVMAAndUnlock(ctx context.Context, vseg vmaIterator, ar hostarch.AddrRange, precommit bool) {
// See populateVMA above for commentary.
if !vseg.ValuePtr().effectivePerms.Any() {
@@ -281,18 +283,18 @@ func (mm *MemoryManager) MapStack(ctx context.Context) (hostarch.AddrRange, erro
// MUnmap implements the semantics of Linux's munmap(2).
func (mm *MemoryManager) MUnmap(ctx context.Context, addr hostarch.Addr, length uint64) error {
if addr != addr.RoundDown() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if length == 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
la, ok := hostarch.Addr(length).RoundUp()
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
ar, ok := addr.ToRange(uint64(la))
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
mm.mappingMu.Lock()
@@ -331,7 +333,7 @@ const (
func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldSize uint64, newSize uint64, opts MRemapOpts) (hostarch.Addr, error) {
// "Note that old_address has to be page aligned." - mremap(2)
if oldAddr.RoundDown() != oldAddr {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Linux treats an old_size that rounds up to 0 as 0, which is otherwise a
@@ -340,13 +342,13 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldS
oldSize = uint64(oldSizeAddr)
newSizeAddr, ok := hostarch.Addr(newSize).RoundUp()
if !ok || newSizeAddr == 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
newSize = uint64(newSizeAddr)
oldEnd, ok := oldAddr.AddLength(oldSize)
if !ok {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
mm.mappingMu.Lock()
@@ -379,7 +381,7 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldS
mlockLimit := limits.FromContext(ctx).Get(limits.MemoryLocked).Cur
if creds := auth.CredentialsFromContext(ctx); !creds.HasCapabilityIn(linux.CAP_IPC_LOCK, creds.UserNamespace.Root()) {
if newLockedAS := mm.lockedAS - oldSize + newSize; newLockedAS > mlockLimit {
- return 0, syserror.EAGAIN
+ return 0, linuxerr.EAGAIN
}
}
}
@@ -450,15 +452,15 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldS
case MRemapMustMove:
newAddr := opts.NewAddr
if newAddr.RoundDown() != newAddr {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
var ok bool
newAR, ok = newAddr.ToRange(newSize)
if !ok {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if (hostarch.AddrRange{oldAddr, oldEnd}).Overlaps(newAR) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Check that the new region is valid.
@@ -504,7 +506,7 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldS
if vma := vseg.ValuePtr(); vma.mappable != nil {
// Check that offset+length does not overflow.
if vma.off+uint64(newAR.Length()) < vma.off {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Inform the Mappable, if any, of the new mapping.
if err := vma.mappable.CopyMapping(ctx, mm, oldAR, newAR, vseg.mappableOffsetAt(oldAR.Start), vma.canWriteMappableLocked()); err != nil {
@@ -590,7 +592,7 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr hostarch.Addr, oldS
// MProtect implements the semantics of Linux's mprotect(2).
func (mm *MemoryManager) MProtect(addr hostarch.Addr, length uint64, realPerms hostarch.AccessType, growsDown bool) error {
if addr.RoundDown() != addr {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if length == 0 {
return nil
@@ -618,7 +620,7 @@ func (mm *MemoryManager) MProtect(addr hostarch.Addr, length uint64, realPerms h
}
if growsDown {
if !vseg.ValuePtr().growsDown {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if ar.End <= vseg.Start() {
return syserror.ENOMEM
@@ -644,7 +646,7 @@ func (mm *MemoryManager) MProtect(addr hostarch.Addr, length uint64, realPerms h
// Check for permission validity before splitting vmas, for consistency
// with Linux.
if !vseg.ValuePtr().maxPerms.SupersetOf(effectivePerms) {
- return syserror.EACCES
+ return linuxerr.EACCES
}
vseg = mm.vmas.Isolate(vseg, ar)
@@ -711,7 +713,7 @@ func (mm *MemoryManager) Brk(ctx context.Context, addr hostarch.Addr) (hostarch.
if addr < mm.brk.Start {
addr = mm.brk.End
mm.mappingMu.Unlock()
- return addr, syserror.EINVAL
+ return addr, linuxerr.EINVAL
}
// TODO(gvisor.dev/issue/156): This enforces RLIMIT_DATA, but is
@@ -780,7 +782,7 @@ func (mm *MemoryManager) MLock(ctx context.Context, addr hostarch.Addr, length u
la, _ := hostarch.Addr(length + addr.PageOffset()).RoundUp()
ar, ok := addr.RoundDown().ToRange(uint64(la))
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
mm.mappingMu.Lock()
@@ -792,7 +794,7 @@ func (mm *MemoryManager) MLock(ctx context.Context, addr hostarch.Addr, length u
mlockLimit := limits.FromContext(ctx).Get(limits.MemoryLocked).Cur
if mlockLimit == 0 {
mm.mappingMu.Unlock()
- return syserror.EPERM
+ return linuxerr.EPERM
}
if newLockedAS := mm.lockedAS + uint64(ar.Length()) - mm.mlockedBytesRangeLocked(ar); newLockedAS > mlockLimit {
mm.mappingMu.Unlock()
@@ -855,11 +857,11 @@ func (mm *MemoryManager) MLock(ctx context.Context, addr hostarch.Addr, length u
mm.activeMu.Unlock()
mm.mappingMu.RUnlock()
// Linux: mm/mlock.c:__mlock_posix_error_return()
- if err == syserror.EFAULT {
+ if linuxerr.Equals(linuxerr.EFAULT, err) {
return syserror.ENOMEM
}
- if err == syserror.ENOMEM {
- return syserror.EAGAIN
+ if linuxerr.Equals(linuxerr.ENOMEM, err) {
+ return linuxerr.EAGAIN
}
return err
}
@@ -898,7 +900,7 @@ type MLockAllOpts struct {
// depending on opts.
func (mm *MemoryManager) MLockAll(ctx context.Context, opts MLockAllOpts) error {
if !opts.Current && !opts.Future {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
mm.mappingMu.Lock()
@@ -911,7 +913,7 @@ func (mm *MemoryManager) MLockAll(ctx context.Context, opts MLockAllOpts) error
mlockLimit := limits.FromContext(ctx).Get(limits.MemoryLocked).Cur
if mlockLimit == 0 {
mm.mappingMu.Unlock()
- return syserror.EPERM
+ return linuxerr.EPERM
}
if uint64(mm.vmas.Span()) > mlockLimit {
mm.mappingMu.Unlock()
@@ -979,13 +981,13 @@ func (mm *MemoryManager) NumaPolicy(addr hostarch.Addr) (linux.NumaPolicy, uint6
// SetNumaPolicy implements the semantics of Linux's mbind().
func (mm *MemoryManager) SetNumaPolicy(addr hostarch.Addr, length uint64, policy linux.NumaPolicy, nodemask uint64) error {
if !addr.IsPageAligned() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Linux allows this to overflow.
la, _ := hostarch.Addr(length).RoundUp()
ar, ok := addr.ToRange(uint64(la))
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if ar.Length() == 0 {
return nil
@@ -1021,7 +1023,7 @@ func (mm *MemoryManager) SetNumaPolicy(addr hostarch.Addr, length uint64, policy
func (mm *MemoryManager) SetDontFork(addr hostarch.Addr, length uint64, dontfork bool) error {
ar, ok := addr.ToRange(length)
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
mm.mappingMu.Lock()
@@ -1047,7 +1049,7 @@ func (mm *MemoryManager) SetDontFork(addr hostarch.Addr, length uint64, dontfork
func (mm *MemoryManager) Decommit(addr hostarch.Addr, length uint64) error {
ar, ok := addr.ToRange(length)
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
mm.mappingMu.RLock()
@@ -1063,7 +1065,7 @@ func (mm *MemoryManager) Decommit(addr hostarch.Addr, length uint64) error {
for vseg := mm.vmas.LowerBoundSegment(ar.Start); vseg.Ok() && vseg.Start() < ar.End; vseg = vseg.NextSegment() {
vma := vseg.ValuePtr()
if vma.mlockMode != memmap.MLockNone {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
vsegAR := vseg.Range().Intersect(ar)
// pseg should already correspond to either this vma or a later one,
@@ -1114,7 +1116,7 @@ type MSyncOpts struct {
// MSync implements the semantics of Linux's msync().
func (mm *MemoryManager) MSync(ctx context.Context, addr hostarch.Addr, length uint64, opts MSyncOpts) error {
if addr != addr.RoundDown() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if length == 0 {
return nil
@@ -1150,7 +1152,7 @@ func (mm *MemoryManager) MSync(ctx context.Context, addr hostarch.Addr, length u
vma := vseg.ValuePtr()
if opts.Invalidate && vma.mlockMode != memmap.MLockNone {
mm.mappingMu.RUnlock()
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
// It's only possible to have dirtied the Mappable through a shared
// mapping. Don't check if the mapping is writable, because mprotect
diff --git a/pkg/sentry/mm/vma.go b/pkg/sentry/mm/vma.go
index 0d019e41d..a411eabbf 100644
--- a/pkg/sentry/mm/vma.go
+++ b/pkg/sentry/mm/vma.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -66,14 +67,14 @@ func (mm *MemoryManager) createVMALocked(ctx context.Context, opts memmap.MMapOp
if creds := auth.CredentialsFromContext(ctx); !creds.HasCapabilityIn(linux.CAP_IPC_LOCK, creds.UserNamespace.Root()) {
mlockLimit := limits.FromContext(ctx).Get(limits.MemoryLocked).Cur
if mlockLimit == 0 {
- return vmaIterator{}, hostarch.AddrRange{}, syserror.EPERM
+ return vmaIterator{}, hostarch.AddrRange{}, linuxerr.EPERM
}
newLockedAS := mm.lockedAS + opts.Length
if opts.Unmap {
newLockedAS -= mm.mlockedBytesRangeLocked(ar)
}
if newLockedAS > mlockLimit {
- return vmaIterator{}, hostarch.AddrRange{}, syserror.EAGAIN
+ return vmaIterator{}, hostarch.AddrRange{}, linuxerr.EAGAIN
}
}
}
@@ -296,7 +297,7 @@ func (mm *MemoryManager) getVMAsLocked(ctx context.Context, ar hostarch.AddrRang
perms = vma.maxPerms
}
if !perms.SupersetOf(at) {
- return vbegin, vgap, syserror.EPERM
+ return vbegin, vgap, linuxerr.EPERM
}
addr = vseg.End()
diff --git a/pkg/sentry/pgalloc/BUILD b/pkg/sentry/pgalloc/BUILD
index 57d73d770..d351869ef 100644
--- a/pkg/sentry/pgalloc/BUILD
+++ b/pkg/sentry/pgalloc/BUILD
@@ -85,6 +85,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/memutil",
diff --git a/pkg/sentry/pgalloc/pgalloc.go b/pkg/sentry/pgalloc/pgalloc.go
index b81292c46..0c8542485 100644
--- a/pkg/sentry/pgalloc/pgalloc.go
+++ b/pkg/sentry/pgalloc/pgalloc.go
@@ -31,6 +31,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/safemem"
@@ -674,7 +675,7 @@ func (f *MemoryFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (s
panic(fmt.Sprintf("invalid range: %v", fr))
}
if at.Execute {
- return safemem.BlockSeq{}, syserror.EACCES
+ return safemem.BlockSeq{}, linuxerr.EACCES
}
chunks := ((fr.End + chunkMask) >> chunkShift) - (fr.Start >> chunkShift)
@@ -944,7 +945,7 @@ func (f *MemoryFile) updateUsageLocked(currentUsage uint64, checkCommitted func(
// NOTE(b/165896008): mincore (which is passed as checkCommitted)
// by f.UpdateUsage() might take a really long time. So unlock f.mu
// while checkCommitted runs.
- f.mu.Unlock()
+ f.mu.Unlock() // +checklocksforce
err := checkCommitted(s, buf)
f.mu.Lock()
if err != nil {
@@ -1062,10 +1063,20 @@ func (f *MemoryFile) runReclaim() {
break
}
- // If ManualZeroing is in effect, pages will be zeroed on allocation
- // and may not be freed by decommitFile, so calling decommitFile is
- // unnecessary.
- if !f.opts.ManualZeroing {
+ if f.opts.ManualZeroing {
+ // If ManualZeroing is in effect, only hugepage-aligned regions may
+ // be safely passed to decommitFile. Pages will be zeroed on
+ // reallocation, so we don't need to perform any manual zeroing
+ // here, whether or not decommitFile succeeds.
+ if startAddr, ok := hostarch.Addr(fr.Start).HugeRoundUp(); ok {
+ if endAddr := hostarch.Addr(fr.End).HugeRoundDown(); startAddr < endAddr {
+ decommitFR := memmap.FileRange{uint64(startAddr), uint64(endAddr)}
+ if err := f.decommitFile(decommitFR); err != nil {
+ log.Warningf("Reclaim failed to decommit %v: %v", decommitFR, err)
+ }
+ }
+ }
+ } else {
if err := f.decommitFile(fr); err != nil {
log.Warningf("Reclaim failed to decommit %v: %v", fr, err)
// Zero the pages manually. This won't reduce memory usage, but at
diff --git a/pkg/sentry/platform/kvm/BUILD b/pkg/sentry/platform/kvm/BUILD
index 77079cae9..8a490b3de 100644
--- a/pkg/sentry/platform/kvm/BUILD
+++ b/pkg/sentry/platform/kvm/BUILD
@@ -79,6 +79,7 @@ go_test(
"requires-kvm",
],
deps = [
+ "//pkg/abi/linux",
"//pkg/hostarch",
"//pkg/ring0",
"//pkg/ring0/pagetables",
diff --git a/pkg/sentry/platform/kvm/bluepill_arm64.go b/pkg/sentry/platform/kvm/bluepill_arm64.go
index 578852c3f..9e5c52923 100644
--- a/pkg/sentry/platform/kvm/bluepill_arm64.go
+++ b/pkg/sentry/platform/kvm/bluepill_arm64.go
@@ -25,29 +25,6 @@ import (
var (
// The action for bluepillSignal is changed by sigaction().
bluepillSignal = unix.SIGILL
-
- // vcpuSErrBounce is the event of system error for bouncing KVM.
- vcpuSErrBounce = kvmVcpuEvents{
- exception: exception{
- sErrPending: 1,
- },
- }
-
- // vcpuSErrNMI is the event of system error to trigger sigbus.
- vcpuSErrNMI = kvmVcpuEvents{
- exception: exception{
- sErrPending: 1,
- sErrHasEsr: 1,
- sErrEsr: _ESR_ELx_SERR_NMI,
- },
- }
-
- // vcpuExtDabt is the event of ext_dabt.
- vcpuExtDabt = kvmVcpuEvents{
- exception: exception{
- extDabtPending: 1,
- },
- }
)
// getTLS returns the value of TPIDR_EL0 register.
diff --git a/pkg/sentry/platform/kvm/bluepill_arm64_unsafe.go b/pkg/sentry/platform/kvm/bluepill_arm64_unsafe.go
index 07fc4f216..f105fdbd0 100644
--- a/pkg/sentry/platform/kvm/bluepill_arm64_unsafe.go
+++ b/pkg/sentry/platform/kvm/bluepill_arm64_unsafe.go
@@ -80,11 +80,18 @@ func getHypercallID(addr uintptr) int {
//
//go:nosplit
func bluepillStopGuest(c *vCPU) {
+ // vcpuSErrBounce is the event of system error for bouncing KVM.
+ vcpuSErrBounce := &kvmVcpuEvents{
+ exception: exception{
+ sErrPending: 1,
+ },
+ }
+
if _, _, errno := unix.RawSyscall( // escapes: no.
unix.SYS_IOCTL,
uintptr(c.fd),
_KVM_SET_VCPU_EVENTS,
- uintptr(unsafe.Pointer(&vcpuSErrBounce))); errno != 0 {
+ uintptr(unsafe.Pointer(vcpuSErrBounce))); errno != 0 {
throw("bounce sErr injection failed")
}
}
@@ -93,12 +100,21 @@ func bluepillStopGuest(c *vCPU) {
//
//go:nosplit
func bluepillSigBus(c *vCPU) {
+ // vcpuSErrNMI is the event of system error to trigger sigbus.
+ vcpuSErrNMI := &kvmVcpuEvents{
+ exception: exception{
+ sErrPending: 1,
+ sErrHasEsr: 1,
+ sErrEsr: _ESR_ELx_SERR_NMI,
+ },
+ }
+
// Host must support ARM64_HAS_RAS_EXTN.
if _, _, errno := unix.RawSyscall( // escapes: no.
unix.SYS_IOCTL,
uintptr(c.fd),
_KVM_SET_VCPU_EVENTS,
- uintptr(unsafe.Pointer(&vcpuSErrNMI))); errno != 0 {
+ uintptr(unsafe.Pointer(vcpuSErrNMI))); errno != 0 {
if errno == unix.EINVAL {
throw("No ARM64_HAS_RAS_EXTN feature in host.")
}
@@ -110,11 +126,18 @@ func bluepillSigBus(c *vCPU) {
//
//go:nosplit
func bluepillExtDabt(c *vCPU) {
+ // vcpuExtDabt is the event of ext_dabt.
+ vcpuExtDabt := &kvmVcpuEvents{
+ exception: exception{
+ extDabtPending: 1,
+ },
+ }
+
if _, _, errno := unix.RawSyscall( // escapes: no.
unix.SYS_IOCTL,
uintptr(c.fd),
_KVM_SET_VCPU_EVENTS,
- uintptr(unsafe.Pointer(&vcpuExtDabt))); errno != 0 {
+ uintptr(unsafe.Pointer(vcpuExtDabt))); errno != 0 {
throw("ext_dabt injection failed")
}
}
diff --git a/pkg/sentry/platform/kvm/context.go b/pkg/sentry/platform/kvm/context.go
index f4d4473a8..183e741ea 100644
--- a/pkg/sentry/platform/kvm/context.go
+++ b/pkg/sentry/platform/kvm/context.go
@@ -17,6 +17,7 @@ package kvm
import (
"sync/atomic"
+ "gvisor.dev/gvisor/pkg/abi/linux"
pkgcontext "gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0"
@@ -32,15 +33,15 @@ type context struct {
// machine is the parent machine, and is immutable.
machine *machine
- // info is the arch.SignalInfo cached for this context.
- info arch.SignalInfo
+ // info is the linux.SignalInfo cached for this context.
+ info linux.SignalInfo
// interrupt is the interrupt context.
interrupt interrupt.Forwarder
}
// Switch runs the provided context in the given address space.
-func (c *context) Switch(ctx pkgcontext.Context, mm platform.MemoryManager, ac arch.Context, _ int32) (*arch.SignalInfo, hostarch.AccessType, error) {
+func (c *context) Switch(ctx pkgcontext.Context, mm platform.MemoryManager, ac arch.Context, _ int32) (*linux.SignalInfo, hostarch.AccessType, error) {
as := mm.AddressSpace()
localAS := as.(*addressSpace)
diff --git a/pkg/sentry/platform/kvm/kvm_amd64_test.go b/pkg/sentry/platform/kvm/kvm_amd64_test.go
index b8dd1e4a5..b1cab89a0 100644
--- a/pkg/sentry/platform/kvm/kvm_amd64_test.go
+++ b/pkg/sentry/platform/kvm/kvm_amd64_test.go
@@ -19,6 +19,7 @@ package kvm
import (
"testing"
+ "gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -30,7 +31,7 @@ func TestSegments(t *testing.T) {
applicationTest(t, true, testutil.TwiddleSegments, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
testutil.SetTestSegments(regs)
for {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -55,7 +56,7 @@ func stmxcsr(addr *uint32)
func TestMXCSR(t *testing.T) {
applicationTest(t, true, testutil.SyscallLoop, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
- var si arch.SignalInfo
+ var si linux.SignalInfo
switchOpts := ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
diff --git a/pkg/sentry/platform/kvm/kvm_test.go b/pkg/sentry/platform/kvm/kvm_test.go
index ceff09a60..fe570aff9 100644
--- a/pkg/sentry/platform/kvm/kvm_test.go
+++ b/pkg/sentry/platform/kvm/kvm_test.go
@@ -22,6 +22,7 @@ import (
"time"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
@@ -157,7 +158,7 @@ func applicationTest(t testHarness, useHostMappings bool, target func(), fn func
func TestApplicationSyscall(t *testing.T) {
applicationTest(t, true, testutil.SyscallLoop, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -171,7 +172,7 @@ func TestApplicationSyscall(t *testing.T) {
return false
})
applicationTest(t, true, testutil.SyscallLoop, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -188,7 +189,7 @@ func TestApplicationSyscall(t *testing.T) {
func TestApplicationFault(t *testing.T) {
applicationTest(t, true, testutil.Touch, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
testutil.SetTouchTarget(regs, nil) // Cause fault.
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -203,7 +204,7 @@ func TestApplicationFault(t *testing.T) {
})
applicationTest(t, true, testutil.Touch, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
testutil.SetTouchTarget(regs, nil) // Cause fault.
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -221,7 +222,7 @@ func TestRegistersSyscall(t *testing.T) {
applicationTest(t, true, testutil.TwiddleRegsSyscall, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
testutil.SetTestRegs(regs) // Fill values for all registers.
for {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -244,7 +245,7 @@ func TestRegistersFault(t *testing.T) {
applicationTest(t, true, testutil.TwiddleRegsFault, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
testutil.SetTestRegs(regs) // Fill values for all registers.
for {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -270,7 +271,7 @@ func TestBounce(t *testing.T) {
time.Sleep(time.Millisecond)
c.BounceToKernel()
}()
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -285,7 +286,7 @@ func TestBounce(t *testing.T) {
time.Sleep(time.Millisecond)
c.BounceToKernel()
}()
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -317,7 +318,7 @@ func TestBounceStress(t *testing.T) {
c.BounceToKernel()
}()
randomSleep()
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -338,7 +339,7 @@ func TestInvalidate(t *testing.T) {
applicationTest(t, true, testutil.Touch, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
testutil.SetTouchTarget(regs, &data) // Read legitimate value.
for {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -353,7 +354,7 @@ func TestInvalidate(t *testing.T) {
// Unmap the page containing data & invalidate.
pt.Unmap(hostarch.Addr(reflect.ValueOf(&data).Pointer() & ^uintptr(hostarch.PageSize-1)), hostarch.PageSize)
for {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -371,13 +372,13 @@ func TestInvalidate(t *testing.T) {
}
// IsFault returns true iff the given signal represents a fault.
-func IsFault(err error, si *arch.SignalInfo) bool {
+func IsFault(err error, si *linux.SignalInfo) bool {
return err == platform.ErrContextSignal && si.Signo == int32(unix.SIGSEGV)
}
func TestEmptyAddressSpace(t *testing.T) {
applicationTest(t, false, testutil.SyscallLoop, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -391,7 +392,7 @@ func TestEmptyAddressSpace(t *testing.T) {
return false
})
applicationTest(t, false, testutil.SyscallLoop, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -467,7 +468,7 @@ func BenchmarkApplicationSyscall(b *testing.B) {
a int // Count for ErrContextInterrupt.
)
applicationTest(b, true, testutil.SyscallLoop, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
@@ -504,7 +505,7 @@ func BenchmarkWorldSwitchToUserRoundtrip(b *testing.B) {
a int
)
applicationTest(b, true, testutil.SyscallLoop, func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
- var si arch.SignalInfo
+ var si linux.SignalInfo
if _, err := c.SwitchToUser(ring0.SwitchOpts{
Registers: regs,
FloatingPointState: &dummyFPState,
diff --git a/pkg/sentry/platform/kvm/machine_amd64.go b/pkg/sentry/platform/kvm/machine_amd64.go
index 9a2337654..7a10fd812 100644
--- a/pkg/sentry/platform/kvm/machine_amd64.go
+++ b/pkg/sentry/platform/kvm/machine_amd64.go
@@ -23,11 +23,11 @@ import (
"runtime/debug"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
"gvisor.dev/gvisor/pkg/sentry/platform"
ktime "gvisor.dev/gvisor/pkg/sentry/time"
@@ -264,10 +264,10 @@ func (c *vCPU) setSystemTime() error {
// nonCanonical generates a canonical address return.
//
//go:nosplit
-func nonCanonical(addr uint64, signal int32, info *arch.SignalInfo) (hostarch.AccessType, error) {
- *info = arch.SignalInfo{
+func nonCanonical(addr uint64, signal int32, info *linux.SignalInfo) (hostarch.AccessType, error) {
+ *info = linux.SignalInfo{
Signo: signal,
- Code: arch.SignalInfoKernel,
+ Code: linux.SI_KERNEL,
}
info.SetAddr(addr) // Include address.
return hostarch.NoAccess, platform.ErrContextSignal
@@ -276,7 +276,7 @@ func nonCanonical(addr uint64, signal int32, info *arch.SignalInfo) (hostarch.Ac
// fault generates an appropriate fault return.
//
//go:nosplit
-func (c *vCPU) fault(signal int32, info *arch.SignalInfo) (hostarch.AccessType, error) {
+func (c *vCPU) fault(signal int32, info *linux.SignalInfo) (hostarch.AccessType, error) {
bluepill(c) // Probably no-op, but may not be.
faultAddr := ring0.ReadCR2()
code, user := c.ErrorCode()
@@ -287,7 +287,7 @@ func (c *vCPU) fault(signal int32, info *arch.SignalInfo) (hostarch.AccessType,
return hostarch.NoAccess, platform.ErrContextInterrupt
}
// Reset the pointed SignalInfo.
- *info = arch.SignalInfo{Signo: signal}
+ *info = linux.SignalInfo{Signo: signal}
info.SetAddr(uint64(faultAddr))
accessType := hostarch.AccessType{
Read: code&(1<<1) == 0,
@@ -325,7 +325,7 @@ func prefaultFloatingPointState(data *fpu.State) {
}
// SwitchToUser unpacks architectural-details.
-func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo) (hostarch.AccessType, error) {
+func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *linux.SignalInfo) (hostarch.AccessType, error) {
// Check for canonical addresses.
if regs := switchOpts.Registers; !ring0.IsCanonical(regs.Rip) {
return nonCanonical(regs.Rip, int32(unix.SIGSEGV), info)
@@ -371,7 +371,7 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
return c.fault(int32(unix.SIGSEGV), info)
case ring0.Debug, ring0.Breakpoint:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGTRAP),
Code: 1, // TRAP_BRKPT (breakpoint).
}
@@ -383,9 +383,9 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
ring0.BoundRangeExceeded,
ring0.InvalidTSS,
ring0.StackSegmentFault:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGSEGV),
- Code: arch.SignalInfoKernel,
+ Code: linux.SI_KERNEL,
}
info.SetAddr(switchOpts.Registers.Rip) // Include address.
if vector == ring0.GeneralProtectionFault {
@@ -397,7 +397,7 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
return hostarch.AccessType{}, platform.ErrContextSignal
case ring0.InvalidOpcode:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGILL),
Code: 1, // ILL_ILLOPC (illegal opcode).
}
@@ -405,7 +405,7 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
return hostarch.AccessType{}, platform.ErrContextSignal
case ring0.DivideByZero:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGFPE),
Code: 1, // FPE_INTDIV (divide by zero).
}
@@ -413,7 +413,7 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
return hostarch.AccessType{}, platform.ErrContextSignal
case ring0.Overflow:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGFPE),
Code: 2, // FPE_INTOVF (integer overflow).
}
@@ -422,7 +422,7 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
case ring0.X87FloatingPointException,
ring0.SIMDFloatingPointException:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGFPE),
Code: 7, // FPE_FLTINV (invalid operation).
}
@@ -433,7 +433,7 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
return hostarch.NoAccess, platform.ErrContextInterrupt
case ring0.AlignmentCheck:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGBUS),
Code: 2, // BUS_ADRERR (physical address does not exist).
}
@@ -469,7 +469,7 @@ func availableRegionsForSetMem() (phyRegions []physicalRegion) {
}
func (m *machine) mapUpperHalf(pageTable *pagetables.PageTables) {
- // Map all the executible regions so that all the entry functions
+ // Map all the executable regions so that all the entry functions
// are mapped in the upper half.
applyVirtualRegions(func(vr virtualRegion) {
if excludeVirtualRegion(vr) || vr.filename == "[vsyscall]" {
@@ -485,7 +485,7 @@ func (m *machine) mapUpperHalf(pageTable *pagetables.PageTables) {
pageTable.Map(
hostarch.Addr(ring0.KernelStartAddress|r.virtual),
r.length,
- pagetables.MapOpts{AccessType: hostarch.Execute},
+ pagetables.MapOpts{AccessType: hostarch.Execute, Global: true},
physical)
}
})
@@ -498,7 +498,7 @@ func (m *machine) mapUpperHalf(pageTable *pagetables.PageTables) {
pageTable.Map(
hostarch.Addr(ring0.KernelStartAddress|start),
regionLen,
- pagetables.MapOpts{AccessType: hostarch.ReadWrite},
+ pagetables.MapOpts{AccessType: hostarch.ReadWrite, Global: true},
physical)
}
}
diff --git a/pkg/sentry/platform/kvm/machine_arm64.go b/pkg/sentry/platform/kvm/machine_arm64.go
index 8926b1d9f..edaccf9bc 100644
--- a/pkg/sentry/platform/kvm/machine_arm64.go
+++ b/pkg/sentry/platform/kvm/machine_arm64.go
@@ -21,10 +21,10 @@ import (
"sync/atomic"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
"gvisor.dev/gvisor/pkg/sentry/platform"
)
@@ -126,10 +126,10 @@ func availableRegionsForSetMem() (phyRegions []physicalRegion) {
// nonCanonical generates a canonical address return.
//
//go:nosplit
-func nonCanonical(addr uint64, signal int32, info *arch.SignalInfo) (hostarch.AccessType, error) {
- *info = arch.SignalInfo{
+func nonCanonical(addr uint64, signal int32, info *linux.SignalInfo) (hostarch.AccessType, error) {
+ *info = linux.SignalInfo{
Signo: signal,
- Code: arch.SignalInfoKernel,
+ Code: linux.SI_KERNEL,
}
info.SetAddr(addr) // Include address.
return hostarch.NoAccess, platform.ErrContextSignal
@@ -157,7 +157,7 @@ func isWriteFault(code uint64) bool {
// fault generates an appropriate fault return.
//
//go:nosplit
-func (c *vCPU) fault(signal int32, info *arch.SignalInfo) (hostarch.AccessType, error) {
+func (c *vCPU) fault(signal int32, info *linux.SignalInfo) (hostarch.AccessType, error) {
bluepill(c) // Probably no-op, but may not be.
faultAddr := c.GetFaultAddr()
code, user := c.ErrorCode()
@@ -170,7 +170,7 @@ func (c *vCPU) fault(signal int32, info *arch.SignalInfo) (hostarch.AccessType,
}
// Reset the pointed SignalInfo.
- *info = arch.SignalInfo{Signo: signal}
+ *info = linux.SignalInfo{Signo: signal}
info.SetAddr(uint64(faultAddr))
ret := code & _ESR_ELx_FSC
diff --git a/pkg/sentry/platform/kvm/machine_arm64_unsafe.go b/pkg/sentry/platform/kvm/machine_arm64_unsafe.go
index 92edc992b..f6aa519b1 100644
--- a/pkg/sentry/platform/kvm/machine_arm64_unsafe.go
+++ b/pkg/sentry/platform/kvm/machine_arm64_unsafe.go
@@ -23,10 +23,10 @@ import (
"unsafe"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
"gvisor.dev/gvisor/pkg/sentry/platform"
ktime "gvisor.dev/gvisor/pkg/sentry/time"
@@ -140,22 +140,15 @@ func (c *vCPU) initArchState() error {
// vbar_el1
reg.id = _KVM_ARM64_REGS_VBAR_EL1
-
- fromLocation := reflect.ValueOf(ring0.Vectors).Pointer()
- offset := fromLocation & (1<<11 - 1)
- if offset != 0 {
- offset = 1<<11 - offset
- }
-
- toLocation := fromLocation + offset
- data = uint64(ring0.KernelStartAddress | toLocation)
+ vectorLocation := reflect.ValueOf(ring0.Vectors).Pointer()
+ data = uint64(ring0.KernelStartAddress | vectorLocation)
if err := c.setOneRegister(&reg); err != nil {
return err
}
// Use the address of the exception vector table as
// the MMIO address base.
- arm64HypercallMMIOBase = toLocation
+ arm64HypercallMMIOBase = vectorLocation
// Initialize the PCID database.
if hasGuestPCID {
@@ -272,7 +265,7 @@ func (c *vCPU) getOneRegister(reg *kvmOneReg) error {
}
// SwitchToUser unpacks architectural-details.
-func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo) (hostarch.AccessType, error) {
+func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *linux.SignalInfo) (hostarch.AccessType, error) {
// Check for canonical addresses.
if regs := switchOpts.Registers; !ring0.IsCanonical(regs.Pc) {
return nonCanonical(regs.Pc, int32(unix.SIGSEGV), info)
@@ -319,14 +312,14 @@ func (c *vCPU) SwitchToUser(switchOpts ring0.SwitchOpts, info *arch.SignalInfo)
case ring0.El0SyncUndef:
return c.fault(int32(unix.SIGILL), info)
case ring0.El0SyncDbg:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGTRAP),
Code: 1, // TRAP_BRKPT (breakpoint).
}
info.SetAddr(switchOpts.Registers.Pc) // Include address.
return hostarch.AccessType{}, platform.ErrContextSignal
case ring0.El0SyncSpPc:
- *info = arch.SignalInfo{
+ *info = linux.SignalInfo{
Signo: int32(unix.SIGBUS),
Code: 2, // BUS_ADRERR (physical address does not exist).
}
diff --git a/pkg/sentry/platform/platform.go b/pkg/sentry/platform/platform.go
index ef7814a6f..a26bc2316 100644
--- a/pkg/sentry/platform/platform.go
+++ b/pkg/sentry/platform/platform.go
@@ -195,8 +195,8 @@ type Context interface {
// - nil: The Context invoked a system call.
//
// - ErrContextSignal: The Context was interrupted by a signal. The
- // returned *arch.SignalInfo contains information about the signal. If
- // arch.SignalInfo.Signo == SIGSEGV, the returned hostarch.AccessType
+ // returned *linux.SignalInfo contains information about the signal. If
+ // linux.SignalInfo.Signo == SIGSEGV, the returned hostarch.AccessType
// contains the access type of the triggering fault. The caller owns
// the returned SignalInfo.
//
@@ -207,7 +207,7 @@ type Context interface {
// concurrent call to Switch().
//
// - ErrContextCPUPreempted: See the definition of that error for details.
- Switch(ctx context.Context, mm MemoryManager, ac arch.Context, cpu int32) (*arch.SignalInfo, hostarch.AccessType, error)
+ Switch(ctx context.Context, mm MemoryManager, ac arch.Context, cpu int32) (*linux.SignalInfo, hostarch.AccessType, error)
// PullFullState() pulls a full state of the application thread.
//
diff --git a/pkg/sentry/platform/ptrace/ptrace.go b/pkg/sentry/platform/ptrace/ptrace.go
index 828458ce2..319b0cf1d 100644
--- a/pkg/sentry/platform/ptrace/ptrace.go
+++ b/pkg/sentry/platform/ptrace/ptrace.go
@@ -73,7 +73,7 @@ var (
type context struct {
// signalInfo is the signal info, if and when a signal is received.
- signalInfo arch.SignalInfo
+ signalInfo linux.SignalInfo
// interrupt is the interrupt context.
interrupt interrupt.Forwarder
@@ -96,7 +96,7 @@ type context struct {
}
// Switch runs the provided context in the given address space.
-func (c *context) Switch(ctx pkgcontext.Context, mm platform.MemoryManager, ac arch.Context, cpu int32) (*arch.SignalInfo, hostarch.AccessType, error) {
+func (c *context) Switch(ctx pkgcontext.Context, mm platform.MemoryManager, ac arch.Context, cpu int32) (*linux.SignalInfo, hostarch.AccessType, error) {
as := mm.AddressSpace()
s := as.(*subprocess)
isSyscall := s.switchToApp(c, ac)
diff --git a/pkg/sentry/platform/ptrace/ptrace_unsafe.go b/pkg/sentry/platform/ptrace/ptrace_unsafe.go
index facb96011..cc93396a9 100644
--- a/pkg/sentry/platform/ptrace/ptrace_unsafe.go
+++ b/pkg/sentry/platform/ptrace/ptrace_unsafe.go
@@ -101,7 +101,7 @@ func (t *thread) setFPRegs(fpState *fpu.State, fpLen uint64, useXsave bool) erro
}
// getSignalInfo retrieves information about the signal that caused the stop.
-func (t *thread) getSignalInfo(si *arch.SignalInfo) error {
+func (t *thread) getSignalInfo(si *linux.SignalInfo) error {
_, _, errno := unix.RawSyscall6(
unix.SYS_PTRACE,
unix.PTRACE_GETSIGINFO,
diff --git a/pkg/sentry/platform/ptrace/subprocess.go b/pkg/sentry/platform/ptrace/subprocess.go
index 9c73a725a..0931795c5 100644
--- a/pkg/sentry/platform/ptrace/subprocess.go
+++ b/pkg/sentry/platform/ptrace/subprocess.go
@@ -20,6 +20,7 @@ import (
"runtime"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/procid"
@@ -524,7 +525,7 @@ func (s *subprocess) switchToApp(c *context, ac arch.Context) bool {
// Check for interrupts, and ensure that future interrupts will signal t.
if !c.interrupt.Enable(t) {
// Pending interrupt; simulate.
- c.signalInfo = arch.SignalInfo{Signo: int32(platform.SignalInterrupt)}
+ c.signalInfo = linux.SignalInfo{Signo: int32(platform.SignalInterrupt)}
return false
}
defer c.interrupt.Disable()
diff --git a/pkg/sentry/platform/ptrace/subprocess_amd64.go b/pkg/sentry/platform/ptrace/subprocess_amd64.go
index 9252c0bd7..90b1ead56 100644
--- a/pkg/sentry/platform/ptrace/subprocess_amd64.go
+++ b/pkg/sentry/platform/ptrace/subprocess_amd64.go
@@ -155,7 +155,7 @@ func initChildProcessPPID(initregs *arch.Registers, ppid int32) {
//
// Note that this should only be called after verifying that the signalInfo has
// been generated by the kernel.
-func patchSignalInfo(regs *arch.Registers, signalInfo *arch.SignalInfo) {
+func patchSignalInfo(regs *arch.Registers, signalInfo *linux.SignalInfo) {
if linux.Signal(signalInfo.Signo) == linux.SIGSYS {
signalInfo.Signo = int32(linux.SIGSEGV)
diff --git a/pkg/sentry/platform/ptrace/subprocess_arm64.go b/pkg/sentry/platform/ptrace/subprocess_arm64.go
index c0cbc0686..e4257e3bf 100644
--- a/pkg/sentry/platform/ptrace/subprocess_arm64.go
+++ b/pkg/sentry/platform/ptrace/subprocess_arm64.go
@@ -138,7 +138,7 @@ func initChildProcessPPID(initregs *arch.Registers, ppid int32) {
//
// Note that this should only be called after verifying that the signalInfo has
// been generated by the kernel.
-func patchSignalInfo(regs *arch.Registers, signalInfo *arch.SignalInfo) {
+func patchSignalInfo(regs *arch.Registers, signalInfo *linux.SignalInfo) {
if linux.Signal(signalInfo.Signo) == linux.SIGSYS {
signalInfo.Signo = int32(linux.SIGSEGV)
diff --git a/pkg/sentry/sighandling/sighandling_unsafe.go b/pkg/sentry/sighandling/sighandling_unsafe.go
index d6a2fbe34..3fe5c6770 100644
--- a/pkg/sentry/sighandling/sighandling_unsafe.go
+++ b/pkg/sentry/sighandling/sighandling_unsafe.go
@@ -21,25 +21,16 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
)
-// FIXME(gvisor.dev/issue/214): Move to pkg/abi/linux along with definitions in
-// pkg/sentry/arch.
-type sigaction struct {
- handler uintptr
- flags uint64
- restorer uintptr
- mask uint64
-}
-
// IgnoreChildStop sets the SA_NOCLDSTOP flag, causing child processes to not
// generate SIGCHLD when they stop.
func IgnoreChildStop() error {
- var sa sigaction
+ var sa linux.SigAction
// Get the existing signal handler information, and set the flag.
if _, _, e := unix.RawSyscall6(unix.SYS_RT_SIGACTION, uintptr(unix.SIGCHLD), 0, uintptr(unsafe.Pointer(&sa)), linux.SignalSetSize, 0, 0); e != 0 {
return e
}
- sa.flags |= linux.SA_NOCLDSTOP
+ sa.Flags |= linux.SA_NOCLDSTOP
if _, _, e := unix.RawSyscall6(unix.SYS_RT_SIGACTION, uintptr(unix.SIGCHLD), uintptr(unsafe.Pointer(&sa)), 0, linux.SignalSetSize, 0, 0); e != 0 {
return e
}
diff --git a/pkg/sentry/socket/control/BUILD b/pkg/sentry/socket/control/BUILD
index 2029e7cf4..b2fc84181 100644
--- a/pkg/sentry/socket/control/BUILD
+++ b/pkg/sentry/socket/control/BUILD
@@ -16,6 +16,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/bits",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/marshal",
"//pkg/marshal/primitive",
@@ -25,7 +26,6 @@ go_library(
"//pkg/sentry/socket",
"//pkg/sentry/socket/unix/transport",
"//pkg/sentry/vfs",
- "//pkg/syserror",
],
)
diff --git a/pkg/sentry/socket/control/control.go b/pkg/sentry/socket/control/control.go
index 235b9c306..00a5e729a 100644
--- a/pkg/sentry/socket/control/control.go
+++ b/pkg/sentry/socket/control/control.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bits"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/marshal/primitive"
@@ -28,7 +29,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/socket"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
- "gvisor.dev/gvisor/pkg/syserror"
)
const maxInt = int(^uint(0) >> 1)
@@ -70,7 +70,7 @@ func NewSCMRights(t *kernel.Task, fds []int32) (SCMRights, error) {
file := t.GetFile(fd)
if file == nil {
files.Release(t)
- return nil, syserror.EBADF
+ return nil, linuxerr.EBADF
}
files = append(files, file)
}
@@ -169,7 +169,7 @@ func NewSCMCredentials(t *kernel.Task, cred linux.ControlMessageCredentials) (SC
return nil, err
}
if kernel.ThreadID(cred.PID) != t.ThreadGroup().ID() && !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.PIDNamespace().UserNamespace()) {
- return nil, syserror.EPERM
+ return nil, linuxerr.EPERM
}
return &scmCredentials{t, kuid, kgid}, nil
}
@@ -473,17 +473,17 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
for i := 0; i < len(buf); {
if i+linux.SizeOfControlMessageHeader > len(buf) {
- return cmsgs, syserror.EINVAL
+ return cmsgs, linuxerr.EINVAL
}
var h linux.ControlMessageHeader
h.UnmarshalUnsafe(buf[i : i+linux.SizeOfControlMessageHeader])
if h.Length < uint64(linux.SizeOfControlMessageHeader) {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
if h.Length > uint64(len(buf)-i) {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
i += linux.SizeOfControlMessageHeader
@@ -497,7 +497,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
numRights := rightsSize / linux.SizeOfControlMessageRight
if len(fds)+numRights > linux.SCM_MAX_FD {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
for j := i; j < i+rightsSize; j += linux.SizeOfControlMessageRight {
@@ -508,7 +508,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
case linux.SCM_CREDENTIALS:
if length < linux.SizeOfControlMessageCredentials {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
var creds linux.ControlMessageCredentials
@@ -522,7 +522,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
case linux.SO_TIMESTAMP:
if length < linux.SizeOfTimeval {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
var ts linux.Timeval
ts.UnmarshalUnsafe(buf[i : i+linux.SizeOfTimeval])
@@ -532,13 +532,13 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
default:
// Unknown message type.
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
case linux.SOL_IP:
switch h.Type {
case linux.IP_TOS:
if length < linux.SizeOfControlMessageTOS {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
cmsgs.IP.HasTOS = true
var tos primitive.Uint8
@@ -548,7 +548,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
case linux.IP_PKTINFO:
if length < linux.SizeOfControlMessageIPPacketInfo {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
cmsgs.IP.HasIPPacketInfo = true
@@ -561,7 +561,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
case linux.IP_RECVORIGDSTADDR:
var addr linux.SockAddrInet
if length < addr.SizeBytes() {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
addr.UnmarshalUnsafe(buf[i : i+addr.SizeBytes()])
cmsgs.IP.OriginalDstAddress = &addr
@@ -570,7 +570,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
case linux.IP_RECVERR:
var errCmsg linux.SockErrCMsgIPv4
if length < errCmsg.SizeBytes() {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
errCmsg.UnmarshalBytes(buf[i : i+errCmsg.SizeBytes()])
@@ -578,13 +578,13 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
i += bits.AlignUp(length, width)
default:
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
case linux.SOL_IPV6:
switch h.Type {
case linux.IPV6_TCLASS:
if length < linux.SizeOfControlMessageTClass {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
cmsgs.IP.HasTClass = true
var tclass primitive.Uint32
@@ -595,7 +595,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
case linux.IPV6_RECVORIGDSTADDR:
var addr linux.SockAddrInet6
if length < addr.SizeBytes() {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
addr.UnmarshalUnsafe(buf[i : i+addr.SizeBytes()])
cmsgs.IP.OriginalDstAddress = &addr
@@ -604,7 +604,7 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
case linux.IPV6_RECVERR:
var errCmsg linux.SockErrCMsgIPv6
if length < errCmsg.SizeBytes() {
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
errCmsg.UnmarshalBytes(buf[i : i+errCmsg.SizeBytes()])
@@ -612,10 +612,10 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
i += bits.AlignUp(length, width)
default:
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
default:
- return socket.ControlMessages{}, syserror.EINVAL
+ return socket.ControlMessages{}, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/socket/control/control_vfs2.go b/pkg/sentry/socket/control/control_vfs2.go
index 37d02948f..0a989cbeb 100644
--- a/pkg/sentry/socket/control/control_vfs2.go
+++ b/pkg/sentry/socket/control/control_vfs2.go
@@ -17,10 +17,10 @@ package control
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
// SCMRightsVFS2 represents a SCM_RIGHTS socket control message.
@@ -51,7 +51,7 @@ func NewSCMRightsVFS2(t *kernel.Task, fds []int32) (SCMRightsVFS2, error) {
file := t.GetFileVFS2(fd)
if file == nil {
files.Release(t)
- return nil, syserror.EBADF
+ return nil, linuxerr.EBADF
}
files = append(files, file)
}
diff --git a/pkg/sentry/socket/hostinet/BUILD b/pkg/sentry/socket/hostinet/BUILD
index 3c6511ead..3950caa0f 100644
--- a/pkg/sentry/socket/hostinet/BUILD
+++ b/pkg/sentry/socket/hostinet/BUILD
@@ -18,6 +18,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fdnotifier",
"//pkg/hostarch",
"//pkg/log",
diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go
index 52ae4bc9c..38cb2c99c 100644
--- a/pkg/sentry/socket/hostinet/socket.go
+++ b/pkg/sentry/socket/hostinet/socket.go
@@ -20,6 +20,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
@@ -67,23 +68,6 @@ type socketOperations struct {
socketOpsCommon
}
-// socketOpsCommon contains the socket operations common to VFS1 and VFS2.
-//
-// +stateify savable
-type socketOpsCommon struct {
- socket.SendReceiveTimeout
-
- family int // Read-only.
- stype linux.SockType // Read-only.
- protocol int // Read-only.
- queue waiter.Queue
-
- // fd is the host socket fd. It must have O_NONBLOCK, so that operations
- // will return EWOULDBLOCK instead of blocking on the host. This allows us to
- // handle blocking behavior independently in the sentry.
- fd int
-}
-
var _ = socket.Socket(&socketOperations{})
func newSocketFile(ctx context.Context, family int, stype linux.SockType, protocol int, fd int, nonblock bool) (*fs.File, *syserr.Error) {
@@ -103,29 +87,6 @@ func newSocketFile(ctx context.Context, family int, stype linux.SockType, protoc
return fs.NewFile(ctx, dirent, fs.FileFlags{NonBlocking: nonblock, Read: true, Write: true, NonSeekable: true}, s), nil
}
-// Release implements fs.FileOperations.Release.
-func (s *socketOpsCommon) Release(context.Context) {
- fdnotifier.RemoveFD(int32(s.fd))
- unix.Close(s.fd)
-}
-
-// Readiness implements waiter.Waitable.Readiness.
-func (s *socketOpsCommon) Readiness(mask waiter.EventMask) waiter.EventMask {
- return fdnotifier.NonBlockingPoll(int32(s.fd), mask)
-}
-
-// EventRegister implements waiter.Waitable.EventRegister.
-func (s *socketOpsCommon) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
- s.queue.EventRegister(e, mask)
- fdnotifier.UpdateFD(int32(s.fd))
-}
-
-// EventUnregister implements waiter.Waitable.EventUnregister.
-func (s *socketOpsCommon) EventUnregister(e *waiter.Entry) {
- s.queue.EventUnregister(e)
- fdnotifier.UpdateFD(int32(s.fd))
-}
-
// Ioctl implements fs.FileOperations.Ioctl.
func (s *socketOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
return ioctl(ctx, s.fd, io, args)
@@ -177,6 +138,96 @@ func (s *socketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IO
return int64(n), err
}
+// Socket implements socket.Provider.Socket.
+func (p *socketProvider) Socket(t *kernel.Task, stypeflags linux.SockType, protocol int) (*fs.File, *syserr.Error) {
+ // Check that we are using the host network stack.
+ stack := t.NetworkContext()
+ if stack == nil {
+ return nil, nil
+ }
+ if _, ok := stack.(*Stack); !ok {
+ return nil, nil
+ }
+
+ // Only accept TCP and UDP.
+ stype := stypeflags & linux.SOCK_TYPE_MASK
+ switch stype {
+ case unix.SOCK_STREAM:
+ switch protocol {
+ case 0, unix.IPPROTO_TCP:
+ // ok
+ default:
+ return nil, nil
+ }
+ case unix.SOCK_DGRAM:
+ switch protocol {
+ case 0, unix.IPPROTO_UDP:
+ // ok
+ default:
+ return nil, nil
+ }
+ default:
+ return nil, nil
+ }
+
+ // Conservatively ignore all flags specified by the application and add
+ // SOCK_NONBLOCK since socketOperations requires it. Pass a protocol of 0
+ // to simplify the syscall filters, since 0 and IPPROTO_* are equivalent.
+ fd, err := unix.Socket(p.family, int(stype)|unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC, 0)
+ if err != nil {
+ return nil, syserr.FromError(err)
+ }
+ return newSocketFile(t, p.family, stype, protocol, fd, stypeflags&unix.SOCK_NONBLOCK != 0)
+}
+
+// Pair implements socket.Provider.Pair.
+func (p *socketProvider) Pair(t *kernel.Task, stype linux.SockType, protocol int) (*fs.File, *fs.File, *syserr.Error) {
+ // Not supported by AF_INET/AF_INET6.
+ return nil, nil, nil
+}
+
+// LINT.ThenChange(./socket_vfs2.go)
+
+// socketOpsCommon contains the socket operations common to VFS1 and VFS2.
+//
+// +stateify savable
+type socketOpsCommon struct {
+ socket.SendReceiveTimeout
+
+ family int // Read-only.
+ stype linux.SockType // Read-only.
+ protocol int // Read-only.
+ queue waiter.Queue
+
+ // fd is the host socket fd. It must have O_NONBLOCK, so that operations
+ // will return EWOULDBLOCK instead of blocking on the host. This allows us to
+ // handle blocking behavior independently in the sentry.
+ fd int
+}
+
+// Release implements fs.FileOperations.Release.
+func (s *socketOpsCommon) Release(context.Context) {
+ fdnotifier.RemoveFD(int32(s.fd))
+ unix.Close(s.fd)
+}
+
+// Readiness implements waiter.Waitable.Readiness.
+func (s *socketOpsCommon) Readiness(mask waiter.EventMask) waiter.EventMask {
+ return fdnotifier.NonBlockingPoll(int32(s.fd), mask)
+}
+
+// EventRegister implements waiter.Waitable.EventRegister.
+func (s *socketOpsCommon) EventRegister(e *waiter.Entry, mask waiter.EventMask) {
+ s.queue.EventRegister(e, mask)
+ fdnotifier.UpdateFD(int32(s.fd))
+}
+
+// EventUnregister implements waiter.Waitable.EventUnregister.
+func (s *socketOpsCommon) EventUnregister(e *waiter.Entry) {
+ s.queue.EventUnregister(e)
+ fdnotifier.UpdateFD(int32(s.fd))
+}
+
// Connect implements socket.Socket.Connect.
func (s *socketOpsCommon) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error {
if len(sockaddr) > sizeofSockaddr {
@@ -596,6 +647,17 @@ func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []b
return 0, syserr.ErrInvalidArgument
}
+ // If the src is zero-length, call SENDTO directly with a null buffer in
+ // order to generate poll/epoll notifications.
+ if src.NumBytes() == 0 {
+ sysflags := flags | unix.MSG_DONTWAIT
+ n, _, errno := unix.Syscall6(unix.SYS_SENDTO, uintptr(s.fd), 0, 0, uintptr(sysflags), uintptr(firstBytePtr(to)), uintptr(len(to)))
+ if errno != 0 {
+ return 0, syserr.FromError(errno)
+ }
+ return int(n), nil
+ }
+
space := uint64(control.CmsgsSpace(t, controlMessages))
if space > maxControlLen {
space = maxControlLen
@@ -653,7 +715,7 @@ func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []b
}
if ch != nil {
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = syserror.ErrWouldBlock
}
break
@@ -709,56 +771,6 @@ type socketProvider struct {
family int
}
-// Socket implements socket.Provider.Socket.
-func (p *socketProvider) Socket(t *kernel.Task, stypeflags linux.SockType, protocol int) (*fs.File, *syserr.Error) {
- // Check that we are using the host network stack.
- stack := t.NetworkContext()
- if stack == nil {
- return nil, nil
- }
- if _, ok := stack.(*Stack); !ok {
- return nil, nil
- }
-
- // Only accept TCP and UDP.
- stype := stypeflags & linux.SOCK_TYPE_MASK
- switch stype {
- case unix.SOCK_STREAM:
- switch protocol {
- case 0, unix.IPPROTO_TCP:
- // ok
- default:
- return nil, nil
- }
- case unix.SOCK_DGRAM:
- switch protocol {
- case 0, unix.IPPROTO_UDP:
- // ok
- default:
- return nil, nil
- }
- default:
- return nil, nil
- }
-
- // Conservatively ignore all flags specified by the application and add
- // SOCK_NONBLOCK since socketOperations requires it. Pass a protocol of 0
- // to simplify the syscall filters, since 0 and IPPROTO_* are equivalent.
- fd, err := unix.Socket(p.family, int(stype)|unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC, 0)
- if err != nil {
- return nil, syserr.FromError(err)
- }
- return newSocketFile(t, p.family, stype, protocol, fd, stypeflags&unix.SOCK_NONBLOCK != 0)
-}
-
-// Pair implements socket.Provider.Pair.
-func (p *socketProvider) Pair(t *kernel.Task, stype linux.SockType, protocol int) (*fs.File, *fs.File, *syserr.Error) {
- // Not supported by AF_INET/AF_INET6.
- return nil, nil, nil
-}
-
-// LINT.ThenChange(./socket_vfs2.go)
-
func init() {
for _, family := range []int{unix.AF_INET, unix.AF_INET6} {
socket.RegisterProvider(family, &socketProvider{family})
diff --git a/pkg/sentry/socket/hostinet/socket_unsafe.go b/pkg/sentry/socket/hostinet/socket_unsafe.go
index d3be2d825..86dc879d5 100644
--- a/pkg/sentry/socket/hostinet/socket_unsafe.go
+++ b/pkg/sentry/socket/hostinet/socket_unsafe.go
@@ -67,7 +67,23 @@ func ioctl(ctx context.Context, fd int, io usermem.IO, args arch.SyscallArgument
AddressSpaceActive: true,
})
return 0, err
-
+ case unix.SIOCGIFFLAGS:
+ cc := &usermem.IOCopyContext{
+ Ctx: ctx,
+ IO: io,
+ Opts: usermem.IOOpts{
+ AddressSpaceActive: true,
+ },
+ }
+ var ifr linux.IFReq
+ if _, err := ifr.CopyIn(cc, args[2].Pointer()); err != nil {
+ return 0, err
+ }
+ if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), cmd, uintptr(unsafe.Pointer(&ifr))); errno != 0 {
+ return 0, translateIOSyscallError(errno)
+ }
+ _, err := ifr.CopyOut(cc, args[2].Pointer())
+ return 0, err
default:
return 0, syserror.ENOTTY
}
diff --git a/pkg/sentry/socket/hostinet/socket_vfs2.go b/pkg/sentry/socket/hostinet/socket_vfs2.go
index 5d55cc64d..2c5c3e2cd 100644
--- a/pkg/sentry/socket/hostinet/socket_vfs2.go
+++ b/pkg/sentry/socket/hostinet/socket_vfs2.go
@@ -18,6 +18,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
@@ -105,7 +106,7 @@ func (s *socketVFS2) Ioctl(ctx context.Context, uio usermem.IO, args arch.Syscal
// PRead implements vfs.FileDescriptionImpl.PRead.
func (s *socketVFS2) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Read implements vfs.FileDescriptionImpl.
@@ -124,7 +125,7 @@ func (s *socketVFS2) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.
// PWrite implements vfs.FileDescriptionImpl.
func (s *socketVFS2) PWrite(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Write implements vfs.FileDescriptionImpl.
diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go
index cbb1e905d..7a4e78a5f 100644
--- a/pkg/sentry/socket/hostinet/stack.go
+++ b/pkg/sentry/socket/hostinet/stack.go
@@ -29,11 +29,11 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/syserr"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/usermem"
@@ -320,12 +320,12 @@ func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
// AddInterfaceAddr implements inet.Stack.AddInterfaceAddr.
func (s *Stack) AddInterfaceAddr(int32, inet.InterfaceAddr) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// RemoveInterfaceAddr implements inet.Stack.RemoveInterfaceAddr.
func (s *Stack) RemoveInterfaceAddr(int32, inet.InterfaceAddr) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// SupportsIPv6 implements inet.Stack.SupportsIPv6.
@@ -340,7 +340,7 @@ func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) {
// SetTCPReceiveBufferSize implements inet.Stack.SetTCPReceiveBufferSize.
func (s *Stack) SetTCPReceiveBufferSize(size inet.TCPBufferSize) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// TCPSendBufferSize implements inet.Stack.TCPSendBufferSize.
@@ -350,7 +350,7 @@ func (s *Stack) TCPSendBufferSize() (inet.TCPBufferSize, error) {
// SetTCPSendBufferSize implements inet.Stack.SetTCPSendBufferSize.
func (s *Stack) SetTCPSendBufferSize(size inet.TCPBufferSize) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// TCPSACKEnabled implements inet.Stack.TCPSACKEnabled.
@@ -360,7 +360,7 @@ func (s *Stack) TCPSACKEnabled() (bool, error) {
// SetTCPSACKEnabled implements inet.Stack.SetTCPSACKEnabled.
func (s *Stack) SetTCPSACKEnabled(bool) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// TCPRecovery implements inet.Stack.TCPRecovery.
@@ -370,7 +370,7 @@ func (s *Stack) TCPRecovery() (inet.TCPLossRecovery, error) {
// SetTCPRecovery implements inet.Stack.SetTCPRecovery.
func (s *Stack) SetTCPRecovery(inet.TCPLossRecovery) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// getLine reads one line from proc file, with specified prefix.
@@ -483,7 +483,7 @@ func (s *Stack) RestoreCleanupEndpoints([]stack.TransportEndpoint) {}
// SetForwarding implements inet.Stack.SetForwarding.
func (s *Stack) SetForwarding(tcpip.NetworkProtocolNumber, bool) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// PortRange implements inet.Stack.PortRange.
@@ -494,5 +494,5 @@ func (*Stack) PortRange() (uint16, uint16) {
// SetPortRange implements inet.Stack.SetPortRange.
func (*Stack) SetPortRange(start uint16, end uint16) error {
- return syserror.EACCES
+ return linuxerr.EACCES
}
diff --git a/pkg/sentry/socket/netfilter/BUILD b/pkg/sentry/socket/netfilter/BUILD
index 61b2c9755..608474fa1 100644
--- a/pkg/sentry/socket/netfilter/BUILD
+++ b/pkg/sentry/socket/netfilter/BUILD
@@ -25,6 +25,7 @@ go_library(
"//pkg/log",
"//pkg/marshal",
"//pkg/sentry/kernel",
+ "//pkg/sentry/kernel/auth",
"//pkg/syserr",
"//pkg/tcpip",
"//pkg/tcpip/header",
diff --git a/pkg/sentry/socket/netfilter/extensions.go b/pkg/sentry/socket/netfilter/extensions.go
index 6fc7781ad..3f1b4a17b 100644
--- a/pkg/sentry/socket/netfilter/extensions.go
+++ b/pkg/sentry/socket/netfilter/extensions.go
@@ -19,20 +19,12 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bits"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserr"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
-// TODO(gvisor.dev/issue/170): The following per-matcher params should be
-// supported:
-// - Table name
-// - Match size
-// - User size
-// - Hooks
-// - Proto
-// - Family
-
// matchMaker knows how to (un)marshal the matcher named name().
type matchMaker interface {
// name is the matcher name as stored in the xt_entry_match struct.
@@ -43,7 +35,7 @@ type matchMaker interface {
// unmarshal converts from the ABI matcher struct to an
// stack.Matcher.
- unmarshal(buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error)
+ unmarshal(task *kernel.Task, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error)
}
type matcher interface {
@@ -94,12 +86,12 @@ func marshalEntryMatch(name string, data []byte) []byte {
return buf
}
-func unmarshalMatcher(match linux.XTEntryMatch, filter stack.IPHeaderFilter, buf []byte) (stack.Matcher, error) {
+func unmarshalMatcher(task *kernel.Task, match linux.XTEntryMatch, filter stack.IPHeaderFilter, buf []byte) (stack.Matcher, error) {
matchMaker, ok := matchMakers[match.Name.String()]
if !ok {
return nil, fmt.Errorf("unsupported matcher with name %q", match.Name.String())
}
- return matchMaker.unmarshal(buf, filter)
+ return matchMaker.unmarshal(task, buf, filter)
}
// targetMaker knows how to (un)marshal a target. Once registered,
diff --git a/pkg/sentry/socket/netfilter/ipv4.go b/pkg/sentry/socket/netfilter/ipv4.go
index a15ac1021..af31cbc5b 100644
--- a/pkg/sentry/socket/netfilter/ipv4.go
+++ b/pkg/sentry/socket/netfilter/ipv4.go
@@ -18,6 +18,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserr"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -125,7 +126,7 @@ func getEntries4(table stack.Table, tablename linux.TableName) (linux.KernelIPTG
return entries, info
}
-func modifyEntries4(stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) {
+func modifyEntries4(task *kernel.Task, stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) {
nflog("set entries: setting entries in table %q", replace.Name.String())
// Convert input into a list of rules and their offsets.
@@ -150,23 +151,19 @@ func modifyEntries4(stk *stack.Stack, optVal []byte, replace *linux.IPTReplace,
return nil, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/170): We should support more IPTIP
- // filtering fields.
filter, err := filterFromIPTIP(entry.IP)
if err != nil {
nflog("bad iptip: %v", err)
return nil, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/170): Matchers and targets can specify
- // that they only work for certain protocols, hooks, tables.
// Get matchers.
matchersSize := entry.TargetOffset - linux.SizeOfIPTEntry
if len(optVal) < int(matchersSize) {
nflog("entry doesn't have enough room for its matchers (only %d bytes remain)", len(optVal))
return nil, syserr.ErrInvalidArgument
}
- matchers, err := parseMatchers(filter, optVal[:matchersSize])
+ matchers, err := parseMatchers(task, filter, optVal[:matchersSize])
if err != nil {
nflog("failed to parse matchers: %v", err)
return nil, syserr.ErrInvalidArgument
diff --git a/pkg/sentry/socket/netfilter/ipv6.go b/pkg/sentry/socket/netfilter/ipv6.go
index b8ddbb61c..6cefe0b9c 100644
--- a/pkg/sentry/socket/netfilter/ipv6.go
+++ b/pkg/sentry/socket/netfilter/ipv6.go
@@ -18,6 +18,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserr"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -128,7 +129,7 @@ func getEntries6(table stack.Table, tablename linux.TableName) (linux.KernelIP6T
return entries, info
}
-func modifyEntries6(stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) {
+func modifyEntries6(task *kernel.Task, stk *stack.Stack, optVal []byte, replace *linux.IPTReplace, table *stack.Table) (map[uint32]int, *syserr.Error) {
nflog("set entries: setting entries in table %q", replace.Name.String())
// Convert input into a list of rules and their offsets.
@@ -153,23 +154,19 @@ func modifyEntries6(stk *stack.Stack, optVal []byte, replace *linux.IPTReplace,
return nil, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/170): We should support more IPTIP
- // filtering fields.
filter, err := filterFromIP6TIP(entry.IPv6)
if err != nil {
nflog("bad iptip: %v", err)
return nil, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/170): Matchers and targets can specify
- // that they only work for certain protocols, hooks, tables.
// Get matchers.
matchersSize := entry.TargetOffset - linux.SizeOfIP6TEntry
if len(optVal) < int(matchersSize) {
nflog("entry doesn't have enough room for its matchers (only %d bytes remain)", len(optVal))
return nil, syserr.ErrInvalidArgument
}
- matchers, err := parseMatchers(filter, optVal[:matchersSize])
+ matchers, err := parseMatchers(task, filter, optVal[:matchersSize])
if err != nil {
nflog("failed to parse matchers: %v", err)
return nil, syserr.ErrInvalidArgument
diff --git a/pkg/sentry/socket/netfilter/netfilter.go b/pkg/sentry/socket/netfilter/netfilter.go
index e1c4b06fc..e3eade180 100644
--- a/pkg/sentry/socket/netfilter/netfilter.go
+++ b/pkg/sentry/socket/netfilter/netfilter.go
@@ -174,13 +174,12 @@ func setHooksAndUnderflow(info *linux.IPTGetinfo, table stack.Table, offset uint
// SetEntries sets iptables rules for a single table. See
// net/ipv4/netfilter/ip_tables.c:translate_table for reference.
-func SetEntries(stk *stack.Stack, optVal []byte, ipv6 bool) *syserr.Error {
+func SetEntries(task *kernel.Task, stk *stack.Stack, optVal []byte, ipv6 bool) *syserr.Error {
var replace linux.IPTReplace
replaceBuf := optVal[:linux.SizeOfIPTReplace]
optVal = optVal[linux.SizeOfIPTReplace:]
replace.UnmarshalBytes(replaceBuf)
- // TODO(gvisor.dev/issue/170): Support other tables.
var table stack.Table
switch replace.Name.String() {
case filterTable:
@@ -188,16 +187,16 @@ func SetEntries(stk *stack.Stack, optVal []byte, ipv6 bool) *syserr.Error {
case natTable:
table = stack.EmptyNATTable()
default:
- nflog("we don't yet support writing to the %q table (gvisor.dev/issue/170)", replace.Name.String())
+ nflog("unknown iptables table %q", replace.Name.String())
return syserr.ErrInvalidArgument
}
var err *syserr.Error
var offsets map[uint32]int
if ipv6 {
- offsets, err = modifyEntries6(stk, optVal, &replace, &table)
+ offsets, err = modifyEntries6(task, stk, optVal, &replace, &table)
} else {
- offsets, err = modifyEntries4(stk, optVal, &replace, &table)
+ offsets, err = modifyEntries4(task, stk, optVal, &replace, &table)
}
if err != nil {
return err
@@ -272,7 +271,6 @@ func SetEntries(stk *stack.Stack, optVal []byte, ipv6 bool) *syserr.Error {
table.Rules[ruleIdx] = rule
}
- // TODO(gvisor.dev/issue/170): Support other chains.
// Since we don't support FORWARD, yet, make sure all other chains point to
// ACCEPT rules.
for hook, ruleIdx := range table.BuiltinChains {
@@ -287,7 +285,7 @@ func SetEntries(stk *stack.Stack, optVal []byte, ipv6 bool) *syserr.Error {
}
}
- // TODO(gvisor.dev/issue/170): Check the following conditions:
+ // TODO(gvisor.dev/issue/6167): Check the following conditions:
// - There are no loops.
// - There are no chains without an unconditional final rule.
// - There are no chains without an unconditional underflow rule.
@@ -297,7 +295,7 @@ func SetEntries(stk *stack.Stack, optVal []byte, ipv6 bool) *syserr.Error {
// parseMatchers parses 0 or more matchers from optVal. optVal should contain
// only the matchers.
-func parseMatchers(filter stack.IPHeaderFilter, optVal []byte) ([]stack.Matcher, error) {
+func parseMatchers(task *kernel.Task, filter stack.IPHeaderFilter, optVal []byte) ([]stack.Matcher, error) {
nflog("set entries: parsing matchers of size %d", len(optVal))
var matchers []stack.Matcher
for len(optVal) > 0 {
@@ -321,13 +319,13 @@ func parseMatchers(filter stack.IPHeaderFilter, optVal []byte) ([]stack.Matcher,
}
// Parse the specific matcher.
- matcher, err := unmarshalMatcher(match, filter, optVal[linux.SizeOfXTEntryMatch:match.MatchSize])
+ matcher, err := unmarshalMatcher(task, match, filter, optVal[linux.SizeOfXTEntryMatch:match.MatchSize])
if err != nil {
return nil, fmt.Errorf("failed to create matcher: %v", err)
}
matchers = append(matchers, matcher)
- // TODO(gvisor.dev/issue/170): Check the revision field.
+ // TODO(gvisor.dev/issue/6167): Check the revision field.
optVal = optVal[match.MatchSize:]
}
diff --git a/pkg/sentry/socket/netfilter/owner_matcher.go b/pkg/sentry/socket/netfilter/owner_matcher.go
index 60845cab3..6eff2ae65 100644
--- a/pkg/sentry/socket/netfilter/owner_matcher.go
+++ b/pkg/sentry/socket/netfilter/owner_matcher.go
@@ -19,6 +19,8 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/marshal"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
+ "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
@@ -40,8 +42,8 @@ func (ownerMarshaler) name() string {
func (ownerMarshaler) marshal(mr matcher) []byte {
matcher := mr.(*OwnerMatcher)
iptOwnerInfo := linux.IPTOwnerInfo{
- UID: matcher.uid,
- GID: matcher.gid,
+ UID: uint32(matcher.uid),
+ GID: uint32(matcher.gid),
}
// Support for UID and GID match.
@@ -63,7 +65,7 @@ func (ownerMarshaler) marshal(mr matcher) []byte {
}
// unmarshal implements matchMaker.unmarshal.
-func (ownerMarshaler) unmarshal(buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) {
+func (ownerMarshaler) unmarshal(task *kernel.Task, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) {
if len(buf) < linux.SizeOfIPTOwnerInfo {
return nil, fmt.Errorf("buf has insufficient size for owner match: %d", len(buf))
}
@@ -72,11 +74,12 @@ func (ownerMarshaler) unmarshal(buf []byte, filter stack.IPHeaderFilter) (stack.
// exceed what's strictly necessary to hold matchData.
var matchData linux.IPTOwnerInfo
matchData.UnmarshalUnsafe(buf[:linux.SizeOfIPTOwnerInfo])
- nflog("parseMatchers: parsed IPTOwnerInfo: %+v", matchData)
+ nflog("parsed IPTOwnerInfo: %+v", matchData)
var owner OwnerMatcher
- owner.uid = matchData.UID
- owner.gid = matchData.GID
+ creds := task.Credentials()
+ owner.uid = creds.UserNamespace.MapToKUID(auth.UID(matchData.UID))
+ owner.gid = creds.UserNamespace.MapToKGID(auth.GID(matchData.GID))
// Check flags.
if matchData.Match&linux.XT_OWNER_UID != 0 {
@@ -97,8 +100,8 @@ func (ownerMarshaler) unmarshal(buf []byte, filter stack.IPHeaderFilter) (stack.
// OwnerMatcher matches against a UID and/or GID.
type OwnerMatcher struct {
- uid uint32
- gid uint32
+ uid auth.KUID
+ gid auth.KGID
matchUID bool
matchGID bool
invertUID bool
@@ -113,7 +116,6 @@ func (*OwnerMatcher) name() string {
// Match implements Matcher.Match.
func (om *OwnerMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ string) (bool, bool) {
// Support only for OUTPUT chain.
- // TODO(gvisor.dev/issue/170): Need to support for POSTROUTING chain also.
if hook != stack.Output {
return false, true
}
@@ -126,7 +128,7 @@ func (om *OwnerMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ str
var matches bool
// Check for UID match.
if om.matchUID {
- if pkt.Owner.UID() == om.uid {
+ if auth.KUID(pkt.Owner.KUID()) == om.uid {
matches = true
}
if matches == om.invertUID {
@@ -137,7 +139,7 @@ func (om *OwnerMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ str
// Check for GID match.
if om.matchGID {
matches = false
- if pkt.Owner.GID() == om.gid {
+ if auth.KGID(pkt.Owner.KGID()) == om.gid {
matches = true
}
if matches == om.invertGID {
diff --git a/pkg/sentry/socket/netfilter/targets.go b/pkg/sentry/socket/netfilter/targets.go
index fa5456eee..ea56f39c1 100644
--- a/pkg/sentry/socket/netfilter/targets.go
+++ b/pkg/sentry/socket/netfilter/targets.go
@@ -331,7 +331,6 @@ func (*redirectTargetMaker) unmarshal(buf []byte, filter stack.IPHeaderFilter) (
return nil, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/170): Check if the flags are valid.
// Also check if we need to map ports or IP.
// For now, redirect target only supports destination port change.
// Port range and IP range are not supported yet.
@@ -340,7 +339,6 @@ func (*redirectTargetMaker) unmarshal(buf []byte, filter stack.IPHeaderFilter) (
return nil, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/170): Port range is not supported yet.
if nfRange.RangeIPV4.MinPort != nfRange.RangeIPV4.MaxPort {
nflog("redirectTargetMaker: MinPort != MaxPort (%d, %d)", nfRange.RangeIPV4.MinPort, nfRange.RangeIPV4.MaxPort)
return nil, syserr.ErrInvalidArgument
@@ -420,7 +418,6 @@ func (*nfNATTargetMaker) unmarshal(buf []byte, filter stack.IPHeaderFilter) (tar
return nil, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/3549): Check for other flags.
// For now, redirect target only supports destination change.
if natRange.Flags != linux.NF_NAT_RANGE_PROTO_SPECIFIED {
nflog("nfNATTargetMaker: invalid range flags %d", natRange.Flags)
@@ -502,7 +499,6 @@ func (*snatTargetMakerV4) unmarshal(buf []byte, filter stack.IPHeaderFilter) (ta
return nil, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/170): Port range is not supported yet.
if nfRange.RangeIPV4.MinPort != nfRange.RangeIPV4.MaxPort {
nflog("snatTargetMakerV4: MinPort != MaxPort (%d, %d)", nfRange.RangeIPV4.MinPort, nfRange.RangeIPV4.MaxPort)
return nil, syserr.ErrInvalidArgument
@@ -594,7 +590,6 @@ func (*snatTargetMakerV6) unmarshal(buf []byte, filter stack.IPHeaderFilter) (ta
// translateToStandardTarget translates from the value in a
// linux.XTStandardTarget to an stack.Verdict.
func translateToStandardTarget(val int32, netProto tcpip.NetworkProtocolNumber) (target, *syserr.Error) {
- // TODO(gvisor.dev/issue/170): Support other verdicts.
switch val {
case -linux.NF_ACCEPT - 1:
return &acceptTarget{stack.AcceptTarget{
diff --git a/pkg/sentry/socket/netfilter/tcp_matcher.go b/pkg/sentry/socket/netfilter/tcp_matcher.go
index 95bb9826e..e5b73a976 100644
--- a/pkg/sentry/socket/netfilter/tcp_matcher.go
+++ b/pkg/sentry/socket/netfilter/tcp_matcher.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/marshal"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
@@ -50,7 +51,7 @@ func (tcpMarshaler) marshal(mr matcher) []byte {
}
// unmarshal implements matchMaker.unmarshal.
-func (tcpMarshaler) unmarshal(buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) {
+func (tcpMarshaler) unmarshal(_ *kernel.Task, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) {
if len(buf) < linux.SizeOfXTTCP {
return nil, fmt.Errorf("buf has insufficient size for TCP match: %d", len(buf))
}
@@ -95,8 +96,6 @@ func (*TCPMatcher) name() string {
// Match implements Matcher.Match.
func (tm *TCPMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ string) (bool, bool) {
- // TODO(gvisor.dev/issue/170): Proto checks should ultimately be moved
- // into the stack.Check codepath as matchers are added.
switch pkt.NetworkProtocolNumber {
case header.IPv4ProtocolNumber:
netHeader := header.IPv4(pkt.NetworkHeader().View())
diff --git a/pkg/sentry/socket/netfilter/udp_matcher.go b/pkg/sentry/socket/netfilter/udp_matcher.go
index fb8be27e6..aa72ee70c 100644
--- a/pkg/sentry/socket/netfilter/udp_matcher.go
+++ b/pkg/sentry/socket/netfilter/udp_matcher.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/marshal"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
@@ -50,7 +51,7 @@ func (udpMarshaler) marshal(mr matcher) []byte {
}
// unmarshal implements matchMaker.unmarshal.
-func (udpMarshaler) unmarshal(buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) {
+func (udpMarshaler) unmarshal(_ *kernel.Task, buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) {
if len(buf) < linux.SizeOfXTUDP {
return nil, fmt.Errorf("buf has insufficient size for UDP match: %d", len(buf))
}
@@ -92,8 +93,6 @@ func (*UDPMatcher) name() string {
// Match implements Matcher.Match.
func (um *UDPMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ string) (bool, bool) {
- // TODO(gvisor.dev/issue/170): Proto checks should ultimately be moved
- // into the stack.Check codepath as matchers are added.
switch pkt.NetworkProtocolNumber {
case header.IPv4ProtocolNumber:
netHeader := header.IPv4(pkt.NetworkHeader().View())
diff --git a/pkg/sentry/socket/netlink/BUILD b/pkg/sentry/socket/netlink/BUILD
index 64cd263da..ed85404da 100644
--- a/pkg/sentry/socket/netlink/BUILD
+++ b/pkg/sentry/socket/netlink/BUILD
@@ -14,8 +14,10 @@ go_library(
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
+ "//pkg/abi/linux/errno",
"//pkg/bits",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/marshal",
"//pkg/marshal/primitive",
diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go
index 280563d09..d53f23a9a 100644
--- a/pkg/sentry/socket/netlink/socket.go
+++ b/pkg/sentry/socket/netlink/socket.go
@@ -20,7 +20,9 @@ import (
"math"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/marshal/primitive"
@@ -56,7 +58,7 @@ const (
maxSendBufferSize = 4 << 20 // 4MB
)
-var errNoFilter = syserr.New("no filter attached", linux.ENOENT)
+var errNoFilter = syserr.New("no filter attached", errno.ENOENT)
// netlinkSocketDevice is the netlink socket virtual device.
var netlinkSocketDevice = device.NewAnonDevice()
@@ -558,7 +560,7 @@ func (s *socketOpsCommon) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
}
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain
}
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
diff --git a/pkg/sentry/socket/netlink/socket_vfs2.go b/pkg/sentry/socket/netlink/socket_vfs2.go
index 842036764..1a98a1373 100644
--- a/pkg/sentry/socket/netlink/socket_vfs2.go
+++ b/pkg/sentry/socket/netlink/socket_vfs2.go
@@ -17,6 +17,7 @@ package netlink
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/socket"
@@ -113,7 +114,7 @@ func (*SocketVFS2) Ioctl(context.Context, usermem.IO, arch.SyscallArguments) (ui
// PRead implements vfs.FileDescriptionImpl.
func (s *SocketVFS2) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Read implements vfs.FileDescriptionImpl.
@@ -134,7 +135,7 @@ func (s *SocketVFS2) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.
// PWrite implements vfs.FileDescriptionImpl.
func (s *SocketVFS2) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Write implements vfs.FileDescriptionImpl.
diff --git a/pkg/sentry/socket/netstack/BUILD b/pkg/sentry/socket/netstack/BUILD
index 9561b7c25..e828982eb 100644
--- a/pkg/sentry/socket/netstack/BUILD
+++ b/pkg/sentry/socket/netstack/BUILD
@@ -19,7 +19,9 @@ go_library(
],
deps = [
"//pkg/abi/linux",
+ "//pkg/abi/linux/errno",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/marshal",
diff --git a/pkg/sentry/socket/netstack/netstack.go b/pkg/sentry/socket/netstack/netstack.go
index 037ccfec8..11f75628c 100644
--- a/pkg/sentry/socket/netstack/netstack.go
+++ b/pkg/sentry/socket/netstack/netstack.go
@@ -36,7 +36,9 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/marshal"
@@ -289,7 +291,7 @@ const DefaultTTL = 64
const sizeOfInt32 int = 4
-var errStackType = syserr.New("expected but did not receive a netstack.Stack", linux.EINVAL)
+var errStackType = syserr.New("expected but did not receive a netstack.Stack", errno.EINVAL)
// commonEndpoint represents the intersection of a tcpip.Endpoint and a
// transport.Endpoint.
@@ -1798,11 +1800,6 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam
}
v := hostarch.ByteOrder.Uint32(optVal)
-
- if v == 0 {
- socket.SetSockOptEmitUnimplementedEvent(t, name)
- }
-
ep.SocketOptions().SetOutOfBandInline(v != 0)
return nil
@@ -2110,10 +2107,10 @@ func setSockOptIPv6(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name
return syserr.ErrNoDevice
}
// Stack must be a netstack stack.
- return netfilter.SetEntries(stack.(*Stack).Stack, optVal, true)
+ return netfilter.SetEntries(t, stack.(*Stack).Stack, optVal, true)
case linux.IP6T_SO_SET_ADD_COUNTERS:
- // TODO(gvisor.dev/issue/170): Counter support.
+ log.Infof("IP6T_SO_SET_ADD_COUNTERS is not supported")
return nil
default:
@@ -2353,10 +2350,10 @@ func setSockOptIP(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name in
return syserr.ErrNoDevice
}
// Stack must be a netstack stack.
- return netfilter.SetEntries(stack.(*Stack).Stack, optVal, false)
+ return netfilter.SetEntries(t, stack.(*Stack).Stack, optVal, false)
case linux.IPT_SO_SET_ADD_COUNTERS:
- // TODO(gvisor.dev/issue/170): Counter support.
+ log.Infof("IPT_SO_SET_ADD_COUNTERS is not supported")
return nil
case linux.IP_ADD_SOURCE_MEMBERSHIP,
@@ -2813,7 +2810,7 @@ func (s *socketOpsCommon) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
if n > 0 {
return n, msgFlags, senderAddr, senderAddrLen, controlMessages, nil
}
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain
}
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
@@ -2881,7 +2878,7 @@ func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []b
// became available between when we last checked and when we setup
// the notification.
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
return int(total), syserr.ErrTryAgain
}
// handleIOError will consume errors from t.Block if needed.
diff --git a/pkg/sentry/socket/netstack/stack.go b/pkg/sentry/socket/netstack/stack.go
index eef5e6519..0fd0ad32c 100644
--- a/pkg/sentry/socket/netstack/stack.go
+++ b/pkg/sentry/socket/netstack/stack.go
@@ -18,10 +18,10 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/syserr"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
@@ -110,24 +110,24 @@ func convertAddr(addr inet.InterfaceAddr) (tcpip.ProtocolAddress, error) {
switch addr.Family {
case linux.AF_INET:
if len(addr.Addr) != header.IPv4AddressSize {
- return protocolAddress, syserror.EINVAL
+ return protocolAddress, linuxerr.EINVAL
}
if addr.PrefixLen > header.IPv4AddressSize*8 {
- return protocolAddress, syserror.EINVAL
+ return protocolAddress, linuxerr.EINVAL
}
protocol = ipv4.ProtocolNumber
address = tcpip.Address(addr.Addr)
case linux.AF_INET6:
if len(addr.Addr) != header.IPv6AddressSize {
- return protocolAddress, syserror.EINVAL
+ return protocolAddress, linuxerr.EINVAL
}
if addr.PrefixLen > header.IPv6AddressSize*8 {
- return protocolAddress, syserror.EINVAL
+ return protocolAddress, linuxerr.EINVAL
}
protocol = ipv6.ProtocolNumber
address = tcpip.Address(addr.Addr)
default:
- return protocolAddress, syserror.ENOTSUP
+ return protocolAddress, linuxerr.ENOTSUP
}
protocolAddress = tcpip.ProtocolAddress{
diff --git a/pkg/sentry/socket/netstack/tun.go b/pkg/sentry/socket/netstack/tun.go
index c7ed52702..e67fe9700 100644
--- a/pkg/sentry/socket/netstack/tun.go
+++ b/pkg/sentry/socket/netstack/tun.go
@@ -16,7 +16,7 @@ package netstack
import (
"gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
)
@@ -41,7 +41,7 @@ func LinuxToTUNFlags(flags uint16) (tun.Flags, error) {
// when there is no sk_filter. See __tun_chr_ioctl() in
// net/drivers/tun.c.
if flags&^uint16(linux.IFF_TUN|linux.IFF_TAP|linux.IFF_NO_PI|linux.IFF_ONE_QUEUE) != 0 {
- return tun.Flags{}, syserror.EINVAL
+ return tun.Flags{}, linuxerr.EINVAL
}
return tun.Flags{
TUN: flags&linux.IFF_TUN != 0,
diff --git a/pkg/sentry/socket/socket.go b/pkg/sentry/socket/socket.go
index 353f4ade0..f5da3c509 100644
--- a/pkg/sentry/socket/socket.go
+++ b/pkg/sentry/socket/socket.go
@@ -659,7 +659,6 @@ func ConvertAddress(family int, addr tcpip.FullAddress) (linux.SockAddr, uint32)
return &out, uint32(sockAddrInet6Size)
case linux.AF_PACKET:
- // TODO(gvisor.dev/issue/173): Return protocol too.
var out linux.SockAddrLink
out.Family = linux.AF_PACKET
out.InterfaceIndex = int32(addr.NIC)
@@ -749,7 +748,6 @@ func AddressAndFamily(addr []byte) (tcpip.FullAddress, uint16, *syserr.Error) {
return tcpip.FullAddress{}, family, syserr.ErrInvalidArgument
}
- // TODO(gvisor.dev/issue/173): Return protocol too.
return tcpip.FullAddress{
NIC: tcpip.NICID(a.InterfaceIndex),
Addr: tcpip.Address(a.HardwareAddr[:header.EthernetAddressSize]),
diff --git a/pkg/sentry/socket/unix/BUILD b/pkg/sentry/socket/unix/BUILD
index c9cbefb3a..5c3cdef6a 100644
--- a/pkg/sentry/socket/unix/BUILD
+++ b/pkg/sentry/socket/unix/BUILD
@@ -39,6 +39,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/hostarch",
"//pkg/log",
diff --git a/pkg/sentry/socket/unix/unix.go b/pkg/sentry/socket/unix/unix.go
index db7b1affe..8ccdadae9 100644
--- a/pkg/sentry/socket/unix/unix.go
+++ b/pkg/sentry/socket/unix/unix.go
@@ -23,6 +23,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal"
@@ -518,7 +519,7 @@ func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []b
}
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = syserror.ErrWouldBlock
}
break
@@ -719,7 +720,7 @@ func (s *socketOpsCommon) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
if total > 0 {
err = nil
}
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
return int(total), msgFlags, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain
}
return int(total), msgFlags, nil, 0, socket.ControlMessages{}, syserr.FromError(err)
diff --git a/pkg/sentry/socket/unix/unix_vfs2.go b/pkg/sentry/socket/unix/unix_vfs2.go
index c39e317ff..5401f99e6 100644
--- a/pkg/sentry/socket/unix/unix_vfs2.go
+++ b/pkg/sentry/socket/unix/unix_vfs2.go
@@ -17,6 +17,7 @@ package unix
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal"
@@ -236,7 +237,7 @@ func (s *SocketVFS2) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
Mode: linux.FileMode(linux.S_IFSOCK | uint(stat.Mode)&^t.FSContext().Umask()),
Endpoint: bep,
})
- if err == syserror.EEXIST {
+ if linuxerr.Equals(linuxerr.EEXIST, err) {
return syserr.ErrAddressInUse
}
return syserr.FromError(err)
@@ -253,7 +254,7 @@ func (s *SocketVFS2) Ioctl(ctx context.Context, uio usermem.IO, args arch.Syscal
// PRead implements vfs.FileDescriptionImpl.
func (s *SocketVFS2) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Read implements vfs.FileDescriptionImpl.
@@ -282,7 +283,7 @@ func (s *SocketVFS2) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.
// PWrite implements vfs.FileDescriptionImpl.
func (s *SocketVFS2) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Write implements vfs.FileDescriptionImpl.
diff --git a/pkg/sentry/state/BUILD b/pkg/sentry/state/BUILD
index 3e801182c..7f02807c5 100644
--- a/pkg/sentry/state/BUILD
+++ b/pkg/sentry/state/BUILD
@@ -13,6 +13,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/sentry/inet",
"//pkg/sentry/kernel",
@@ -20,7 +21,6 @@ go_library(
"//pkg/sentry/vfs",
"//pkg/sentry/watchdog",
"//pkg/state/statefile",
- "//pkg/syserror",
"@org_golang_x_sys//unix:go_default_library",
],
)
diff --git a/pkg/sentry/state/state.go b/pkg/sentry/state/state.go
index 167754537..e9d544f3d 100644
--- a/pkg/sentry/state/state.go
+++ b/pkg/sentry/state/state.go
@@ -20,6 +20,7 @@ import (
"io"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -27,7 +28,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sentry/watchdog"
"gvisor.dev/gvisor/pkg/state/statefile"
- "gvisor.dev/gvisor/pkg/syserror"
)
var previousMetadata map[string]string
@@ -88,7 +88,7 @@ func (opts SaveOpts) Save(ctx context.Context, k *kernel.Kernel, w *watchdog.Wat
// ENOSPC is a state file error. This error can only come from
// writing the state file, and not from fs.FileOperations.Fsync
// because we wrap those in kernel.TaskSet.flushWritesToFiles.
- if err == syserror.ENOSPC {
+ if linuxerr.Equals(linuxerr.ENOSPC, err) {
err = ErrStateFile{err}
}
@@ -110,7 +110,7 @@ type LoadOpts struct {
}
// Load loads the given kernel, setting the provided platform and stack.
-func (opts LoadOpts) Load(ctx context.Context, k *kernel.Kernel, n inet.Stack, clocks time.Clocks, vfsOpts *vfs.CompleteRestoreOptions) error {
+func (opts LoadOpts) Load(ctx context.Context, k *kernel.Kernel, timeReady chan struct{}, n inet.Stack, clocks time.Clocks, vfsOpts *vfs.CompleteRestoreOptions) error {
// Open the file.
r, m, err := statefile.NewReader(opts.Source, opts.Key)
if err != nil {
@@ -120,5 +120,5 @@ func (opts LoadOpts) Load(ctx context.Context, k *kernel.Kernel, n inet.Stack, c
previousMetadata = m
// Restore the Kernel object graph.
- return k.LoadFrom(ctx, r, n, clocks, vfsOpts)
+ return k.LoadFrom(ctx, r, timeReady, n, clocks, vfsOpts)
}
diff --git a/pkg/sentry/strace/BUILD b/pkg/sentry/strace/BUILD
index 1fbbd133c..369541c7a 100644
--- a/pkg/sentry/strace/BUILD
+++ b/pkg/sentry/strace/BUILD
@@ -11,6 +11,7 @@ go_library(
"futex.go",
"linux64_amd64.go",
"linux64_arm64.go",
+ "mmap.go",
"open.go",
"poll.go",
"ptrace.go",
@@ -35,7 +36,6 @@ go_library(
"//pkg/sentry/socket",
"//pkg/sentry/socket/netlink",
"//pkg/sentry/syscalls/linux",
- "@org_golang_x_sys//unix:go_default_library",
],
)
diff --git a/pkg/sentry/strace/clone.go b/pkg/sentry/strace/clone.go
index ab1060426..bfb4d7f5c 100644
--- a/pkg/sentry/strace/clone.go
+++ b/pkg/sentry/strace/clone.go
@@ -15,98 +15,98 @@
package strace
import (
- "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi"
+ "gvisor.dev/gvisor/pkg/abi/linux"
)
// CloneFlagSet is the set of clone(2) flags.
var CloneFlagSet = abi.FlagSet{
{
- Flag: unix.CLONE_VM,
+ Flag: linux.CLONE_VM,
Name: "CLONE_VM",
},
{
- Flag: unix.CLONE_FS,
+ Flag: linux.CLONE_FS,
Name: "CLONE_FS",
},
{
- Flag: unix.CLONE_FILES,
+ Flag: linux.CLONE_FILES,
Name: "CLONE_FILES",
},
{
- Flag: unix.CLONE_SIGHAND,
+ Flag: linux.CLONE_SIGHAND,
Name: "CLONE_SIGHAND",
},
{
- Flag: unix.CLONE_PTRACE,
+ Flag: linux.CLONE_PTRACE,
Name: "CLONE_PTRACE",
},
{
- Flag: unix.CLONE_VFORK,
+ Flag: linux.CLONE_VFORK,
Name: "CLONE_VFORK",
},
{
- Flag: unix.CLONE_PARENT,
+ Flag: linux.CLONE_PARENT,
Name: "CLONE_PARENT",
},
{
- Flag: unix.CLONE_THREAD,
+ Flag: linux.CLONE_THREAD,
Name: "CLONE_THREAD",
},
{
- Flag: unix.CLONE_NEWNS,
+ Flag: linux.CLONE_NEWNS,
Name: "CLONE_NEWNS",
},
{
- Flag: unix.CLONE_SYSVSEM,
+ Flag: linux.CLONE_SYSVSEM,
Name: "CLONE_SYSVSEM",
},
{
- Flag: unix.CLONE_SETTLS,
+ Flag: linux.CLONE_SETTLS,
Name: "CLONE_SETTLS",
},
{
- Flag: unix.CLONE_PARENT_SETTID,
+ Flag: linux.CLONE_PARENT_SETTID,
Name: "CLONE_PARENT_SETTID",
},
{
- Flag: unix.CLONE_CHILD_CLEARTID,
+ Flag: linux.CLONE_CHILD_CLEARTID,
Name: "CLONE_CHILD_CLEARTID",
},
{
- Flag: unix.CLONE_DETACHED,
+ Flag: linux.CLONE_DETACHED,
Name: "CLONE_DETACHED",
},
{
- Flag: unix.CLONE_UNTRACED,
+ Flag: linux.CLONE_UNTRACED,
Name: "CLONE_UNTRACED",
},
{
- Flag: unix.CLONE_CHILD_SETTID,
+ Flag: linux.CLONE_CHILD_SETTID,
Name: "CLONE_CHILD_SETTID",
},
{
- Flag: unix.CLONE_NEWUTS,
+ Flag: linux.CLONE_NEWUTS,
Name: "CLONE_NEWUTS",
},
{
- Flag: unix.CLONE_NEWIPC,
+ Flag: linux.CLONE_NEWIPC,
Name: "CLONE_NEWIPC",
},
{
- Flag: unix.CLONE_NEWUSER,
+ Flag: linux.CLONE_NEWUSER,
Name: "CLONE_NEWUSER",
},
{
- Flag: unix.CLONE_NEWPID,
+ Flag: linux.CLONE_NEWPID,
Name: "CLONE_NEWPID",
},
{
- Flag: unix.CLONE_NEWNET,
+ Flag: linux.CLONE_NEWNET,
Name: "CLONE_NEWNET",
},
{
- Flag: unix.CLONE_IO,
+ Flag: linux.CLONE_IO,
Name: "CLONE_IO",
},
}
diff --git a/pkg/sentry/strace/linux64_amd64.go b/pkg/sentry/strace/linux64_amd64.go
index d66befe81..6ce1bb592 100644
--- a/pkg/sentry/strace/linux64_amd64.go
+++ b/pkg/sentry/strace/linux64_amd64.go
@@ -33,7 +33,7 @@ var linuxAMD64 = SyscallMap{
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),
+ 9: makeSyscallInfo("mmap", Hex, Hex, MmapProt, MmapFlags, FD, Hex),
10: makeSyscallInfo("mprotect", Hex, Hex, Hex),
11: makeSyscallInfo("munmap", Hex, Hex),
12: makeSyscallInfo("brk", Hex),
diff --git a/pkg/sentry/strace/linux64_arm64.go b/pkg/sentry/strace/linux64_arm64.go
index 1a2d7d75f..ce5594301 100644
--- a/pkg/sentry/strace/linux64_arm64.go
+++ b/pkg/sentry/strace/linux64_arm64.go
@@ -246,7 +246,7 @@ var linuxARM64 = SyscallMap{
219: makeSyscallInfo("keyctl", Hex, Hex, Hex, Hex, Hex),
220: makeSyscallInfo("clone", CloneFlags, Hex, Hex, Hex, Hex),
221: makeSyscallInfo("execve", Path, ExecveStringVector, ExecveStringVector),
- 222: makeSyscallInfo("mmap", Hex, Hex, Hex, Hex, FD, Hex),
+ 222: makeSyscallInfo("mmap", Hex, Hex, MmapProt, MmapFlags, FD, Hex),
223: makeSyscallInfo("fadvise64", FD, Hex, Hex, Hex),
224: makeSyscallInfo("swapon", Hex, Hex),
225: makeSyscallInfo("swapoff", Hex),
diff --git a/pkg/sentry/strace/mmap.go b/pkg/sentry/strace/mmap.go
new file mode 100644
index 000000000..0035be586
--- /dev/null
+++ b/pkg/sentry/strace/mmap.go
@@ -0,0 +1,92 @@
+// Copyright 2021 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.dev/gvisor/pkg/abi"
+ "gvisor.dev/gvisor/pkg/abi/linux"
+)
+
+// ProtectionFlagSet represents the protection to mmap(2).
+var ProtectionFlagSet = abi.FlagSet{
+ {
+ Flag: linux.PROT_READ,
+ Name: "PROT_READ",
+ },
+ {
+ Flag: linux.PROT_WRITE,
+ Name: "PROT_WRITE",
+ },
+ {
+ Flag: linux.PROT_EXEC,
+ Name: "PROT_EXEC",
+ },
+}
+
+// MmapFlagSet is the set of mmap(2) flags.
+var MmapFlagSet = abi.FlagSet{
+ {
+ Flag: linux.MAP_SHARED,
+ Name: "MAP_SHARED",
+ },
+ {
+ Flag: linux.MAP_PRIVATE,
+ Name: "MAP_PRIVATE",
+ },
+ {
+ Flag: linux.MAP_FIXED,
+ Name: "MAP_FIXED",
+ },
+ {
+ Flag: linux.MAP_ANONYMOUS,
+ Name: "MAP_ANONYMOUS",
+ },
+ {
+ Flag: linux.MAP_GROWSDOWN,
+ Name: "MAP_GROWSDOWN",
+ },
+ {
+ Flag: linux.MAP_DENYWRITE,
+ Name: "MAP_DENYWRITE",
+ },
+ {
+ Flag: linux.MAP_EXECUTABLE,
+ Name: "MAP_EXECUTABLE",
+ },
+ {
+ Flag: linux.MAP_LOCKED,
+ Name: "MAP_LOCKED",
+ },
+ {
+ Flag: linux.MAP_NORESERVE,
+ Name: "MAP_NORESERVE",
+ },
+ {
+ Flag: linux.MAP_POPULATE,
+ Name: "MAP_POPULATE",
+ },
+ {
+ Flag: linux.MAP_NONBLOCK,
+ Name: "MAP_NONBLOCK",
+ },
+ {
+ Flag: linux.MAP_STACK,
+ Name: "MAP_STACK",
+ },
+ {
+ Flag: linux.MAP_HUGETLB,
+ Name: "MAP_HUGETLB",
+ },
+}
diff --git a/pkg/sentry/strace/open.go b/pkg/sentry/strace/open.go
index 5769360da..e7c7649f4 100644
--- a/pkg/sentry/strace/open.go
+++ b/pkg/sentry/strace/open.go
@@ -15,61 +15,61 @@
package strace
import (
- "golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi"
+ "gvisor.dev/gvisor/pkg/abi/linux"
)
// OpenMode represents the mode to open(2) a file.
var OpenMode = abi.ValueSet{
- unix.O_RDWR: "O_RDWR",
- unix.O_WRONLY: "O_WRONLY",
- unix.O_RDONLY: "O_RDONLY",
+ linux.O_RDWR: "O_RDWR",
+ linux.O_WRONLY: "O_WRONLY",
+ linux.O_RDONLY: "O_RDONLY",
}
// OpenFlagSet is the set of open(2) flags.
var OpenFlagSet = abi.FlagSet{
{
- Flag: unix.O_APPEND,
+ Flag: linux.O_APPEND,
Name: "O_APPEND",
},
{
- Flag: unix.O_ASYNC,
+ Flag: linux.O_ASYNC,
Name: "O_ASYNC",
},
{
- Flag: unix.O_CLOEXEC,
+ Flag: linux.O_CLOEXEC,
Name: "O_CLOEXEC",
},
{
- Flag: unix.O_CREAT,
+ Flag: linux.O_CREAT,
Name: "O_CREAT",
},
{
- Flag: unix.O_DIRECT,
+ Flag: linux.O_DIRECT,
Name: "O_DIRECT",
},
{
- Flag: unix.O_DIRECTORY,
+ Flag: linux.O_DIRECTORY,
Name: "O_DIRECTORY",
},
{
- Flag: unix.O_EXCL,
+ Flag: linux.O_EXCL,
Name: "O_EXCL",
},
{
- Flag: unix.O_NOATIME,
+ Flag: linux.O_NOATIME,
Name: "O_NOATIME",
},
{
- Flag: unix.O_NOCTTY,
+ Flag: linux.O_NOCTTY,
Name: "O_NOCTTY",
},
{
- Flag: unix.O_NOFOLLOW,
+ Flag: linux.O_NOFOLLOW,
Name: "O_NOFOLLOW",
},
{
- Flag: unix.O_NONBLOCK,
+ Flag: linux.O_NONBLOCK,
Name: "O_NONBLOCK",
},
{
@@ -77,18 +77,22 @@ var OpenFlagSet = abi.FlagSet{
Name: "O_PATH",
},
{
- Flag: unix.O_SYNC,
+ Flag: linux.O_SYNC,
Name: "O_SYNC",
},
{
- Flag: unix.O_TRUNC,
+ Flag: linux.O_TMPFILE,
+ Name: "O_TMPFILE",
+ },
+ {
+ Flag: linux.O_TRUNC,
Name: "O_TRUNC",
},
}
func open(val uint64) string {
- s := OpenMode.Parse(val & unix.O_ACCMODE)
- if flags := OpenFlagSet.Parse(val &^ unix.O_ACCMODE); flags != "" {
+ s := OpenMode.Parse(val & linux.O_ACCMODE)
+ if flags := OpenFlagSet.Parse(val &^ linux.O_ACCMODE); flags != "" {
s += "|" + flags
}
return s
diff --git a/pkg/sentry/strace/signal.go b/pkg/sentry/strace/signal.go
index e5b379a20..5afc9525b 100644
--- a/pkg/sentry/strace/signal.go
+++ b/pkg/sentry/strace/signal.go
@@ -130,8 +130,8 @@ func sigAction(t *kernel.Task, addr hostarch.Addr) string {
return "null"
}
- sa, err := t.CopyInSignalAct(addr)
- if err != nil {
+ var sa linux.SigAction
+ if _, err := sa.CopyIn(t, addr); err != nil {
return fmt.Sprintf("%#x (error copying sigaction: %v)", addr, err)
}
diff --git a/pkg/sentry/strace/strace.go b/pkg/sentry/strace/strace.go
index ec5d5f846..3ea9c31dd 100644
--- a/pkg/sentry/strace/strace.go
+++ b/pkg/sentry/strace/strace.go
@@ -133,6 +133,9 @@ func dump(t *kernel.Task, addr hostarch.Addr, size uint, maximumBlobSize uint) s
}
func path(t *kernel.Task, addr hostarch.Addr) string {
+ if addr == 0 {
+ return "<null>"
+ }
path, err := t.CopyInString(addr, linux.PATH_MAX)
if err != nil {
return fmt.Sprintf("%#x (error decoding path: %s)", addr, err)
@@ -489,6 +492,10 @@ func (i *SyscallInfo) pre(t *kernel.Task, args arch.SyscallArguments, maximumBlo
output = append(output, epollEvents(t, args[arg].Pointer(), 0 /* numEvents */, uint64(maximumBlobSize)))
case SelectFDSet:
output = append(output, fdSet(t, int(args[0].Int()), args[arg].Pointer()))
+ case MmapProt:
+ output = append(output, ProtectionFlagSet.Parse(uint64(args[arg].Uint())))
+ case MmapFlags:
+ output = append(output, MmapFlagSet.Parse(uint64(args[arg].Uint())))
case Oct:
output = append(output, "0o"+strconv.FormatUint(args[arg].Uint64(), 8))
case Hex:
diff --git a/pkg/sentry/strace/syscalls.go b/pkg/sentry/strace/syscalls.go
index 7e69b9279..5893443a7 100644
--- a/pkg/sentry/strace/syscalls.go
+++ b/pkg/sentry/strace/syscalls.go
@@ -238,6 +238,12 @@ const (
// EpollEvents is an array of struct epoll_event. It is the events
// argument in epoll_wait(2)/epoll_pwait(2).
EpollEvents
+
+ // MmapProt is the protection argument in mmap(2).
+ MmapProt
+
+ // MmapFlags is the flags argument in mmap(2).
+ MmapFlags
)
// defaultFormat is the syscall argument format to use if the actual format is
diff --git a/pkg/sentry/syscalls/BUILD b/pkg/sentry/syscalls/BUILD
index b8d1bd415..f2c55588f 100644
--- a/pkg/sentry/syscalls/BUILD
+++ b/pkg/sentry/syscalls/BUILD
@@ -11,6 +11,7 @@ go_library(
visibility = ["//:sandbox"],
deps = [
"//pkg/abi/linux",
+ "//pkg/errors/linuxerr",
"//pkg/sentry/arch",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/epoll",
diff --git a/pkg/sentry/syscalls/epoll.go b/pkg/sentry/syscalls/epoll.go
index 3b4d79889..a69ed0746 100644
--- a/pkg/sentry/syscalls/epoll.go
+++ b/pkg/sentry/syscalls/epoll.go
@@ -18,10 +18,10 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/epoll"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -45,21 +45,21 @@ func AddEpoll(t *kernel.Task, epfd int32, fd int32, flags epoll.EntryFlags, mask
// Get epoll from the file descriptor.
epollfile := t.GetFile(epfd)
if epollfile == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer epollfile.DecRef(t)
// Get the target file id.
file := t.GetFile(fd)
if file == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer file.DecRef(t)
// Extract the epollPoll operations.
e, ok := epollfile.FileOperations.(*epoll.EventPoll)
if !ok {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// Try to add the entry.
@@ -71,21 +71,21 @@ func UpdateEpoll(t *kernel.Task, epfd int32, fd int32, flags epoll.EntryFlags, m
// Get epoll from the file descriptor.
epollfile := t.GetFile(epfd)
if epollfile == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer epollfile.DecRef(t)
// Get the target file id.
file := t.GetFile(fd)
if file == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer file.DecRef(t)
// Extract the epollPoll operations.
e, ok := epollfile.FileOperations.(*epoll.EventPoll)
if !ok {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// Try to update the entry.
@@ -97,21 +97,21 @@ func RemoveEpoll(t *kernel.Task, epfd int32, fd int32) error {
// Get epoll from the file descriptor.
epollfile := t.GetFile(epfd)
if epollfile == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer epollfile.DecRef(t)
// Get the target file id.
file := t.GetFile(fd)
if file == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer file.DecRef(t)
// Extract the epollPoll operations.
e, ok := epollfile.FileOperations.(*epoll.EventPoll)
if !ok {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// Try to remove the entry.
@@ -123,14 +123,14 @@ func WaitEpoll(t *kernel.Task, fd int32, max int, timeoutInNanos int64) ([]linux
// Get epoll from the file descriptor.
epollfile := t.GetFile(fd)
if epollfile == nil {
- return nil, syserror.EBADF
+ return nil, linuxerr.EBADF
}
defer epollfile.DecRef(t)
// Extract the epollPoll operations.
e, ok := epollfile.FileOperations.(*epoll.EventPoll)
if !ok {
- return nil, syserror.EBADF
+ return nil, linuxerr.EBADF
}
// Try to read events and return right away if we got them or if the
@@ -163,7 +163,7 @@ func WaitEpoll(t *kernel.Task, fd int32, max int, timeoutInNanos int64) ([]linux
}
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
return nil, nil
}
diff --git a/pkg/sentry/syscalls/linux/BUILD b/pkg/sentry/syscalls/linux/BUILD
index 408a6c422..a2f612f45 100644
--- a/pkg/sentry/syscalls/linux/BUILD
+++ b/pkg/sentry/syscalls/linux/BUILD
@@ -64,6 +64,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/bpf",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/log",
"//pkg/marshal",
diff --git a/pkg/sentry/syscalls/linux/error.go b/pkg/sentry/syscalls/linux/error.go
index 6eabfd219..76389fbe3 100644
--- a/pkg/sentry/syscalls/linux/error.go
+++ b/pkg/sentry/syscalls/linux/error.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -94,13 +95,13 @@ func handleIOErrorImpl(ctx context.Context, partialResult bool, errOrig, intr er
if errno, ok := syserror.TranslateError(errOrig); ok {
translatedErr = errno
}
- switch translatedErr {
- case io.EOF:
+ switch {
+ case translatedErr == io.EOF:
// EOF is always consumed. If this is a partial read/write
// (result != 0), the application will see that, otherwise
// they will see 0.
return true, nil
- case syserror.EFBIG:
+ case linuxerr.Equals(linuxerr.EFBIG, translatedErr):
t := kernel.TaskFromContext(ctx)
if t == nil {
panic("I/O error should only occur from a context associated with a Task")
@@ -112,8 +113,8 @@ func handleIOErrorImpl(ctx context.Context, partialResult bool, errOrig, intr er
// Do not consume the error and return it as EFBIG.
// Simultaneously send a SIGXFSZ per setrlimit(2).
t.SendSignal(kernel.SignalInfoNoInfo(linux.SIGXFSZ, t, t))
- return true, syserror.EFBIG
- case syserror.EINTR:
+ return true, linuxerr.EFBIG
+ case linuxerr.Equals(linuxerr.EINTR, translatedErr):
// The syscall was interrupted. Return nil if it completed
// partially, otherwise return the error code that the syscall
// needs (to indicate to the kernel what it should do).
@@ -128,21 +129,21 @@ func handleIOErrorImpl(ctx context.Context, partialResult bool, errOrig, intr er
return true, errOrig
}
- switch translatedErr {
- case syserror.EINTR:
+ switch {
+ case linuxerr.Equals(linuxerr.EINTR, translatedErr):
// Syscall interrupted, but completed a partial
// read/write. Like ErrWouldBlock, since we have a
// partial read/write, we consume the error and return
// the partial result.
return true, nil
- case syserror.EFAULT:
+ case linuxerr.Equals(linuxerr.EFAULT, translatedErr):
// EFAULT is only shown the user if nothing was
// read/written. If we read something (this case), they see
// a partial read/write. They will then presumably try again
// with an incremented buffer, which will EFAULT with
// result == 0.
return true, nil
- case syserror.EPIPE:
+ case linuxerr.Equals(linuxerr.EPIPE, translatedErr):
// Writes to a pipe or socket will return EPIPE if the other
// side is gone. The partial write is returned. EPIPE will be
// returned on the next call.
@@ -150,15 +151,17 @@ func handleIOErrorImpl(ctx context.Context, partialResult bool, errOrig, intr er
// TODO(gvisor.dev/issue/161): In some cases SIGPIPE should
// also be sent to the application.
return true, nil
- case syserror.ENOSPC:
+ case linuxerr.Equals(linuxerr.ENOSPC, translatedErr):
// Similar to EPIPE. Return what we wrote this time, and let
// ENOSPC be returned on the next call.
return true, nil
- case syserror.ECONNRESET, syserror.ETIMEDOUT:
+ case linuxerr.Equals(linuxerr.ECONNRESET, translatedErr):
+ fallthrough
+ case linuxerr.Equals(linuxerr.ETIMEDOUT, translatedErr):
// For TCP sendfile connections, we may have a reset or timeout. But we
// should just return n as the result.
return true, nil
- case syserror.EWOULDBLOCK:
+ case linuxerr.Equals(linuxerr.EWOULDBLOCK, translatedErr):
// Syscall would block, but completed a partial read/write.
// This case should only be returned by IssueIO for nonblocking
// files. Since we have a partial read/write, we consume
diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go
index 090c5ffcb..8935ea5b8 100644
--- a/pkg/sentry/syscalls/linux/linux64.go
+++ b/pkg/sentry/syscalls/linux/linux64.go
@@ -18,6 +18,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -187,7 +188,7 @@ var AMD64 = &kernel.SyscallTable{
132: syscalls.Supported("utime", Utime),
133: syscalls.PartiallySupported("mknod", Mknod, "Device creation is not generally supported. Only regular file and FIFO creation are supported.", nil),
134: syscalls.Error("uselib", syserror.ENOSYS, "Obsolete", nil),
- 135: syscalls.ErrorWithEvent("personality", syserror.EINVAL, "Unable to change personality.", nil),
+ 135: syscalls.ErrorWithEvent("personality", linuxerr.EINVAL, "Unable to change personality.", nil),
136: syscalls.ErrorWithEvent("ustat", syserror.ENOSYS, "Needs filesystem support.", nil),
137: syscalls.PartiallySupported("statfs", Statfs, "Depends on the backing file system implementation.", nil),
138: syscalls.PartiallySupported("fstatfs", Fstatfs, "Depends on the backing file system implementation.", nil),
@@ -200,15 +201,15 @@ var AMD64 = &kernel.SyscallTable{
145: syscalls.PartiallySupported("sched_getscheduler", SchedGetscheduler, "Stub implementation.", nil),
146: syscalls.PartiallySupported("sched_get_priority_max", SchedGetPriorityMax, "Stub implementation.", nil),
147: syscalls.PartiallySupported("sched_get_priority_min", SchedGetPriorityMin, "Stub implementation.", nil),
- 148: syscalls.ErrorWithEvent("sched_rr_get_interval", syserror.EPERM, "", nil),
+ 148: syscalls.ErrorWithEvent("sched_rr_get_interval", linuxerr.EPERM, "", nil),
149: syscalls.PartiallySupported("mlock", Mlock, "Stub implementation. The sandbox lacks appropriate permissions.", nil),
150: syscalls.PartiallySupported("munlock", Munlock, "Stub implementation. The sandbox lacks appropriate permissions.", nil),
151: syscalls.PartiallySupported("mlockall", Mlockall, "Stub implementation. The sandbox lacks appropriate permissions.", nil),
152: syscalls.PartiallySupported("munlockall", Munlockall, "Stub implementation. The sandbox lacks appropriate permissions.", nil),
153: syscalls.CapError("vhangup", linux.CAP_SYS_TTY_CONFIG, "", nil),
- 154: syscalls.Error("modify_ldt", syserror.EPERM, "", nil),
- 155: syscalls.Error("pivot_root", syserror.EPERM, "", nil),
- 156: syscalls.Error("sysctl", syserror.EPERM, "Deprecated. Use /proc/sys instead.", nil),
+ 154: syscalls.Error("modify_ldt", linuxerr.EPERM, "", nil),
+ 155: syscalls.Error("pivot_root", linuxerr.EPERM, "", nil),
+ 156: syscalls.Error("sysctl", linuxerr.EPERM, "Deprecated. Use /proc/sys instead.", nil),
157: syscalls.PartiallySupported("prctl", Prctl, "Not all options are supported.", nil),
158: syscalls.PartiallySupported("arch_prctl", ArchPrctl, "Options ARCH_GET_GS, ARCH_SET_GS not supported.", nil),
159: syscalls.CapError("adjtimex", linux.CAP_SYS_TIME, "", nil),
@@ -300,9 +301,9 @@ var AMD64 = &kernel.SyscallTable{
245: syscalls.ErrorWithEvent("mq_getsetattr", syserror.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
246: syscalls.CapError("kexec_load", linux.CAP_SYS_BOOT, "", nil),
247: syscalls.Supported("waitid", Waitid),
- 248: syscalls.Error("add_key", syserror.EACCES, "Not available to user.", nil),
- 249: syscalls.Error("request_key", syserror.EACCES, "Not available to user.", nil),
- 250: syscalls.Error("keyctl", syserror.EACCES, "Not available to user.", nil),
+ 248: syscalls.Error("add_key", linuxerr.EACCES, "Not available to user.", nil),
+ 249: syscalls.Error("request_key", linuxerr.EACCES, "Not available to user.", nil),
+ 250: syscalls.Error("keyctl", linuxerr.EACCES, "Not available to user.", nil),
251: syscalls.CapError("ioprio_set", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_nice or cap_sys_admin (depending)
252: syscalls.CapError("ioprio_get", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_nice or cap_sys_admin (depending)
253: syscalls.PartiallySupported("inotify_init", InotifyInit, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
@@ -350,7 +351,7 @@ var AMD64 = &kernel.SyscallTable{
295: syscalls.Supported("preadv", Preadv),
296: syscalls.Supported("pwritev", Pwritev),
297: syscalls.Supported("rt_tgsigqueueinfo", RtTgsigqueueinfo),
- 298: syscalls.ErrorWithEvent("perf_event_open", syserror.ENODEV, "No support for perf counters", nil),
+ 298: syscalls.ErrorWithEvent("perf_event_open", linuxerr.ENODEV, "No support for perf counters", nil),
299: syscalls.PartiallySupported("recvmmsg", RecvMMsg, "Not all flags and control messages are supported.", nil),
300: syscalls.ErrorWithEvent("fanotify_init", syserror.ENOSYS, "Needs CONFIG_FANOTIFY", nil),
301: syscalls.ErrorWithEvent("fanotify_mark", syserror.ENOSYS, "Needs CONFIG_FANOTIFY", nil),
@@ -470,7 +471,7 @@ var ARM64 = &kernel.SyscallTable{
38: syscalls.Supported("renameat", Renameat),
39: syscalls.PartiallySupported("umount2", Umount2, "Not all options or file systems are supported.", nil),
40: syscalls.PartiallySupported("mount", Mount, "Not all options or file systems are supported.", nil),
- 41: syscalls.Error("pivot_root", syserror.EPERM, "", nil),
+ 41: syscalls.Error("pivot_root", linuxerr.EPERM, "", nil),
42: syscalls.Error("nfsservctl", syserror.ENOSYS, "Removed after Linux 3.1.", nil),
43: syscalls.PartiallySupported("statfs", Statfs, "Depends on the backing file system implementation.", nil),
44: syscalls.PartiallySupported("fstatfs", Fstatfs, "Depends on the backing file system implementation.", nil),
@@ -521,7 +522,7 @@ var ARM64 = &kernel.SyscallTable{
89: syscalls.CapError("acct", linux.CAP_SYS_PACCT, "", nil),
90: syscalls.Supported("capget", Capget),
91: syscalls.Supported("capset", Capset),
- 92: syscalls.ErrorWithEvent("personality", syserror.EINVAL, "Unable to change personality.", nil),
+ 92: syscalls.ErrorWithEvent("personality", linuxerr.EINVAL, "Unable to change personality.", nil),
93: syscalls.Supported("exit", Exit),
94: syscalls.Supported("exit_group", ExitGroup),
95: syscalls.Supported("waitid", Waitid),
@@ -556,7 +557,7 @@ var ARM64 = &kernel.SyscallTable{
124: syscalls.Supported("sched_yield", SchedYield),
125: syscalls.PartiallySupported("sched_get_priority_max", SchedGetPriorityMax, "Stub implementation.", nil),
126: syscalls.PartiallySupported("sched_get_priority_min", SchedGetPriorityMin, "Stub implementation.", nil),
- 127: syscalls.ErrorWithEvent("sched_rr_get_interval", syserror.EPERM, "", nil),
+ 127: syscalls.ErrorWithEvent("sched_rr_get_interval", linuxerr.EPERM, "", nil),
128: syscalls.Supported("restart_syscall", RestartSyscall),
129: syscalls.Supported("kill", Kill),
130: syscalls.Supported("tkill", Tkill),
@@ -646,9 +647,9 @@ var ARM64 = &kernel.SyscallTable{
214: syscalls.Supported("brk", Brk),
215: syscalls.Supported("munmap", Munmap),
216: syscalls.Supported("mremap", Mremap),
- 217: syscalls.Error("add_key", syserror.EACCES, "Not available to user.", nil),
- 218: syscalls.Error("request_key", syserror.EACCES, "Not available to user.", nil),
- 219: syscalls.Error("keyctl", syserror.EACCES, "Not available to user.", nil),
+ 217: syscalls.Error("add_key", linuxerr.EACCES, "Not available to user.", nil),
+ 218: syscalls.Error("request_key", linuxerr.EACCES, "Not available to user.", nil),
+ 219: syscalls.Error("keyctl", linuxerr.EACCES, "Not available to user.", nil),
220: syscalls.PartiallySupported("clone", Clone, "Mount namespace (CLONE_NEWNS) not supported. Options CLONE_PARENT, CLONE_SYSVSEM not supported.", nil),
221: syscalls.Supported("execve", Execve),
222: syscalls.PartiallySupported("mmap", Mmap, "Generally supported with exceptions. Options MAP_FIXED_NOREPLACE, MAP_SHARED_VALIDATE, MAP_SYNC MAP_GROWSDOWN, MAP_HUGETLB are not supported.", nil),
@@ -670,7 +671,7 @@ var ARM64 = &kernel.SyscallTable{
238: syscalls.CapError("migrate_pages", linux.CAP_SYS_NICE, "", nil),
239: syscalls.CapError("move_pages", linux.CAP_SYS_NICE, "", nil), // requires cap_sys_nice (mostly)
240: syscalls.Supported("rt_tgsigqueueinfo", RtTgsigqueueinfo),
- 241: syscalls.ErrorWithEvent("perf_event_open", syserror.ENODEV, "No support for perf counters", nil),
+ 241: syscalls.ErrorWithEvent("perf_event_open", linuxerr.ENODEV, "No support for perf counters", nil),
242: syscalls.Supported("accept4", Accept4),
243: syscalls.PartiallySupported("recvmmsg", RecvMMsg, "Not all flags and control messages are supported.", nil),
260: syscalls.Supported("wait4", Wait4),
diff --git a/pkg/sentry/syscalls/linux/sigset.go b/pkg/sentry/syscalls/linux/sigset.go
index e8c2d8f9e..9dea78085 100644
--- a/pkg/sentry/syscalls/linux/sigset.go
+++ b/pkg/sentry/syscalls/linux/sigset.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
@@ -29,7 +30,7 @@ import (
// syscalls are moved into this package, then they can be unexported.
func CopyInSigSet(t *kernel.Task, sigSetAddr hostarch.Addr, size uint) (linux.SignalSet, error) {
if size != linux.SignalSetSize {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
b := t.CopyScratchBuffer(8)
if _, err := t.CopyInBytes(sigSetAddr, b); err != nil {
diff --git a/pkg/sentry/syscalls/linux/sys_aio.go b/pkg/sentry/syscalls/linux/sys_aio.go
index 70e8569a8..4ce3430e2 100644
--- a/pkg/sentry/syscalls/linux/sys_aio.go
+++ b/pkg/sentry/syscalls/linux/sys_aio.go
@@ -17,6 +17,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -42,7 +43,7 @@ func IoSetup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
return 0, nil, err
}
if idIn != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
id, err := t.MemoryManager().NewAIOContext(t, uint32(nrEvents))
@@ -66,7 +67,7 @@ func IoDestroy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
ctx := t.MemoryManager().DestroyAIOContext(t, id)
if ctx == nil {
// Does not exist.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Drain completed requests amd wait for pending requests until there are no
@@ -97,12 +98,12 @@ func IoGetevents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// Sanity check arguments.
if minEvents < 0 || minEvents > events {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ctx, ok := t.MemoryManager().LookupAIOContext(t, id)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Setup the timeout.
@@ -114,7 +115,7 @@ func IoGetevents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
return 0, nil, err
}
if !d.Valid() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
deadline = t.Kernel().MonotonicClock().Now().Add(d.ToDuration())
haveDeadline = true
@@ -134,7 +135,7 @@ func IoGetevents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
var err error
v, err = waitForRequest(ctx, t, haveDeadline, deadline)
if err != nil {
- if count > 0 || err == syserror.ETIMEDOUT {
+ if count > 0 || linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
return uintptr(count), nil, nil
}
return 0, nil, syserror.ConvertIntr(err, syserror.EINTR)
@@ -171,7 +172,7 @@ func waitForRequest(ctx *mm.AIOContext, t *kernel.Task, haveDeadline bool, deadl
done := ctx.WaitChannel()
if done == nil {
// Context has been destroyed.
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
if err := t.BlockWithDeadline(done, haveDeadline, deadline); err != nil {
return nil, err
@@ -184,7 +185,7 @@ func memoryFor(t *kernel.Task, cb *linux.IOCallback) (usermem.IOSequence, error)
bytes := int(cb.Bytes)
if bytes < 0 {
// Linux also requires that this field fit in ssize_t.
- return usermem.IOSequence{}, syserror.EINVAL
+ return usermem.IOSequence{}, linuxerr.EINVAL
}
// Since this I/O will be asynchronous with respect to t's task goroutine,
@@ -206,7 +207,7 @@ func memoryFor(t *kernel.Task, cb *linux.IOCallback) (usermem.IOSequence, error)
default:
// Not a supported command.
- return usermem.IOSequence{}, syserror.EINVAL
+ return usermem.IOSequence{}, linuxerr.EINVAL
}
}
@@ -269,7 +270,7 @@ func submitCallback(t *kernel.Task, id uint64, cb *linux.IOCallback, cbAddr host
file := t.GetFile(cb.FD)
if file == nil {
// File not found.
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer file.DecRef(t)
@@ -279,14 +280,14 @@ func submitCallback(t *kernel.Task, id uint64, cb *linux.IOCallback, cbAddr host
eventFile = t.GetFile(cb.ResFD)
if eventFile == nil {
// Bad FD.
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer eventFile.DecRef(t)
// Check that it is an eventfd.
if _, ok := eventFile.FileOperations.(*eventfd.EventOperations); !ok {
// Not an event FD.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
@@ -299,14 +300,14 @@ func submitCallback(t *kernel.Task, id uint64, cb *linux.IOCallback, cbAddr host
switch cb.OpCode {
case linux.IOCB_CMD_PREAD, linux.IOCB_CMD_PREADV, linux.IOCB_CMD_PWRITE, linux.IOCB_CMD_PWRITEV:
if cb.Offset < 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
// Prepare the request.
ctx, ok := t.MemoryManager().LookupAIOContext(t, id)
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if err := ctx.Prepare(); err != nil {
return err
@@ -335,7 +336,7 @@ func IoSubmit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
addr := args[2].Pointer()
if nrEvents < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
for i := int32(0); i < nrEvents; i++ {
diff --git a/pkg/sentry/syscalls/linux/sys_capability.go b/pkg/sentry/syscalls/linux/sys_capability.go
index d3b85e11b..8318be001 100644
--- a/pkg/sentry/syscalls/linux/sys_capability.go
+++ b/pkg/sentry/syscalls/linux/sys_capability.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -24,7 +25,7 @@ import (
func lookupCaps(t *kernel.Task, tid kernel.ThreadID) (permitted, inheritable, effective auth.CapabilitySet, err error) {
if tid < 0 {
- err = syserror.EINVAL
+ err = linuxerr.EINVAL
return
}
if tid > 0 {
@@ -97,7 +98,7 @@ func Capget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
return 0, nil, err
}
if dataAddr != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, nil
}
@@ -115,7 +116,7 @@ func Capset(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
switch hdr.Version {
case linux.LINUX_CAPABILITY_VERSION_1:
if tid := kernel.ThreadID(hdr.Pid); tid != 0 && tid != t.ThreadID() {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
var data linux.CapUserData
if _, err := data.CopyIn(t, dataAddr); err != nil {
@@ -128,7 +129,7 @@ func Capset(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
case linux.LINUX_CAPABILITY_VERSION_2, linux.LINUX_CAPABILITY_VERSION_3:
if tid := kernel.ThreadID(hdr.Pid); tid != 0 && tid != t.ThreadID() {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
var data [2]linux.CapUserData
if _, err := linux.CopyCapUserDataSliceIn(t, dataAddr, data[:]); err != nil {
@@ -144,6 +145,6 @@ func Capset(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
if _, err := hdr.CopyOut(t, hdrAddr); err != nil {
return 0, nil, err
}
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/syscalls/linux/sys_epoll.go b/pkg/sentry/syscalls/linux/sys_epoll.go
index 69cbc98d0..daa151bb4 100644
--- a/pkg/sentry/syscalls/linux/sys_epoll.go
+++ b/pkg/sentry/syscalls/linux/sys_epoll.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -31,7 +32,7 @@ import (
func EpollCreate1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
flags := args[0].Int()
if flags & ^linux.EPOLL_CLOEXEC != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
closeOnExec := flags&linux.EPOLL_CLOEXEC != 0
@@ -48,7 +49,7 @@ func EpollCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
size := args[0].Int()
if size <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
fd, err := syscalls.CreateEpoll(t, false)
@@ -101,7 +102,7 @@ func EpollCtl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
mask |= waiter.EventHUp | waiter.EventErr
return 0, nil, syscalls.UpdateEpoll(t, epfd, fd, flags, mask, data)
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/syscalls/linux/sys_eventfd.go b/pkg/sentry/syscalls/linux/sys_eventfd.go
index 3b4f879e4..7ba9a755e 100644
--- a/pkg/sentry/syscalls/linux/sys_eventfd.go
+++ b/pkg/sentry/syscalls/linux/sys_eventfd.go
@@ -16,11 +16,11 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/eventfd"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Eventfd2 implements linux syscall eventfd2(2).
@@ -30,7 +30,7 @@ func Eventfd2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
allOps := uint(linux.EFD_SEMAPHORE | linux.EFD_NONBLOCK | linux.EFD_CLOEXEC)
if flags & ^allOps != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
event := eventfd.New(t, uint64(initVal), flags&linux.EFD_SEMAPHORE != 0)
diff --git a/pkg/sentry/syscalls/linux/sys_file.go b/pkg/sentry/syscalls/linux/sys_file.go
index 37443ab78..a08449824 100644
--- a/pkg/sentry/syscalls/linux/sys_file.go
+++ b/pkg/sentry/syscalls/linux/sys_file.go
@@ -18,6 +18,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -79,7 +80,7 @@ func fileOpOn(t *kernel.Task, dirFD int32, path string, resolve bool, fn func(ro
// Need to extract the given FD.
f = t.GetFile(dirFD)
if f == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
rel = f.Dirent
if !fs.IsDir(rel.Inode.StableAttr) {
@@ -152,7 +153,7 @@ func openAt(t *kernel.Task, dirFD int32, addr hostarch.Addr, flags uint) (fd uin
}
if fs.IsSymlink(d.Inode.StableAttr) && !resolve {
- return syserror.ELOOP
+ return linuxerr.ELOOP
}
fileFlags := linuxToFlags(flags)
@@ -270,12 +271,12 @@ func mknodAt(t *kernel.Task, dirFD int32, addr hostarch.Addr, mode linux.FileMod
//
// When we start supporting block and character devices, we'll
// need to check for CAP_MKNOD here.
- return syserror.EPERM
+ return linuxerr.EPERM
default:
// "EINVAL - mode requested creation of something other than a
// regular file, device special file, FIFO or socket." - mknod(2)
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
})
}
@@ -350,7 +351,7 @@ func createAt(t *kernel.Task, dirFD int32, addr hostarch.Addr, flags uint, mode
// If O_NOFOLLOW was passed, then don't try to resolve
// anything.
if flags&linux.O_NOFOLLOW != 0 {
- return syserror.ELOOP
+ return linuxerr.ELOOP
}
// Try to resolve the symlink directly to a Dirent.
@@ -394,8 +395,8 @@ func createAt(t *kernel.Task, dirFD int32, addr hostarch.Addr, flags uint, mode
}
var newFile *fs.File
- switch err {
- case nil:
+ switch {
+ case err == nil:
// Like sys_open, check for a few things about the
// filesystem before trying to get a reference to the
// fs.File. The same constraints on Check apply.
@@ -418,7 +419,7 @@ func createAt(t *kernel.Task, dirFD int32, addr hostarch.Addr, flags uint, mode
return syserror.ConvertIntr(err, syserror.ERESTARTSYS)
}
defer newFile.DecRef(t)
- case syserror.ENOENT:
+ case linuxerr.Equals(linuxerr.ENOENT, err):
// File does not exist. Proceed with creation.
// Do we have write permissions on the parent?
@@ -527,7 +528,7 @@ func accessAt(t *kernel.Task, dirFD int32, addr hostarch.Addr, mode uint) error
// Sanity check the mode.
if mode&^(rOK|wOK|xOK) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
return fileOpOn(t, dirFD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
@@ -595,7 +596,7 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -703,7 +704,7 @@ func Chroot(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
addr := args[0].Pointer()
if !t.HasCapability(linux.CAP_SYS_CHROOT) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
path, _, err := copyInPath(t, addr, false /* allowEmpty */)
@@ -758,7 +759,7 @@ func Fchdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -789,7 +790,7 @@ func Close(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// (and other reference-holding operations complete).
file, _ := t.FDTable().Remove(t, fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -803,13 +804,13 @@ func Dup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
newFD, err := t.NewFDFrom(0, file, kernel.FDFlags{})
if err != nil {
- return 0, nil, syserror.EMFILE
+ return 0, nil, linuxerr.EMFILE
}
return uintptr(newFD), nil, nil
}
@@ -824,7 +825,7 @@ func Dup2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
if oldfd == newfd {
oldFile := t.GetFile(oldfd)
if oldFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer oldFile.DecRef(t)
@@ -843,12 +844,12 @@ func Dup3(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
flags := args[2].Uint()
if oldfd == newfd {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
oldFile := t.GetFile(oldfd)
if oldFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer oldFile.DecRef(t)
@@ -905,7 +906,7 @@ func fSetOwn(t *kernel.Task, fd int, file *fs.File, who int32) error {
if who < 0 {
// Check for overflow before flipping the sign.
if who-1 > who {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
pg := t.PIDNamespace().ProcessGroupWithID(kernel.ProcessGroupID(-who))
a.SetOwnerProcessGroup(t, pg)
@@ -923,7 +924,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file, flags := t.FDTable().Get(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -956,7 +957,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// Normally pipe and socket types lack lock operations. We diverge and use a heavy
// hammer by only allowing locks on files and directories.
if !fs.IsFile(file.Dirent.Inode.StableAttr) && !fs.IsDir(file.Dirent.Inode.StableAttr) {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Copy in the lock request.
@@ -976,7 +977,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case 2:
sw = fs.SeekEnd
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Compute the lock offset.
@@ -995,7 +996,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
}
off = uattr.Size
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Compute the lock range.
@@ -1009,12 +1010,12 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
switch flock.Type {
case linux.F_RDLCK:
if !file.Flags().Read {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
if cmd == linux.F_SETLK {
// Non-blocking lock, provide a nil lock.Blocker.
if !file.Dirent.Inode.LockCtx.Posix.LockRegionVFS1(t.FDTable(), lock.ReadLock, rng, nil) {
- return 0, nil, syserror.EAGAIN
+ return 0, nil, linuxerr.EAGAIN
}
} else {
// Blocking lock, pass in the task to satisfy the lock.Blocker interface.
@@ -1025,12 +1026,12 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return 0, nil, nil
case linux.F_WRLCK:
if !file.Flags().Write {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
if cmd == linux.F_SETLK {
// Non-blocking lock, provide a nil lock.Blocker.
if !file.Dirent.Inode.LockCtx.Posix.LockRegionVFS1(t.FDTable(), lock.WriteLock, rng, nil) {
- return 0, nil, syserror.EAGAIN
+ return 0, nil, linuxerr.EAGAIN
}
} else {
// Blocking lock, pass in the task to satisfy the lock.Blocker interface.
@@ -1043,7 +1044,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file.Dirent.Inode.LockCtx.Posix.UnlockRegion(t.FDTable(), rng)
return 0, nil, nil
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
case linux.F_GETOWN:
return uintptr(fGetOwn(t, file)), nil, nil
@@ -1085,28 +1086,28 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
a.SetOwnerProcessGroup(t, pg)
return 0, nil, nil
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
case linux.F_GET_SEALS:
val, err := tmpfs.GetSeals(file.Dirent.Inode)
return uintptr(val), nil, err
case linux.F_ADD_SEALS:
if !file.Flags().Write {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
err := tmpfs.AddSeals(file.Dirent.Inode, args[2].Uint())
return 0, nil, err
case linux.F_GETPIPE_SZ:
sz, ok := file.FileOperations.(fs.FifoSizer)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
size, err := sz.FifoSize(t, file)
return uintptr(size), nil, err
case linux.F_SETPIPE_SZ:
sz, ok := file.FileOperations.(fs.FifoSizer)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
n, err := sz.SetFifoSize(int64(args[2].Int()))
return uintptr(n), nil, err
@@ -1118,7 +1119,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return 0, nil, a.SetSignal(linux.Signal(args[2].Int()))
default:
// Everything else is not yet supported.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
@@ -1131,18 +1132,18 @@ func Fadvise64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
// Note: offset is allowed to be negative.
if length < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// If the FD refers to a pipe or FIFO, return error.
if fs.IsPipe(file.Dirent.Inode.StableAttr) {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
switch advice {
@@ -1153,7 +1154,7 @@ func Fadvise64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
case linux.POSIX_FADV_DONTNEED:
case linux.POSIX_FADV_NOREUSE:
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Sure, whatever.
@@ -1178,12 +1179,12 @@ func mkdirAt(t *kernel.Task, dirFD int32, addr hostarch.Addr, mode linux.FileMod
// Does this directory exist already?
remainingTraversals := uint(linux.MaxSymlinkTraversals)
f, err := t.MountNamespace().FindInode(t, root, d, name, &remainingTraversals)
- switch err {
- case nil:
+ switch {
+ case err == nil:
// The directory existed.
defer f.DecRef(t)
return syserror.EEXIST
- case syserror.EACCES:
+ case linuxerr.Equals(linuxerr.EACCES, err):
// Permission denied while walking to the directory.
return err
default:
@@ -1224,7 +1225,7 @@ func rmdirAt(t *kernel.Task, dirFD int32, addr hostarch.Addr) error {
// Special case: removing the root always returns EBUSY.
if path == "/" {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string, _ uint) error {
@@ -1236,9 +1237,9 @@ func rmdirAt(t *kernel.Task, dirFD int32, addr hostarch.Addr) error {
// dot vs. double dots.
switch name {
case ".":
- return syserror.EINVAL
+ return linuxerr.EINVAL
case "..":
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
if err := d.MayDelete(t, root, name); err != nil {
@@ -1329,10 +1330,10 @@ func mayLinkAt(t *kernel.Task, target *fs.Inode) error {
// If we are not the owner, then the file must be regular and have
// Read+Write permissions.
if !fs.IsRegular(target.StableAttr) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if target.CheckPermission(t, fs.PermMask{Read: true, Write: true}) != nil {
- return syserror.EPERM
+ return linuxerr.EPERM
}
return nil
@@ -1357,7 +1358,7 @@ func linkAt(t *kernel.Task, oldDirFD int32, oldAddr hostarch.Addr, newDirFD int3
if allowEmpty && oldPath == "" {
target := t.GetFile(oldDirFD)
if target == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer target.DecRef(t)
if err := mayLinkAt(t, target.Dirent.Inode); err != nil {
@@ -1431,7 +1432,7 @@ func Linkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// Sanity check flags.
if flags&^(linux.AT_SYMLINK_FOLLOW|linux.AT_EMPTY_PATH) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
resolve := flags&linux.AT_SYMLINK_FOLLOW == linux.AT_SYMLINK_FOLLOW
@@ -1464,8 +1465,8 @@ func readlinkAt(t *kernel.Task, dirFD int32, addr hostarch.Addr, bufAddr hostarc
}
s, err := d.Inode.Readlink(t)
- if err == syserror.ENOLINK {
- return syserror.EINVAL
+ if linuxerr.Equals(linuxerr.ENOLINK, err) {
+ return linuxerr.EINVAL
}
if err != nil {
return err
@@ -1557,7 +1558,7 @@ func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
length := args[1].Int64()
if length < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
path, dirPath, err := copyInPath(t, addr, false /* allowEmpty */)
@@ -1565,15 +1566,15 @@ func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
return 0, nil, err
}
if dirPath {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if uint64(length) >= t.ThreadGroup().Limits().Get(limits.FileSize).Cur {
- t.SendSignal(&arch.SignalInfo{
+ t.SendSignal(&linux.SignalInfo{
Signo: int32(linux.SIGXFSZ),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
})
- return 0, nil, syserror.EFBIG
+ return 0, nil, linuxerr.EFBIG
}
return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
@@ -1583,7 +1584,7 @@ func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// In contrast to open(O_TRUNC), truncate(2) is only valid for file
// types.
if !fs.IsFile(d.Inode.StableAttr) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Reject truncation if the access permissions do not allow truncation.
@@ -1610,33 +1611,33 @@ func Ftruncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Reject truncation if the file flags do not permit this operation.
// This is different from truncate(2) above.
if !file.Flags().Write {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// In contrast to open(O_TRUNC), truncate(2) is only valid for file
// types. Note that this is different from truncate(2) above, where a
// directory returns EISDIR.
if !fs.IsFile(file.Dirent.Inode.StableAttr) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if length < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if uint64(length) >= t.ThreadGroup().Limits().Get(limits.FileSize).Cur {
- t.SendSignal(&arch.SignalInfo{
+ t.SendSignal(&linux.SignalInfo{
Signo: int32(linux.SIGXFSZ),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
})
- return 0, nil, syserror.EFBIG
+ return 0, nil, linuxerr.EFBIG
}
if err := file.Dirent.Inode.Truncate(t, file.Dirent, length); err != nil {
@@ -1682,7 +1683,7 @@ func chown(t *kernel.Task, d *fs.Dirent, uid auth.UID, gid auth.GID) error {
kuid := c.UserNamespace.MapToKUID(uid)
// Valid UID must be supplied if UID is to be changed.
if !kuid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// "Only a privileged process (CAP_CHOWN) may change the owner
@@ -1692,7 +1693,7 @@ func chown(t *kernel.Task, d *fs.Dirent, uid auth.UID, gid auth.GID) error {
// explicitly not changing its UID.
isNoop := uattr.Owner.UID == kuid
if !(hasCap || (isOwner && isNoop)) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// The setuid and setgid bits are cleared during a chown.
@@ -1706,7 +1707,7 @@ func chown(t *kernel.Task, d *fs.Dirent, uid auth.UID, gid auth.GID) error {
kgid := c.UserNamespace.MapToKGID(gid)
// Valid GID must be supplied if GID is to be changed.
if !kgid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// "The owner of a file may change the group of the file to any
@@ -1715,7 +1716,7 @@ func chown(t *kernel.Task, d *fs.Dirent, uid auth.UID, gid auth.GID) error {
isNoop := uattr.Owner.GID == kgid
isMemberGroup := c.InGroup(kgid)
if !(hasCap || (isOwner && (isNoop || isMemberGroup))) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// The setuid and setgid bits are cleared during a chown.
@@ -1737,7 +1738,7 @@ func chown(t *kernel.Task, d *fs.Dirent, uid auth.UID, gid auth.GID) error {
if clearPrivilege && uattr.Perms.HasSetUIDOrGID() && !fs.IsDir(d.Inode.StableAttr) {
uattr.Perms.DropSetUIDAndMaybeGID()
if !d.Inode.SetPermissions(t, d, uattr.Perms) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
@@ -1754,7 +1755,7 @@ func chownAt(t *kernel.Task, fd int32, addr hostarch.Addr, resolve, allowEmpty b
// Annoying. What's wrong with fchown?
file := t.GetFile(fd)
if file == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer file.DecRef(t)
@@ -1792,7 +1793,7 @@ func Fchown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -1808,7 +1809,7 @@ func Fchownat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
flags := args[4].Int()
if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, chownAt(t, dirFD, addr, flags&linux.AT_SYMLINK_NOFOLLOW == 0, flags&linux.AT_EMPTY_PATH != 0, uid, gid)
@@ -1817,12 +1818,12 @@ func Fchownat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
func chmod(t *kernel.Task, d *fs.Dirent, mode linux.FileMode) error {
// Must own file to change mode.
if !d.Inode.CheckOwnership(t) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
p := fs.FilePermsFromMode(mode)
if !d.Inode.SetPermissions(t, d, p) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// File attribute changed, generate notification.
@@ -1857,7 +1858,7 @@ func Fchmod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -1888,7 +1889,7 @@ func utimes(t *kernel.Task, dirFD int32, addr hostarch.Addr, ts fs.TimeSpec, res
if !d.Inode.CheckOwnership(t) {
// Trying to set a specific time? Must be owner.
if (ts.ATimeOmit || !ts.ATimeSetSystemTime) && (ts.MTimeOmit || !ts.MTimeSetSystemTime) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Trying to set to current system time? Must have write access.
@@ -1913,11 +1914,11 @@ func utimes(t *kernel.Task, dirFD int32, addr hostarch.Addr, ts fs.TimeSpec, res
if addr == 0 && dirFD != linux.AT_FDCWD {
if !resolve {
// Linux returns EINVAL in this case. See utimes.c.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
f := t.GetFile(dirFD)
if f == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer f.DecRef(t)
@@ -1996,7 +1997,7 @@ func Utimensat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
return 0, nil, err
}
if !timespecIsValid(times[0]) || !timespecIsValid(times[1]) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// If both are UTIME_OMIT, this is a noop.
@@ -2031,7 +2032,7 @@ func Futimesat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
}
if times[0].Usec >= 1e6 || times[0].Usec < 0 ||
times[1].Usec >= 1e6 || times[1].Usec < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ts = fs.TimeSpec{
@@ -2065,7 +2066,7 @@ func renameAt(t *kernel.Task, oldDirFD int32, oldAddr hostarch.Addr, newDirFD in
// the root) with EBUSY.
switch oldName {
case "", ".", "..":
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return fileOpAt(t, newDirFD, newPath, func(root *fs.Dirent, newParent *fs.Dirent, newName string, _ uint) error {
@@ -2077,7 +2078,7 @@ func renameAt(t *kernel.Task, oldDirFD int32, oldAddr hostarch.Addr, newDirFD in
// (i.e. the root) with EBUSY.
switch newName {
case "", ".", "..":
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return fs.Rename(t, root, oldParent, oldName, newParent, newName)
@@ -2112,39 +2113,39 @@ func Fallocate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
if offset < 0 || length <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if mode != 0 {
t.Kernel().EmitUnimplementedEvent(t)
- return 0, nil, syserror.ENOTSUP
+ return 0, nil, linuxerr.ENOTSUP
}
if !file.Flags().Write {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
if fs.IsPipe(file.Dirent.Inode.StableAttr) {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
if fs.IsDir(file.Dirent.Inode.StableAttr) {
return 0, nil, syserror.EISDIR
}
if !fs.IsRegular(file.Dirent.Inode.StableAttr) {
- return 0, nil, syserror.ENODEV
+ return 0, nil, linuxerr.ENODEV
}
size := offset + length
if size < 0 {
- return 0, nil, syserror.EFBIG
+ return 0, nil, linuxerr.EFBIG
}
if uint64(size) >= t.ThreadGroup().Limits().Get(limits.FileSize).Cur {
- t.SendSignal(&arch.SignalInfo{
+ t.SendSignal(&linux.SignalInfo{
Signo: int32(linux.SIGXFSZ),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
})
- return 0, nil, syserror.EFBIG
+ return 0, nil, linuxerr.EFBIG
}
if err := file.Dirent.Inode.Allocate(t, file.Dirent, offset, length); err != nil {
@@ -2165,7 +2166,7 @@ func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFile(fd)
if file == nil {
// flock(2): EBADF fd is not an open file descriptor.
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -2183,7 +2184,7 @@ func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
if nonblocking {
// Since we're nonblocking we pass a nil lock.Blocker implementation.
if !file.Dirent.Inode.LockCtx.BSD.LockRegionVFS1(file, lock.WriteLock, rng, nil) {
- return 0, nil, syserror.EWOULDBLOCK
+ return 0, nil, linuxerr.EWOULDBLOCK
}
} else {
// Because we're blocking we will pass the task to satisfy the lock.Blocker interface.
@@ -2195,7 +2196,7 @@ func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
if nonblocking {
// Since we're nonblocking we pass a nil lock.Blocker implementation.
if !file.Dirent.Inode.LockCtx.BSD.LockRegionVFS1(file, lock.ReadLock, rng, nil) {
- return 0, nil, syserror.EWOULDBLOCK
+ return 0, nil, linuxerr.EWOULDBLOCK
}
} else {
// Because we're blocking we will pass the task to satisfy the lock.Blocker interface.
@@ -2207,7 +2208,7 @@ func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file.Dirent.Inode.LockCtx.BSD.UnlockRegion(file, rng)
default:
// flock(2): EINVAL operation is invalid.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, nil
@@ -2226,7 +2227,7 @@ func MemfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
if flags&^memfdAllFlags != 0 {
// Unknown bits in flags.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
allowSeals := flags&linux.MFD_ALLOW_SEALING != 0
@@ -2237,7 +2238,7 @@ func MemfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
return 0, nil, err
}
if len(name) > memfdMaxNameLen {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
name = memfdPrefix + name
diff --git a/pkg/sentry/syscalls/linux/sys_futex.go b/pkg/sentry/syscalls/linux/sys_futex.go
index eeea1613b..a66514c7f 100644
--- a/pkg/sentry/syscalls/linux/sys_futex.go
+++ b/pkg/sentry/syscalls/linux/sys_futex.go
@@ -18,6 +18,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -159,7 +160,7 @@ func tryLockPI(t *kernel.Task, addr hostarch.Addr, private bool) error {
return err
}
if !locked {
- return syserror.EWOULDBLOCK
+ return linuxerr.EWOULDBLOCK
}
return nil
}
@@ -210,7 +211,7 @@ func Futex(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// WAIT_BITSET uses an absolute timeout which is either
// CLOCK_MONOTONIC or CLOCK_REALTIME.
if mask == 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
n, err := futexWaitAbsolute(t, clockRealtime, timespec, forever, addr, private, uint32(val), mask)
return n, nil, err
@@ -224,7 +225,7 @@ func Futex(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.FUTEX_WAKE_BITSET:
if mask == 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if val <= 0 {
// The Linux kernel wakes one waiter even if val is
@@ -295,7 +296,7 @@ func SetRobustList(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
length := args[1].SizeT()
if length != uint(linux.SizeOfRobustListHead) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
t.SetRobustList(head)
return 0, nil, nil
@@ -310,7 +311,7 @@ func GetRobustList(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
sizeAddr := args[2].Pointer()
if tid < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ot := t
diff --git a/pkg/sentry/syscalls/linux/sys_getdents.go b/pkg/sentry/syscalls/linux/sys_getdents.go
index bbba71d8f..917717e31 100644
--- a/pkg/sentry/syscalls/linux/sys_getdents.go
+++ b/pkg/sentry/syscalls/linux/sys_getdents.go
@@ -19,6 +19,7 @@ import (
"io"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -38,7 +39,7 @@ func Getdents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
minSize := int(smallestDirent(t.Arch()))
if size < minSize {
// size is smaller than smallest possible dirent.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
n, err := getdents(t, fd, addr, size, (*dirent).Serialize)
@@ -54,7 +55,7 @@ func Getdents64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
minSize := int(smallestDirent64(t.Arch()))
if size < minSize {
// size is smaller than smallest possible dirent.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
n, err := getdents(t, fd, addr, size, (*dirent).Serialize64)
@@ -66,7 +67,7 @@ func Getdents64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
func getdents(t *kernel.Task, fd int32, addr hostarch.Addr, size int, f func(*dirent, io.Writer) (int, error)) (uintptr, error) {
dir := t.GetFile(fd)
if dir == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
defer dir.DecRef(t)
diff --git a/pkg/sentry/syscalls/linux/sys_identity.go b/pkg/sentry/syscalls/linux/sys_identity.go
index a29d307e5..50fcadb58 100644
--- a/pkg/sentry/syscalls/linux/sys_identity.go
+++ b/pkg/sentry/syscalls/linux/sys_identity.go
@@ -15,10 +15,10 @@
package linux
import (
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/syserror"
)
const (
@@ -142,7 +142,7 @@ func Setresgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
func Getgroups(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
size := int(args[0].Int())
if size < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
kgids := t.Credentials().ExtraKGIDs
// "If size is zero, list is not modified, but the total number of
@@ -151,7 +151,7 @@ func Getgroups(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
return uintptr(len(kgids)), nil, nil
}
if size < len(kgids) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
gids := make([]auth.GID, len(kgids))
for i, kgid := range kgids {
@@ -167,7 +167,7 @@ func Getgroups(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
func Setgroups(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
size := args[0].Int()
if size < 0 || size > maxNGroups {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if size == 0 {
return 0, nil, t.SetExtraGIDs(nil)
diff --git a/pkg/sentry/syscalls/linux/sys_inotify.go b/pkg/sentry/syscalls/linux/sys_inotify.go
index cf47bb9dd..3738e4cfb 100644
--- a/pkg/sentry/syscalls/linux/sys_inotify.go
+++ b/pkg/sentry/syscalls/linux/sys_inotify.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/anon"
@@ -30,7 +31,7 @@ func InotifyInit1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
flags := int(args[0].Int())
if flags&^allFlags != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
dirent := fs.NewDirent(t, anon.NewInode(t), "inotify")
@@ -65,14 +66,14 @@ func fdToInotify(t *kernel.Task, fd int32) (*fs.Inotify, *fs.File, error) {
file := t.GetFile(fd)
if file == nil {
// Invalid fd.
- return nil, nil, syserror.EBADF
+ return nil, nil, linuxerr.EBADF
}
ino, ok := file.FileOperations.(*fs.Inotify)
if !ok {
// Not an inotify fd.
file.DecRef(t)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
return ino, file, nil
@@ -91,7 +92,7 @@ func InotifyAddWatch(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kern
// "EINVAL: The given event mask contains no valid events."
// -- inotify_add_watch(2)
if validBits := mask & linux.ALL_INOTIFY_BITS; validBits == 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ino, file, err := fdToInotify(t, fd)
diff --git a/pkg/sentry/syscalls/linux/sys_lseek.go b/pkg/sentry/syscalls/linux/sys_lseek.go
index 0046347cb..bf71a9af3 100644
--- a/pkg/sentry/syscalls/linux/sys_lseek.go
+++ b/pkg/sentry/syscalls/linux/sys_lseek.go
@@ -15,6 +15,7 @@
package linux
import (
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -31,7 +32,7 @@ func Lseek(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -44,7 +45,7 @@ func Lseek(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case 2:
sw = fs.SeekEnd
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
offset, serr := file.Seek(t, sw, offset)
diff --git a/pkg/sentry/syscalls/linux/sys_membarrier.go b/pkg/sentry/syscalls/linux/sys_membarrier.go
index 63ee5d435..6ceedc086 100644
--- a/pkg/sentry/syscalls/linux/sys_membarrier.go
+++ b/pkg/sentry/syscalls/linux/sys_membarrier.go
@@ -16,9 +16,9 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Membarrier implements syscall membarrier(2).
@@ -29,7 +29,7 @@ func Membarrier(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
switch cmd {
case linux.MEMBARRIER_CMD_QUERY:
if flags != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var supportedCommands uintptr
if t.Kernel().Platform.HaveGlobalMemoryBarrier() {
@@ -46,58 +46,58 @@ func Membarrier(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
return supportedCommands, nil, nil
case linux.MEMBARRIER_CMD_GLOBAL, linux.MEMBARRIER_CMD_GLOBAL_EXPEDITED, linux.MEMBARRIER_CMD_PRIVATE_EXPEDITED:
if flags != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if !t.Kernel().Platform.HaveGlobalMemoryBarrier() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if cmd == linux.MEMBARRIER_CMD_PRIVATE_EXPEDITED && !t.MemoryManager().IsMembarrierPrivateEnabled() {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
return 0, nil, t.Kernel().Platform.GlobalMemoryBarrier()
case linux.MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED:
if flags != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if !t.Kernel().Platform.HaveGlobalMemoryBarrier() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// no-op
return 0, nil, nil
case linux.MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED:
if flags != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if !t.Kernel().Platform.HaveGlobalMemoryBarrier() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
t.MemoryManager().EnableMembarrierPrivate()
return 0, nil, nil
case linux.MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ:
if flags&^linux.MEMBARRIER_CMD_FLAG_CPU != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if !t.RSeqAvailable() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if !t.MemoryManager().IsMembarrierRSeqEnabled() {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
// MEMBARRIER_CMD_FLAG_CPU and cpu_id are ignored since we don't have
// the ability to preempt specific CPUs.
return 0, nil, t.Kernel().Platform.PreemptAllCPUs()
case linux.MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ:
if flags != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if !t.RSeqAvailable() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
t.MemoryManager().EnableMembarrierRSeq()
return 0, nil, nil
default:
// Probably a command we don't implement.
t.Kernel().EmitUnimplementedEvent(t)
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/syscalls/linux/sys_mempolicy.go b/pkg/sentry/syscalls/linux/sys_mempolicy.go
index 6d27f4292..adb513a3d 100644
--- a/pkg/sentry/syscalls/linux/sys_mempolicy.go
+++ b/pkg/sentry/syscalls/linux/sys_mempolicy.go
@@ -18,6 +18,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -43,7 +44,7 @@ func copyInNodemask(t *kernel.Task, addr hostarch.Addr, maxnode uint32) (uint64,
// maxnode-1, not maxnode, as the number of bits.
bits := maxnode - 1
if bits > hostarch.PageSize*8 { // also handles overflow from maxnode == 0
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if bits == 0 {
return 0, nil
@@ -58,12 +59,12 @@ func copyInNodemask(t *kernel.Task, addr hostarch.Addr, maxnode uint32) (uint64,
// Check that only allowed bits in the first unsigned long in the nodemask
// are set.
if val&^allowedNodemask != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Check that all remaining bits in the nodemask are 0.
for i := 8; i < len(buf); i++ {
if buf[i] != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
return val, nil
@@ -74,7 +75,7 @@ func copyOutNodemask(t *kernel.Task, addr hostarch.Addr, maxnode uint32, val uin
// bits.
bits := maxnode - 1
if bits > hostarch.PageSize*8 { // also handles overflow from maxnode == 0
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if bits == 0 {
return nil
@@ -110,7 +111,7 @@ func GetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
flags := args[4].Uint()
if flags&^(linux.MPOL_F_NODE|linux.MPOL_F_ADDR|linux.MPOL_F_MEMS_ALLOWED) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
nodeFlag := flags&linux.MPOL_F_NODE != 0
addrFlag := flags&linux.MPOL_F_ADDR != 0
@@ -119,7 +120,7 @@ func GetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
// "EINVAL: The value specified by maxnode is less than the number of node
// IDs supported by the system." - get_mempolicy(2)
if nodemask != 0 && maxnode < maxNodes {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// "If flags specifies MPOL_F_MEMS_ALLOWED [...], the mode argument is
@@ -130,7 +131,7 @@ func GetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
// "It is not permitted to combine MPOL_F_MEMS_ALLOWED with either
// MPOL_F_ADDR or MPOL_F_NODE."
if nodeFlag || addrFlag {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if err := copyOutNodemask(t, nodemask, maxnode, allowedNodemask); err != nil {
return 0, nil, err
@@ -184,7 +185,7 @@ func GetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
// mm/mempolicy.c:do_get_mempolicy() doesn't special-case NULL; it will
// just (usually) fail to find a VMA at address 0 and return EFAULT.
if addr != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// "If flags is specified as 0, then information about the calling thread's
@@ -198,7 +199,7 @@ func GetMempolicy(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
policy, nodemaskVal := t.NumaPolicy()
if nodeFlag {
if policy&^linux.MPOL_MODE_FLAGS != linux.MPOL_INTERLEAVE {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
policy = linux.MPOL_DEFAULT // maxNodes == 1
}
@@ -240,12 +241,12 @@ func Mbind(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
flags := args[5].Uint()
if flags&^linux.MPOL_MF_VALID != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// "If MPOL_MF_MOVE_ALL is passed in flags ... [the] calling thread must be
// privileged (CAP_SYS_NICE) to use this flag." - mbind(2)
if flags&linux.MPOL_MF_MOVE_ALL != 0 && !t.HasCapability(linux.CAP_SYS_NICE) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
mode, nodemaskVal, err := copyInMempolicyNodemask(t, mode, nodemask, maxnode)
@@ -264,11 +265,11 @@ func copyInMempolicyNodemask(t *kernel.Task, modeWithFlags linux.NumaPolicy, nod
mode := linux.NumaPolicy(modeWithFlags &^ linux.MPOL_MODE_FLAGS)
if flags == linux.MPOL_MODE_FLAGS {
// Can't specify both mode flags simultaneously.
- return 0, 0, syserror.EINVAL
+ return 0, 0, linuxerr.EINVAL
}
if mode < 0 || mode >= linux.MPOL_MAX {
// Must specify a valid mode.
- return 0, 0, syserror.EINVAL
+ return 0, 0, linuxerr.EINVAL
}
var nodemaskVal uint64
@@ -285,22 +286,22 @@ func copyInMempolicyNodemask(t *kernel.Task, modeWithFlags linux.NumaPolicy, nod
// "nodemask must be specified as NULL." - set_mempolicy(2). This is inaccurate;
// Linux allows a nodemask to be specified, as long as it is empty.
if nodemaskVal != 0 {
- return 0, 0, syserror.EINVAL
+ return 0, 0, linuxerr.EINVAL
}
case linux.MPOL_BIND, linux.MPOL_INTERLEAVE:
// These require a non-empty nodemask.
if nodemaskVal == 0 {
- return 0, 0, syserror.EINVAL
+ return 0, 0, linuxerr.EINVAL
}
case linux.MPOL_PREFERRED:
// This permits an empty nodemask, as long as no flags are set.
if nodemaskVal == 0 && flags != 0 {
- return 0, 0, syserror.EINVAL
+ return 0, 0, linuxerr.EINVAL
}
case linux.MPOL_LOCAL:
// This requires an empty nodemask and no flags set ...
if nodemaskVal != 0 || flags != 0 {
- return 0, 0, syserror.EINVAL
+ return 0, 0, linuxerr.EINVAL
}
// ... and is implemented as MPOL_PREFERRED.
mode = linux.MPOL_PREFERRED
diff --git a/pkg/sentry/syscalls/linux/sys_mmap.go b/pkg/sentry/syscalls/linux/sys_mmap.go
index 70da0707d..cee621791 100644
--- a/pkg/sentry/syscalls/linux/sys_mmap.go
+++ b/pkg/sentry/syscalls/linux/sys_mmap.go
@@ -18,13 +18,13 @@ import (
"bytes"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/mm"
"gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Brk implements linux syscall brk(2).
@@ -51,7 +51,7 @@ func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
// Require exactly one of MAP_PRIVATE and MAP_SHARED.
if private == shared {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
opts := memmap.MMapOpts{
@@ -84,14 +84,14 @@ func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
// Convert the passed FD to a file reference.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
flags := file.Flags()
// mmap unconditionally requires that the FD is readable.
if !flags.Read {
- return 0, nil, syserror.EACCES
+ return 0, nil, linuxerr.EACCES
}
// MAP_SHARED requires that the FD be writable for PROT_WRITE.
if shared && !flags.Write {
@@ -132,7 +132,7 @@ func Mremap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
newAddr := args[4].Pointer()
if flags&^(linux.MREMAP_MAYMOVE|linux.MREMAP_FIXED) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
mayMove := flags&linux.MREMAP_MAYMOVE != 0
fixed := flags&linux.MREMAP_FIXED != 0
@@ -147,7 +147,7 @@ func Mremap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
case !mayMove && fixed:
// "If MREMAP_FIXED is specified, then MREMAP_MAYMOVE must also be
// specified." - mremap(2)
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
rv, err := t.MemoryManager().MRemap(t, oldAddr, oldSize, newSize, mm.MRemapOpts{
@@ -178,7 +178,7 @@ func Madvise(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// "The Linux implementation requires that the address addr be
// page-aligned, and allows length to be zero." - madvise(2)
if addr.RoundDown() != addr {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if length == 0 {
return 0, nil, nil
@@ -186,7 +186,7 @@ func Madvise(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Not explicitly stated: length need not be page-aligned.
lenAddr, ok := hostarch.Addr(length).RoundUp()
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
length = uint64(lenAddr)
@@ -214,10 +214,10 @@ func Madvise(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
return 0, nil, syserror.ENOSYS
case linux.MADV_HWPOISON:
// Only privileged processes are allowed to poison pages.
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
default:
// If adv is not a valid value tell the caller.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
@@ -228,7 +228,7 @@ func Mincore(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
vec := args[2].Pointer()
if addr != addr.RoundDown() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// "The length argument need not be a multiple of the page size, but since
// residency information is returned for whole pages, length is effectively
@@ -265,11 +265,11 @@ func Msync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// semantics that are (currently) equivalent to specifying MS_ASYNC." -
// msync(2)
if flags&^(linux.MS_ASYNC|linux.MS_SYNC|linux.MS_INVALIDATE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
sync := flags&linux.MS_SYNC != 0
if sync && flags&linux.MS_ASYNC != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
err := t.MemoryManager().MSync(t, addr, uint64(length), mm.MSyncOpts{
Sync: sync,
@@ -295,7 +295,7 @@ func Mlock2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
flags := args[2].Int()
if flags&^(linux.MLOCK_ONFAULT) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
mode := memmap.MLockEager
@@ -318,7 +318,7 @@ func Mlockall(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
flags := args[0].Int()
if flags&^(linux.MCL_CURRENT|linux.MCL_FUTURE|linux.MCL_ONFAULT) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
mode := memmap.MLockEager
diff --git a/pkg/sentry/syscalls/linux/sys_mount.go b/pkg/sentry/syscalls/linux/sys_mount.go
index 864d2138c..6d26f89b9 100644
--- a/pkg/sentry/syscalls/linux/sys_mount.go
+++ b/pkg/sentry/syscalls/linux/sys_mount.go
@@ -16,12 +16,11 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Mount implements Linux syscall mount(2).
@@ -67,7 +66,7 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// Must have CAP_SYS_ADMIN in the mount namespace's associated user
// namespace.
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespace().UserNamespace()) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
const unsupportedOps = linux.MS_REMOUNT | linux.MS_BIND |
@@ -83,15 +82,15 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// unknown or unsupported flags are passed. Since we don't implement
// everything, we fail explicitly on flags that are unimplemented.
if flags&(unsupportedOps|unsupportedFlags) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
rsys, ok := fs.FindFilesystem(fsType)
if !ok {
- return 0, nil, syserror.ENODEV
+ return 0, nil, linuxerr.ENODEV
}
if !rsys.AllowUserMount() {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
var superFlags fs.MountSourceFlags
@@ -107,7 +106,7 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
rootInode, err := rsys.Mount(t, sourcePath, superFlags, data, nil)
if err != nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if err := fileOpOn(t, linux.AT_FDCWD, targetPath, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
@@ -130,7 +129,7 @@ func Umount2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
const unsupported = linux.MNT_FORCE | linux.MNT_EXPIRE
if flags&unsupported != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
path, _, err := copyInPath(t, addr, false /* allowEmpty */)
@@ -143,7 +142,7 @@ func Umount2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
//
// Currently, this is always the init task's user namespace.
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespace().UserNamespace()) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
resolve := flags&linux.UMOUNT_NOFOLLOW != linux.UMOUNT_NOFOLLOW
diff --git a/pkg/sentry/syscalls/linux/sys_pipe.go b/pkg/sentry/syscalls/linux/sys_pipe.go
index d95034347..5925c2263 100644
--- a/pkg/sentry/syscalls/linux/sys_pipe.go
+++ b/pkg/sentry/syscalls/linux/sys_pipe.go
@@ -16,13 +16,13 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
- "gvisor.dev/gvisor/pkg/syserror"
)
// LINT.IfChange
@@ -30,7 +30,7 @@ import (
// pipe2 implements the actual system call with flags.
func pipe2(t *kernel.Task, addr hostarch.Addr, flags uint) (uintptr, error) {
if flags&^(linux.O_NONBLOCK|linux.O_CLOEXEC) != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
r, w := pipe.NewConnectedPipe(t, pipe.DefaultPipeSize)
diff --git a/pkg/sentry/syscalls/linux/sys_poll.go b/pkg/sentry/syscalls/linux/sys_poll.go
index da548a14a..a80c84fcd 100644
--- a/pkg/sentry/syscalls/linux/sys_poll.go
+++ b/pkg/sentry/syscalls/linux/sys_poll.go
@@ -18,6 +18,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -128,7 +129,7 @@ func pollBlock(t *kernel.Task, pfd []linux.PollFD, timeout time.Duration) (time.
// Wait for a notification.
timeout, err = t.BlockWithTimeout(ch, !forever, timeout)
if err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = nil
}
return timeout, 0, err
@@ -157,7 +158,7 @@ func pollBlock(t *kernel.Task, pfd []linux.PollFD, timeout time.Duration) (time.
// CopyInPollFDs copies an array of struct pollfd unless nfds exceeds the max.
func CopyInPollFDs(t *kernel.Task, addr hostarch.Addr, nfds uint) ([]linux.PollFD, error) {
if uint64(nfds) > t.ThreadGroup().Limits().GetCapped(limits.NumberOfFiles, fileCap) {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
pfd := make([]linux.PollFD, nfds)
@@ -217,7 +218,7 @@ func CopyInFDSet(t *kernel.Task, addr hostarch.Addr, nBytes, nBitsInLastPartialB
func doSelect(t *kernel.Task, nfds int, readFDs, writeFDs, exceptFDs hostarch.Addr, timeout time.Duration) (uintptr, error) {
if nfds < 0 || nfds > fileCap {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Calculate the size of the fd sets (one bit per fd).
@@ -264,7 +265,7 @@ func doSelect(t *kernel.Task, nfds int, readFDs, writeFDs, exceptFDs hostarch.Ad
// OK. Linux is racy in the same way.
file := t.GetFile(fd)
if file == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
file.DecRef(t)
@@ -404,7 +405,7 @@ func (p *pollRestartBlock) Restart(t *kernel.Task) (uintptr, error) {
func poll(t *kernel.Task, pfdAddr hostarch.Addr, nfds uint, timeout time.Duration) (uintptr, error) {
remainingTimeout, n, err := doPoll(t, pfdAddr, nfds, timeout)
// On an interrupt poll(2) is restarted with the remaining timeout.
- if err == syserror.EINTR {
+ if linuxerr.Equals(linuxerr.EINTR, err) {
t.SetSyscallRestartBlock(&pollRestartBlock{
pfdAddr: pfdAddr,
nfds: nfds,
@@ -463,7 +464,7 @@ func Ppoll(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
//
// Note that this means that if err is nil but copyErr is not, copyErr is
// ignored. This is consistent with Linux.
- if err == syserror.EINTR && copyErr == nil {
+ if linuxerr.Equals(linuxerr.EINTR, err) && copyErr == nil {
err = syserror.ERESTARTNOHAND
}
return n, nil, err
@@ -485,7 +486,7 @@ func Select(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
return 0, nil, err
}
if timeval.Sec < 0 || timeval.Usec < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
timeout = time.Duration(timeval.ToNsecCapped())
}
@@ -493,7 +494,7 @@ func Select(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
n, err := doSelect(t, nfds, readFDs, writeFDs, exceptFDs, timeout)
copyErr := copyOutTimevalRemaining(t, startNs, timeout, timevalAddr)
// See comment in Ppoll.
- if err == syserror.EINTR && copyErr == nil {
+ if linuxerr.Equals(linuxerr.EINTR, err) && copyErr == nil {
err = syserror.ERESTARTNOHAND
}
return n, nil, err
@@ -538,7 +539,7 @@ func Pselect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
n, err := doSelect(t, nfds, readFDs, writeFDs, exceptFDs, timeout)
copyErr := copyOutTimespecRemaining(t, startNs, timeout, timespecAddr)
// See comment in Ppoll.
- if err == syserror.EINTR && copyErr == nil {
+ if linuxerr.Equals(linuxerr.EINTR, err) && copyErr == nil {
err = syserror.ERESTARTNOHAND
}
return n, nil, err
diff --git a/pkg/sentry/syscalls/linux/sys_prctl.go b/pkg/sentry/syscalls/linux/sys_prctl.go
index 9890dd946..a16b6b4d6 100644
--- a/pkg/sentry/syscalls/linux/sys_prctl.go
+++ b/pkg/sentry/syscalls/linux/sys_prctl.go
@@ -18,6 +18,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -25,7 +26,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/mm"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Prctl implements linux syscall prctl(2).
@@ -38,7 +38,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.PR_SET_PDEATHSIG:
sig := linux.Signal(args[1].Int())
if sig != 0 && !sig.IsValid() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
t.SetParentDeathSignal(sig)
return 0, nil, nil
@@ -69,7 +69,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
d = mm.UserDumpable
default:
// N.B. Userspace may not pass SUID_DUMP_ROOT.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
t.MemoryManager().SetDumpability(d)
return 0, nil, nil
@@ -90,7 +90,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
} else if val == 1 {
t.SetKeepCaps(true)
} else {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, nil
@@ -98,7 +98,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.PR_SET_NAME:
addr := args[1].Pointer()
name, err := t.CopyInString(addr, linux.TASK_COMM_LEN-1)
- if err != nil && err != syserror.ENAMETOOLONG {
+ if err != nil && !linuxerr.Equals(linuxerr.ENAMETOOLONG, err) {
return 0, nil, err
}
t.SetName(name)
@@ -118,7 +118,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.PR_SET_MM:
if !t.HasCapability(linux.CAP_SYS_RESOURCE) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
switch args[1].Int() {
@@ -127,13 +127,13 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// They trying to set exe to a non-file?
if !fs.IsFile(file.Dirent.Inode.StableAttr) {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Set the underlying executable.
@@ -155,12 +155,12 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
t.Kernel().EmitUnimplementedEvent(t)
fallthrough
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
case linux.PR_SET_NO_NEW_PRIVS:
if args[1].Int() != 1 || args[2].Int() != 0 || args[3].Int() != 0 || args[4].Int() != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// PR_SET_NO_NEW_PRIVS is assumed to always be set.
// See kernel.Task.updateCredsForExecLocked.
@@ -168,7 +168,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.PR_GET_NO_NEW_PRIVS:
if args[1].Int() != 0 || args[2].Int() != 0 || args[3].Int() != 0 || args[4].Int() != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 1, nil, nil
@@ -184,7 +184,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
default:
tracer := t.PIDNamespace().TaskWithID(kernel.ThreadID(pid))
if tracer == nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
t.SetYAMAException(tracer)
return 0, nil, nil
@@ -193,7 +193,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.PR_SET_SECCOMP:
if args[1].Int() != linux.SECCOMP_MODE_FILTER {
// Unsupported mode.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, seccomp(t, linux.SECCOMP_SET_MODE_FILTER, 0, args[2].Pointer())
@@ -204,7 +204,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.PR_CAPBSET_READ:
cp := linux.Capability(args[1].Uint64())
if !cp.Ok() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var rv uintptr
if auth.CapabilitySetOf(cp)&t.Credentials().BoundingCaps != 0 {
@@ -215,7 +215,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.PR_CAPBSET_DROP:
cp := linux.Capability(args[1].Uint64())
if !cp.Ok() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, t.DropBoundingCapability(cp)
@@ -240,7 +240,7 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
t.Kernel().EmitUnimplementedEvent(t)
fallthrough
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, nil
diff --git a/pkg/sentry/syscalls/linux/sys_random.go b/pkg/sentry/syscalls/linux/sys_random.go
index ae545f80f..ec6c80de5 100644
--- a/pkg/sentry/syscalls/linux/sys_random.go
+++ b/pkg/sentry/syscalls/linux/sys_random.go
@@ -18,14 +18,14 @@ import (
"io"
"math"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/rand"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
const (
@@ -47,7 +47,7 @@ func GetRandom(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
// Flags are checked for validity but otherwise ignored. See above.
if flags & ^(_GRND_NONBLOCK|_GRND_RANDOM) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if length > math.MaxInt32 {
diff --git a/pkg/sentry/syscalls/linux/sys_read.go b/pkg/sentry/syscalls/linux/sys_read.go
index 13e5e3a51..50f98fc8f 100644
--- a/pkg/sentry/syscalls/linux/sys_read.go
+++ b/pkg/sentry/syscalls/linux/sys_read.go
@@ -18,6 +18,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -46,19 +47,19 @@ func Read(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the file is readable.
if !file.Flags().Read {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Check that the size is legitimate.
si := int(size)
if si < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the destination of the read.
@@ -82,29 +83,29 @@ func Readahead(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the file is readable.
if !file.Flags().Read {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Check that the size is valid.
if int(size) < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Check that the offset is legitimate and does not overflow.
if offset < 0 || offset+int64(size) < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Return EINVAL; if the underlying file type does not support readahead,
// then Linux will return EINVAL to indicate as much. In the future, we
// may extend this function to actually support readahead hints.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Pread64 implements linux syscall pread64(2).
@@ -116,29 +117,29 @@ func Pread64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate and does not overflow.
if offset < 0 || offset+int64(size) < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Is reading at an offset supported?
if !file.Flags().Pread {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
// Check that the file is readable.
if !file.Flags().Read {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Check that the size is legitimate.
si := int(size)
if si < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the destination of the read.
@@ -162,13 +163,13 @@ func Readv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the file is readable.
if !file.Flags().Read {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Read the iovecs that specify the destination of the read.
@@ -193,23 +194,23 @@ func Preadv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate.
if offset < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Is reading at an offset supported?
if !file.Flags().Pread {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
// Check that the file is readable.
if !file.Flags().Read {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Read the iovecs that specify the destination of the read.
@@ -242,23 +243,23 @@ func Preadv2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate.
if offset < -1 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Is reading at an offset supported?
if offset > -1 && !file.Flags().Pread {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
// Check that the file is readable.
if !file.Flags().Read {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Check flags field.
@@ -331,7 +332,7 @@ func readv(t *kernel.Task, f *fs.File, dst usermem.IOSequence) (int64, error) {
// Wait for a notification that we should retry.
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = syserror.ErrWouldBlock
}
break
diff --git a/pkg/sentry/syscalls/linux/sys_rlimit.go b/pkg/sentry/syscalls/linux/sys_rlimit.go
index e64246d57..06f51f49c 100644
--- a/pkg/sentry/syscalls/linux/sys_rlimit.go
+++ b/pkg/sentry/syscalls/linux/sys_rlimit.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -105,7 +106,7 @@ func prlimit64(t *kernel.Task, resource limits.LimitType, newLim *limits.Limit)
}
if _, ok := setableLimits[resource]; !ok {
- return limits.Limit{}, syserror.EPERM
+ return limits.Limit{}, linuxerr.EPERM
}
// "A privileged process (under Linux: one with the CAP_SYS_RESOURCE
@@ -129,7 +130,7 @@ func Getrlimit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
resource, ok := limits.FromLinuxResource[int(args[0].Int())]
if !ok {
// Return err; unknown limit.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
addr := args[1].Pointer()
rlim, err := newRlimit(t)
@@ -150,7 +151,7 @@ func Setrlimit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
resource, ok := limits.FromLinuxResource[int(args[0].Int())]
if !ok {
// Return err; unknown limit.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
addr := args[1].Pointer()
rlim, err := newRlimit(t)
@@ -170,7 +171,7 @@ func Prlimit64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
resource, ok := limits.FromLinuxResource[int(args[1].Int())]
if !ok {
// Return err; unknown limit.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
newRlimAddr := args[2].Pointer()
oldRlimAddr := args[3].Pointer()
@@ -185,7 +186,7 @@ func Prlimit64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
}
if tid < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ot := t
if tid > 0 {
@@ -207,7 +208,7 @@ func Prlimit64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
cred.RealKGID != tcred.RealKGID ||
cred.RealKGID != tcred.EffectiveKGID ||
cred.RealKGID != tcred.SavedKGID {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
}
diff --git a/pkg/sentry/syscalls/linux/sys_rseq.go b/pkg/sentry/syscalls/linux/sys_rseq.go
index 90db10ea6..5fe196647 100644
--- a/pkg/sentry/syscalls/linux/sys_rseq.go
+++ b/pkg/sentry/syscalls/linux/sys_rseq.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
@@ -43,6 +44,6 @@ func RSeq(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
return 0, nil, t.ClearRSeq(addr, length, signature)
default:
// Unknown flag.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/syscalls/linux/sys_rusage.go b/pkg/sentry/syscalls/linux/sys_rusage.go
index ac5c98a54..a689abcc9 100644
--- a/pkg/sentry/syscalls/linux/sys_rusage.go
+++ b/pkg/sentry/syscalls/linux/sys_rusage.go
@@ -16,11 +16,11 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/usage"
- "gvisor.dev/gvisor/pkg/syserror"
)
func getrusage(t *kernel.Task, which int32) linux.Rusage {
@@ -76,7 +76,7 @@ func Getrusage(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
addr := args[1].Pointer()
if which != linux.RUSAGE_SELF && which != linux.RUSAGE_CHILDREN && which != linux.RUSAGE_THREAD {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ru := getrusage(t, which)
diff --git a/pkg/sentry/syscalls/linux/sys_sched.go b/pkg/sentry/syscalls/linux/sys_sched.go
index bfcf44b6f..b5e7b70b5 100644
--- a/pkg/sentry/syscalls/linux/sys_sched.go
+++ b/pkg/sentry/syscalls/linux/sys_sched.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
@@ -38,10 +39,10 @@ func SchedGetparam(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
pid := args[0].Int()
param := args[1].Pointer()
if param == 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if pid < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if pid != 0 && t.PIDNamespace().TaskWithID(kernel.ThreadID(pid)) == nil {
return 0, nil, syserror.ESRCH
@@ -58,7 +59,7 @@ func SchedGetparam(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
func SchedGetscheduler(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
pid := args[0].Int()
if pid < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if pid != 0 && t.PIDNamespace().TaskWithID(kernel.ThreadID(pid)) == nil {
return 0, nil, syserror.ESRCH
@@ -72,20 +73,20 @@ func SchedSetscheduler(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ke
policy := args[1].Int()
param := args[2].Pointer()
if pid < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if policy != onlyScheduler {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if pid != 0 && t.PIDNamespace().TaskWithID(kernel.ThreadID(pid)) == nil {
return 0, nil, syserror.ESRCH
}
var r SchedParam
if _, err := r.CopyIn(t, param); err != nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if r.schedPriority != onlyPriority {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, nil
}
diff --git a/pkg/sentry/syscalls/linux/sys_seccomp.go b/pkg/sentry/syscalls/linux/sys_seccomp.go
index e16d6ff3f..b0dc84b8d 100644
--- a/pkg/sentry/syscalls/linux/sys_seccomp.go
+++ b/pkg/sentry/syscalls/linux/sys_seccomp.go
@@ -17,10 +17,10 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bpf"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
)
// userSockFprog is equivalent to Linux's struct sock_fprog on amd64.
@@ -44,7 +44,7 @@ func seccomp(t *kernel.Task, mode, flags uint64, addr hostarch.Addr) error {
// We only support SECCOMP_SET_MODE_FILTER at the moment.
if mode != linux.SECCOMP_SET_MODE_FILTER {
// Unsupported mode.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
tsync := flags&linux.SECCOMP_FILTER_FLAG_TSYNC != 0
@@ -52,7 +52,7 @@ func seccomp(t *kernel.Task, mode, flags uint64, addr hostarch.Addr) error {
// The only flag we support now is SECCOMP_FILTER_FLAG_TSYNC.
if flags&^linux.SECCOMP_FILTER_FLAG_TSYNC != 0 {
// Unsupported flag.
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
var fprog userSockFprog
@@ -66,7 +66,7 @@ func seccomp(t *kernel.Task, mode, flags uint64, addr hostarch.Addr) error {
compiledFilter, err := bpf.Compile(filter)
if err != nil {
t.Debugf("Invalid seccomp-bpf filter: %v", err)
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
return t.AppendSyscallFilter(compiledFilter, tsync)
diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go
index c84260080..ef61b8f33 100644
--- a/pkg/sentry/syscalls/linux/sys_sem.go
+++ b/pkg/sentry/syscalls/linux/sys_sem.go
@@ -19,6 +19,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -61,10 +62,10 @@ func Semtimedop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
nsops := args[2].SizeT()
timespecAddr := args[3].Pointer()
if nsops <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if nsops > opsMax {
- return 0, nil, syserror.E2BIG
+ return 0, nil, linuxerr.E2BIG
}
ops := make([]linux.Sembuf, nsops)
@@ -77,12 +78,12 @@ func Semtimedop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
return 0, nil, err
}
if timeout.Sec < 0 || timeout.Nsec < 0 || timeout.Nsec >= 1e9 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if err := semTimedOp(t, id, ops, true, timeout.ToDuration()); err != nil {
- if err == syserror.ETIMEDOUT {
- return 0, nil, syserror.EAGAIN
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
+ return 0, nil, linuxerr.EAGAIN
}
return 0, nil, err
}
@@ -96,10 +97,10 @@ func Semop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
nsops := args[2].SizeT()
if nsops <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if nsops > opsMax {
- return 0, nil, syserror.E2BIG
+ return 0, nil, linuxerr.E2BIG
}
ops := make([]linux.Sembuf, nsops)
@@ -113,7 +114,7 @@ func semTimedOp(t *kernel.Task, id int32, ops []linux.Sembuf, haveTimeout bool,
set := t.IPCNamespace().SemaphoreRegistry().FindByID(id)
if set == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
pid := t.Kernel().GlobalInit().PIDNamespace().IDOfThreadGroup(t.ThreadGroup())
@@ -232,7 +233,7 @@ func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
return uintptr(semid), nil, err
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
@@ -246,17 +247,17 @@ func ipcSet(t *kernel.Task, id int32, uid auth.UID, gid auth.GID, perms fs.FileP
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
kuid := creds.UserNamespace.MapToKUID(uid)
if !kuid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
kgid := creds.UserNamespace.MapToKGID(gid)
if !kgid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
owner := fs.FileOwner{UID: kuid, GID: kgid}
return set.Change(t, creds, owner, perms)
@@ -266,7 +267,7 @@ func ipcStat(t *kernel.Task, id int32) (*linux.SemidDS, error) {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
return set.GetStat(creds)
@@ -276,7 +277,7 @@ func semStat(t *kernel.Task, index int32) (int32, *linux.SemidDS, error) {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByIndex(index)
if set == nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
ds, err := set.GetStat(creds)
@@ -289,7 +290,7 @@ func semStat(t *kernel.Task, index int32) (int32, *linux.SemidDS, error) {
func semStatAny(t *kernel.Task, index int32) (int32, *linux.SemidDS, error) {
set := t.IPCNamespace().SemaphoreRegistry().FindByIndex(index)
if set == nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
ds, err := set.GetStatAny(creds)
@@ -303,7 +304,7 @@ func setVal(t *kernel.Task, id int32, num int32, val int16) error {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
pid := t.Kernel().GlobalInit().PIDNamespace().IDOfThreadGroup(t.ThreadGroup())
@@ -314,7 +315,7 @@ func setValAll(t *kernel.Task, id int32, array hostarch.Addr) error {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
vals := make([]uint16, set.Size())
if _, err := primitive.CopyUint16SliceIn(t, array, vals); err != nil {
@@ -329,7 +330,7 @@ func getVal(t *kernel.Task, id int32, num int32) (int16, error) {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
return set.GetVal(num, creds)
@@ -339,7 +340,7 @@ func getValAll(t *kernel.Task, id int32, array hostarch.Addr) error {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
vals, err := set.GetValAll(creds)
@@ -354,7 +355,7 @@ func getPID(t *kernel.Task, id int32, num int32) (int32, error) {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
gpid, err := set.GetPID(num, creds)
@@ -373,7 +374,7 @@ func getZCnt(t *kernel.Task, id int32, num int32) (uint16, error) {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
return set.CountZeroWaiters(num, creds)
@@ -383,7 +384,7 @@ func getNCnt(t *kernel.Task, id int32, num int32) (uint16, error) {
r := t.IPCNamespace().SemaphoreRegistry()
set := r.FindByID(id)
if set == nil {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
creds := auth.CredentialsFromContext(t)
return set.CountNegativeWaiters(num, creds)
diff --git a/pkg/sentry/syscalls/linux/sys_shm.go b/pkg/sentry/syscalls/linux/sys_shm.go
index 584064143..3e3a952ce 100644
--- a/pkg/sentry/syscalls/linux/sys_shm.go
+++ b/pkg/sentry/syscalls/linux/sys_shm.go
@@ -16,10 +16,10 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/shm"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Shmget implements shmget(2).
@@ -51,7 +51,7 @@ func findSegment(t *kernel.Task, id shm.ID) (*shm.Shm, error) {
segment := r.FindByID(id)
if segment == nil {
// No segment with provided id.
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
return segment, nil
}
@@ -64,7 +64,7 @@ func Shmat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
segment, err := findSegment(t, id)
if err != nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
defer segment.DecRef(t)
@@ -106,7 +106,7 @@ func Shmctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
case linux.IPC_STAT:
segment, err := findSegment(t, id)
if err != nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
defer segment.DecRef(t)
@@ -130,7 +130,7 @@ func Shmctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// Remaining commands refer to a specific segment.
segment, err := findSegment(t, id)
if err != nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
defer segment.DecRef(t)
@@ -155,6 +155,6 @@ func Shmctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
return 0, nil, nil
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/syscalls/linux/sys_signal.go b/pkg/sentry/syscalls/linux/sys_signal.go
index 53b12dc41..f98d5c903 100644
--- a/pkg/sentry/syscalls/linux/sys_signal.go
+++ b/pkg/sentry/syscalls/linux/sys_signal.go
@@ -19,6 +19,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -82,15 +83,15 @@ func Kill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
return 0, nil, syserror.ESRCH
}
if !mayKill(t, target, sig) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
- info := &arch.SignalInfo{
+ info := &linux.SignalInfo{
Signo: int32(sig),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
}
info.SetPID(int32(target.PIDNamespace().IDOfTask(t)))
info.SetUID(int32(t.Credentials().RealKUID.In(target.UserNamespace()).OrOverflow()))
- if err := target.SendGroupSignal(info); err != syserror.ESRCH {
+ if err := target.SendGroupSignal(info); !linuxerr.Equals(linuxerr.ESRCH, err) {
return 0, nil, err
}
}
@@ -123,14 +124,14 @@ func Kill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
// depend on the iteration order. We at least implement the
// semantics documented by the man page: "On success (at least
// one signal was sent), zero is returned."
- info := &arch.SignalInfo{
+ info := &linux.SignalInfo{
Signo: int32(sig),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
}
info.SetPID(int32(tg.PIDNamespace().IDOfTask(t)))
info.SetUID(int32(t.Credentials().RealKUID.In(tg.Leader().UserNamespace()).OrOverflow()))
err := tg.SendSignal(info)
- if err == syserror.ESRCH {
+ if linuxerr.Equals(linuxerr.ESRCH, err) {
// ESRCH is ignored because it means the task
// exited while we were iterating. This is a
// race which would not normally exist on
@@ -163,18 +164,18 @@ func Kill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
for _, tg := range t.PIDNamespace().ThreadGroups() {
if t.PIDNamespace().IDOfProcessGroup(tg.ProcessGroup()) == pgid {
if !mayKill(t, tg.Leader(), sig) {
- lastErr = syserror.EPERM
+ lastErr = linuxerr.EPERM
continue
}
- info := &arch.SignalInfo{
+ info := &linux.SignalInfo{
Signo: int32(sig),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
}
info.SetPID(int32(tg.PIDNamespace().IDOfTask(t)))
info.SetUID(int32(t.Credentials().RealKUID.In(tg.Leader().UserNamespace()).OrOverflow()))
// See note above regarding ESRCH race above.
- if err := tg.SendSignal(info); err != syserror.ESRCH {
+ if err := tg.SendSignal(info); !linuxerr.Equals(linuxerr.ESRCH, err) {
lastErr = err
}
}
@@ -184,10 +185,10 @@ func Kill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
}
}
-func tkillSigInfo(sender, receiver *kernel.Task, sig linux.Signal) *arch.SignalInfo {
- info := &arch.SignalInfo{
+func tkillSigInfo(sender, receiver *kernel.Task, sig linux.Signal) *linux.SignalInfo {
+ info := &linux.SignalInfo{
Signo: int32(sig),
- Code: arch.SignalInfoTkill,
+ Code: linux.SI_TKILL,
}
info.SetPID(int32(receiver.PIDNamespace().IDOfThreadGroup(sender.ThreadGroup())))
info.SetUID(int32(sender.Credentials().RealKUID.In(receiver.UserNamespace()).OrOverflow()))
@@ -202,7 +203,7 @@ func Tkill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// N.B. Inconsistent with man page, linux actually rejects calls with
// tid <=0 by EINVAL. This isn't the same for all signal calls.
if tid <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
target := t.PIDNamespace().TaskWithID(tid)
@@ -211,7 +212,7 @@ func Tkill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
}
if !mayKill(t, target, sig) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
return 0, nil, target.SendSignal(tkillSigInfo(t, target, sig))
}
@@ -225,7 +226,7 @@ func Tgkill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// N.B. Inconsistent with man page, linux actually rejects calls with
// tgid/tid <=0 by EINVAL. This isn't the same for all signal calls.
if tgid <= 0 || tid <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
targetTG := t.PIDNamespace().ThreadGroupWithID(tgid)
@@ -235,7 +236,7 @@ func Tgkill(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
}
if !mayKill(t, target, sig) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
return 0, nil, target.SendSignal(tkillSigInfo(t, target, sig))
}
@@ -248,23 +249,23 @@ func RtSigaction(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
sigsetsize := args[3].SizeT()
if sigsetsize != linux.SignalSetSize {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
- var newactptr *arch.SignalAct
+ var newactptr *linux.SigAction
if newactarg != 0 {
- newact, err := t.CopyInSignalAct(newactarg)
- if err != nil {
+ var newact linux.SigAction
+ if _, err := newact.CopyIn(t, newactarg); err != nil {
return 0, nil, err
}
newactptr = &newact
}
- oldact, err := t.ThreadGroup().SetSignalAct(sig, newactptr)
+ oldact, err := t.ThreadGroup().SetSigAction(sig, newactptr)
if err != nil {
return 0, nil, err
}
if oldactarg != 0 {
- if err := t.CopyOutSignalAct(oldactarg, &oldact); err != nil {
+ if _, err := oldact.CopyOut(t, oldactarg); err != nil {
return 0, nil, err
}
}
@@ -291,7 +292,7 @@ func RtSigprocmask(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
sigsetsize := args[3].SizeT()
if sigsetsize != linux.SignalSetSize {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
oldmask := t.SignalMask()
if setaddr != 0 {
@@ -308,7 +309,7 @@ func RtSigprocmask(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
case linux.SIG_SETMASK:
t.SetSignalMask(mask)
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
if oldaddr != 0 {
@@ -325,13 +326,12 @@ func Sigaltstack(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
alt := t.SignalStack()
if oldaddr != 0 {
- if err := t.CopyOutSignalStack(oldaddr, &alt); err != nil {
+ if _, err := alt.CopyOut(t, oldaddr); err != nil {
return 0, nil, err
}
}
if setaddr != 0 {
- alt, err := t.CopyInSignalStack(setaddr)
- if err != nil {
+ if _, err := alt.CopyIn(t, setaddr); err != nil {
return 0, nil, err
}
// The signal stack cannot be changed if the task is currently
@@ -339,7 +339,7 @@ func Sigaltstack(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// these semantics apply to changing the signal stack via a
// ucontext during a signal handler.
if !t.SetSignalStack(alt) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
}
@@ -378,7 +378,7 @@ func RtSigtimedwait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
return 0, nil, err
}
if !d.Valid() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
timeout = time.Duration(d.ToNsecCapped())
} else {
@@ -410,7 +410,7 @@ func RtSigqueueinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
// We must ensure that the Signo is set (Linux overrides this in the
// same way), and that the code is in the allowed set. This same logic
// appears below in RtSigtgqueueinfo and should be kept in sync.
- var info arch.SignalInfo
+ var info linux.SignalInfo
if _, err := info.CopyIn(t, infoAddr); err != nil {
return 0, nil, err
}
@@ -426,15 +426,15 @@ func RtSigqueueinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
// If the sender is not the receiver, it can't use si_codes used by the
// kernel or SI_TKILL.
- if (info.Code >= 0 || info.Code == arch.SignalInfoTkill) && target != t {
- return 0, nil, syserror.EPERM
+ if (info.Code >= 0 || info.Code == linux.SI_TKILL) && target != t {
+ return 0, nil, linuxerr.EPERM
}
if !mayKill(t, target, sig) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
- if err := target.SendGroupSignal(&info); err != syserror.ESRCH {
+ if err := target.SendGroupSignal(&info); !linuxerr.Equals(linuxerr.ESRCH, err) {
return 0, nil, err
}
}
@@ -450,11 +450,11 @@ func RtTgsigqueueinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ker
// N.B. Inconsistent with man page, linux actually rejects calls with
// tgid/tid <=0 by EINVAL. This isn't the same for all signal calls.
if tgid <= 0 || tid <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Copy in the info. See RtSigqueueinfo above.
- var info arch.SignalInfo
+ var info linux.SignalInfo
if _, err := info.CopyIn(t, infoAddr); err != nil {
return 0, nil, err
}
@@ -469,12 +469,12 @@ func RtTgsigqueueinfo(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ker
// If the sender is not the receiver, it can't use si_codes used by the
// kernel or SI_TKILL.
- if (info.Code >= 0 || info.Code == arch.SignalInfoTkill) && target != t {
- return 0, nil, syserror.EPERM
+ if (info.Code >= 0 || info.Code == linux.SI_TKILL) && target != t {
+ return 0, nil, linuxerr.EPERM
}
if !mayKill(t, target, sig) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
return 0, nil, target.SendSignal(&info)
}
@@ -525,7 +525,7 @@ func sharedSignalfd(t *kernel.Task, fd int32, sigset hostarch.Addr, sigsetsize u
// Always check for valid flags, even if not creating.
if flags&^(linux.SFD_NONBLOCK|linux.SFD_CLOEXEC) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Is this a change to an existing signalfd?
@@ -534,7 +534,7 @@ func sharedSignalfd(t *kernel.Task, fd int32, sigset hostarch.Addr, sigsetsize u
if fd != -1 {
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -545,7 +545,7 @@ func sharedSignalfd(t *kernel.Task, fd int32, sigset hostarch.Addr, sigsetsize u
}
// Not a signalfd.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Create a new file.
diff --git a/pkg/sentry/syscalls/linux/sys_socket.go b/pkg/sentry/syscalls/linux/sys_socket.go
index e07917613..ea2b2f40c 100644
--- a/pkg/sentry/syscalls/linux/sys_socket.go
+++ b/pkg/sentry/syscalls/linux/sys_socket.go
@@ -18,6 +18,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/marshal/primitive"
@@ -117,7 +118,7 @@ type multipleMessageHeader64 struct {
// from the untrusted address space range.
func CaptureAddress(t *kernel.Task, addr hostarch.Addr, addrlen uint32) ([]byte, error) {
if addrlen > maxAddrLen {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
addrBuf := make([]byte, addrlen)
@@ -139,7 +140,7 @@ func writeAddress(t *kernel.Task, addr linux.SockAddr, addrLen uint32, addrPtr h
}
if int32(bufLen) < 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Write the length unconditionally.
@@ -173,7 +174,7 @@ func Socket(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// Check and initialize the flags.
if stype & ^(0xf|linux.SOCK_NONBLOCK|linux.SOCK_CLOEXEC) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Create the new socket.
@@ -205,7 +206,7 @@ func SocketPair(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
// Check and initialize the flags.
if stype & ^(0xf|linux.SOCK_NONBLOCK|linux.SOCK_CLOEXEC) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
fileFlags := fs.SettableFileFlags{
@@ -252,7 +253,7 @@ func Connect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -277,13 +278,13 @@ func Connect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
func accept(t *kernel.Task, fd int32, addr hostarch.Addr, addrLen hostarch.Addr, flags int) (uintptr, error) {
// Check that no unsupported flags are passed in.
if flags & ^(linux.SOCK_NONBLOCK|linux.SOCK_CLOEXEC) != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -305,7 +306,7 @@ func accept(t *kernel.Task, fd int32, addr hostarch.Addr, addrLen hostarch.Addr,
if peerRequested {
// NOTE(magi): Linux does not give you an error if it can't
// write the data back out so neither do we.
- if err := writeAddress(t, peer, peerLen, addr, addrLen); err == syserror.EINVAL {
+ if err := writeAddress(t, peer, peerLen, addr, addrLen); linuxerr.Equals(linuxerr.EINVAL, err) {
return 0, err
}
}
@@ -342,7 +343,7 @@ func Bind(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -369,7 +370,7 @@ func Listen(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -407,7 +408,7 @@ func Shutdown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -421,7 +422,7 @@ func Shutdown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
switch how {
case linux.SHUT_RD, linux.SHUT_WR, linux.SHUT_RDWR:
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, s.Shutdown(t, int(how)).ToError()
@@ -438,7 +439,7 @@ func GetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -454,7 +455,7 @@ func GetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
return 0, nil, err
}
if optLen < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Call syscall implementation then copy both value and value len out.
@@ -519,7 +520,7 @@ func SetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -530,10 +531,10 @@ func SetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
}
if optLen < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if optLen > maxOptLen {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
buf := t.CopyScratchBuffer(int(optLen))
if _, err := t.CopyInBytes(optValAddr, buf); err != nil {
@@ -557,7 +558,7 @@ func GetSockName(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -585,7 +586,7 @@ func GetPeerName(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -612,13 +613,13 @@ func RecvMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -630,7 +631,7 @@ func RecvMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Reject flags that we don't handle yet.
if flags & ^(baseRecvFlags|linux.MSG_PEEK|linux.MSG_CMSG_CLOEXEC|linux.MSG_ERRQUEUE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if file.Flags().NonBlocking {
@@ -660,7 +661,7 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if vlen > linux.UIO_MAXIOV {
@@ -669,13 +670,13 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Reject flags that we don't handle yet.
if flags & ^(baseRecvFlags|linux.MSG_CMSG_CLOEXEC|linux.MSG_ERRQUEUE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -697,7 +698,7 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
return 0, nil, err
}
if !ts.Valid() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
deadline = t.Kernel().MonotonicClock().Now().Add(ts.ToDuration())
haveDeadline = true
@@ -749,7 +750,7 @@ func recvSingleMsg(t *kernel.Task, s socket.Socket, msgPtr hostarch.Addr, flags
}
if msg.IovLen > linux.UIO_MAXIOV {
- return 0, syserror.EMSGSIZE
+ return 0, linuxerr.EMSGSIZE
}
dst, err := t.IovecsIOSequence(hostarch.Addr(msg.Iov), int(msg.IovLen), usermem.IOOpts{
AddressSpaceActive: true,
@@ -780,7 +781,7 @@ func recvSingleMsg(t *kernel.Task, s socket.Socket, msgPtr hostarch.Addr, flags
}
if msg.ControlLen > maxControlLen {
- return 0, syserror.ENOBUFS
+ return 0, linuxerr.ENOBUFS
}
n, mflags, sender, senderLen, cms, e := s.RecvMsg(t, dst, int(flags), haveDeadline, deadline, msg.NameLen != 0, msg.ControlLen)
if e != nil {
@@ -829,18 +830,18 @@ func recvSingleMsg(t *kernel.Task, s socket.Socket, msgPtr hostarch.Addr, flags
// recvfrom and recv syscall handlers.
func recvFrom(t *kernel.Task, fd int32, bufPtr hostarch.Addr, bufLen uint64, flags int32, namePtr hostarch.Addr, nameLenPtr hostarch.Addr) (uintptr, error) {
if int(bufLen) < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Reject flags that we don't handle yet.
if flags & ^(baseRecvFlags|linux.MSG_PEEK|linux.MSG_CONFIRM) != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -907,13 +908,13 @@ func SendMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -925,7 +926,7 @@ func SendMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Reject flags that we don't handle yet.
if flags & ^(linux.MSG_DONTWAIT|linux.MSG_EOR|linux.MSG_MORE|linux.MSG_NOSIGNAL) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if file.Flags().NonBlocking {
@@ -945,7 +946,7 @@ func SendMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if vlen > linux.UIO_MAXIOV {
@@ -955,7 +956,7 @@ func SendMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -967,7 +968,7 @@ func SendMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Reject flags that we don't handle yet.
if flags & ^(linux.MSG_DONTWAIT|linux.MSG_EOR|linux.MSG_MORE|linux.MSG_NOSIGNAL) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if file.Flags().NonBlocking {
@@ -1014,7 +1015,7 @@ func sendSingleMsg(t *kernel.Task, s socket.Socket, file *fs.File, msgPtr hostar
if msg.ControlLen > 0 {
// Put an upper bound to prevent large allocations.
if msg.ControlLen > maxControlLen {
- return 0, syserror.ENOBUFS
+ return 0, linuxerr.ENOBUFS
}
controlData = make([]byte, msg.ControlLen)
if _, err := t.CopyInBytes(hostarch.Addr(msg.Control), controlData); err != nil {
@@ -1034,7 +1035,7 @@ func sendSingleMsg(t *kernel.Task, s socket.Socket, file *fs.File, msgPtr hostar
// Read data then call the sendmsg implementation.
if msg.IovLen > linux.UIO_MAXIOV {
- return 0, syserror.EMSGSIZE
+ return 0, linuxerr.EMSGSIZE
}
src, err := t.IovecsIOSequence(hostarch.Addr(msg.Iov), int(msg.IovLen), usermem.IOOpts{
AddressSpaceActive: true,
@@ -1073,13 +1074,13 @@ func sendSingleMsg(t *kernel.Task, s socket.Socket, file *fs.File, msgPtr hostar
func sendTo(t *kernel.Task, fd int32, bufPtr hostarch.Addr, bufLen uint64, flags int32, namePtr hostarch.Addr, nameLen uint32) (uintptr, error) {
bl := int(bufLen)
if bl < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFile(fd)
if file == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
defer file.DecRef(t)
diff --git a/pkg/sentry/syscalls/linux/sys_splice.go b/pkg/sentry/syscalls/linux/sys_splice.go
index 134051124..34d87ac1f 100644
--- a/pkg/sentry/syscalls/linux/sys_splice.go
+++ b/pkg/sentry/syscalls/linux/sys_splice.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -27,7 +28,7 @@ import (
// doSplice implements a blocking splice operation.
func doSplice(t *kernel.Task, outFile, inFile *fs.File, opts fs.SpliceOpts, nonBlocking bool) (int64, error) {
if opts.Length < 0 || opts.SrcStart < 0 || opts.DstStart < 0 || (opts.SrcStart+opts.Length < 0) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if opts.Length == 0 {
return 0, nil
@@ -105,33 +106,33 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Get files.
inFile := t.GetFile(inFD)
if inFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer inFile.DecRef(t)
if !inFile.Flags().Read {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
outFile := t.GetFile(outFD)
if outFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer outFile.DecRef(t)
if !outFile.Flags().Write {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Verify that the outfile Append flag is not set.
if outFile.Flags().Append {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Verify that we have a regular infile. This is a requirement; the
// same check appears in Linux (fs/splice.c:splice_direct_to_actor).
if !fs.IsRegular(inFile.Dirent.Inode.StableAttr) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var (
@@ -142,7 +143,7 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Verify that when offset address is not null, infile must be
// seekable. The fs.Splice routine itself validates basic read.
if !inFile.Flags().Pread {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
// Copy in the offset.
@@ -190,19 +191,19 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// Check for invalid flags.
if flags&^(linux.SPLICE_F_MOVE|linux.SPLICE_F_NONBLOCK|linux.SPLICE_F_MORE|linux.SPLICE_F_GIFT) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get files.
outFile := t.GetFile(outFD)
if outFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer outFile.DecRef(t)
inFile := t.GetFile(inFD)
if inFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer inFile.DecRef(t)
@@ -226,11 +227,11 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
switch {
case fs.IsPipe(inFileAttr) && !fs.IsPipe(outFileAttr):
if inOffset != 0 {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
if outOffset != 0 {
if !outFile.Flags().Pwrite {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var offset int64
@@ -244,11 +245,11 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
}
case !fs.IsPipe(inFileAttr) && fs.IsPipe(outFileAttr):
if outOffset != 0 {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
if inOffset != 0 {
if !inFile.Flags().Pread {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var offset int64
@@ -262,15 +263,15 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
}
case fs.IsPipe(inFileAttr) && fs.IsPipe(outFileAttr):
if inOffset != 0 || outOffset != 0 {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
// We may not refer to the same pipe; otherwise it's a continuous loop.
if inFileAttr.InodeID == outFileAttr.InodeID {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Splice data.
@@ -298,30 +299,30 @@ func Tee(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo
// Check for invalid flags.
if flags&^(linux.SPLICE_F_MOVE|linux.SPLICE_F_NONBLOCK|linux.SPLICE_F_MORE|linux.SPLICE_F_GIFT) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get files.
outFile := t.GetFile(outFD)
if outFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer outFile.DecRef(t)
inFile := t.GetFile(inFD)
if inFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer inFile.DecRef(t)
// All files must be pipes.
if !fs.IsPipe(inFile.Dirent.Inode.StableAttr) || !fs.IsPipe(outFile.Dirent.Inode.StableAttr) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// We may not refer to the same pipe; see above.
if inFile.Dirent.Inode.StableAttr.InodeID == outFile.Dirent.Inode.StableAttr.InodeID {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// The operation is non-blocking if anything is non-blocking.
diff --git a/pkg/sentry/syscalls/linux/sys_stat.go b/pkg/sentry/syscalls/linux/sys_stat.go
index 2338ba44b..46a69a78a 100644
--- a/pkg/sentry/syscalls/linux/sys_stat.go
+++ b/pkg/sentry/syscalls/linux/sys_stat.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -56,7 +57,7 @@ func Fstatat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Annoying. What's wrong with fstat?
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -98,7 +99,7 @@ func Fstat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -139,13 +140,13 @@ func Statx(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
statxAddr := args[4].Pointer()
if mask&linux.STATX__RESERVED != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if flags&^(linux.AT_SYMLINK_NOFOLLOW|linux.AT_EMPTY_PATH|linux.AT_STATX_SYNC_TYPE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if flags&linux.AT_STATX_SYNC_TYPE == linux.AT_STATX_SYNC_TYPE {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
path, dirPath, err := copyInPath(t, pathAddr, flags&linux.AT_EMPTY_PATH != 0)
@@ -156,7 +157,7 @@ func Statx(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
if path == "" {
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
uattr, err := file.UnstableAttr(t)
@@ -247,7 +248,7 @@ func Fstatfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
diff --git a/pkg/sentry/syscalls/linux/sys_sync.go b/pkg/sentry/syscalls/linux/sys_sync.go
index 5ebd4461f..6278bef21 100644
--- a/pkg/sentry/syscalls/linux/sys_sync.go
+++ b/pkg/sentry/syscalls/linux/sys_sync.go
@@ -16,6 +16,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -37,7 +38,7 @@ func Syncfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -52,7 +53,7 @@ func Fsync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -68,7 +69,7 @@ func Fdatasync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -86,13 +87,13 @@ func SyncFileRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
uflags := args[3].Uint()
if offset < 0 || offset+nbytes < offset {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if uflags&^(linux.SYNC_FILE_RANGE_WAIT_BEFORE|
linux.SYNC_FILE_RANGE_WRITE|
linux.SYNC_FILE_RANGE_WAIT_AFTER) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if nbytes == 0 {
@@ -101,7 +102,7 @@ func SyncFileRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
diff --git a/pkg/sentry/syscalls/linux/sys_syslog.go b/pkg/sentry/syscalls/linux/sys_syslog.go
index 40c8bb061..ba372f9e3 100644
--- a/pkg/sentry/syscalls/linux/sys_syslog.go
+++ b/pkg/sentry/syscalls/linux/sys_syslog.go
@@ -15,6 +15,7 @@
package linux
import (
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
@@ -40,7 +41,7 @@ func Syslog(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
switch command {
case _SYSLOG_ACTION_READ_ALL:
if size < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if size > logBufLen {
size = logBufLen
diff --git a/pkg/sentry/syscalls/linux/sys_thread.go b/pkg/sentry/syscalls/linux/sys_thread.go
index 3185ea527..fe08c931f 100644
--- a/pkg/sentry/syscalls/linux/sys_thread.go
+++ b/pkg/sentry/syscalls/linux/sys_thread.go
@@ -19,6 +19,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -112,7 +113,7 @@ func execveat(t *kernel.Task, dirFD int32, pathnameAddr, argvAddr, envvAddr host
}
if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
atEmptyPath := flags&linux.AT_EMPTY_PATH != 0
if !atEmptyPath && len(pathname) == 0 {
@@ -135,7 +136,7 @@ func execveat(t *kernel.Task, dirFD int32, pathnameAddr, argvAddr, envvAddr host
// Need to extract the given FD.
f, fdFlags := t.FDTable().Get(dirFD)
if f == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer f.DecRef(t)
closeOnExec = fdFlags.CloseOnExec
@@ -260,7 +261,7 @@ func parseCommonWaitOptions(wopts *kernel.WaitOptions, options int) error {
wopts.NonCloneTasks = true
wopts.CloneTasks = true
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if options&linux.WCONTINUED != 0 {
wopts.Events |= kernel.EventGroupContinue
@@ -277,7 +278,7 @@ func parseCommonWaitOptions(wopts *kernel.WaitOptions, options int) error {
// wait4 waits for the given child process to exit.
func wait4(t *kernel.Task, pid int, statusAddr hostarch.Addr, options int, rusageAddr hostarch.Addr) (uintptr, error) {
if options&^(linux.WNOHANG|linux.WUNTRACED|linux.WCONTINUED|linux.WNOTHREAD|linux.WALL|linux.WCLONE) != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
wopts := kernel.WaitOptions{
Events: kernel.EventExit | kernel.EventTraceeStop,
@@ -358,10 +359,10 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
rusageAddr := args[4].Pointer()
if options&^(linux.WNOHANG|linux.WEXITED|linux.WSTOPPED|linux.WCONTINUED|linux.WNOWAIT|linux.WNOTHREAD|linux.WALL|linux.WCLONE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if options&(linux.WEXITED|linux.WSTOPPED|linux.WCONTINUED) == 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
wopts := kernel.WaitOptions{
Events: kernel.EventTraceeStop,
@@ -374,7 +375,7 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
case linux.P_PGID:
wopts.SpecificPGID = kernel.ProcessGroupID(id)
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if err := parseCommonWaitOptions(&wopts, options); err != nil {
@@ -398,7 +399,7 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// out the fields it would set for a successful waitid in this case
// as well.
if infop != 0 {
- var si arch.SignalInfo
+ var si linux.SignalInfo
_, err = si.CopyOut(t, infop)
}
}
@@ -413,7 +414,7 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
if infop == 0 {
return 0, nil, nil
}
- si := arch.SignalInfo{
+ si := linux.SignalInfo{
Signo: int32(linux.SIGCHLD),
}
si.SetPID(int32(wr.TID))
@@ -423,24 +424,24 @@ func Waitid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
s := unix.WaitStatus(wr.Status)
switch {
case s.Exited():
- si.Code = arch.CLD_EXITED
+ si.Code = linux.CLD_EXITED
si.SetStatus(int32(s.ExitStatus()))
case s.Signaled():
- si.Code = arch.CLD_KILLED
+ si.Code = linux.CLD_KILLED
si.SetStatus(int32(s.Signal()))
case s.CoreDump():
- si.Code = arch.CLD_DUMPED
+ si.Code = linux.CLD_DUMPED
si.SetStatus(int32(s.Signal()))
case s.Stopped():
if wr.Event == kernel.EventTraceeStop {
- si.Code = arch.CLD_TRAPPED
+ si.Code = linux.CLD_TRAPPED
si.SetStatus(int32(s.TrapCause()))
} else {
- si.Code = arch.CLD_STOPPED
+ si.Code = linux.CLD_STOPPED
si.SetStatus(int32(s.StopSignal()))
}
case s.Continued():
- si.Code = arch.CLD_CONTINUED
+ si.Code = linux.CLD_CONTINUED
si.SetStatus(int32(linux.SIGCONT))
default:
t.Warningf("waitid got incomprehensible wait status %d", s)
@@ -528,7 +529,7 @@ func SchedGetaffinity(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ker
// in an array of "unsigned long" so the buffer needs to
// be a multiple of the word size.
if size&(t.Arch().Width()-1) > 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var task *kernel.Task
@@ -545,7 +546,7 @@ func SchedGetaffinity(t *kernel.Task, args arch.SyscallArguments) (uintptr, *ker
// The buffer needs to be big enough to hold a cpumask with
// all possible cpus.
if size < mask.Size() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
_, err := t.CopyOutBytes(maskAddr, mask)
@@ -594,7 +595,7 @@ func Setpgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
}
tg = ot.ThreadGroup()
if tg.Leader() != ot {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Setpgid only operates on child threadgroups.
@@ -609,7 +610,7 @@ func Setpgid(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
if pgid == 0 {
pgid = defaultPGID
} else if pgid < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// If the pgid is the same as the group, then create a new one. Otherwise,
@@ -712,7 +713,7 @@ func Getpriority(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// PRIO_USER and PRIO_PGRP have no further implementation yet.
return 0, nil, nil
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
@@ -754,7 +755,7 @@ func Setpriority(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// PRIO_USER and PRIO_PGRP have no further implementation yet.
return 0, nil, nil
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, nil
diff --git a/pkg/sentry/syscalls/linux/sys_time.go b/pkg/sentry/syscalls/linux/sys_time.go
index 5c3b3dee2..674e74f82 100644
--- a/pkg/sentry/syscalls/linux/sys_time.go
+++ b/pkg/sentry/syscalls/linux/sys_time.go
@@ -19,6 +19,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -75,7 +76,7 @@ func ClockGetres(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
}
if _, err := getClock(t, clockID); err != nil {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if addr == 0 {
@@ -94,12 +95,12 @@ type cpuClocker interface {
func getClock(t *kernel.Task, clockID int32) (ktime.Clock, error) {
if clockID < 0 {
if !isValidCPUClock(clockID) {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
targetTask := targetTask(t, clockID)
if targetTask == nil {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
var target cpuClocker
@@ -116,7 +117,7 @@ func getClock(t *kernel.Task, clockID int32) (ktime.Clock, error) {
// CPUCLOCK_SCHED is approximated by CPUCLOCK_PROF.
return target.CPUClock(), nil
default:
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
}
@@ -138,7 +139,7 @@ func getClock(t *kernel.Task, clockID int32) (ktime.Clock, error) {
case linux.CLOCK_THREAD_CPUTIME_ID:
return t.CPUClock(), nil
default:
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
}
@@ -157,7 +158,7 @@ func ClockGettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
// ClockSettime implements linux syscall clock_settime(2).
func ClockSettime(*kernel.Task, arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
// Time implements linux syscall time(2).
@@ -209,11 +210,11 @@ func clockNanosleepUntil(t *kernel.Task, c ktime.Clock, end ktime.Time, rem host
timer.Destroy()
- switch err {
- case syserror.ETIMEDOUT:
+ switch {
+ case linuxerr.Equals(linuxerr.ETIMEDOUT, err):
// Slept for entire timeout.
return nil
- case syserror.ErrInterrupted:
+ case err == syserror.ErrInterrupted:
// Interrupted.
remaining := end.Sub(c.Now())
if remaining <= 0 {
@@ -253,7 +254,7 @@ func Nanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
}
if !ts.Valid() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Just like linux, we cap the timeout with the max number that int64 can
@@ -276,7 +277,7 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
}
if !req.Valid() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Only allow clock constants also allowed by Linux.
@@ -284,7 +285,7 @@ func ClockNanosleep(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
if clockID != linux.CLOCK_REALTIME &&
clockID != linux.CLOCK_MONOTONIC &&
clockID != linux.CLOCK_PROCESS_CPUTIME_ID {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/syscalls/linux/sys_timerfd.go b/pkg/sentry/syscalls/linux/sys_timerfd.go
index cadd9d348..4eeb94231 100644
--- a/pkg/sentry/syscalls/linux/sys_timerfd.go
+++ b/pkg/sentry/syscalls/linux/sys_timerfd.go
@@ -16,12 +16,12 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/timerfd"
"gvisor.dev/gvisor/pkg/sentry/kernel"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
- "gvisor.dev/gvisor/pkg/syserror"
)
// TimerfdCreate implements Linux syscall timerfd_create(2).
@@ -30,7 +30,7 @@ func TimerfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
flags := args[1].Int()
if flags&^(linux.TFD_CLOEXEC|linux.TFD_NONBLOCK) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var c ktime.Clock
@@ -40,7 +40,7 @@ func TimerfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
case linux.CLOCK_MONOTONIC, linux.CLOCK_BOOTTIME:
c = t.Kernel().MonotonicClock()
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
f := timerfd.NewFile(t, c)
defer f.DecRef(t)
@@ -66,18 +66,18 @@ func TimerfdSettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
oldValAddr := args[3].Pointer()
if flags&^(linux.TFD_TIMER_ABSTIME) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
f := t.GetFile(fd)
if f == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer f.DecRef(t)
tf, ok := f.FileOperations.(*timerfd.TimerOperations)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var newVal linux.Itimerspec
@@ -105,13 +105,13 @@ func TimerfdGettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
f := t.GetFile(fd)
if f == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer f.DecRef(t)
tf, ok := f.FileOperations.(*timerfd.TimerOperations)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
tm, s := tf.GetTime()
diff --git a/pkg/sentry/syscalls/linux/sys_tls_amd64.go b/pkg/sentry/syscalls/linux/sys_tls_amd64.go
index 6ddd30d5c..c5899a359 100644
--- a/pkg/sentry/syscalls/linux/sys_tls_amd64.go
+++ b/pkg/sentry/syscalls/linux/sys_tls_amd64.go
@@ -18,6 +18,7 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -42,13 +43,13 @@ func ArchPrctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
case linux.ARCH_SET_FS:
fsbase := args[1].Uint64()
if !t.Arch().SetTLS(uintptr(fsbase)) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
case linux.ARCH_GET_GS, linux.ARCH_SET_GS:
t.Kernel().EmitUnimplementedEvent(t)
fallthrough
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, nil
diff --git a/pkg/sentry/syscalls/linux/sys_utsname.go b/pkg/sentry/syscalls/linux/sys_utsname.go
index 66c5974f5..4e945d2c0 100644
--- a/pkg/sentry/syscalls/linux/sys_utsname.go
+++ b/pkg/sentry/syscalls/linux/sys_utsname.go
@@ -16,9 +16,9 @@ package linux
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Uname implements linux syscall uname.
@@ -57,10 +57,10 @@ func Setdomainname(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
utsns := t.UTSNamespace()
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, utsns.UserNamespace()) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
if size < 0 || size > linux.UTSLen {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
name, err := t.CopyInString(nameAddr, int(size))
@@ -79,10 +79,10 @@ func Sethostname(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
utsns := t.UTSNamespace()
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, utsns.UserNamespace()) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
if size < 0 || size > linux.UTSLen {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
name := make([]byte, size)
diff --git a/pkg/sentry/syscalls/linux/sys_write.go b/pkg/sentry/syscalls/linux/sys_write.go
index 95bfe6606..eec917d95 100644
--- a/pkg/sentry/syscalls/linux/sys_write.go
+++ b/pkg/sentry/syscalls/linux/sys_write.go
@@ -18,6 +18,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -46,19 +47,19 @@ func Write(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the file is writable.
if !file.Flags().Write {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Check that the size is legitimate.
si := int(size)
if si < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the source of the write.
@@ -83,29 +84,29 @@ func Pwrite64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate and does not overflow.
if offset < 0 || offset+int64(size) < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Is writing at an offset supported?
if !file.Flags().Pwrite {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
// Check that the file is writable.
if !file.Flags().Write {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Check that the size is legitimate.
si := int(size)
if si < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the source of the write.
@@ -129,13 +130,13 @@ func Writev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the file is writable.
if !file.Flags().Write {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Read the iovecs that specify the source of the write.
@@ -160,23 +161,23 @@ func Pwritev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate.
if offset < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Is writing at an offset supported?
if !file.Flags().Pwrite {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
// Check that the file is writable.
if !file.Flags().Write {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Read the iovecs that specify the source of the write.
@@ -208,23 +209,23 @@ func Pwritev2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
flags := int(args[5].Int())
if int(args[4].Int())&0x4 == 1 {
- return 0, nil, syserror.EACCES
+ return 0, nil, linuxerr.EACCES
}
file := t.GetFile(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate.
if offset < -1 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Is writing at an offset supported?
if offset > -1 && !file.Flags().Pwrite {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
// Note: gVisor does not implement the RWF_HIPRI feature, but the flag is
@@ -235,7 +236,7 @@ func Pwritev2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Check that the file is writeable.
if !file.Flags().Write {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Read the iovecs that specify the source of the write.
@@ -301,7 +302,7 @@ func writev(t *kernel.Task, f *fs.File, src usermem.IOSequence) (int64, error) {
// Wait for a notification that we should retry.
if err = t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = syserror.ErrWouldBlock
}
break
diff --git a/pkg/sentry/syscalls/linux/sys_xattr.go b/pkg/sentry/syscalls/linux/sys_xattr.go
index 28ad6a60e..717e23c59 100644
--- a/pkg/sentry/syscalls/linux/sys_xattr.go
+++ b/pkg/sentry/syscalls/linux/sys_xattr.go
@@ -18,6 +18,7 @@ import (
"strings"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -47,7 +48,7 @@ func FGetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
f := t.GetFile(fd)
if f == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer f.DecRef(t)
@@ -151,7 +152,7 @@ func FSetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
f := t.GetFile(fd)
if f == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer f.DecRef(t)
@@ -182,7 +183,7 @@ func setXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink
// setXattr implements setxattr(2) from the given *fs.Dirent.
func setXattr(t *kernel.Task, d *fs.Dirent, nameAddr, valueAddr hostarch.Addr, size uint64, flags uint32) error {
if flags&^(linux.XATTR_CREATE|linux.XATTR_REPLACE) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
name, err := copyInXattrName(t, nameAddr)
@@ -195,7 +196,7 @@ func setXattr(t *kernel.Task, d *fs.Dirent, nameAddr, valueAddr hostarch.Addr, s
}
if size > linux.XATTR_SIZE_MAX {
- return syserror.E2BIG
+ return linuxerr.E2BIG
}
buf := make([]byte, size)
if _, err := t.CopyInBytes(valueAddr, buf); err != nil {
@@ -217,7 +218,7 @@ func setXattr(t *kernel.Task, d *fs.Dirent, nameAddr, valueAddr hostarch.Addr, s
func copyInXattrName(t *kernel.Task, nameAddr hostarch.Addr) (string, error) {
name, err := t.CopyInString(nameAddr, linux.XATTR_NAME_MAX+1)
if err != nil {
- if err == syserror.ENAMETOOLONG {
+ if linuxerr.Equals(linuxerr.ENAMETOOLONG, err) {
return "", syserror.ERANGE
}
return "", err
@@ -241,9 +242,9 @@ func checkXattrPermissions(t *kernel.Task, i *fs.Inode, perms fs.PermMask) error
// Restrict xattrs to regular files and directories.
if !xattrFileTypeOk(i) {
if perms.Write {
- return syserror.EPERM
+ return linuxerr.EPERM
}
- return syserror.ENODATA
+ return linuxerr.ENODATA
}
return i.CheckPermission(t, perms)
@@ -268,7 +269,7 @@ func FListXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
f := t.GetFile(fd)
if f == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer f.DecRef(t)
@@ -333,7 +334,7 @@ func listXattr(t *kernel.Task, d *fs.Dirent, addr hostarch.Addr, size uint64) (i
listSize := xattrListSize(xattrs)
if listSize > linux.XATTR_SIZE_MAX {
- return 0, syserror.E2BIG
+ return 0, linuxerr.E2BIG
}
if uint64(listSize) > requestedSize {
return 0, syserror.ERANGE
@@ -382,7 +383,7 @@ func FRemoveXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
// TODO(b/113957122): Return EBADF if the fd was opened with O_PATH.
f := t.GetFile(fd)
if f == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer f.DecRef(t)
diff --git a/pkg/sentry/syscalls/linux/timespec.go b/pkg/sentry/syscalls/linux/timespec.go
index 3edc922eb..b327e27d6 100644
--- a/pkg/sentry/syscalls/linux/timespec.go
+++ b/pkg/sentry/syscalls/linux/timespec.go
@@ -18,6 +18,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
@@ -103,7 +104,7 @@ func copyTimespecInToDuration(t *kernel.Task, timespecAddr hostarch.Addr) (time.
return 0, err
}
if !timespec.Valid() {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
timeout = time.Duration(timespec.ToNsecCapped())
}
diff --git a/pkg/sentry/syscalls/linux/vfs2/BUILD b/pkg/sentry/syscalls/linux/vfs2/BUILD
index 5ce0bc714..a73f096ff 100644
--- a/pkg/sentry/syscalls/linux/vfs2/BUILD
+++ b/pkg/sentry/syscalls/linux/vfs2/BUILD
@@ -41,6 +41,7 @@ go_library(
"//pkg/abi/linux",
"//pkg/bits",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fspath",
"//pkg/gohacks",
"//pkg/hostarch",
diff --git a/pkg/sentry/syscalls/linux/vfs2/aio.go b/pkg/sentry/syscalls/linux/vfs2/aio.go
index fd1863ef3..a8fa86cdc 100644
--- a/pkg/sentry/syscalls/linux/vfs2/aio.go
+++ b/pkg/sentry/syscalls/linux/vfs2/aio.go
@@ -17,6 +17,8 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
@@ -26,8 +28,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// IoSubmit implements linux syscall io_submit(2).
@@ -37,7 +37,7 @@ func IoSubmit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
addr := args[2].Pointer()
if nrEvents < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
for i := int32(0); i < nrEvents; i++ {
@@ -90,12 +90,12 @@ func IoSubmit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// submitCallback processes a single callback.
func submitCallback(t *kernel.Task, id uint64, cb *linux.IOCallback, cbAddr hostarch.Addr) error {
if cb.Reserved2 != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
fd := t.GetFileVFS2(cb.FD)
if fd == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer fd.DecRef(t)
@@ -104,13 +104,13 @@ func submitCallback(t *kernel.Task, id uint64, cb *linux.IOCallback, cbAddr host
if cb.Flags&linux.IOCB_FLAG_RESFD != 0 {
eventFD = t.GetFileVFS2(cb.ResFD)
if eventFD == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
defer eventFD.DecRef(t)
// Check that it is an eventfd.
if _, ok := eventFD.Impl().(*eventfd.EventFileDescription); !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
@@ -123,14 +123,14 @@ func submitCallback(t *kernel.Task, id uint64, cb *linux.IOCallback, cbAddr host
switch cb.OpCode {
case linux.IOCB_CMD_PREAD, linux.IOCB_CMD_PREADV, linux.IOCB_CMD_PWRITE, linux.IOCB_CMD_PWRITEV:
if cb.Offset < 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
// Prepare the request.
aioCtx, ok := t.MemoryManager().LookupAIOContext(t, id)
if !ok {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if err := aioCtx.Prepare(); err != nil {
return err
@@ -200,7 +200,7 @@ func memoryFor(t *kernel.Task, cb *linux.IOCallback) (usermem.IOSequence, error)
bytes := int(cb.Bytes)
if bytes < 0 {
// Linux also requires that this field fit in ssize_t.
- return usermem.IOSequence{}, syserror.EINVAL
+ return usermem.IOSequence{}, linuxerr.EINVAL
}
// Since this I/O will be asynchronous with respect to t's task goroutine,
@@ -222,6 +222,6 @@ func memoryFor(t *kernel.Task, cb *linux.IOCallback) (usermem.IOSequence, error)
default:
// Not a supported command.
- return usermem.IOSequence{}, syserror.EINVAL
+ return usermem.IOSequence{}, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/syscalls/linux/vfs2/epoll.go b/pkg/sentry/syscalls/linux/vfs2/epoll.go
index 047d955b6..84010db77 100644
--- a/pkg/sentry/syscalls/linux/vfs2/epoll.go
+++ b/pkg/sentry/syscalls/linux/vfs2/epoll.go
@@ -19,12 +19,12 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -34,7 +34,7 @@ var sizeofEpollEvent = (*linux.EpollEvent)(nil).SizeBytes()
func EpollCreate1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
flags := args[0].Int()
if flags&^linux.EPOLL_CLOEXEC != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file, err := t.Kernel().VFS().NewEpollInstanceFD(t)
@@ -59,7 +59,7 @@ func EpollCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// "Since Linux 2.6.8, the size argument is ignored, but must be greater
// than zero" - epoll_create(2)
if size <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file, err := t.Kernel().VFS().NewEpollInstanceFD(t)
@@ -84,20 +84,20 @@ func EpollCtl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
epfile := t.GetFileVFS2(epfd)
if epfile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer epfile.DecRef(t)
ep, ok := epfile.Impl().(*vfs.EpollInstance)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
if epfile == file {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var event linux.EpollEvent
@@ -115,24 +115,24 @@ func EpollCtl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
}
return 0, nil, ep.ModifyInterest(file, fd, event)
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
func waitEpoll(t *kernel.Task, epfd int32, eventsAddr hostarch.Addr, maxEvents int, timeoutInNanos int64) (uintptr, *kernel.SyscallControl, error) {
var _EP_MAX_EVENTS = math.MaxInt32 / sizeofEpollEvent // Linux: fs/eventpoll.c:EP_MAX_EVENTS
if maxEvents <= 0 || maxEvents > _EP_MAX_EVENTS {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
epfile := t.GetFileVFS2(epfd)
if epfile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer epfile.DecRef(t)
ep, ok := epfile.Impl().(*vfs.EpollInstance)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Allocate space for a few events on the stack for the common case in
@@ -174,7 +174,7 @@ func waitEpoll(t *kernel.Task, epfd int32, eventsAddr hostarch.Addr, maxEvents i
haveDeadline = true
}
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = nil
}
return 0, nil, err
diff --git a/pkg/sentry/syscalls/linux/vfs2/eventfd.go b/pkg/sentry/syscalls/linux/vfs2/eventfd.go
index 807f909da..0dcf1fbff 100644
--- a/pkg/sentry/syscalls/linux/vfs2/eventfd.go
+++ b/pkg/sentry/syscalls/linux/vfs2/eventfd.go
@@ -16,10 +16,10 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Eventfd2 implements linux syscall eventfd2(2).
@@ -29,7 +29,7 @@ func Eventfd2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
allOps := uint(linux.EFD_SEMAPHORE | linux.EFD_NONBLOCK | linux.EFD_CLOEXEC)
if flags & ^allOps != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
vfsObj := t.Kernel().VFS()
diff --git a/pkg/sentry/syscalls/linux/vfs2/execve.go b/pkg/sentry/syscalls/linux/vfs2/execve.go
index 3315398a4..38818c175 100644
--- a/pkg/sentry/syscalls/linux/vfs2/execve.go
+++ b/pkg/sentry/syscalls/linux/vfs2/execve.go
@@ -16,7 +16,9 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsbridge"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -24,8 +26,6 @@ import (
slinux "gvisor.dev/gvisor/pkg/sentry/syscalls/linux"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Execve implements linux syscall execve(2).
@@ -48,7 +48,7 @@ func Execveat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
func execveat(t *kernel.Task, dirfd int32, pathnameAddr, argvAddr, envvAddr hostarch.Addr, flags int32) (uintptr, *kernel.SyscallControl, error) {
if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
pathname, err := t.CopyInString(pathnameAddr, linux.PATH_MAX)
@@ -87,7 +87,7 @@ func execveat(t *kernel.Task, dirfd int32, pathnameAddr, argvAddr, envvAddr host
}
dirfile, dirfileFlags := t.FDTable().GetVFS2(dirfd)
if dirfile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
start := dirfile.VirtualDentry()
start.IncRef()
diff --git a/pkg/sentry/syscalls/linux/vfs2/fd.go b/pkg/sentry/syscalls/linux/vfs2/fd.go
index 1a31898e8..55272fbbc 100644
--- a/pkg/sentry/syscalls/linux/vfs2/fd.go
+++ b/pkg/sentry/syscalls/linux/vfs2/fd.go
@@ -16,6 +16,7 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
@@ -36,7 +37,7 @@ func Close(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// (and other reference-holding operations complete).
_, file := t.FDTable().Remove(t, fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -50,13 +51,13 @@ func Dup(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
newFD, err := t.NewFDFromVFS2(0, file, kernel.FDFlags{})
if err != nil {
- return 0, nil, syserror.EMFILE
+ return 0, nil, linuxerr.EMFILE
}
return uintptr(newFD), nil, nil
}
@@ -70,7 +71,7 @@ func Dup2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
// As long as oldfd is valid, dup2() does nothing and returns newfd.
file := t.GetFileVFS2(oldfd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
file.DecRef(t)
return uintptr(newfd), nil, nil
@@ -86,7 +87,7 @@ func Dup3(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
flags := args[2].Uint()
if oldfd == newfd {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return dup3(t, oldfd, newfd, flags)
@@ -94,12 +95,12 @@ func Dup3(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
func dup3(t *kernel.Task, oldfd, newfd int32, flags uint32) (uintptr, *kernel.SyscallControl, error) {
if flags&^linux.O_CLOEXEC != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file := t.GetFileVFS2(oldfd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -119,7 +120,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file, flags := t.FDTable().GetVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -128,7 +129,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.F_DUPFD, linux.F_DUPFD_CLOEXEC, linux.F_GETFD, linux.F_SETFD, linux.F_GETFL:
// allowed
default:
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
}
@@ -169,7 +170,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
if who < 0 {
// Check for overflow before flipping the sign.
if who-1 > who {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ownerType = linux.F_OWNER_PGRP
who = -who
@@ -192,7 +193,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.F_SETPIPE_SZ:
pipefile, ok := file.Impl().(*pipe.VFSPipeFD)
if !ok {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
n, err := pipefile.SetPipeSize(int64(args[2].Int()))
if err != nil {
@@ -202,7 +203,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.F_GETPIPE_SZ:
pipefile, ok := file.Impl().(*pipe.VFSPipeFD)
if !ok {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
return uintptr(pipefile.PipeSize()), nil, nil
case linux.F_GET_SEALS:
@@ -210,7 +211,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return uintptr(val), nil, err
case linux.F_ADD_SEALS:
if !file.IsWritable() {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
err := tmpfs.AddSeals(file, args[2].Uint())
return 0, nil, err
@@ -232,7 +233,7 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return 0, nil, a.SetSignal(linux.Signal(args[2].Int()))
default:
// Everything else is not yet supported.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
@@ -269,7 +270,7 @@ func setAsyncOwner(t *kernel.Task, fd int, file *vfs.FileDescription, ownerType,
case linux.F_OWNER_TID, linux.F_OWNER_PID, linux.F_OWNER_PGRP:
// Acceptable type.
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
a := file.SetAsyncHandler(fasync.NewVFS2(fd)).(*fasync.FileAsync)
@@ -301,7 +302,7 @@ func setAsyncOwner(t *kernel.Task, fd int, file *vfs.FileDescription, ownerType,
a.SetOwnerProcessGroup(t, pg)
return nil
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
@@ -319,7 +320,7 @@ func posixTestLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDes
case linux.F_WRLCK:
typ = lock.WriteLock
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
r, err := file.ComputeLockRange(t, uint64(flock.Start), uint64(flock.Len), flock.Whence)
if err != nil {
@@ -368,13 +369,13 @@ func posixLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDescrip
switch flock.Type {
case linux.F_RDLCK:
if !file.IsReadable() {
- return syserror.EBADF
+ return linuxerr.EBADF
}
return file.LockPOSIX(t, t.FDTable(), int32(t.TGIDInRoot()), lock.ReadLock, r, blocker)
case linux.F_WRLCK:
if !file.IsWritable() {
- return syserror.EBADF
+ return linuxerr.EBADF
}
return file.LockPOSIX(t, t.FDTable(), int32(t.TGIDInRoot()), lock.WriteLock, r, blocker)
@@ -382,7 +383,7 @@ func posixLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDescrip
return file.UnlockPOSIX(t, t.FDTable(), r)
default:
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
@@ -395,22 +396,22 @@ func Fadvise64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
// Note: offset is allowed to be negative.
if length < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
if file.StatusFlags()&linux.O_PATH != 0 {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// If the FD refers to a pipe or FIFO, return error.
if _, isPipe := file.Impl().(*pipe.VFSPipeFD); isPipe {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
switch advice {
@@ -421,7 +422,7 @@ func Fadvise64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
case linux.POSIX_FADV_DONTNEED:
case linux.POSIX_FADV_NOREUSE:
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Sure, whatever.
diff --git a/pkg/sentry/syscalls/linux/vfs2/filesystem.go b/pkg/sentry/syscalls/linux/vfs2/filesystem.go
index 36aa1d3ae..534355237 100644
--- a/pkg/sentry/syscalls/linux/vfs2/filesystem.go
+++ b/pkg/sentry/syscalls/linux/vfs2/filesystem.go
@@ -16,12 +16,12 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Link implements Linux syscall link(2).
@@ -43,7 +43,7 @@ func Linkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
func linkat(t *kernel.Task, olddirfd int32, oldpathAddr hostarch.Addr, newdirfd int32, newpathAddr hostarch.Addr, flags int32) error {
if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_FOLLOW) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if flags&linux.AT_EMPTY_PATH != 0 && !t.HasCapability(linux.CAP_DAC_READ_SEARCH) {
return syserror.ENOENT
@@ -290,7 +290,7 @@ func Unlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
flags := args[2].Int()
if flags&^linux.AT_REMOVEDIR != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if flags&linux.AT_REMOVEDIR != 0 {
diff --git a/pkg/sentry/syscalls/linux/vfs2/fscontext.go b/pkg/sentry/syscalls/linux/vfs2/fscontext.go
index a7d4d2a36..f74d6197c 100644
--- a/pkg/sentry/syscalls/linux/vfs2/fscontext.go
+++ b/pkg/sentry/syscalls/linux/vfs2/fscontext.go
@@ -16,6 +16,7 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -106,7 +107,7 @@ func Chroot(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
addr := args[0].Pointer()
if !t.HasCapability(linux.CAP_SYS_CHROOT) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
path, err := copyInPath(t, addr)
diff --git a/pkg/sentry/syscalls/linux/vfs2/getdents.go b/pkg/sentry/syscalls/linux/vfs2/getdents.go
index b41a3056a..c2c3172bc 100644
--- a/pkg/sentry/syscalls/linux/vfs2/getdents.go
+++ b/pkg/sentry/syscalls/linux/vfs2/getdents.go
@@ -17,13 +17,12 @@ package vfs2
import (
"fmt"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Getdents implements Linux syscall getdents(2).
@@ -43,7 +42,7 @@ func getdents(t *kernel.Task, args arch.SyscallArguments, isGetdents64 bool) (ui
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -100,7 +99,7 @@ func (cb *getdentsCallback) Handle(dirent vfs.Dirent) error {
size := 8 + 8 + 2 + 1 + 1 + len(dirent.Name)
size = (size + 7) &^ 7 // round up to multiple of 8
if size > cb.remaining {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
buf = cb.t.CopyScratchBuffer(size)
hostarch.ByteOrder.PutUint64(buf[0:8], dirent.Ino)
@@ -134,7 +133,7 @@ func (cb *getdentsCallback) Handle(dirent vfs.Dirent) error {
size := 8 + 8 + 2 + 1 + 1 + len(dirent.Name)
size = (size + 7) &^ 7 // round up to multiple of sizeof(long)
if size > cb.remaining {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
buf = cb.t.CopyScratchBuffer(size)
hostarch.ByteOrder.PutUint64(buf[0:8], dirent.Ino)
diff --git a/pkg/sentry/syscalls/linux/vfs2/inotify.go b/pkg/sentry/syscalls/linux/vfs2/inotify.go
index 11753d8e5..d8d5dd7ad 100644
--- a/pkg/sentry/syscalls/linux/vfs2/inotify.go
+++ b/pkg/sentry/syscalls/linux/vfs2/inotify.go
@@ -16,10 +16,10 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
)
const allFlags = linux.IN_NONBLOCK | linux.IN_CLOEXEC
@@ -28,7 +28,7 @@ const allFlags = linux.IN_NONBLOCK | linux.IN_CLOEXEC
func InotifyInit1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
flags := args[0].Int()
if flags&^allFlags != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ino, err := vfs.NewInotifyFD(t, t.Kernel().VFS(), uint32(flags))
@@ -60,14 +60,14 @@ func fdToInotify(t *kernel.Task, fd int32) (*vfs.Inotify, *vfs.FileDescription,
f := t.GetFileVFS2(fd)
if f == nil {
// Invalid fd.
- return nil, nil, syserror.EBADF
+ return nil, nil, linuxerr.EBADF
}
ino, ok := f.Impl().(*vfs.Inotify)
if !ok {
// Not an inotify fd.
f.DecRef(t)
- return nil, nil, syserror.EINVAL
+ return nil, nil, linuxerr.EINVAL
}
return ino, f, nil
@@ -82,7 +82,7 @@ func InotifyAddWatch(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kern
// "EINVAL: The given event mask contains no valid events."
// -- inotify_add_watch(2)
if mask&linux.ALL_INOTIFY_BITS == 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// "IN_DONT_FOLLOW: Don't dereference pathname if it is a symbolic link."
diff --git a/pkg/sentry/syscalls/linux/vfs2/ioctl.go b/pkg/sentry/syscalls/linux/vfs2/ioctl.go
index c7c3fed57..b806120cd 100644
--- a/pkg/sentry/syscalls/linux/vfs2/ioctl.go
+++ b/pkg/sentry/syscalls/linux/vfs2/ioctl.go
@@ -16,10 +16,10 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Ioctl implements Linux syscall ioctl(2).
@@ -28,12 +28,12 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
if file.StatusFlags()&linux.O_PATH != 0 {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Handle ioctls that apply to all FDs.
@@ -99,7 +99,7 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
if who < 0 {
// Check for overflow before flipping the sign.
if who-1 > who {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
ownerType = linux.F_OWNER_PGRP
who = -who
diff --git a/pkg/sentry/syscalls/linux/vfs2/lock.go b/pkg/sentry/syscalls/linux/vfs2/lock.go
index d1452a04d..008603173 100644
--- a/pkg/sentry/syscalls/linux/vfs2/lock.go
+++ b/pkg/sentry/syscalls/linux/vfs2/lock.go
@@ -16,10 +16,10 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Flock implements linux syscall flock(2).
@@ -30,7 +30,7 @@ func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFileVFS2(fd)
if file == nil {
// flock(2): EBADF fd is not an open file descriptor.
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -57,7 +57,7 @@ func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
}
default:
// flock(2): EINVAL operation is invalid.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, nil
diff --git a/pkg/sentry/syscalls/linux/vfs2/memfd.go b/pkg/sentry/syscalls/linux/vfs2/memfd.go
index c4c0f9e0a..70c2cf5a5 100644
--- a/pkg/sentry/syscalls/linux/vfs2/memfd.go
+++ b/pkg/sentry/syscalls/linux/vfs2/memfd.go
@@ -16,10 +16,10 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
- "gvisor.dev/gvisor/pkg/syserror"
)
const (
@@ -35,7 +35,7 @@ func MemfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
if flags&^memfdAllFlags != 0 {
// Unknown bits in flags.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
allowSeals := flags&linux.MFD_ALLOW_SEALING != 0
diff --git a/pkg/sentry/syscalls/linux/vfs2/mmap.go b/pkg/sentry/syscalls/linux/vfs2/mmap.go
index c961545f6..c804f9fd3 100644
--- a/pkg/sentry/syscalls/linux/vfs2/mmap.go
+++ b/pkg/sentry/syscalls/linux/vfs2/mmap.go
@@ -16,13 +16,12 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/memmap"
- "gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Mmap implements Linux syscall mmap(2).
@@ -38,7 +37,7 @@ func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
// Require exactly one of MAP_PRIVATE and MAP_SHARED.
if private == shared {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
opts := memmap.MMapOpts{
@@ -71,13 +70,13 @@ func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
// Convert the passed FD to a file reference.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// mmap unconditionally requires that the FD is readable.
if !file.IsReadable() {
- return 0, nil, syserror.EACCES
+ return 0, nil, linuxerr.EACCES
}
// MAP_SHARED requires that the FD be writable for PROT_WRITE.
if shared && !file.IsWritable() {
diff --git a/pkg/sentry/syscalls/linux/vfs2/mount.go b/pkg/sentry/syscalls/linux/vfs2/mount.go
index dd93430e2..4d73d46ef 100644
--- a/pkg/sentry/syscalls/linux/vfs2/mount.go
+++ b/pkg/sentry/syscalls/linux/vfs2/mount.go
@@ -16,12 +16,11 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Mount implements Linux syscall mount(2).
@@ -69,7 +68,7 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// namespace.
creds := t.Credentials()
if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespaceVFS2().Owner) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
const unsupportedOps = linux.MS_REMOUNT | linux.MS_BIND |
@@ -84,7 +83,7 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// unknown or unsupported flags are passed. Since we don't implement
// everything, we fail explicitly on flags that are unimplemented.
if flags&(unsupportedOps|unsupportedFlags) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var opts vfs.MountOptions
@@ -125,12 +124,12 @@ func Umount2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Currently, this is always the init task's user namespace.
creds := t.Credentials()
if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespaceVFS2().Owner) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
const unsupported = linux.MNT_FORCE | linux.MNT_EXPIRE
if flags&unsupported != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
path, err := copyInPath(t, addr)
diff --git a/pkg/sentry/syscalls/linux/vfs2/path.go b/pkg/sentry/syscalls/linux/vfs2/path.go
index 2aaf1ed74..2bb783a85 100644
--- a/pkg/sentry/syscalls/linux/vfs2/path.go
+++ b/pkg/sentry/syscalls/linux/vfs2/path.go
@@ -16,12 +16,12 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
func copyInPath(t *kernel.Task, addr hostarch.Addr) (fspath.Path, error) {
@@ -53,7 +53,7 @@ func getTaskPathOperation(t *kernel.Task, dirfd int32, path fspath.Path, shouldA
dirfile := t.GetFileVFS2(dirfd)
if dirfile == nil {
root.DecRef(t)
- return taskPathOperation{}, syserror.EBADF
+ return taskPathOperation{}, linuxerr.EBADF
}
start = dirfile.VirtualDentry()
start.IncRef()
diff --git a/pkg/sentry/syscalls/linux/vfs2/pipe.go b/pkg/sentry/syscalls/linux/vfs2/pipe.go
index c6fc1954c..07a89cf4e 100644
--- a/pkg/sentry/syscalls/linux/vfs2/pipe.go
+++ b/pkg/sentry/syscalls/linux/vfs2/pipe.go
@@ -16,14 +16,13 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Pipe implements Linux syscall pipe(2).
@@ -41,7 +40,7 @@ func Pipe2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
func pipe2(t *kernel.Task, addr hostarch.Addr, flags int32) error {
if flags&^(linux.O_NONBLOCK|linux.O_CLOEXEC) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
r, w, err := pipefs.NewConnectedPipeFDs(t, t.Kernel().PipeMount(), uint32(flags&linux.O_NONBLOCK))
if err != nil {
diff --git a/pkg/sentry/syscalls/linux/vfs2/poll.go b/pkg/sentry/syscalls/linux/vfs2/poll.go
index a69c80edd..042aa4c97 100644
--- a/pkg/sentry/syscalls/linux/vfs2/poll.go
+++ b/pkg/sentry/syscalls/linux/vfs2/poll.go
@@ -19,6 +19,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
@@ -132,7 +133,7 @@ func pollBlock(t *kernel.Task, pfd []linux.PollFD, timeout time.Duration) (time.
// Wait for a notification.
timeout, err = t.BlockWithTimeout(ch, haveTimeout, timeout)
if err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = nil
}
return timeout, 0, err
@@ -161,7 +162,7 @@ func pollBlock(t *kernel.Task, pfd []linux.PollFD, timeout time.Duration) (time.
// copyInPollFDs copies an array of struct pollfd unless nfds exceeds the max.
func copyInPollFDs(t *kernel.Task, addr hostarch.Addr, nfds uint) ([]linux.PollFD, error) {
if uint64(nfds) > t.ThreadGroup().Limits().GetCapped(limits.NumberOfFiles, fileCap) {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
pfd := make([]linux.PollFD, nfds)
@@ -221,7 +222,7 @@ func CopyInFDSet(t *kernel.Task, addr hostarch.Addr, nBytes, nBitsInLastPartialB
func doSelect(t *kernel.Task, nfds int, readFDs, writeFDs, exceptFDs hostarch.Addr, timeout time.Duration) (uintptr, error) {
if nfds < 0 || nfds > fileCap {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Calculate the size of the fd sets (one bit per fd).
@@ -268,7 +269,7 @@ func doSelect(t *kernel.Task, nfds int, readFDs, writeFDs, exceptFDs hostarch.Ad
// OK. Linux is racy in the same way.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
file.DecRef(t)
@@ -410,7 +411,7 @@ func (p *pollRestartBlock) Restart(t *kernel.Task) (uintptr, error) {
func poll(t *kernel.Task, pfdAddr hostarch.Addr, nfds uint, timeout time.Duration) (uintptr, error) {
remainingTimeout, n, err := doPoll(t, pfdAddr, nfds, timeout)
// On an interrupt poll(2) is restarted with the remaining timeout.
- if err == syserror.EINTR {
+ if linuxerr.Equals(linuxerr.EINTR, err) {
t.SetSyscallRestartBlock(&pollRestartBlock{
pfdAddr: pfdAddr,
nfds: nfds,
@@ -462,7 +463,7 @@ func Ppoll(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
//
// Note that this means that if err is nil but copyErr is not, copyErr is
// ignored. This is consistent with Linux.
- if err == syserror.EINTR && copyErr == nil {
+ if linuxerr.Equals(linuxerr.EINTR, err) && copyErr == nil {
err = syserror.ERESTARTNOHAND
}
return n, nil, err
@@ -484,7 +485,7 @@ func Select(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
return 0, nil, err
}
if timeval.Sec < 0 || timeval.Usec < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
timeout = time.Duration(timeval.ToNsecCapped())
}
@@ -492,7 +493,7 @@ func Select(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
n, err := doSelect(t, nfds, readFDs, writeFDs, exceptFDs, timeout)
copyErr := copyOutTimevalRemaining(t, startNs, timeout, timevalAddr)
// See comment in Ppoll.
- if err == syserror.EINTR && copyErr == nil {
+ if linuxerr.Equals(linuxerr.EINTR, err) && copyErr == nil {
err = syserror.ERESTARTNOHAND
}
return n, nil, err
@@ -539,7 +540,7 @@ func Pselect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
n, err := doSelect(t, nfds, readFDs, writeFDs, exceptFDs, timeout)
copyErr := copyOutTimespecRemaining(t, startNs, timeout, timespecAddr)
// See comment in Ppoll.
- if err == syserror.EINTR && copyErr == nil {
+ if linuxerr.Equals(linuxerr.EINTR, err) && copyErr == nil {
err = syserror.ERESTARTNOHAND
}
return n, nil, err
@@ -561,7 +562,7 @@ func copyTimespecInToDuration(t *kernel.Task, timespecAddr hostarch.Addr) (time.
return 0, err
}
if !timespec.Valid() {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
timeout = time.Duration(timespec.ToNsecCapped())
}
@@ -573,7 +574,7 @@ func setTempSignalSet(t *kernel.Task, maskAddr hostarch.Addr, maskSize uint) err
return nil
}
if maskSize != linux.SignalSetSize {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
var mask linux.SignalSet
if _, err := mask.CopyIn(t, maskAddr); err != nil {
diff --git a/pkg/sentry/syscalls/linux/vfs2/read_write.go b/pkg/sentry/syscalls/linux/vfs2/read_write.go
index b863d7b84..fe8aa06da 100644
--- a/pkg/sentry/syscalls/linux/vfs2/read_write.go
+++ b/pkg/sentry/syscalls/linux/vfs2/read_write.go
@@ -18,6 +18,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
@@ -42,14 +43,14 @@ func Read(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the size is legitimate.
si := int(size)
if si < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the destination of the read.
@@ -73,7 +74,7 @@ func Readv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -120,7 +121,7 @@ func read(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, opt
// Wait for a notification that we should retry.
if err = t.BlockWithDeadline(ch, hasDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = syserror.ErrWouldBlock
}
break
@@ -140,19 +141,19 @@ func Pread64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate and does not overflow.
if offset < 0 || offset+int64(size) < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Check that the size is legitimate.
si := int(size)
if si < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the destination of the read.
@@ -177,13 +178,13 @@ func Preadv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate.
if offset < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the destination of the read.
@@ -215,13 +216,13 @@ func Preadv2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate.
if offset < -1 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the destination of the read.
@@ -275,7 +276,7 @@ func pread(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, of
// Wait for a notification that we should retry.
if err = t.BlockWithDeadline(ch, hasDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = syserror.ErrWouldBlock
}
break
@@ -293,14 +294,14 @@ func Write(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the size is legitimate.
si := int(size)
if si < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the source of the write.
@@ -324,7 +325,7 @@ func Writev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -371,7 +372,7 @@ func write(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, op
// Wait for a notification that we should retry.
if err = t.BlockWithDeadline(ch, hasDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = syserror.ErrWouldBlock
}
break
@@ -390,19 +391,19 @@ func Pwrite64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate and does not overflow.
if offset < 0 || offset+int64(size) < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Check that the size is legitimate.
si := int(size)
if si < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the source of the write.
@@ -427,13 +428,13 @@ func Pwritev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate.
if offset < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the source of the write.
@@ -465,13 +466,13 @@ func Pwritev2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the offset is legitimate.
if offset < -1 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get the source of the write.
@@ -525,7 +526,7 @@ func pwrite(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, o
// Wait for a notification that we should retry.
if err = t.BlockWithDeadline(ch, hasDeadline, deadline); err != nil {
- if err == syserror.ETIMEDOUT {
+ if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
err = syserror.ErrWouldBlock
}
break
@@ -560,7 +561,7 @@ func Lseek(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -576,27 +577,27 @@ func Readahead(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
// Check that the file is readable.
if !file.IsReadable() {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Check that the size is valid.
if int(size) < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Check that the offset is legitimate and does not overflow.
if offset < 0 || offset+int64(size) < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Return EINVAL; if the underlying file type does not support readahead,
// then Linux will return EINVAL to indicate as much. In the future, we
// may extend this function to actually support readahead hints.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
diff --git a/pkg/sentry/syscalls/linux/vfs2/setstat.go b/pkg/sentry/syscalls/linux/vfs2/setstat.go
index c6330c21a..b5a3b92c5 100644
--- a/pkg/sentry/syscalls/linux/vfs2/setstat.go
+++ b/pkg/sentry/syscalls/linux/vfs2/setstat.go
@@ -16,15 +16,15 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
const chmodMask = 0777 | linux.S_ISUID | linux.S_ISGID | linux.S_ISVTX
@@ -65,7 +65,7 @@ func Fchmod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -105,7 +105,7 @@ func Fchownat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
func fchownat(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr, owner, group, flags int32) error {
if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
path, err := copyInPath(t, pathAddr)
@@ -126,7 +126,7 @@ func populateSetStatOptionsForChown(t *kernel.Task, owner, group int32, opts *vf
if owner != -1 {
kuid := userns.MapToKUID(auth.UID(owner))
if !kuid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
opts.Stat.Mask |= linux.STATX_UID
opts.Stat.UID = uint32(kuid)
@@ -134,7 +134,7 @@ func populateSetStatOptionsForChown(t *kernel.Task, owner, group int32, opts *vf
if group != -1 {
kgid := userns.MapToKGID(auth.GID(group))
if !kgid.Ok() {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
opts.Stat.Mask |= linux.STATX_GID
opts.Stat.GID = uint32(kgid)
@@ -150,7 +150,7 @@ func Fchown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -167,7 +167,7 @@ func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
length := args[1].Int64()
if length < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
path, err := copyInPath(t, addr)
@@ -191,17 +191,17 @@ func Ftruncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
length := args[1].Int64()
if length < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
if !file.IsWritable() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
err := file.SetStat(t, vfs.SetStatOptions{
@@ -222,31 +222,31 @@ func Fallocate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
if !file.IsWritable() {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
if mode != 0 {
- return 0, nil, syserror.ENOTSUP
+ return 0, nil, linuxerr.ENOTSUP
}
if offset < 0 || length <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
size := offset + length
if size < 0 {
- return 0, nil, syserror.EFBIG
+ return 0, nil, linuxerr.EFBIG
}
limit := limits.FromContext(t).Get(limits.FileSize).Cur
if uint64(size) >= limit {
- t.SendSignal(&arch.SignalInfo{
+ t.SendSignal(&linux.SignalInfo{
Signo: int32(linux.SIGXFSZ),
- Code: arch.SignalInfoUser,
+ Code: linux.SI_USER,
})
- return 0, nil, syserror.EFBIG
+ return 0, nil, linuxerr.EFBIG
}
return 0, nil, file.Allocate(t, mode, uint64(offset), uint64(length))
@@ -340,7 +340,7 @@ func populateSetStatOptionsForUtimes(t *kernel.Task, timesAddr hostarch.Addr, op
return err
}
if times[0].Usec < 0 || times[0].Usec > 999999 || times[1].Usec < 0 || times[1].Usec > 999999 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
opts.Stat.Mask = linux.STATX_ATIME | linux.STATX_MTIME
opts.Stat.Atime = linux.StatxTimestamp{
@@ -372,7 +372,7 @@ func Utimensat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
}
if flags&^linux.AT_SYMLINK_NOFOLLOW != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// "If filename is NULL and dfd refers to an open file, then operate on the
@@ -405,7 +405,7 @@ func populateSetStatOptionsForUtimens(t *kernel.Task, timesAddr hostarch.Addr, o
}
if times[0].Nsec != linux.UTIME_OMIT {
if times[0].Nsec != linux.UTIME_NOW && (times[0].Nsec < 0 || times[0].Nsec > 999999999) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
opts.Stat.Mask |= linux.STATX_ATIME
opts.Stat.Atime = linux.StatxTimestamp{
@@ -415,7 +415,7 @@ func populateSetStatOptionsForUtimens(t *kernel.Task, timesAddr hostarch.Addr, o
}
if times[1].Nsec != linux.UTIME_OMIT {
if times[1].Nsec != linux.UTIME_NOW && (times[1].Nsec < 0 || times[1].Nsec > 999999999) {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
opts.Stat.Mask |= linux.STATX_MTIME
opts.Stat.Mtime = linux.StatxTimestamp{
@@ -440,7 +440,7 @@ func setstatat(t *kernel.Task, dirfd int32, path fspath.Path, shouldAllowEmptyPa
} else {
dirfile := t.GetFileVFS2(dirfd)
if dirfile == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
if !path.HasComponents() {
// Use FileDescription.SetStat() instead of
@@ -468,7 +468,7 @@ func handleSetSizeError(t *kernel.Task, err error) error {
if err == syserror.ErrExceedsFileSizeLimit {
// Convert error to EFBIG and send a SIGXFSZ per setrlimit(2).
t.SendSignal(kernel.SignalInfoNoInfo(linux.SIGXFSZ, t, t))
- return syserror.EFBIG
+ return linuxerr.EFBIG
}
return err
}
diff --git a/pkg/sentry/syscalls/linux/vfs2/signal.go b/pkg/sentry/syscalls/linux/vfs2/signal.go
index 6163da103..27fb2139b 100644
--- a/pkg/sentry/syscalls/linux/vfs2/signal.go
+++ b/pkg/sentry/syscalls/linux/vfs2/signal.go
@@ -16,13 +16,12 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/signalfd"
"gvisor.dev/gvisor/pkg/sentry/kernel"
slinux "gvisor.dev/gvisor/pkg/sentry/syscalls/linux"
- "gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// sharedSignalfd is shared between the two calls.
@@ -35,7 +34,7 @@ func sharedSignalfd(t *kernel.Task, fd int32, sigset hostarch.Addr, sigsetsize u
// Always check for valid flags, even if not creating.
if flags&^(linux.SFD_NONBLOCK|linux.SFD_CLOEXEC) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Is this a change to an existing signalfd?
@@ -44,7 +43,7 @@ func sharedSignalfd(t *kernel.Task, fd int32, sigset hostarch.Addr, sigsetsize u
if fd != -1 {
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -55,7 +54,7 @@ func sharedSignalfd(t *kernel.Task, fd int32, sigset hostarch.Addr, sigsetsize u
}
// Not a signalfd.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
fileFlags := uint32(linux.O_RDWR)
diff --git a/pkg/sentry/syscalls/linux/vfs2/socket.go b/pkg/sentry/syscalls/linux/vfs2/socket.go
index 69f69e3af..b39292f50 100644
--- a/pkg/sentry/syscalls/linux/vfs2/socket.go
+++ b/pkg/sentry/syscalls/linux/vfs2/socket.go
@@ -18,6 +18,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -117,7 +118,7 @@ type multipleMessageHeader64 struct {
// from the untrusted address space range.
func CaptureAddress(t *kernel.Task, addr hostarch.Addr, addrlen uint32) ([]byte, error) {
if addrlen > maxAddrLen {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
addrBuf := make([]byte, addrlen)
@@ -139,7 +140,7 @@ func writeAddress(t *kernel.Task, addr linux.SockAddr, addrLen uint32, addrPtr h
}
if int32(bufLen) < 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Write the length unconditionally.
@@ -173,7 +174,7 @@ func Socket(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// Check and initialize the flags.
if stype & ^(0xf|linux.SOCK_NONBLOCK|linux.SOCK_CLOEXEC) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Create the new socket.
@@ -206,7 +207,7 @@ func SocketPair(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
// Check and initialize the flags.
if stype & ^(0xf|linux.SOCK_NONBLOCK|linux.SOCK_CLOEXEC) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Create the socket pair.
@@ -256,7 +257,7 @@ func Connect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -281,13 +282,13 @@ func Connect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
func accept(t *kernel.Task, fd int32, addr hostarch.Addr, addrLen hostarch.Addr, flags int) (uintptr, error) {
// Check that no unsupported flags are passed in.
if flags & ^(linux.SOCK_NONBLOCK|linux.SOCK_CLOEXEC) != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -309,7 +310,7 @@ func accept(t *kernel.Task, fd int32, addr hostarch.Addr, addrLen hostarch.Addr,
if peerRequested {
// NOTE(magi): Linux does not give you an error if it can't
// write the data back out so neither do we.
- if err := writeAddress(t, peer, peerLen, addr, addrLen); err == syserror.EINVAL {
+ if err := writeAddress(t, peer, peerLen, addr, addrLen); linuxerr.Equals(linuxerr.EINVAL, err) {
return 0, err
}
}
@@ -346,7 +347,7 @@ func Bind(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -373,7 +374,7 @@ func Listen(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -411,7 +412,7 @@ func Shutdown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -425,7 +426,7 @@ func Shutdown(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
switch how {
case linux.SHUT_RD, linux.SHUT_WR, linux.SHUT_RDWR:
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
return 0, nil, s.Shutdown(t, int(how)).ToError()
@@ -442,7 +443,7 @@ func GetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -458,7 +459,7 @@ func GetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
return 0, nil, err
}
if optLen < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Call syscall implementation then copy both value and value len out.
@@ -523,7 +524,7 @@ func SetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -534,10 +535,10 @@ func SetSockOpt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
}
if optLen < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if optLen > maxOptLen {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
buf := t.CopyScratchBuffer(int(optLen))
if _, err := t.CopyInBytes(optValAddr, buf); err != nil {
@@ -561,7 +562,7 @@ func GetSockName(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -589,7 +590,7 @@ func GetPeerName(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -616,13 +617,13 @@ func RecvMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -634,7 +635,7 @@ func RecvMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Reject flags that we don't handle yet.
if flags & ^(baseRecvFlags|linux.MSG_PEEK|linux.MSG_CMSG_CLOEXEC|linux.MSG_ERRQUEUE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if (file.StatusFlags() & linux.SOCK_NONBLOCK) != 0 {
@@ -664,7 +665,7 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if vlen > linux.UIO_MAXIOV {
@@ -673,13 +674,13 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Reject flags that we don't handle yet.
if flags & ^(baseRecvFlags|linux.MSG_CMSG_CLOEXEC|linux.MSG_ERRQUEUE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -701,7 +702,7 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
return 0, nil, err
}
if !ts.Valid() {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
deadline = t.Kernel().MonotonicClock().Now().Add(ts.ToDuration())
haveDeadline = true
@@ -753,7 +754,7 @@ func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr hostarch.Addr, fl
}
if msg.IovLen > linux.UIO_MAXIOV {
- return 0, syserror.EMSGSIZE
+ return 0, linuxerr.EMSGSIZE
}
dst, err := t.IovecsIOSequence(hostarch.Addr(msg.Iov), int(msg.IovLen), usermem.IOOpts{
AddressSpaceActive: true,
@@ -784,7 +785,7 @@ func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr hostarch.Addr, fl
}
if msg.ControlLen > maxControlLen {
- return 0, syserror.ENOBUFS
+ return 0, linuxerr.ENOBUFS
}
n, mflags, sender, senderLen, cms, e := s.RecvMsg(t, dst, int(flags), haveDeadline, deadline, msg.NameLen != 0, msg.ControlLen)
if e != nil {
@@ -833,18 +834,18 @@ func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr hostarch.Addr, fl
// recvfrom and recv syscall handlers.
func recvFrom(t *kernel.Task, fd int32, bufPtr hostarch.Addr, bufLen uint64, flags int32, namePtr hostarch.Addr, nameLenPtr hostarch.Addr) (uintptr, error) {
if int(bufLen) < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Reject flags that we don't handle yet.
if flags & ^(baseRecvFlags|linux.MSG_PEEK|linux.MSG_CONFIRM) != 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -911,13 +912,13 @@ func SendMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -929,7 +930,7 @@ func SendMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
// Reject flags that we don't handle yet.
if flags & ^(linux.MSG_DONTWAIT|linux.MSG_EOR|linux.MSG_MORE|linux.MSG_NOSIGNAL) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if (file.StatusFlags() & linux.SOCK_NONBLOCK) != 0 {
@@ -949,7 +950,7 @@ func SendMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
if t.Arch().Width() != 8 {
// We only handle 64-bit for now.
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if vlen > linux.UIO_MAXIOV {
@@ -959,7 +960,7 @@ func SendMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -971,7 +972,7 @@ func SendMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
// Reject flags that we don't handle yet.
if flags & ^(linux.MSG_DONTWAIT|linux.MSG_EOR|linux.MSG_MORE|linux.MSG_NOSIGNAL) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if (file.StatusFlags() & linux.SOCK_NONBLOCK) != 0 {
@@ -1018,7 +1019,7 @@ func sendSingleMsg(t *kernel.Task, s socket.SocketVFS2, file *vfs.FileDescriptio
if msg.ControlLen > 0 {
// Put an upper bound to prevent large allocations.
if msg.ControlLen > maxControlLen {
- return 0, syserror.ENOBUFS
+ return 0, linuxerr.ENOBUFS
}
controlData = make([]byte, msg.ControlLen)
if _, err := t.CopyInBytes(hostarch.Addr(msg.Control), controlData); err != nil {
@@ -1038,7 +1039,7 @@ func sendSingleMsg(t *kernel.Task, s socket.SocketVFS2, file *vfs.FileDescriptio
// Read data then call the sendmsg implementation.
if msg.IovLen > linux.UIO_MAXIOV {
- return 0, syserror.EMSGSIZE
+ return 0, linuxerr.EMSGSIZE
}
src, err := t.IovecsIOSequence(hostarch.Addr(msg.Iov), int(msg.IovLen), usermem.IOOpts{
AddressSpaceActive: true,
@@ -1077,13 +1078,13 @@ func sendSingleMsg(t *kernel.Task, s socket.SocketVFS2, file *vfs.FileDescriptio
func sendTo(t *kernel.Task, fd int32, bufPtr hostarch.Addr, bufLen uint64, flags int32, namePtr hostarch.Addr, nameLen uint32) (uintptr, error) {
bl := int(bufLen)
if bl < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Get socket from the file descriptor.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
defer file.DecRef(t)
diff --git a/pkg/sentry/syscalls/linux/vfs2/splice.go b/pkg/sentry/syscalls/linux/vfs2/splice.go
index 19e175203..d8009123f 100644
--- a/pkg/sentry/syscalls/linux/vfs2/splice.go
+++ b/pkg/sentry/syscalls/linux/vfs2/splice.go
@@ -18,6 +18,7 @@ import (
"io"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/arch"
@@ -46,29 +47,29 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
count = int64(kernel.MAX_RW_COUNT)
}
if count < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Check for invalid flags.
if flags&^(linux.SPLICE_F_MOVE|linux.SPLICE_F_NONBLOCK|linux.SPLICE_F_MORE|linux.SPLICE_F_GIFT) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get file descriptions.
inFile := t.GetFileVFS2(inFD)
if inFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer inFile.DecRef(t)
outFile := t.GetFileVFS2(outFD)
if outFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer outFile.DecRef(t)
// Check that both files support the required directionality.
if !inFile.IsReadable() || !outFile.IsWritable() {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// The operation is non-blocking if anything is non-blocking.
@@ -82,38 +83,38 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
inPipeFD, inIsPipe := inFile.Impl().(*pipe.VFSPipeFD)
outPipeFD, outIsPipe := outFile.Impl().(*pipe.VFSPipeFD)
if !inIsPipe && !outIsPipe {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Copy in offsets.
inOffset := int64(-1)
if inOffsetPtr != 0 {
if inIsPipe {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
if inFile.Options().DenyPRead {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if _, err := primitive.CopyInt64In(t, inOffsetPtr, &inOffset); err != nil {
return 0, nil, err
}
if inOffset < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
outOffset := int64(-1)
if outOffsetPtr != 0 {
if outIsPipe {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
if outFile.Options().DenyPWrite {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if _, err := primitive.CopyInt64In(t, outOffsetPtr, &outOffset); err != nil {
return 0, nil, err
}
if outOffset < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
@@ -189,29 +190,29 @@ func Tee(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo
count = int64(kernel.MAX_RW_COUNT)
}
if count < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Check for invalid flags.
if flags&^(linux.SPLICE_F_MOVE|linux.SPLICE_F_NONBLOCK|linux.SPLICE_F_MORE|linux.SPLICE_F_GIFT) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Get file descriptions.
inFile := t.GetFileVFS2(inFD)
if inFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer inFile.DecRef(t)
outFile := t.GetFileVFS2(outFD)
if outFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer outFile.DecRef(t)
// Check that both files support the required directionality.
if !inFile.IsReadable() || !outFile.IsWritable() {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// The operation is non-blocking if anything is non-blocking.
@@ -225,7 +226,7 @@ func Tee(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo
inPipeFD, inIsPipe := inFile.Impl().(*pipe.VFSPipeFD)
outPipeFD, outIsPipe := outFile.Impl().(*pipe.VFSPipeFD)
if !inIsPipe || !outIsPipe {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Copy data.
@@ -270,25 +271,25 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
inFile := t.GetFileVFS2(inFD)
if inFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer inFile.DecRef(t)
if !inFile.IsReadable() {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
outFile := t.GetFileVFS2(outFD)
if outFile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer outFile.DecRef(t)
if !outFile.IsWritable() {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
// Verify that the outFile Append flag is not set.
if outFile.StatusFlags()&linux.O_APPEND != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Verify that inFile is a regular file or block device. This is a
@@ -298,14 +299,14 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
return 0, nil, err
} else if stat.Mask&linux.STATX_TYPE == 0 ||
(stat.Mode&linux.S_IFMT != linux.S_IFREG && stat.Mode&linux.S_IFMT != linux.S_IFBLK) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Copy offset if it exists.
offset := int64(-1)
if offsetAddr != 0 {
if inFile.Options().DenyPRead {
- return 0, nil, syserror.ESPIPE
+ return 0, nil, linuxerr.ESPIPE
}
var offsetP primitive.Int64
if _, err := offsetP.CopyIn(t, offsetAddr); err != nil {
@@ -314,16 +315,16 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
offset = int64(offsetP)
if offset < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if offset+count < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
}
// Validate count. This must come after offset checks.
if count < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if count == 0 {
return 0, nil, nil
diff --git a/pkg/sentry/syscalls/linux/vfs2/stat.go b/pkg/sentry/syscalls/linux/vfs2/stat.go
index 69e77fa99..ba1d30823 100644
--- a/pkg/sentry/syscalls/linux/vfs2/stat.go
+++ b/pkg/sentry/syscalls/linux/vfs2/stat.go
@@ -17,15 +17,15 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bits"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/gohacks"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// Stat implements Linux syscall stat(2).
@@ -53,7 +53,7 @@ func Newfstatat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
func fstatat(t *kernel.Task, dirfd int32, pathAddr, statAddr hostarch.Addr, flags int32) error {
if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
opts := vfs.StatOptions{
@@ -78,7 +78,7 @@ func fstatat(t *kernel.Task, dirfd int32, pathAddr, statAddr hostarch.Addr, flag
} else {
dirfile := t.GetFileVFS2(dirfd)
if dirfile == nil {
- return syserror.EBADF
+ return linuxerr.EBADF
}
if !path.HasComponents() {
// Use FileDescription.Stat() instead of
@@ -131,7 +131,7 @@ func Fstat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -156,15 +156,15 @@ func Statx(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
statxAddr := args[4].Pointer()
if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW|linux.AT_STATX_SYNC_TYPE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Make sure that only one sync type option is set.
syncType := uint32(flags & linux.AT_STATX_SYNC_TYPE)
if syncType != 0 && !bits.IsPowerOfTwo32(syncType) {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if mask&linux.STATX__RESERVED != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
opts := vfs.StatOptions{
@@ -190,7 +190,7 @@ func Statx(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
} else {
dirfile := t.GetFileVFS2(dirfd)
if dirfile == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
if !path.HasComponents() {
// Use FileDescription.Stat() instead of
@@ -272,7 +272,7 @@ func accessAt(t *kernel.Task, dirfd int32, pathAddr hostarch.Addr, mode uint) er
// Sanity check the mode.
if mode&^(rOK|wOK|xOK) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
path, err := copyInPath(t, pathAddr)
@@ -315,7 +315,7 @@ func Readlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
func readlinkat(t *kernel.Task, dirfd int32, pathAddr, bufAddr hostarch.Addr, size uint) (uintptr, *kernel.SyscallControl, error) {
if int(size) <= 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
path, err := copyInPath(t, pathAddr)
diff --git a/pkg/sentry/syscalls/linux/vfs2/sync.go b/pkg/sentry/syscalls/linux/vfs2/sync.go
index 1f8a5878c..d0ffc7c32 100644
--- a/pkg/sentry/syscalls/linux/vfs2/sync.go
+++ b/pkg/sentry/syscalls/linux/vfs2/sync.go
@@ -16,6 +16,7 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
@@ -32,12 +33,12 @@ func Syncfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
if file.StatusFlags()&linux.O_PATH != 0 {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
return 0, nil, file.SyncFS(t)
@@ -49,7 +50,7 @@ func Fsync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -71,15 +72,15 @@ func SyncFileRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
// Check for negative values and overflow.
if offset < 0 || offset+nbytes < 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
if flags&^(linux.SYNC_FILE_RANGE_WAIT_BEFORE|linux.SYNC_FILE_RANGE_WRITE|linux.SYNC_FILE_RANGE_WAIT_AFTER) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
diff --git a/pkg/sentry/syscalls/linux/vfs2/timerfd.go b/pkg/sentry/syscalls/linux/vfs2/timerfd.go
index 250870c03..b8f96a757 100644
--- a/pkg/sentry/syscalls/linux/vfs2/timerfd.go
+++ b/pkg/sentry/syscalls/linux/vfs2/timerfd.go
@@ -16,11 +16,11 @@ package vfs2
import (
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/timerfd"
"gvisor.dev/gvisor/pkg/sentry/kernel"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
- "gvisor.dev/gvisor/pkg/syserror"
)
// TimerfdCreate implements Linux syscall timerfd_create(2).
@@ -29,7 +29,7 @@ func TimerfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
flags := args[1].Int()
if flags&^(linux.TFD_CLOEXEC|linux.TFD_NONBLOCK) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
// Timerfds aren't writable per se (their implementation of Write just
@@ -47,7 +47,7 @@ func TimerfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
case linux.CLOCK_MONOTONIC, linux.CLOCK_BOOTTIME:
clock = t.Kernel().MonotonicClock()
default:
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
vfsObj := t.Kernel().VFS()
file, err := timerfd.New(t, vfsObj, clock, fileFlags)
@@ -72,18 +72,18 @@ func TimerfdSettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
oldValAddr := args[3].Pointer()
if flags&^(linux.TFD_TIMER_ABSTIME) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
tfd, ok := file.Impl().(*timerfd.TimerFileDescription)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
var newVal linux.Itimerspec
@@ -111,13 +111,13 @@ func TimerfdGettime(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
tfd, ok := file.Impl().(*timerfd.TimerFileDescription)
if !ok {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
tm, s := tfd.GetTime()
diff --git a/pkg/sentry/syscalls/linux/vfs2/xattr.go b/pkg/sentry/syscalls/linux/vfs2/xattr.go
index c261050c6..0f54529bc 100644
--- a/pkg/sentry/syscalls/linux/vfs2/xattr.go
+++ b/pkg/sentry/syscalls/linux/vfs2/xattr.go
@@ -18,6 +18,7 @@ import (
"bytes"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/gohacks"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
@@ -71,7 +72,7 @@ func Flistxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -140,7 +141,7 @@ func Fgetxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -178,7 +179,7 @@ func setxattr(t *kernel.Task, args arch.SyscallArguments, shouldFollowFinalSymli
flags := args[4].Int()
if flags&^(linux.XATTR_CREATE|linux.XATTR_REPLACE) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
path, err := copyInPath(t, pathAddr)
@@ -216,12 +217,12 @@ func Fsetxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
flags := args[4].Int()
if flags&^(linux.XATTR_CREATE|linux.XATTR_REPLACE) != 0 {
- return 0, nil, syserror.EINVAL
+ return 0, nil, linuxerr.EINVAL
}
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -280,7 +281,7 @@ func Fremovexattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
file := t.GetFileVFS2(fd)
if file == nil {
- return 0, nil, syserror.EBADF
+ return 0, nil, linuxerr.EBADF
}
defer file.DecRef(t)
@@ -295,7 +296,7 @@ func Fremovexattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.
func copyInXattrName(t *kernel.Task, nameAddr hostarch.Addr) (string, error) {
name, err := t.CopyInString(nameAddr, linux.XATTR_NAME_MAX+1)
if err != nil {
- if err == syserror.ENAMETOOLONG {
+ if linuxerr.Equals(linuxerr.ENAMETOOLONG, err) {
return "", syserror.ERANGE
}
return "", err
@@ -321,7 +322,7 @@ func copyOutXattrNameList(t *kernel.Task, listAddr hostarch.Addr, size uint, nam
}
if buf.Len() > int(size) {
if size >= linux.XATTR_LIST_MAX {
- return 0, syserror.E2BIG
+ return 0, linuxerr.E2BIG
}
return 0, syserror.ERANGE
}
@@ -330,7 +331,7 @@ func copyOutXattrNameList(t *kernel.Task, listAddr hostarch.Addr, size uint, nam
func copyInXattrValue(t *kernel.Task, valueAddr hostarch.Addr, size uint) (string, error) {
if size > linux.XATTR_SIZE_MAX {
- return "", syserror.E2BIG
+ return "", linuxerr.E2BIG
}
buf := make([]byte, size)
if _, err := t.CopyInBytes(valueAddr, buf); err != nil {
@@ -349,7 +350,7 @@ func copyOutXattrValue(t *kernel.Task, valueAddr hostarch.Addr, size uint, value
}
if len(value) > int(size) {
if size >= linux.XATTR_SIZE_MAX {
- return 0, syserror.E2BIG
+ return 0, linuxerr.E2BIG
}
return 0, syserror.ERANGE
}
diff --git a/pkg/sentry/syscalls/syscalls.go b/pkg/sentry/syscalls/syscalls.go
index f88055676..511fb8b28 100644
--- a/pkg/sentry/syscalls/syscalls.go
+++ b/pkg/sentry/syscalls/syscalls.go
@@ -28,6 +28,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/syserror"
@@ -99,13 +100,13 @@ func CapError(name string, c linux.Capability, note string, urls []string) kerne
Name: name,
Fn: func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
if !t.HasCapability(c) {
- return 0, nil, syserror.EPERM
+ return 0, nil, linuxerr.EPERM
}
t.Kernel().EmitUnimplementedEvent(t)
return 0, nil, syserror.ENOSYS
},
SupportLevel: kernel.SupportUnimplemented,
- Note: fmt.Sprintf("%sReturns %q if the process does not have %s; %q otherwise.", note, syserror.EPERM, c.String(), syserror.ENOSYS),
+ Note: fmt.Sprintf("%sReturns %q if the process does not have %s; %q otherwise.", note, linuxerr.EPERM, c.String(), syserror.ENOSYS),
URLs: urls,
}
}
diff --git a/pkg/sentry/time/BUILD b/pkg/sentry/time/BUILD
index 1f617ca8f..36d999c47 100644
--- a/pkg/sentry/time/BUILD
+++ b/pkg/sentry/time/BUILD
@@ -8,7 +8,7 @@ go_template_instance(
out = "seqatomic_parameters_unsafe.go",
package = "time",
suffix = "Parameters",
- template = "//pkg/sync:generic_seqatomic",
+ template = "//pkg/sync/seqatomic:generic_seqatomic",
types = {
"Value": "Parameters",
},
@@ -25,6 +25,8 @@ go_library(
"muldiv_arm64.s",
"parameters.go",
"sampler.go",
+ "sampler_amd64.go",
+ "sampler_arm64.go",
"sampler_unsafe.go",
"seqatomic_parameters_unsafe.go",
"tsc_amd64.s",
@@ -32,6 +34,7 @@ go_library(
],
visibility = ["//:sandbox"],
deps = [
+ "//pkg/errors/linuxerr",
"//pkg/gohacks",
"//pkg/log",
"//pkg/metric",
diff --git a/pkg/sentry/time/calibrated_clock.go b/pkg/sentry/time/calibrated_clock.go
index 39bf1e0de..eed74f6bd 100644
--- a/pkg/sentry/time/calibrated_clock.go
+++ b/pkg/sentry/time/calibrated_clock.go
@@ -19,10 +19,10 @@ package time
import (
"time"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
// CalibratedClock implements a clock that tracks a reference clock.
@@ -259,6 +259,6 @@ func (c *CalibratedClocks) GetTime(id ClockID) (int64, error) {
case Realtime:
return c.realtime.GetTime()
default:
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
}
diff --git a/pkg/sentry/time/calibrated_clock_test.go b/pkg/sentry/time/calibrated_clock_test.go
index d6622bfe2..0a4b1f1bf 100644
--- a/pkg/sentry/time/calibrated_clock_test.go
+++ b/pkg/sentry/time/calibrated_clock_test.go
@@ -50,6 +50,7 @@ func TestConstantFrequency(t *testing.T) {
if !c.ready {
c.mu.RUnlock()
t.Fatalf("clock not ready")
+ return // For checklocks consistency.
}
// A bit after the last sample.
now, ok := c.params.ComputeTime(750000)
diff --git a/pkg/sentry/time/sampler.go b/pkg/sentry/time/sampler.go
index 4ac9c4474..24a47f5d5 100644
--- a/pkg/sentry/time/sampler.go
+++ b/pkg/sentry/time/sampler.go
@@ -21,13 +21,6 @@ import (
)
const (
- // defaultOverheadTSC is the default estimated syscall overhead in TSC cycles.
- // It is further refined as syscalls are made.
- defaultOverheadCycles = 1 * 1000
-
- // maxOverheadCycles is the maximum allowed syscall overhead in TSC cycles.
- maxOverheadCycles = 100 * defaultOverheadCycles
-
// maxSampleLoops is the maximum number of times to try to get a clock sample
// under the expected overhead.
maxSampleLoops = 5
diff --git a/pkg/sentry/time/sampler_amd64.go b/pkg/sentry/time/sampler_amd64.go
new file mode 100644
index 000000000..9f1b4b2fb
--- /dev/null
+++ b/pkg/sentry/time/sampler_amd64.go
@@ -0,0 +1,26 @@
+// 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.
+
+//+build amd64
+
+package time
+
+const (
+ // defaultOverheadTSC is the default estimated syscall overhead in TSC cycles.
+ // It is further refined as syscalls are made.
+ defaultOverheadCycles = 1 * 1000
+
+ // maxOverheadCycles is the maximum allowed syscall overhead in TSC cycles.
+ maxOverheadCycles = 100 * defaultOverheadCycles
+)
diff --git a/pkg/sentry/time/sampler_arm64.go b/pkg/sentry/time/sampler_arm64.go
new file mode 100644
index 000000000..4c8d33ae4
--- /dev/null
+++ b/pkg/sentry/time/sampler_arm64.go
@@ -0,0 +1,42 @@
+// 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.
+
+//+build arm64
+
+package time
+
+// getCNTFRQ get ARM counter-timer frequency
+func getCNTFRQ() TSCValue
+
+// getDefaultArchOverheadCycles get default OverheadCycles based on
+// ARM counter-timer frequency. Usually ARM counter-timer frequency
+// is range from 1-50Mhz which is much less than that on x86, so we
+// calibrate defaultOverheadCycles for ARM.
+func getDefaultArchOverheadCycles() TSCValue {
+ // estimated the clock frequency on x86 is 1Ghz.
+ // 1Ghz devided by counter-timer frequency of ARM to get
+ // frqRatio. defaultOverheadCycles of ARM equals to that on
+ // x86 devided by frqRatio
+ cntfrq := getCNTFRQ()
+ frqRatio := 1000000000 / cntfrq
+ overheadCycles := (1 * 1000) / frqRatio
+ return overheadCycles
+}
+
+// defaultOverheadTSC is the default estimated syscall overhead in TSC cycles.
+// It is further refined as syscalls are made.
+var defaultOverheadCycles = getDefaultArchOverheadCycles()
+
+// maxOverheadCycles is the maximum allowed syscall overhead in TSC cycles.
+var maxOverheadCycles = 100 * defaultOverheadCycles
diff --git a/pkg/sentry/time/tsc_arm64.s b/pkg/sentry/time/tsc_arm64.s
index da9fa4112..711349fa1 100644
--- a/pkg/sentry/time/tsc_arm64.s
+++ b/pkg/sentry/time/tsc_arm64.s
@@ -20,3 +20,9 @@ TEXT ·Rdtsc(SB),NOSPLIT,$0-8
WORD $0xd53be040 //MRS CNTVCT_EL0, R0
MOVD R0, ret+0(FP)
RET
+
+TEXT ·getCNTFRQ(SB),NOSPLIT,$0-8
+ // Get the virtual counter frequency.
+ WORD $0xd53be000 //MRS CNTFRQ_EL0, R0
+ MOVD R0, ret+0(FP)
+ RET
diff --git a/pkg/sentry/usage/memory.go b/pkg/sentry/usage/memory.go
index 581862ee2..e7073ec87 100644
--- a/pkg/sentry/usage/memory.go
+++ b/pkg/sentry/usage/memory.go
@@ -132,7 +132,7 @@ func Init() error {
// always be the case for a newly mapped page from /dev/shm. If we obtain
// the shared memory through some other means in the future, we may have to
// explicitly zero the page.
- mmap, err := unix.Mmap(int(file.Fd()), 0, int(RTMemoryStatsSize), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
+ mmap, err := memutil.MapFile(0, RTMemoryStatsSize, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED, file.Fd(), 0)
if err != nil {
return fmt.Errorf("error mapping usage file: %v", err)
}
diff --git a/pkg/sentry/usage/memory_unsafe.go b/pkg/sentry/usage/memory_unsafe.go
index 9e0014ca0..bc1531b91 100644
--- a/pkg/sentry/usage/memory_unsafe.go
+++ b/pkg/sentry/usage/memory_unsafe.go
@@ -21,7 +21,7 @@ import (
// RTMemoryStatsSize is the size of the RTMemoryStats struct.
var RTMemoryStatsSize = unsafe.Sizeof(RTMemoryStats{})
-// RTMemoryStatsPointer casts the address of the byte slice into a RTMemoryStats pointer.
-func RTMemoryStatsPointer(b []byte) *RTMemoryStats {
- return (*RTMemoryStats)(unsafe.Pointer(&b[0]))
+// RTMemoryStatsPointer casts addr to a RTMemoryStats pointer.
+func RTMemoryStatsPointer(addr uintptr) *RTMemoryStats {
+ return (*RTMemoryStats)(unsafe.Pointer(addr))
}
diff --git a/pkg/sentry/vfs/BUILD b/pkg/sentry/vfs/BUILD
index ac60fe8bf..a2032162d 100644
--- a/pkg/sentry/vfs/BUILD
+++ b/pkg/sentry/vfs/BUILD
@@ -95,6 +95,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdnotifier",
"//pkg/fspath",
@@ -133,6 +134,7 @@ go_test(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/sentry/contexttest",
"//pkg/sync",
"//pkg/syserror",
diff --git a/pkg/sentry/vfs/anonfs.go b/pkg/sentry/vfs/anonfs.go
index f48817132..40863cbb6 100644
--- a/pkg/sentry/vfs/anonfs.go
+++ b/pkg/sentry/vfs/anonfs.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -132,7 +133,7 @@ func (fs *anonFilesystem) LinkAt(ctx context.Context, rp *ResolvingPath, vd Virt
if !rp.Final() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// MkdirAt implements FilesystemImpl.MkdirAt.
@@ -140,7 +141,7 @@ func (fs *anonFilesystem) MkdirAt(ctx context.Context, rp *ResolvingPath, opts M
if !rp.Final() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// MknodAt implements FilesystemImpl.MknodAt.
@@ -148,7 +149,7 @@ func (fs *anonFilesystem) MknodAt(ctx context.Context, rp *ResolvingPath, opts M
if !rp.Final() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// OpenAt implements FilesystemImpl.OpenAt.
@@ -156,7 +157,7 @@ func (fs *anonFilesystem) OpenAt(ctx context.Context, rp *ResolvingPath, opts Op
if !rp.Done() {
return nil, syserror.ENOTDIR
}
- return nil, syserror.ENODEV
+ return nil, linuxerr.ENODEV
}
// ReadlinkAt implements FilesystemImpl.ReadlinkAt.
@@ -164,7 +165,7 @@ func (fs *anonFilesystem) ReadlinkAt(ctx context.Context, rp *ResolvingPath) (st
if !rp.Done() {
return "", syserror.ENOTDIR
}
- return "", syserror.EINVAL
+ return "", linuxerr.EINVAL
}
// RenameAt implements FilesystemImpl.RenameAt.
@@ -172,7 +173,7 @@ func (fs *anonFilesystem) RenameAt(ctx context.Context, rp *ResolvingPath, oldPa
if !rp.Final() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// RmdirAt implements FilesystemImpl.RmdirAt.
@@ -180,7 +181,7 @@ func (fs *anonFilesystem) RmdirAt(ctx context.Context, rp *ResolvingPath) error
if !rp.Final() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// SetStatAt implements FilesystemImpl.SetStatAt.
@@ -231,7 +232,7 @@ func (fs *anonFilesystem) SymlinkAt(ctx context.Context, rp *ResolvingPath, targ
if !rp.Final() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// UnlinkAt implements FilesystemImpl.UnlinkAt.
@@ -239,7 +240,7 @@ func (fs *anonFilesystem) UnlinkAt(ctx context.Context, rp *ResolvingPath) error
if !rp.Final() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// BoundEndpointAt implements FilesystemImpl.BoundEndpointAt.
@@ -250,7 +251,7 @@ func (fs *anonFilesystem) BoundEndpointAt(ctx context.Context, rp *ResolvingPath
if err := GenericCheckPermissions(rp.Credentials(), MayWrite, anonFileMode, anonFileUID, anonFileGID); err != nil {
return nil, err
}
- return nil, syserror.ECONNREFUSED
+ return nil, linuxerr.ECONNREFUSED
}
// ListXattrAt implements FilesystemImpl.ListXattrAt.
@@ -266,7 +267,7 @@ func (fs *anonFilesystem) GetXattrAt(ctx context.Context, rp *ResolvingPath, opt
if !rp.Done() {
return "", syserror.ENOTDIR
}
- return "", syserror.ENOTSUP
+ return "", linuxerr.ENOTSUP
}
// SetXattrAt implements FilesystemImpl.SetXattrAt.
@@ -274,7 +275,7 @@ func (fs *anonFilesystem) SetXattrAt(ctx context.Context, rp *ResolvingPath, opt
if !rp.Done() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// RemoveXattrAt implements FilesystemImpl.RemoveXattrAt.
@@ -282,7 +283,7 @@ func (fs *anonFilesystem) RemoveXattrAt(ctx context.Context, rp *ResolvingPath,
if !rp.Done() {
return syserror.ENOTDIR
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// PrependPath implements FilesystemImpl.PrependPath.
diff --git a/pkg/sentry/vfs/dentry.go b/pkg/sentry/vfs/dentry.go
index e7ca24d96..cb92b6eee 100644
--- a/pkg/sentry/vfs/dentry.go
+++ b/pkg/sentry/vfs/dentry.go
@@ -18,8 +18,8 @@ import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
)
// Dentry represents a node in a Filesystem tree at which a file exists.
@@ -196,11 +196,12 @@ func (d *Dentry) OnZeroWatches(ctx context.Context) {
// PrepareDeleteDentry must be called before attempting to delete the file
// represented by d. If PrepareDeleteDentry succeeds, the caller must call
// AbortDeleteDentry or CommitDeleteDentry depending on the deletion's outcome.
+// +checklocksacquire:d.mu
func (vfs *VirtualFilesystem) PrepareDeleteDentry(mntns *MountNamespace, d *Dentry) error {
vfs.mountMu.Lock()
if mntns.mountpoints[d] != 0 {
vfs.mountMu.Unlock()
- return syserror.EBUSY
+ return linuxerr.EBUSY // +checklocksforce: inconsistent return.
}
d.mu.Lock()
vfs.mountMu.Unlock()
@@ -211,14 +212,14 @@ func (vfs *VirtualFilesystem) PrepareDeleteDentry(mntns *MountNamespace, d *Dent
// AbortDeleteDentry must be called after PrepareDeleteDentry if the deletion
// fails.
-// +checklocks:d.mu
+// +checklocksrelease:d.mu
func (vfs *VirtualFilesystem) AbortDeleteDentry(d *Dentry) {
d.mu.Unlock()
}
// CommitDeleteDentry must be called after PrepareDeleteDentry if the deletion
// succeeds.
-// +checklocks:d.mu
+// +checklocksrelease:d.mu
func (vfs *VirtualFilesystem) CommitDeleteDentry(ctx context.Context, d *Dentry) {
d.dead = true
d.mu.Unlock()
@@ -249,16 +250,18 @@ func (vfs *VirtualFilesystem) InvalidateDentry(ctx context.Context, d *Dentry) {
// Preconditions:
// * If to is not nil, it must be a child Dentry from the same Filesystem.
// * from != to.
+// +checklocksacquire:from.mu
+// +checklocksacquire:to.mu
func (vfs *VirtualFilesystem) PrepareRenameDentry(mntns *MountNamespace, from, to *Dentry) error {
vfs.mountMu.Lock()
if mntns.mountpoints[from] != 0 {
vfs.mountMu.Unlock()
- return syserror.EBUSY
+ return linuxerr.EBUSY // +checklocksforce: no locks acquired.
}
if to != nil {
if mntns.mountpoints[to] != 0 {
vfs.mountMu.Unlock()
- return syserror.EBUSY
+ return linuxerr.EBUSY // +checklocksforce: no locks acquired.
}
to.mu.Lock()
}
@@ -267,13 +270,13 @@ func (vfs *VirtualFilesystem) PrepareRenameDentry(mntns *MountNamespace, from, t
// Return with from.mu and to.mu locked, which will be unlocked by
// AbortRenameDentry, CommitRenameReplaceDentry, or
// CommitRenameExchangeDentry.
- return nil
+ return nil // +checklocksforce: to may not be acquired.
}
// AbortRenameDentry must be called after PrepareRenameDentry if the rename
// fails.
-// +checklocks:from.mu
-// +checklocks:to.mu
+// +checklocksrelease:from.mu
+// +checklocksrelease:to.mu
func (vfs *VirtualFilesystem) AbortRenameDentry(from, to *Dentry) {
from.mu.Unlock()
if to != nil {
@@ -286,8 +289,8 @@ func (vfs *VirtualFilesystem) AbortRenameDentry(from, to *Dentry) {
// that was replaced by from.
//
// Preconditions: PrepareRenameDentry was previously called on from and to.
-// +checklocks:from.mu
-// +checklocks:to.mu
+// +checklocksrelease:from.mu
+// +checklocksrelease:to.mu
func (vfs *VirtualFilesystem) CommitRenameReplaceDentry(ctx context.Context, from, to *Dentry) {
from.mu.Unlock()
if to != nil {
@@ -303,8 +306,8 @@ func (vfs *VirtualFilesystem) CommitRenameReplaceDentry(ctx context.Context, fro
// from and to are exchanged by rename(RENAME_EXCHANGE).
//
// Preconditions: PrepareRenameDentry was previously called on from and to.
-// +checklocks:from.mu
-// +checklocks:to.mu
+// +checklocksrelease:from.mu
+// +checklocksrelease:to.mu
func (vfs *VirtualFilesystem) CommitRenameExchangeDentry(from, to *Dentry) {
from.mu.Unlock()
to.mu.Unlock()
diff --git a/pkg/sentry/vfs/device.go b/pkg/sentry/vfs/device.go
index dde2ad79b..572d81afc 100644
--- a/pkg/sentry/vfs/device.go
+++ b/pkg/sentry/vfs/device.go
@@ -18,7 +18,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
)
// DeviceKind indicates whether a device is a block or character device.
@@ -100,7 +100,7 @@ func (vfs *VirtualFilesystem) OpenDeviceSpecialFile(ctx context.Context, mnt *Mo
defer vfs.devicesMu.RUnlock()
rd, ok := vfs.devices[tup]
if !ok {
- return nil, syserror.ENXIO
+ return nil, linuxerr.ENXIO
}
return rd.dev.Open(ctx, mnt, d, *opts)
}
@@ -120,7 +120,7 @@ func (vfs *VirtualFilesystem) GetAnonBlockDevMinor() (uint32, error) {
}
minor++
}
- return 0, syserror.EMFILE
+ return 0, linuxerr.EMFILE
}
// PutAnonBlockDevMinor deallocates a minor device number returned by a
diff --git a/pkg/sentry/vfs/epoll.go b/pkg/sentry/vfs/epoll.go
index ae004b371..a93e757f8 100644
--- a/pkg/sentry/vfs/epoll.go
+++ b/pkg/sentry/vfs/epoll.go
@@ -17,6 +17,7 @@ package vfs
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/waiter"
@@ -174,7 +175,7 @@ func (ep *EpollInstance) AddInterest(file *FileDescription, num int32, event lin
// that cyclic polling is not introduced after the check.
defer epollCycleMu.Unlock()
if subep.mightPoll(ep) {
- return syserror.ELOOP
+ return linuxerr.ELOOP
}
}
diff --git a/pkg/sentry/vfs/file_description.go b/pkg/sentry/vfs/file_description.go
index ef8d8a813..ca3303dec 100644
--- a/pkg/sentry/vfs/file_description.go
+++ b/pkg/sentry/vfs/file_description.go
@@ -20,13 +20,13 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sync"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -252,7 +252,7 @@ func (fd *FileDescription) SetStatusFlags(ctx context.Context, creds *auth.Crede
return err
}
if (stat.AttributesMask&linux.STATX_ATTR_APPEND != 0) && (stat.Attributes&linux.STATX_ATTR_APPEND != 0) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
if (flags&linux.O_NOATIME != 0) && (oldFlags&linux.O_NOATIME == 0) {
@@ -266,14 +266,14 @@ func (fd *FileDescription) SetStatusFlags(ctx context.Context, creds *auth.Crede
return err
}
if stat.Mask&linux.STATX_UID == 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if !CanActAsOwner(creds, auth.KUID(stat.UID)) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
if flags&linux.O_DIRECT != 0 && !fd.opts.AllowDirectIO {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// TODO(gvisor.dev/issue/1035): FileDescriptionImpl.SetOAsync()?
const settableFlags = linux.O_APPEND | linux.O_ASYNC | linux.O_DIRECT | linux.O_NOATIME | linux.O_NONBLOCK
@@ -567,7 +567,7 @@ func (fd *FileDescription) StatFS(ctx context.Context) (linux.Statfs, error) {
// Allocate grows file represented by FileDescription to offset + length bytes.
func (fd *FileDescription) Allocate(ctx context.Context, mode, offset, length uint64) error {
if !fd.IsWritable() {
- return syserror.EBADF
+ return linuxerr.EBADF
}
if err := fd.impl.Allocate(ctx, mode, offset, length); err != nil {
return err
@@ -602,10 +602,10 @@ func (fd *FileDescription) EventUnregister(e *waiter.Entry) {
// partial reads with a nil error.
func (fd *FileDescription) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) {
if fd.opts.DenyPRead {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
if !fd.readable {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
start := fsmetric.StartReadWait()
n, err := fd.impl.PRead(ctx, dst, offset, opts)
@@ -620,7 +620,7 @@ func (fd *FileDescription) PRead(ctx context.Context, dst usermem.IOSequence, of
// Read is similar to PRead, but does not specify an offset.
func (fd *FileDescription) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
if !fd.readable {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
start := fsmetric.StartReadWait()
n, err := fd.impl.Read(ctx, dst, opts)
@@ -637,10 +637,10 @@ func (fd *FileDescription) Read(ctx context.Context, dst usermem.IOSequence, opt
// return partial writes with a nil error.
func (fd *FileDescription) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
if fd.opts.DenyPWrite {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
if !fd.writable {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
n, err := fd.impl.PWrite(ctx, src, offset, opts)
if n > 0 {
@@ -652,7 +652,7 @@ func (fd *FileDescription) PWrite(ctx context.Context, src usermem.IOSequence, o
// Write is similar to PWrite, but does not specify an offset.
func (fd *FileDescription) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) {
if !fd.writable {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
n, err := fd.impl.Write(ctx, src, opts)
if n > 0 {
@@ -708,8 +708,8 @@ func (fd *FileDescription) ListXattr(ctx context.Context, size uint64) ([]string
return names, err
}
names, err := fd.impl.ListXattr(ctx, size)
- if err == syserror.ENOTSUP {
- // Linux doesn't actually return ENOTSUP in this case; instead,
+ if linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
+ // Linux doesn't actually return EOPNOTSUPP in this case; instead,
// fs/xattr.c:vfs_listxattr() falls back to allowing the security
// subsystem to return security extended attributes, which by default
// don't exist.
@@ -873,7 +873,7 @@ func (fd *FileDescription) ComputeLockRange(ctx context.Context, start uint64, l
}
off = int64(stat.Size)
default:
- return lock.LockRange{}, syserror.EINVAL
+ return lock.LockRange{}, linuxerr.EINVAL
}
return lock.ComputeRange(int64(start), int64(length), off)
diff --git a/pkg/sentry/vfs/file_description_impl_util.go b/pkg/sentry/vfs/file_description_impl_util.go
index 2b6f47b4b..c1ab2f56c 100644
--- a/pkg/sentry/vfs/file_description_impl_util.go
+++ b/pkg/sentry/vfs/file_description_impl_util.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -65,7 +66,7 @@ func (FileDescriptionDefaultImpl) StatFS(ctx context.Context) (linux.Statfs, err
// should technically return EISDIR. Allocate should never be called for a
// directory, because it requires a writable fd.
func (FileDescriptionDefaultImpl) Allocate(ctx context.Context, mode, offset, length uint64) error {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
// Readiness implements waiter.Waitable.Readiness analogously to
@@ -88,25 +89,25 @@ func (FileDescriptionDefaultImpl) EventUnregister(e *waiter.Entry) {
// PRead implements FileDescriptionImpl.PRead analogously to
// file_operations::read == file_operations::read_iter == NULL in Linux.
func (FileDescriptionDefaultImpl) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Read implements FileDescriptionImpl.Read analogously to
// file_operations::read == file_operations::read_iter == NULL in Linux.
func (FileDescriptionDefaultImpl) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// PWrite implements FileDescriptionImpl.PWrite analogously to
// file_operations::write == file_operations::write_iter == NULL in Linux.
func (FileDescriptionDefaultImpl) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Write implements FileDescriptionImpl.Write analogously to
// file_operations::write == file_operations::write_iter == NULL in Linux.
func (FileDescriptionDefaultImpl) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// IterDirents implements FileDescriptionImpl.IterDirents analogously to
@@ -119,19 +120,19 @@ func (FileDescriptionDefaultImpl) IterDirents(ctx context.Context, cb IterDirent
// Seek implements FileDescriptionImpl.Seek analogously to
// file_operations::llseek == NULL in Linux.
func (FileDescriptionDefaultImpl) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Sync implements FileDescriptionImpl.Sync analogously to
// file_operations::fsync == NULL in Linux.
func (FileDescriptionDefaultImpl) Sync(ctx context.Context) error {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// ConfigureMMap implements FileDescriptionImpl.ConfigureMMap analogously to
// file_operations::mmap == NULL in Linux.
func (FileDescriptionDefaultImpl) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
- return syserror.ENODEV
+ return linuxerr.ENODEV
}
// Ioctl implements FileDescriptionImpl.Ioctl analogously to
@@ -144,25 +145,25 @@ func (FileDescriptionDefaultImpl) Ioctl(ctx context.Context, uio usermem.IO, arg
// inode_operations::listxattr == NULL in Linux.
func (FileDescriptionDefaultImpl) ListXattr(ctx context.Context, size uint64) ([]string, error) {
// This isn't exactly accurate; see FileDescription.ListXattr.
- return nil, syserror.ENOTSUP
+ return nil, linuxerr.ENOTSUP
}
// GetXattr implements FileDescriptionImpl.GetXattr analogously to
// inode::i_opflags & IOP_XATTR == 0 in Linux.
func (FileDescriptionDefaultImpl) GetXattr(ctx context.Context, opts GetXattrOptions) (string, error) {
- return "", syserror.ENOTSUP
+ return "", linuxerr.ENOTSUP
}
// SetXattr implements FileDescriptionImpl.SetXattr analogously to
// inode::i_opflags & IOP_XATTR == 0 in Linux.
func (FileDescriptionDefaultImpl) SetXattr(ctx context.Context, opts SetXattrOptions) error {
- return syserror.ENOTSUP
+ return linuxerr.ENOTSUP
}
// RemoveXattr implements FileDescriptionImpl.RemoveXattr analogously to
// inode::i_opflags & IOP_XATTR == 0 in Linux.
func (FileDescriptionDefaultImpl) RemoveXattr(ctx context.Context, name string) error {
- return syserror.ENOTSUP
+ return linuxerr.ENOTSUP
}
// DirectoryFileDescriptionDefaultImpl may be embedded by implementations of
@@ -333,10 +334,10 @@ func (fd *DynamicBytesFileDescriptionImpl) Seek(ctx context.Context, offset int6
offset += fd.off
default:
// fs/seq_file:seq_lseek() rejects SEEK_END etc.
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
if offset != fd.lastRead {
// Regenerate the file's contents immediately. Compare
@@ -467,27 +468,27 @@ func (NoLockFD) SupportsLocks() bool {
// LockBSD implements FileDescriptionImpl.LockBSD.
func (NoLockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error {
- return syserror.ENOLCK
+ return linuxerr.ENOLCK
}
// UnlockBSD implements FileDescriptionImpl.UnlockBSD.
func (NoLockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error {
- return syserror.ENOLCK
+ return linuxerr.ENOLCK
}
// LockPOSIX implements FileDescriptionImpl.LockPOSIX.
func (NoLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
- return syserror.ENOLCK
+ return linuxerr.ENOLCK
}
// UnlockPOSIX implements FileDescriptionImpl.UnlockPOSIX.
func (NoLockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error {
- return syserror.ENOLCK
+ return linuxerr.ENOLCK
}
// TestPOSIX implements FileDescriptionImpl.TestPOSIX.
func (NoLockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
- return linux.Flock{}, syserror.ENOLCK
+ return linux.Flock{}, linuxerr.ENOLCK
}
// BadLockFD implements Lock*/Unlock* portion of FileDescriptionImpl interface
@@ -503,25 +504,25 @@ func (BadLockFD) SupportsLocks() bool {
// LockBSD implements FileDescriptionImpl.LockBSD.
func (BadLockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// UnlockBSD implements FileDescriptionImpl.UnlockBSD.
func (BadLockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// LockPOSIX implements FileDescriptionImpl.LockPOSIX.
func (BadLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// UnlockPOSIX implements FileDescriptionImpl.UnlockPOSIX.
func (BadLockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// TestPOSIX implements FileDescriptionImpl.TestPOSIX.
func (BadLockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
- return linux.Flock{}, syserror.EBADF
+ return linux.Flock{}, linuxerr.EBADF
}
diff --git a/pkg/sentry/vfs/file_description_impl_util_test.go b/pkg/sentry/vfs/file_description_impl_util_test.go
index 1cd607c0a..3423dede1 100644
--- a/pkg/sentry/vfs/file_description_impl_util_test.go
+++ b/pkg/sentry/vfs/file_description_impl_util_test.go
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
@@ -102,7 +103,7 @@ func (fd *testFD) Stat(ctx context.Context, opts StatOptions) (linux.Statx, erro
// SetStat implements FileDescriptionImpl.SetStat.
func (fd *testFD) SetStat(ctx context.Context, opts SetStatOptions) error {
- return syserror.EPERM
+ return linuxerr.EPERM
}
func TestGenCountFD(t *testing.T) {
@@ -155,10 +156,10 @@ func TestGenCountFD(t *testing.T) {
}
// Write and PWrite fails.
- if _, err := fd.Write(ctx, ioseq, WriteOptions{}); err != syserror.EIO {
+ if _, err := fd.Write(ctx, ioseq, WriteOptions{}); !linuxerr.Equals(linuxerr.EIO, err) {
t.Errorf("Write: got err %v, wanted %v", err, syserror.EIO)
}
- if _, err := fd.PWrite(ctx, ioseq, 0, WriteOptions{}); err != syserror.EIO {
+ if _, err := fd.PWrite(ctx, ioseq, 0, WriteOptions{}); !linuxerr.Equals(linuxerr.EIO, err) {
t.Errorf("Write: got err %v, wanted %v", err, syserror.EIO)
}
}
@@ -215,10 +216,10 @@ func TestWritable(t *testing.T) {
if n, err := fd.Seek(ctx, 1, linux.SEEK_SET); n != 0 && err != nil {
t.Errorf("Seek: got err (%v, %v), wanted (0, nil)", n, err)
}
- if n, err := fd.Write(ctx, writeIOSeq, WriteOptions{}); n != 0 && err != syserror.EINVAL {
+ if n, err := fd.Write(ctx, writeIOSeq, WriteOptions{}); n != 0 && !linuxerr.Equals(linuxerr.EINVAL, err) {
t.Errorf("Write: got err (%v, %v), wanted (0, EINVAL)", n, err)
}
- if n, err := fd.PWrite(ctx, writeIOSeq, 2, WriteOptions{}); n != 0 && err != syserror.EINVAL {
+ if n, err := fd.PWrite(ctx, writeIOSeq, 2, WriteOptions{}); n != 0 && !linuxerr.Equals(linuxerr.EINVAL, err) {
t.Errorf("PWrite: got err (%v, %v), wanted (0, EINVAL)", n, err)
}
}
diff --git a/pkg/sentry/vfs/inotify.go b/pkg/sentry/vfs/inotify.go
index 49d29e20b..e0624b2fa 100644
--- a/pkg/sentry/vfs/inotify.go
+++ b/pkg/sentry/vfs/inotify.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/uniqueid"
@@ -98,7 +99,7 @@ func NewInotifyFD(ctx context.Context, vfsObj *VirtualFilesystem, flags uint32)
// O_CLOEXEC affects file descriptors, so it must be handled outside of vfs.
flags &^= linux.O_CLOEXEC
if flags&^linux.O_NONBLOCK != 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
id := uniqueid.GlobalFromContext(ctx)
@@ -184,23 +185,23 @@ func (i *Inotify) Readiness(mask waiter.EventMask) waiter.EventMask {
// PRead implements FileDescriptionImpl.PRead.
func (*Inotify) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// PWrite implements FileDescriptionImpl.PWrite.
func (*Inotify) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
- return 0, syserror.ESPIPE
+ return 0, linuxerr.ESPIPE
}
// Write implements FileDescriptionImpl.Write.
func (*Inotify) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Read implements FileDescriptionImpl.Read.
func (i *Inotify) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
if dst.NumBytes() < inotifyEventBaseSize {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
i.evMu.Lock()
@@ -226,7 +227,7 @@ func (i *Inotify) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOpt
// write some events out.
return writeLen, nil
}
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
// Linux always dequeues an available event as long as there's enough
@@ -332,7 +333,7 @@ func (i *Inotify) AddWatch(target *Dentry, mask uint32) (int32, error) {
if ws == nil {
// While Linux supports inotify watches on all filesystem types, watches on
// filesystems like kernfs are not generally useful, so we do not.
- return 0, syserror.EPERM
+ return 0, linuxerr.EPERM
}
// Does the target already have a watch from this inotify instance?
if existing := ws.Lookup(i.id); existing != nil {
@@ -360,7 +361,7 @@ func (i *Inotify) RmWatch(ctx context.Context, wd int32) error {
w, ok := i.watches[wd]
if !ok {
i.mu.Unlock()
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Remove the watch from this instance.
diff --git a/pkg/sentry/vfs/memxattr/BUILD b/pkg/sentry/vfs/memxattr/BUILD
index d8c4d27b9..49127896c 100644
--- a/pkg/sentry/vfs/memxattr/BUILD
+++ b/pkg/sentry/vfs/memxattr/BUILD
@@ -8,6 +8,8 @@ go_library(
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
+ "//pkg/errors/linuxerr",
+ "//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
"//pkg/sync",
"//pkg/syserror",
diff --git a/pkg/sentry/vfs/memxattr/xattr.go b/pkg/sentry/vfs/memxattr/xattr.go
index 638b5d830..efc9c6a5d 100644
--- a/pkg/sentry/vfs/memxattr/xattr.go
+++ b/pkg/sentry/vfs/memxattr/xattr.go
@@ -17,7 +17,11 @@
package memxattr
import (
+ "strings"
+
"gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
+ "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserror"
@@ -26,6 +30,9 @@ import (
// SimpleExtendedAttributes implements extended attributes using a map of
// names to values.
//
+// SimpleExtendedAttributes calls vfs.CheckXattrPermissions, so callers are not
+// required to do so.
+//
// +stateify savable
type SimpleExtendedAttributes struct {
// mu protects the below fields.
@@ -34,12 +41,16 @@ type SimpleExtendedAttributes struct {
}
// GetXattr returns the value at 'name'.
-func (x *SimpleExtendedAttributes) GetXattr(opts *vfs.GetXattrOptions) (string, error) {
+func (x *SimpleExtendedAttributes) GetXattr(creds *auth.Credentials, mode linux.FileMode, kuid auth.KUID, opts *vfs.GetXattrOptions) (string, error) {
+ if err := vfs.CheckXattrPermissions(creds, vfs.MayRead, mode, kuid, opts.Name); err != nil {
+ return "", err
+ }
+
x.mu.RLock()
value, ok := x.xattrs[opts.Name]
x.mu.RUnlock()
if !ok {
- return "", syserror.ENODATA
+ return "", linuxerr.ENODATA
}
// Check that the size of the buffer provided in getxattr(2) is large enough
// to contain the value.
@@ -50,12 +61,16 @@ func (x *SimpleExtendedAttributes) GetXattr(opts *vfs.GetXattrOptions) (string,
}
// SetXattr sets 'value' at 'name'.
-func (x *SimpleExtendedAttributes) SetXattr(opts *vfs.SetXattrOptions) error {
+func (x *SimpleExtendedAttributes) SetXattr(creds *auth.Credentials, mode linux.FileMode, kuid auth.KUID, opts *vfs.SetXattrOptions) error {
+ if err := vfs.CheckXattrPermissions(creds, vfs.MayWrite, mode, kuid, opts.Name); err != nil {
+ return err
+ }
+
x.mu.Lock()
defer x.mu.Unlock()
if x.xattrs == nil {
if opts.Flags&linux.XATTR_REPLACE != 0 {
- return syserror.ENODATA
+ return linuxerr.ENODATA
}
x.xattrs = make(map[string]string)
}
@@ -65,7 +80,7 @@ func (x *SimpleExtendedAttributes) SetXattr(opts *vfs.SetXattrOptions) error {
return syserror.EEXIST
}
if !ok && opts.Flags&linux.XATTR_REPLACE != 0 {
- return syserror.ENODATA
+ return linuxerr.ENODATA
}
x.xattrs[opts.Name] = opts.Value
@@ -73,12 +88,19 @@ func (x *SimpleExtendedAttributes) SetXattr(opts *vfs.SetXattrOptions) error {
}
// ListXattr returns all names in xattrs.
-func (x *SimpleExtendedAttributes) ListXattr(size uint64) ([]string, error) {
+func (x *SimpleExtendedAttributes) ListXattr(creds *auth.Credentials, size uint64) ([]string, error) {
// Keep track of the size of the buffer needed in listxattr(2) for the list.
listSize := 0
x.mu.RLock()
names := make([]string, 0, len(x.xattrs))
+ haveCap := creds.HasCapability(linux.CAP_SYS_ADMIN)
for n := range x.xattrs {
+ // Hide extended attributes in the "trusted" namespace from
+ // non-privileged users. This is consistent with Linux's
+ // fs/xattr.c:simple_xattr_list().
+ if !haveCap && strings.HasPrefix(n, linux.XATTR_TRUSTED_PREFIX) {
+ continue
+ }
names = append(names, n)
// Add one byte per null terminator.
listSize += len(n) + 1
@@ -91,11 +113,15 @@ func (x *SimpleExtendedAttributes) ListXattr(size uint64) ([]string, error) {
}
// RemoveXattr removes the xattr at 'name'.
-func (x *SimpleExtendedAttributes) RemoveXattr(name string) error {
+func (x *SimpleExtendedAttributes) RemoveXattr(creds *auth.Credentials, mode linux.FileMode, kuid auth.KUID, name string) error {
+ if err := vfs.CheckXattrPermissions(creds, vfs.MayWrite, mode, kuid, name); err != nil {
+ return err
+ }
+
x.mu.Lock()
defer x.mu.Unlock()
if _, ok := x.xattrs[name]; !ok {
- return syserror.ENODATA
+ return linuxerr.ENODATA
}
delete(x.xattrs, name)
return nil
diff --git a/pkg/sentry/vfs/mount.go b/pkg/sentry/vfs/mount.go
index 82fd382c2..4d6b59a26 100644
--- a/pkg/sentry/vfs/mount.go
+++ b/pkg/sentry/vfs/mount.go
@@ -24,6 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/refsvfs2"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/syserror"
@@ -159,7 +160,7 @@ func (vfs *VirtualFilesystem) NewMountNamespace(ctx context.Context, creds *auth
rft := vfs.getFilesystemType(fsTypeName)
if rft == nil {
ctx.Warningf("Unknown filesystem type: %s", fsTypeName)
- return nil, syserror.ENODEV
+ return nil, linuxerr.ENODEV
}
fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions)
if err != nil {
@@ -192,10 +193,10 @@ func (vfs *VirtualFilesystem) NewDisconnectedMount(fs *Filesystem, root *Dentry,
func (vfs *VirtualFilesystem) MountDisconnected(ctx context.Context, creds *auth.Credentials, source string, fsTypeName string, opts *MountOptions) (*Mount, error) {
rft := vfs.getFilesystemType(fsTypeName)
if rft == nil {
- return nil, syserror.ENODEV
+ return nil, linuxerr.ENODEV
}
if !opts.InternalMount && !rft.opts.AllowUserMount {
- return nil, syserror.ENODEV
+ return nil, linuxerr.ENODEV
}
fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions)
if err != nil {
@@ -220,7 +221,7 @@ func (vfs *VirtualFilesystem) ConnectMountAt(ctx context.Context, creds *auth.Cr
vdDentry := vd.dentry
vdDentry.mu.Lock()
for {
- if vdDentry.dead {
+ if vd.mount.umounted || vdDentry.dead {
vdDentry.mu.Unlock()
vfs.mountMu.Unlock()
vd.DecRef(ctx)
@@ -284,7 +285,7 @@ func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentia
// UmountAt removes the Mount at the given path.
func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, opts *UmountOptions) error {
if opts.Flags&^(linux.MNT_FORCE|linux.MNT_DETACH) != 0 {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// MNT_FORCE is currently unimplemented except for the permission check.
@@ -292,7 +293,7 @@ func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credenti
// namespace, and not in the owner user namespace for the target mount. See
// fs/namespace.c:SYSCALL_DEFINE2(umount, ...)
if opts.Flags&linux.MNT_FORCE != 0 && creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, creds.UserNamespace.Root()) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
vd, err := vfs.GetDentryAt(ctx, creds, pop, &GetDentryOptions{})
@@ -301,19 +302,19 @@ func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credenti
}
defer vd.DecRef(ctx)
if vd.dentry != vd.mount.root {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
vfs.mountMu.Lock()
if mntns := MountNamespaceFromContext(ctx); mntns != nil {
defer mntns.DecRef(ctx)
if mntns != vd.mount.ns {
vfs.mountMu.Unlock()
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
if vd.mount == vd.mount.ns.root {
vfs.mountMu.Unlock()
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
}
@@ -326,7 +327,7 @@ func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credenti
if len(vd.mount.children) != 0 {
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
// We are holding a reference on vd.mount.
expectedRefs := int64(1)
@@ -336,7 +337,7 @@ func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credenti
if atomic.LoadInt64(&vd.mount.refs)&^math.MinInt64 != expectedRefs { // mask out MSB
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
}
vdsToDecRef, mountsToDecRef := vfs.umountRecursiveLocked(vd.mount, &umountRecursiveOptions{
@@ -710,7 +711,7 @@ func (vfs *VirtualFilesystem) SetMountReadOnly(mnt *Mount, ro bool) error {
func (mnt *Mount) CheckBeginWrite() error {
if atomic.AddInt64(&mnt.writers, 1) < 0 {
atomic.AddInt64(&mnt.writers, -1)
- return syserror.EROFS
+ return linuxerr.EROFS
}
return nil
}
@@ -728,7 +729,7 @@ func (mnt *Mount) setReadOnlyLocked(ro bool) error {
}
if ro {
if !atomic.CompareAndSwapInt64(&mnt.writers, 0, math.MinInt64) {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return nil
}
diff --git a/pkg/sentry/vfs/opath.go b/pkg/sentry/vfs/opath.go
index e9651b631..da0b33b79 100644
--- a/pkg/sentry/vfs/opath.go
+++ b/pkg/sentry/vfs/opath.go
@@ -17,10 +17,10 @@ package vfs
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/usermem"
)
@@ -40,77 +40,77 @@ func (fd *opathFD) Release(context.Context) {
// Allocate implements FileDescriptionImpl.Allocate.
func (fd *opathFD) Allocate(ctx context.Context, mode, offset, length uint64) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// PRead implements FileDescriptionImpl.PRead.
func (fd *opathFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts ReadOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Read implements FileDescriptionImpl.Read.
func (fd *opathFD) Read(ctx context.Context, dst usermem.IOSequence, opts ReadOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// PWrite implements FileDescriptionImpl.PWrite.
func (fd *opathFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts WriteOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Write implements FileDescriptionImpl.Write.
func (fd *opathFD) Write(ctx context.Context, src usermem.IOSequence, opts WriteOptions) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// Ioctl implements FileDescriptionImpl.Ioctl.
func (fd *opathFD) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// IterDirents implements FileDescriptionImpl.IterDirents.
func (fd *opathFD) IterDirents(ctx context.Context, cb IterDirentsCallback) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// Seek implements FileDescriptionImpl.Seek.
func (fd *opathFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
- return 0, syserror.EBADF
+ return 0, linuxerr.EBADF
}
// ConfigureMMap implements FileDescriptionImpl.ConfigureMMap.
func (fd *opathFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// ListXattr implements FileDescriptionImpl.ListXattr.
func (fd *opathFD) ListXattr(ctx context.Context, size uint64) ([]string, error) {
- return nil, syserror.EBADF
+ return nil, linuxerr.EBADF
}
// GetXattr implements FileDescriptionImpl.GetXattr.
func (fd *opathFD) GetXattr(ctx context.Context, opts GetXattrOptions) (string, error) {
- return "", syserror.EBADF
+ return "", linuxerr.EBADF
}
// SetXattr implements FileDescriptionImpl.SetXattr.
func (fd *opathFD) SetXattr(ctx context.Context, opts SetXattrOptions) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// RemoveXattr implements FileDescriptionImpl.RemoveXattr.
func (fd *opathFD) RemoveXattr(ctx context.Context, name string) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// Sync implements FileDescriptionImpl.Sync.
func (fd *opathFD) Sync(ctx context.Context) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// SetStat implements FileDescriptionImpl.SetStat.
func (fd *opathFD) SetStat(ctx context.Context, opts SetStatOptions) error {
- return syserror.EBADF
+ return linuxerr.EBADF
}
// Stat implements FileDescriptionImpl.Stat.
diff --git a/pkg/sentry/vfs/permissions.go b/pkg/sentry/vfs/permissions.go
index b7704874f..4744514bd 100644
--- a/pkg/sentry/vfs/permissions.go
+++ b/pkg/sentry/vfs/permissions.go
@@ -20,6 +20,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/limits"
"gvisor.dev/gvisor/pkg/syserror"
@@ -77,7 +78,7 @@ func GenericCheckPermissions(creds *auth.Credentials, ats AccessTypes, mode linu
// the caller's user namespace; compare
// kernel/capability.c:privileged_wrt_inode_uidgid().
if !kuid.In(creds.UserNamespace).Ok() || !kgid.In(creds.UserNamespace).Ok() {
- return syserror.EACCES
+ return linuxerr.EACCES
}
// CAP_DAC_READ_SEARCH allows the caller to read and search arbitrary
// directories, and read arbitrary non-directory files.
@@ -94,7 +95,7 @@ func GenericCheckPermissions(creds *auth.Credentials, ats AccessTypes, mode linu
return nil
}
}
- return syserror.EACCES
+ return linuxerr.EACCES
}
// MayLink determines whether creating a hard link to a file with the given
@@ -110,12 +111,12 @@ func MayLink(creds *auth.Credentials, mode linux.FileMode, kuid auth.KUID, kgid
// Only regular files can be hard linked.
if mode.FileType() != linux.S_IFREG {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Setuid files should not get pinned to the filesystem.
if mode&linux.S_ISUID != 0 {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// Executable setgid files should not get pinned to the filesystem, but we
@@ -123,7 +124,7 @@ func MayLink(creds *auth.Credentials, mode linux.FileMode, kuid auth.KUID, kgid
// Hardlinking to unreadable or unwritable sources is dangerous.
if err := GenericCheckPermissions(creds, MayRead|MayWrite, mode, kuid, kgid); err != nil {
- return syserror.EPERM
+ return linuxerr.EPERM
}
return nil
}
@@ -199,7 +200,7 @@ func CheckSetStat(ctx context.Context, creds *auth.Credentials, opts *SetStatOpt
}
if stat.Mask&linux.STATX_MODE != 0 {
if !CanActAsOwner(creds, kuid) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
// TODO(b/30815691): "If the calling process is not privileged (Linux:
// does not have the CAP_FSETID capability), and the group of the file
@@ -210,13 +211,13 @@ func CheckSetStat(ctx context.Context, creds *auth.Credentials, opts *SetStatOpt
if stat.Mask&linux.STATX_UID != 0 {
if !((creds.EffectiveKUID == kuid && auth.KUID(stat.UID) == kuid) ||
HasCapabilityOnFile(creds, linux.CAP_CHOWN, kuid, kgid)) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
if stat.Mask&linux.STATX_GID != 0 {
if !((creds.EffectiveKUID == kuid && creds.InGroup(auth.KGID(stat.GID))) ||
HasCapabilityOnFile(creds, linux.CAP_CHOWN, kuid, kgid)) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
if opts.NeedWritePerm && !creds.HasCapability(linux.CAP_DAC_OVERRIDE) {
@@ -229,7 +230,7 @@ func CheckSetStat(ctx context.Context, creds *auth.Credentials, opts *SetStatOpt
if (stat.Mask&linux.STATX_ATIME != 0 && stat.Atime.Nsec != linux.UTIME_NOW) ||
(stat.Mask&linux.STATX_MTIME != 0 && stat.Mtime.Nsec != linux.UTIME_NOW) ||
(stat.Mask&linux.STATX_CTIME != 0 && stat.Ctime.Nsec != linux.UTIME_NOW) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
if err := GenericCheckPermissions(creds, MayWrite, mode, kuid, kgid); err != nil {
return err
@@ -252,7 +253,7 @@ func CheckDeleteSticky(creds *auth.Credentials, parentMode linux.FileMode, paren
HasCapabilityOnFile(creds, linux.CAP_FOWNER, childKUID, childKGID) {
return nil
}
- return syserror.EPERM
+ return linuxerr.EPERM
}
// CanActAsOwner returns true if creds can act as the owner of a file with the
@@ -306,9 +307,9 @@ func CheckXattrPermissions(creds *auth.Credentials, ats AccessTypes, mode linux.
return nil
}
if ats.MayWrite() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
- return syserror.ENODATA
+ return linuxerr.ENODATA
case strings.HasPrefix(name, linux.XATTR_USER_PREFIX):
// In the user.* namespace, only regular files and directories can have
// extended attributes. For sticky directories, only the owner and
@@ -316,12 +317,12 @@ func CheckXattrPermissions(creds *auth.Credentials, ats AccessTypes, mode linux.
filetype := mode.FileType()
if filetype != linux.ModeRegular && filetype != linux.ModeDirectory {
if ats.MayWrite() {
- return syserror.EPERM
+ return linuxerr.EPERM
}
- return syserror.ENODATA
+ return linuxerr.ENODATA
}
if filetype == linux.ModeDirectory && mode&linux.ModeSticky != 0 && ats.MayWrite() && !CanActAsOwner(creds, kuid) {
- return syserror.EPERM
+ return linuxerr.EPERM
}
}
return nil
diff --git a/pkg/sentry/vfs/resolving_path.go b/pkg/sentry/vfs/resolving_path.go
index 97b898aba..6f58f33ce 100644
--- a/pkg/sentry/vfs/resolving_path.go
+++ b/pkg/sentry/vfs/resolving_path.go
@@ -19,6 +19,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sync"
@@ -327,7 +328,7 @@ func (rp *ResolvingPath) ShouldFollowSymlink() bool {
// Postconditions: If HandleSymlink returns a nil error, then !rp.Done().
func (rp *ResolvingPath) HandleSymlink(target string) error {
if rp.symlinks >= linux.MaxSymlinkTraversals {
- return syserror.ELOOP
+ return linuxerr.ELOOP
}
if len(target) == 0 {
return syserror.ENOENT
@@ -377,7 +378,7 @@ func (rp *ResolvingPath) relpathPrepend(path fspath.Path) {
// Preconditions: !rp.Done().
func (rp *ResolvingPath) HandleJump(target VirtualDentry) error {
if rp.symlinks >= linux.MaxSymlinkTraversals {
- return syserror.ELOOP
+ return linuxerr.ELOOP
}
rp.symlinks++
// Consume the path component that represented the magic link.
diff --git a/pkg/sentry/vfs/vfs.go b/pkg/sentry/vfs/vfs.go
index 87fdcf403..cb9ed81d8 100644
--- a/pkg/sentry/vfs/vfs.go
+++ b/pkg/sentry/vfs/vfs.go
@@ -42,6 +42,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/fsmetric"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -285,7 +286,7 @@ func (vfs *VirtualFilesystem) LinkAt(ctx context.Context, creds *auth.Credential
if newpop.FollowFinalSymlink {
oldVD.DecRef(ctx)
ctx.Warningf("VirtualFilesystem.LinkAt: file creation paths can't follow final symlink")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
rp := vfs.getResolvingPath(creds, newpop)
@@ -321,7 +322,7 @@ func (vfs *VirtualFilesystem) MkdirAt(ctx context.Context, creds *auth.Credentia
}
if pop.FollowFinalSymlink {
ctx.Warningf("VirtualFilesystem.MkdirAt: file creation paths can't follow final symlink")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// "Under Linux, apart from the permission bits, the S_ISVTX mode bit is
// also honored." - mkdir(2)
@@ -359,7 +360,7 @@ func (vfs *VirtualFilesystem) MknodAt(ctx context.Context, creds *auth.Credentia
}
if pop.FollowFinalSymlink {
ctx.Warningf("VirtualFilesystem.MknodAt: file creation paths can't follow final symlink")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
rp := vfs.getResolvingPath(creds, pop)
@@ -402,13 +403,13 @@ func (vfs *VirtualFilesystem) OpenAt(ctx context.Context, creds *auth.Credential
// filesystem implementations that do not support it).
if opts.Flags&linux.O_TMPFILE != 0 {
if opts.Flags&linux.O_DIRECTORY == 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
if opts.Flags&linux.O_CREAT != 0 {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
if opts.Flags&linux.O_ACCMODE == linux.O_RDONLY {
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
}
// O_PATH causes most other flags to be ignored.
@@ -448,7 +449,7 @@ func (vfs *VirtualFilesystem) OpenAt(ctx context.Context, creds *auth.Credential
if opts.FileExec {
if fd.Mount().Flags.NoExec {
fd.DecRef(ctx)
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
// Only a regular file can be executed.
@@ -459,7 +460,7 @@ func (vfs *VirtualFilesystem) OpenAt(ctx context.Context, creds *auth.Credential
}
if stat.Mask&linux.STATX_TYPE == 0 || stat.Mode&linux.S_IFMT != linux.S_IFREG {
fd.DecRef(ctx)
- return nil, syserror.EACCES
+ return nil, linuxerr.EACCES
}
}
@@ -493,13 +494,13 @@ func (vfs *VirtualFilesystem) ReadlinkAt(ctx context.Context, creds *auth.Creden
func (vfs *VirtualFilesystem) RenameAt(ctx context.Context, creds *auth.Credentials, oldpop, newpop *PathOperation, opts *RenameOptions) error {
if !oldpop.Path.Begin.Ok() {
if oldpop.Path.Absolute {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return syserror.ENOENT
}
if oldpop.FollowFinalSymlink {
ctx.Warningf("VirtualFilesystem.RenameAt: source path can't follow final symlink")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
oldParentVD, oldName, err := vfs.getParentDirAndName(ctx, creds, oldpop)
@@ -508,20 +509,20 @@ func (vfs *VirtualFilesystem) RenameAt(ctx context.Context, creds *auth.Credenti
}
if oldName == "." || oldName == ".." {
oldParentVD.DecRef(ctx)
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
if !newpop.Path.Begin.Ok() {
oldParentVD.DecRef(ctx)
if newpop.Path.Absolute {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return syserror.ENOENT
}
if newpop.FollowFinalSymlink {
oldParentVD.DecRef(ctx)
ctx.Warningf("VirtualFilesystem.RenameAt: destination path can't follow final symlink")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
rp := vfs.getResolvingPath(creds, newpop)
@@ -555,13 +556,13 @@ func (vfs *VirtualFilesystem) RmdirAt(ctx context.Context, creds *auth.Credentia
// pop.Path should not be empty in operations that create/delete files.
// This is consistent with unlinkat(dirfd, "", AT_REMOVEDIR).
if pop.Path.Absolute {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return syserror.ENOENT
}
if pop.FollowFinalSymlink {
ctx.Warningf("VirtualFilesystem.RmdirAt: file deletion paths can't follow final symlink")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
rp := vfs.getResolvingPath(creds, pop)
@@ -644,7 +645,7 @@ func (vfs *VirtualFilesystem) SymlinkAt(ctx context.Context, creds *auth.Credent
}
if pop.FollowFinalSymlink {
ctx.Warningf("VirtualFilesystem.SymlinkAt: file creation paths can't follow final symlink")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
rp := vfs.getResolvingPath(creds, pop)
@@ -672,13 +673,13 @@ func (vfs *VirtualFilesystem) UnlinkAt(ctx context.Context, creds *auth.Credenti
// pop.Path should not be empty in operations that create/delete files.
// This is consistent with unlinkat(dirfd, "", 0).
if pop.Path.Absolute {
- return syserror.EBUSY
+ return linuxerr.EBUSY
}
return syserror.ENOENT
}
if pop.FollowFinalSymlink {
ctx.Warningf("VirtualFilesystem.UnlinkAt: file deletion paths can't follow final symlink")
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
rp := vfs.getResolvingPath(creds, pop)
@@ -731,8 +732,8 @@ func (vfs *VirtualFilesystem) ListXattrAt(ctx context.Context, creds *auth.Crede
rp.Release(ctx)
return names, nil
}
- if err == syserror.ENOTSUP {
- // Linux doesn't actually return ENOTSUP in this case; instead,
+ if linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
+ // Linux doesn't actually return EOPNOTSUPP in this case; instead,
// fs/xattr.c:vfs_listxattr() falls back to allowing the security
// subsystem to return security extended attributes, which by
// default don't exist.
@@ -830,14 +831,14 @@ func (vfs *VirtualFilesystem) MkdirAllAt(ctx context.Context, currentPath string
Path: fspath.Parse(currentPath),
}
stat, err := vfs.StatAt(ctx, creds, pop, &StatOptions{Mask: linux.STATX_TYPE})
- switch err {
- case nil:
+ switch {
+ case err == nil:
if stat.Mask&linux.STATX_TYPE == 0 || stat.Mode&linux.FileTypeMask != linux.ModeDirectory {
return syserror.ENOTDIR
}
// Directory already exists.
return nil
- case syserror.ENOENT:
+ case linuxerr.Equals(linuxerr.ENOENT, err):
// Expected, we will create the dir.
default:
return fmt.Errorf("stat failed for %q during directory creation: %w", currentPath, err)
@@ -871,7 +872,7 @@ func (vfs *VirtualFilesystem) MakeSyntheticMountpoint(ctx context.Context, targe
Root: root,
Start: root,
Path: fspath.Parse(target),
- }, mkdirOpts); err != nil && err != syserror.EEXIST {
+ }, mkdirOpts); err != nil && !linuxerr.Equals(linuxerr.EEXIST, err) {
return fmt.Errorf("failed to create mountpoint %q: %w", target, err)
}
return nil
diff --git a/pkg/sentry/watchdog/watchdog.go b/pkg/sentry/watchdog/watchdog.go
index dfe85f31d..e8f7d1f01 100644
--- a/pkg/sentry/watchdog/watchdog.go
+++ b/pkg/sentry/watchdog/watchdog.go
@@ -77,11 +77,6 @@ var DefaultOpts = Opts{
// trigger it.
const descheduleThreshold = 1 * time.Second
-var (
- stuckStartup = metric.MustCreateNewUint64Metric("/watchdog/stuck_startup_detected", true /* sync */, "Incremented once on startup watchdog timeout")
- stuckTasks = metric.MustCreateNewUint64Metric("/watchdog/stuck_tasks_detected", true /* sync */, "Cumulative count of stuck tasks detected")
-)
-
// Amount of time to wait before dumping the stack to the log again when the same task(s) remains stuck.
var stackDumpSameTaskPeriod = time.Minute
@@ -115,14 +110,14 @@ func (a *Action) Get() interface{} {
}
// String returns Action's string representation.
-func (a *Action) String() string {
- switch *a {
+func (a Action) String() string {
+ switch a {
case LogWarning:
return "logWarning"
case Panic:
return "panic"
default:
- panic(fmt.Sprintf("Invalid watchdog action: %d", *a))
+ panic(fmt.Sprintf("Invalid watchdog action: %d", a))
}
}
@@ -242,7 +237,6 @@ func (w *Watchdog) waitForStart() {
return
}
- stuckStartup.Increment()
metric.WeirdnessMetric.Increment("watchdog_stuck_startup")
var buf bytes.Buffer
@@ -316,7 +310,6 @@ func (w *Watchdog) runTurn() {
// unless they are surrounded by
// Task.UninterruptibleSleepStart/Finish.
tc = &offender{lastUpdateTime: lastUpdateTime}
- stuckTasks.Increment()
metric.WeirdnessMetric.Increment("watchdog_stuck_tasks")
newTaskFound = true
}
diff --git a/pkg/shim/BUILD b/pkg/shim/BUILD
index fd6127b97..367765209 100644
--- a/pkg/shim/BUILD
+++ b/pkg/shim/BUILD
@@ -6,6 +6,7 @@ go_library(
name = "shim",
srcs = [
"api.go",
+ "debug.go",
"epoll.go",
"options.go",
"service.go",
diff --git a/pkg/shim/debug.go b/pkg/shim/debug.go
new file mode 100644
index 000000000..49f01990e
--- /dev/null
+++ b/pkg/shim/debug.go
@@ -0,0 +1,48 @@
+// Copyright 2021 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
+//
+// https://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 shim
+
+import (
+ "os"
+ "os/signal"
+ "runtime"
+ "sync"
+ "syscall"
+
+ "github.com/containerd/containerd/log"
+)
+
+var once sync.Once
+
+func setDebugSigHandler() {
+ once.Do(func() {
+ dumpCh := make(chan os.Signal, 1)
+ signal.Notify(dumpCh, syscall.SIGUSR2)
+ go func() {
+ buf := make([]byte, 10240)
+ for range dumpCh {
+ for {
+ n := runtime.Stack(buf, true)
+ if n >= len(buf) {
+ buf = make([]byte, 2*len(buf))
+ continue
+ }
+ log.L.Debugf("User requested stack trace:\n%s", buf[:n])
+ }
+ }
+ }()
+ log.L.Debugf("For full process dump run: kill -%d %d", syscall.SIGUSR2, os.Getpid())
+ })
+}
diff --git a/pkg/shim/proc/BUILD b/pkg/shim/proc/BUILD
index 544bdc170..c8527a6d9 100644
--- a/pkg/shim/proc/BUILD
+++ b/pkg/shim/proc/BUILD
@@ -20,7 +20,9 @@ go_library(
"//shim:__subpackages__",
],
deps = [
+ "//pkg/cleanup",
"//pkg/shim/runsc",
+ "//pkg/shim/utils",
"@com_github_containerd_console//:go_default_library",
"@com_github_containerd_containerd//errdefs:go_default_library",
"@com_github_containerd_containerd//log:go_default_library",
diff --git a/pkg/shim/proc/deleted_state.go b/pkg/shim/proc/deleted_state.go
index d9b970c4d..b0bbe4d7e 100644
--- a/pkg/shim/proc/deleted_state.go
+++ b/pkg/shim/proc/deleted_state.go
@@ -22,28 +22,38 @@ import (
"github.com/containerd/console"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/pkg/process"
+ runc "github.com/containerd/go-runc"
)
type deletedState struct{}
-func (*deletedState) Resize(ws console.WinSize) error {
- return fmt.Errorf("cannot resize a deleted process.ss")
+func (*deletedState) Resize(console.WinSize) error {
+ return fmt.Errorf("cannot resize a deleted container/process")
}
-func (*deletedState) Start(ctx context.Context) error {
- return fmt.Errorf("cannot start a deleted process.ss")
+func (*deletedState) Start(context.Context) error {
+ return fmt.Errorf("cannot start a deleted container/process")
}
-func (*deletedState) Delete(ctx context.Context) error {
- return fmt.Errorf("cannot delete a deleted process.ss: %w", errdefs.ErrNotFound)
+func (*deletedState) Delete(context.Context) error {
+ return fmt.Errorf("cannot delete a deleted container/process: %w", errdefs.ErrNotFound)
}
-func (*deletedState) Kill(ctx context.Context, sig uint32, all bool) error {
- return fmt.Errorf("cannot kill a deleted process.ss: %w", errdefs.ErrNotFound)
+func (*deletedState) Kill(_ context.Context, signal uint32, _ bool) error {
+ return handleStoppedKill(signal)
}
-func (*deletedState) SetExited(status int) {}
+func (*deletedState) SetExited(int) {}
-func (*deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (process.Process, error) {
+func (*deletedState) Exec(context.Context, string, *ExecConfig) (process.Process, error) {
return nil, fmt.Errorf("cannot exec in a deleted state")
}
+
+func (s *deletedState) State(context.Context) (string, error) {
+ // There is no "deleted" state, closest one is stopped.
+ return "stopped", nil
+}
+
+func (s *deletedState) Stats(context.Context, string) (*runc.Stats, error) {
+ return nil, fmt.Errorf("cannot stat a stopped container/process")
+}
diff --git a/pkg/shim/proc/exec.go b/pkg/shim/proc/exec.go
index e7968d9d5..e0f2ae6fa 100644
--- a/pkg/shim/proc/exec.go
+++ b/pkg/shim/proc/exec.go
@@ -26,11 +26,13 @@ import (
"github.com/containerd/console"
"github.com/containerd/containerd/errdefs"
+ "github.com/containerd/containerd/log"
"github.com/containerd/containerd/pkg/stdio"
"github.com/containerd/fifo"
runc "github.com/containerd/go-runc"
specs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/cleanup"
"gvisor.dev/gvisor/pkg/shim/runsc"
)
@@ -92,6 +94,12 @@ func (e *execProcess) SetExited(status int) {
}
func (e *execProcess) setExited(status int) {
+ if !e.exited.IsZero() {
+ log.L.Debugf("Exec: status already set to %d, ignoring status: %d", e.status, status)
+ return
+ }
+
+ log.L.Debugf("Exec: setting status: %d", status)
e.status = status
e.exited = time.Now()
e.parent.Platform.ShutdownConsole(context.Background(), e.console)
@@ -105,7 +113,7 @@ func (e *execProcess) Delete(ctx context.Context) error {
return e.execState.Delete(ctx)
}
-func (e *execProcess) delete(ctx context.Context) error {
+func (e *execProcess) delete() error {
e.wg.Wait()
if e.io != nil {
for _, c := range e.closers {
@@ -113,12 +121,6 @@ func (e *execProcess) delete(ctx context.Context) error {
}
e.io.Close()
}
- pidfile := filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id))
- // silently ignore error
- os.Remove(pidfile)
- internalPidfile := filepath.Join(e.path, fmt.Sprintf("%s-internal.pid", e.id))
- // silently ignore error
- os.Remove(internalPidfile)
return nil
}
@@ -145,16 +147,13 @@ func (e *execProcess) Kill(ctx context.Context, sig uint32, _ bool) error {
func (e *execProcess) kill(ctx context.Context, sig uint32, _ bool) error {
internalPid := e.internalPid
- if internalPid != 0 {
- if err := e.parent.runtime.Kill(ctx, e.parent.id, int(sig), &runsc.KillOpts{
- Pid: internalPid,
- }); err != nil {
- // If this returns error, consider the process has
- // already stopped.
- //
- // TODO: Fix after signal handling is fixed.
- return fmt.Errorf("%s: %w", err.Error(), errdefs.ErrNotFound)
- }
+ if internalPid == 0 {
+ return nil
+ }
+
+ opts := runsc.KillOpts{Pid: internalPid}
+ if err := e.parent.runtime.Kill(ctx, e.parent.id, int(sig), &opts); err != nil {
+ return fmt.Errorf("%s: %w", err.Error(), errdefs.ErrNotFound)
}
return nil
}
@@ -174,42 +173,53 @@ func (e *execProcess) Start(ctx context.Context) error {
return e.execState.Start(ctx)
}
-func (e *execProcess) start(ctx context.Context) (err error) {
- var (
- socket *runc.Socket
- pidfile = filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id))
- internalPidfile = filepath.Join(e.path, fmt.Sprintf("%s-internal.pid", e.id))
- )
- if e.stdio.Terminal {
- if socket, err = runc.NewTempConsoleSocket(); err != nil {
+func (e *execProcess) start(ctx context.Context) error {
+ var socket *runc.Socket
+
+ switch {
+ case e.stdio.Terminal:
+ s, err := runc.NewTempConsoleSocket()
+ if err != nil {
return fmt.Errorf("failed to create runc console socket: %w", err)
}
- defer socket.Close()
- } else if e.stdio.IsNull() {
- if e.io, err = runc.NewNullIO(); err != nil {
+ defer s.Close()
+ socket = s
+
+ case e.stdio.IsNull():
+ io, err := runc.NewNullIO()
+ if err != nil {
return fmt.Errorf("creating new NULL IO: %w", err)
}
- } else {
- if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID, withConditionalIO(e.stdio)); err != nil {
+ e.io = io
+
+ default:
+ io, err := runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID, withConditionalIO(e.stdio))
+ if err != nil {
return fmt.Errorf("failed to create runc io pipes: %w", err)
}
+ e.io = io
}
+
opts := &runsc.ExecOpts{
- PidFile: pidfile,
- InternalPidFile: internalPidfile,
+ PidFile: filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id)),
+ InternalPidFile: filepath.Join(e.path, fmt.Sprintf("%s-internal.pid", e.id)),
IO: e.io,
Detach: true,
}
+ defer func() {
+ _ = os.Remove(opts.PidFile)
+ _ = os.Remove(opts.InternalPidFile)
+ }()
if socket != nil {
opts.ConsoleSocket = socket
}
+
eventCh := e.parent.Monitor.Subscribe()
- defer func() {
- // Unsubscribe if an error is returned.
- if err != nil {
- e.parent.Monitor.Unsubscribe(eventCh)
- }
- }()
+ cu := cleanup.Make(func() {
+ e.parent.Monitor.Unsubscribe(eventCh)
+ })
+ defer cu.Clean()
+
if err := e.parent.runtime.Exec(ctx, e.parent.id, e.spec, opts); err != nil {
close(e.waitBlock)
return e.parent.runtimeError(err, "OCI runtime exec failed")
@@ -237,6 +247,7 @@ func (e *execProcess) start(ctx context.Context) (err error) {
return fmt.Errorf("failed to start io pipe copy: %w", err)
}
}
+
pid, err := runc.ReadPidFile(opts.PidFile)
if err != nil {
return fmt.Errorf("failed to retrieve OCI runtime exec pid: %w", err)
@@ -247,6 +258,7 @@ func (e *execProcess) start(ctx context.Context) (err error) {
return fmt.Errorf("failed to retrieve OCI runtime exec internal pid: %w", err)
}
e.internalPid = internalPid
+
go func() {
defer e.parent.Monitor.Unsubscribe(eventCh)
for event := range eventCh {
@@ -260,21 +272,25 @@ func (e *execProcess) start(ctx context.Context) (err error) {
}
}
}()
+
+ cu.Release() // cancel cleanup on success.
return nil
}
-func (e *execProcess) Status(ctx context.Context) (string, error) {
+func (e *execProcess) Status(context.Context) (string, error) {
e.mu.Lock()
defer e.mu.Unlock()
// if we don't have a pid then the exec process has just been created
if e.pid == 0 {
return "created", nil
}
- // if we have a pid and it can be signaled, the process is running
- // TODO(random-liu): Use `runsc kill --pid`.
- if err := unix.Kill(e.pid, 0); err == nil {
- return "running", nil
+ // This checks that `runsc exec` process is still running. This process has
+ // the same lifetime as the process executing inside the container. So instead
+ // of calling `runsc kill --pid`, just do a quick check that `runsc exec` is
+ // still running.
+ if err := unix.Kill(e.pid, 0); err != nil {
+ // Can't signal the process, it must have exited.
+ return "stopped", nil
}
- // else if we have a pid but it can nolonger be signaled, it has stopped
- return "stopped", nil
+ return "running", nil
}
diff --git a/pkg/shim/proc/exec_state.go b/pkg/shim/proc/exec_state.go
index 4dcda8b44..8d8ecf541 100644
--- a/pkg/shim/proc/exec_state.go
+++ b/pkg/shim/proc/exec_state.go
@@ -34,18 +34,21 @@ type execCreatedState struct {
p *execProcess
}
-func (s *execCreatedState) transition(name string) error {
- switch name {
- case "running":
+func (s *execCreatedState) name() string {
+ return "created"
+}
+
+func (s *execCreatedState) transition(transition stateTransition) {
+ switch transition {
+ case running:
s.p.execState = &execRunningState{p: s.p}
- case "stopped":
+ case stopped:
s.p.execState = &execStoppedState{p: s.p}
- case "deleted":
+ case deleted:
s.p.execState = &deletedState{}
default:
- return fmt.Errorf("invalid state transition %q to %q", stateName(s), name)
+ panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition))
}
- return nil
}
func (s *execCreatedState) Resize(ws console.WinSize) error {
@@ -56,14 +59,16 @@ func (s *execCreatedState) Start(ctx context.Context) error {
if err := s.p.start(ctx); err != nil {
return err
}
- return s.transition("running")
+ s.transition(running)
+ return nil
}
-func (s *execCreatedState) Delete(ctx context.Context) error {
- if err := s.p.delete(ctx); err != nil {
+func (s *execCreatedState) Delete(context.Context) error {
+ if err := s.p.delete(); err != nil {
return err
}
- return s.transition("deleted")
+ s.transition(deleted)
+ return nil
}
func (s *execCreatedState) Kill(ctx context.Context, sig uint32, all bool) error {
@@ -72,35 +77,35 @@ func (s *execCreatedState) Kill(ctx context.Context, sig uint32, all bool) error
func (s *execCreatedState) SetExited(status int) {
s.p.setExited(status)
-
- if err := s.transition("stopped"); err != nil {
- panic(err)
- }
+ s.transition(stopped)
}
type execRunningState struct {
p *execProcess
}
-func (s *execRunningState) transition(name string) error {
- switch name {
- case "stopped":
+func (s *execRunningState) name() string {
+ return "running"
+}
+
+func (s *execRunningState) transition(transition stateTransition) {
+ switch transition {
+ case stopped:
s.p.execState = &execStoppedState{p: s.p}
default:
- return fmt.Errorf("invalid state transition %q to %q", stateName(s), name)
+ panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition))
}
- return nil
}
func (s *execRunningState) Resize(ws console.WinSize) error {
return s.p.resize(ws)
}
-func (s *execRunningState) Start(ctx context.Context) error {
+func (s *execRunningState) Start(context.Context) error {
return fmt.Errorf("cannot start a running process")
}
-func (s *execRunningState) Delete(ctx context.Context) error {
+func (s *execRunningState) Delete(context.Context) error {
return fmt.Errorf("cannot delete a running process")
}
@@ -110,45 +115,46 @@ func (s *execRunningState) Kill(ctx context.Context, sig uint32, all bool) error
func (s *execRunningState) SetExited(status int) {
s.p.setExited(status)
-
- if err := s.transition("stopped"); err != nil {
- panic(err)
- }
+ s.transition(stopped)
}
type execStoppedState struct {
p *execProcess
}
-func (s *execStoppedState) transition(name string) error {
- switch name {
- case "deleted":
+func (s *execStoppedState) name() string {
+ return "stopped"
+}
+
+func (s *execStoppedState) transition(transition stateTransition) {
+ switch transition {
+ case deleted:
s.p.execState = &deletedState{}
default:
- return fmt.Errorf("invalid state transition %q to %q", stateName(s), name)
+ panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition))
}
- return nil
}
-func (s *execStoppedState) Resize(ws console.WinSize) error {
+func (s *execStoppedState) Resize(console.WinSize) error {
return fmt.Errorf("cannot resize a stopped container")
}
-func (s *execStoppedState) Start(ctx context.Context) error {
+func (s *execStoppedState) Start(context.Context) error {
return fmt.Errorf("cannot start a stopped process")
}
-func (s *execStoppedState) Delete(ctx context.Context) error {
- if err := s.p.delete(ctx); err != nil {
+func (s *execStoppedState) Delete(context.Context) error {
+ if err := s.p.delete(); err != nil {
return err
}
- return s.transition("deleted")
+ s.transition(deleted)
+ return nil
}
-func (s *execStoppedState) Kill(ctx context.Context, sig uint32, all bool) error {
- return s.p.kill(ctx, sig, all)
+func (s *execStoppedState) Kill(_ context.Context, sig uint32, _ bool) error {
+ return handleStoppedKill(sig)
}
-func (s *execStoppedState) SetExited(status int) {
+func (s *execStoppedState) SetExited(int) {
// no op
}
diff --git a/pkg/shim/proc/init.go b/pkg/shim/proc/init.go
index 664465e0d..6bf090813 100644
--- a/pkg/shim/proc/init.go
+++ b/pkg/shim/proc/init.go
@@ -39,6 +39,8 @@ import (
"gvisor.dev/gvisor/pkg/shim/runsc"
)
+const statusStopped = "stopped"
+
// Init represents an initial process for a container.
type Init struct {
wg sync.WaitGroup
@@ -201,10 +203,15 @@ func (p *Init) ExitedAt() time.Time {
func (p *Init) Status(ctx context.Context) (string, error) {
p.mu.Lock()
defer p.mu.Unlock()
+
+ return p.initState.State(ctx)
+}
+
+func (p *Init) state(ctx context.Context) (string, error) {
c, err := p.runtime.State(ctx, p.id)
if err != nil {
if strings.Contains(err.Error(), "does not exist") {
- return "stopped", nil
+ return statusStopped, nil
}
return "", p.runtimeError(err, "OCI runtime state failed")
}
@@ -231,10 +238,7 @@ func (p *Init) start(ctx context.Context) error {
status, err := p.runtime.Wait(context.Background(), p.id)
if err != nil {
log.G(ctx).WithError(err).Errorf("Failed to wait for container %q", p.id)
- // TODO(random-liu): Handle runsc kill error.
- if err := p.killAll(ctx); err != nil {
- log.G(ctx).WithError(err).Errorf("Failed to kill container %q", p.id)
- }
+ p.killAllLocked(ctx)
status = internalErrorCode
}
ExitCh <- Exit{
@@ -255,6 +259,12 @@ func (p *Init) SetExited(status int) {
}
func (p *Init) setExited(status int) {
+ if !p.exited.IsZero() {
+ log.L.Debugf("Status already set to %d, ignoring status: %d", p.status, status)
+ return
+ }
+
+ log.L.Debugf("Setting status: %d", status)
p.exited = time.Now()
p.status = status
p.Platform.ShutdownConsole(context.Background(), p.console)
@@ -270,15 +280,16 @@ func (p *Init) Delete(ctx context.Context) error {
}
func (p *Init) delete(ctx context.Context) error {
- p.killAll(ctx)
+ p.killAllLocked(ctx)
p.wg.Wait()
+
err := p.runtime.Delete(ctx, p.id, nil)
- // ignore errors if a runtime has already deleted the process
- // but we still hold metadata and pipes
- //
- // this is common during a checkpoint, runc will delete the container state
- // after a checkpoint and the container will no longer exist within runc
if err != nil {
+ // ignore errors if a runtime has already deleted the process
+ // but we still hold metadata and pipes
+ //
+ // this is common during a checkpoint, runc will delete the container state
+ // after a checkpoint and the container will no longer exist within runc
if strings.Contains(err.Error(), "does not exist") {
err = nil
} else {
@@ -326,29 +337,24 @@ func (p *Init) Kill(ctx context.Context, signal uint32, all bool) error {
return p.initState.Kill(ctx, signal, all)
}
-func (p *Init) kill(context context.Context, signal uint32, all bool) error {
+func (p *Init) kill(ctx context.Context, signal uint32, all bool) error {
var (
killErr error
backoff = 100 * time.Millisecond
)
- timeout := 1 * time.Second
- for start := time.Now(); time.Now().Sub(start) < timeout; {
- c, err := p.runtime.State(context, p.id)
+ const timeout = time.Second
+ for start := time.Now(); time.Since(start) < timeout; {
+ state, err := p.initState.State(ctx)
if err != nil {
- if strings.Contains(err.Error(), "does not exist") {
- return fmt.Errorf("no such process: %w", errdefs.ErrNotFound)
- }
return p.runtimeError(err, "OCI runtime state failed")
}
// For runsc, signal only works when container is running state.
// If the container is not in running state, directly return
// "no such process"
- if p.convertStatus(c.Status) == "stopped" {
+ if state == statusStopped {
return fmt.Errorf("no such process: %w", errdefs.ErrNotFound)
}
- killErr = p.runtime.Kill(context, p.id, int(signal), &runsc.KillOpts{
- All: all,
- })
+ killErr = p.runtime.Kill(ctx, p.id, int(signal), &runsc.KillOpts{All: all})
if killErr == nil {
return nil
}
@@ -358,22 +364,18 @@ func (p *Init) kill(context context.Context, signal uint32, all bool) error {
return p.runtimeError(killErr, "kill timeout")
}
-// KillAll kills all processes belonging to the init process.
-func (p *Init) KillAll(context context.Context) error {
+// KillAll kills all processes belonging to the init process. If
+// `runsc kill --all` returns error, assume the container has already stopped.
+func (p *Init) KillAll(context context.Context) {
p.mu.Lock()
defer p.mu.Unlock()
- return p.killAll(context)
+ p.killAllLocked(context)
}
-func (p *Init) killAll(context context.Context) error {
- p.runtime.Kill(context, p.id, int(unix.SIGKILL), &runsc.KillOpts{
- All: true,
- })
- // Ignore error handling for `runsc kill --all` for now.
- // * If it doesn't return error, it is good;
- // * If it returns error, consider the container has already stopped.
- // TODO: Fix `runsc kill --all` error handling.
- return nil
+func (p *Init) killAllLocked(context context.Context) {
+ if err := p.runtime.Kill(context, p.id, int(unix.SIGKILL), &runsc.KillOpts{All: true}); err != nil {
+ log.L.Warningf("Ignoring error killing container %q: %v", p.id, err)
+ }
}
// Stdin returns the stdin of the process.
@@ -396,7 +398,6 @@ func (p *Init) Exec(ctx context.Context, path string, r *ExecConfig) (process.Pr
// exec returns a new exec'd process.
func (p *Init) exec(path string, r *ExecConfig) (process.Process, error) {
- // process exec request
var spec specs.Process
if err := json.Unmarshal(r.Spec.Value, &spec); err != nil {
return nil, err
@@ -420,6 +421,17 @@ func (p *Init) exec(path string, r *ExecConfig) (process.Process, error) {
return e, nil
}
+func (p *Init) Stats(ctx context.Context, id string) (*runc.Stats, error) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ return p.initState.Stats(ctx, id)
+}
+
+func (p *Init) stats(ctx context.Context, id string) (*runc.Stats, error) {
+ return p.Runtime().Stats(ctx, id)
+}
+
// Stdio returns the stdio of the process.
func (p *Init) Stdio() stdio.Stdio {
return p.stdio
@@ -444,7 +456,7 @@ func (p *Init) runtimeError(rErr error, msg string) error {
func (p *Init) convertStatus(status string) string {
if status == "created" && !p.Sandbox && p.status == internalErrorCode {
// Treat start failure state for non-root container as stopped.
- return "stopped"
+ return statusStopped
}
return status
}
diff --git a/pkg/shim/proc/init_state.go b/pkg/shim/proc/init_state.go
index 0065fc385..5347ddefe 100644
--- a/pkg/shim/proc/init_state.go
+++ b/pkg/shim/proc/init_state.go
@@ -19,16 +19,40 @@ import (
"context"
"fmt"
- "github.com/containerd/console"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/pkg/process"
+ runc "github.com/containerd/go-runc"
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/shim/utils"
)
+type stateTransition int
+
+const (
+ running stateTransition = iota
+ stopped
+ deleted
+)
+
+func (s stateTransition) String() string {
+ switch s {
+ case running:
+ return "running"
+ case stopped:
+ return "stopped"
+ case deleted:
+ return "deleted"
+ default:
+ panic(fmt.Sprintf("unknown state: %d", s))
+ }
+}
+
type initState interface {
- Resize(console.WinSize) error
Start(context.Context) error
Delete(context.Context) error
Exec(context.Context, string, *ExecConfig) (process.Process, error)
+ State(ctx context.Context) (string, error)
+ Stats(context.Context, string) (*runc.Stats, error)
Kill(context.Context, uint32, bool) error
SetExited(int)
}
@@ -37,22 +61,21 @@ type createdState struct {
p *Init
}
-func (s *createdState) transition(name string) error {
- switch name {
- case "running":
+func (s *createdState) name() string {
+ return "created"
+}
+
+func (s *createdState) transition(transition stateTransition) {
+ switch transition {
+ case running:
s.p.initState = &runningState{p: s.p}
- case "stopped":
- s.p.initState = &stoppedState{p: s.p}
- case "deleted":
+ case stopped:
+ s.p.initState = &stoppedState{process: s.p}
+ case deleted:
s.p.initState = &deletedState{}
default:
- return fmt.Errorf("invalid state transition %q to %q", stateName(s), name)
+ panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition))
}
- return nil
-}
-
-func (s *createdState) Resize(ws console.WinSize) error {
- return s.p.resize(ws)
}
func (s *createdState) Start(ctx context.Context) error {
@@ -66,20 +89,20 @@ func (s *createdState) Start(ctx context.Context) error {
if !s.p.Sandbox {
s.p.io.Close()
s.p.setExited(internalErrorCode)
- if err := s.transition("stopped"); err != nil {
- panic(err)
- }
+ s.transition(stopped)
}
return err
}
- return s.transition("running")
+ s.transition(running)
+ return nil
}
func (s *createdState) Delete(ctx context.Context) error {
if err := s.p.delete(ctx); err != nil {
return err
}
- return s.transition("deleted")
+ s.transition(deleted)
+ return nil
}
func (s *createdState) Kill(ctx context.Context, sig uint32, all bool) error {
@@ -88,40 +111,48 @@ func (s *createdState) Kill(ctx context.Context, sig uint32, all bool) error {
func (s *createdState) SetExited(status int) {
s.p.setExited(status)
-
- if err := s.transition("stopped"); err != nil {
- panic(err)
- }
+ s.transition(stopped)
}
func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (process.Process, error) {
return s.p.exec(path, r)
}
+func (s *createdState) State(ctx context.Context) (string, error) {
+ state, err := s.p.state(ctx)
+ if err == nil && state == statusStopped {
+ s.transition(stopped)
+ }
+ return state, err
+}
+
+func (s *createdState) Stats(ctx context.Context, id string) (*runc.Stats, error) {
+ return s.p.stats(ctx, id)
+}
+
type runningState struct {
p *Init
}
-func (s *runningState) transition(name string) error {
- switch name {
- case "stopped":
- s.p.initState = &stoppedState{p: s.p}
- default:
- return fmt.Errorf("invalid state transition %q to %q", stateName(s), name)
- }
- return nil
+func (s *runningState) name() string {
+ return "running"
}
-func (s *runningState) Resize(ws console.WinSize) error {
- return s.p.resize(ws)
+func (s *runningState) transition(transition stateTransition) {
+ switch transition {
+ case stopped:
+ s.p.initState = &stoppedState{process: s.p}
+ default:
+ panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition))
+ }
}
func (s *runningState) Start(ctx context.Context) error {
- return fmt.Errorf("cannot start a running process.ss")
+ return fmt.Errorf("cannot start a running container")
}
func (s *runningState) Delete(ctx context.Context) error {
- return fmt.Errorf("cannot delete a running process.ss")
+ return fmt.Errorf("cannot delete a running container")
}
func (s *runningState) Kill(ctx context.Context, sig uint32, all bool) error {
@@ -130,53 +161,81 @@ func (s *runningState) Kill(ctx context.Context, sig uint32, all bool) error {
func (s *runningState) SetExited(status int) {
s.p.setExited(status)
+ s.transition(stopped)
+}
+
+func (s *runningState) Exec(_ context.Context, path string, r *ExecConfig) (process.Process, error) {
+ return s.p.exec(path, r)
+}
- if err := s.transition("stopped"); err != nil {
- panic(err)
+func (s *runningState) State(ctx context.Context) (string, error) {
+ state, err := s.p.state(ctx)
+ if err == nil && state == "stopped" {
+ s.transition(stopped)
}
+ return state, err
}
-func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (process.Process, error) {
- return s.p.exec(path, r)
+func (s *runningState) Stats(ctx context.Context, id string) (*runc.Stats, error) {
+ return s.p.stats(ctx, id)
}
type stoppedState struct {
- p *Init
+ process *Init
}
-func (s *stoppedState) transition(name string) error {
- switch name {
- case "deleted":
- s.p.initState = &deletedState{}
- default:
- return fmt.Errorf("invalid state transition %q to %q", stateName(s), name)
- }
- return nil
+func (s *stoppedState) name() string {
+ return "stopped"
}
-func (s *stoppedState) Resize(ws console.WinSize) error {
- return fmt.Errorf("cannot resize a stopped container")
+func (s *stoppedState) transition(transition stateTransition) {
+ switch transition {
+ case deleted:
+ s.process.initState = &deletedState{}
+ default:
+ panic(fmt.Sprintf("invalid state transition %q to %q", s.name(), transition))
+ }
}
-func (s *stoppedState) Start(ctx context.Context) error {
- return fmt.Errorf("cannot start a stopped process.ss")
+func (s *stoppedState) Start(context.Context) error {
+ return fmt.Errorf("cannot start a stopped container")
}
func (s *stoppedState) Delete(ctx context.Context) error {
- if err := s.p.delete(ctx); err != nil {
+ if err := s.process.delete(ctx); err != nil {
return err
}
- return s.transition("deleted")
+ s.transition(deleted)
+ return nil
}
-func (s *stoppedState) Kill(ctx context.Context, sig uint32, all bool) error {
- return errdefs.ToGRPCf(errdefs.ErrNotFound, "process.ss %s not found", s.p.id)
+func (s *stoppedState) Kill(_ context.Context, signal uint32, _ bool) error {
+ return handleStoppedKill(signal)
}
func (s *stoppedState) SetExited(status int) {
- // no op
+ s.process.setExited(status)
}
-func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (process.Process, error) {
+func (s *stoppedState) Exec(context.Context, string, *ExecConfig) (process.Process, error) {
return nil, fmt.Errorf("cannot exec in a stopped state")
}
+
+func (s *stoppedState) State(context.Context) (string, error) {
+ return "stopped", nil
+}
+
+func (s *stoppedState) Stats(context.Context, string) (*runc.Stats, error) {
+ return nil, fmt.Errorf("cannot stat a stopped container")
+}
+
+func handleStoppedKill(signal uint32) error {
+ switch unix.Signal(signal) {
+ case unix.SIGTERM, unix.SIGKILL:
+ // Container is already stopped, so everything inside the container has
+ // already been killed.
+ return nil
+ default:
+ return utils.ErrToGRPCf(errdefs.ErrNotFound, "process not found")
+ }
+}
diff --git a/pkg/shim/proc/proc.go b/pkg/shim/proc/proc.go
index edba3fca5..89ad3f505 100644
--- a/pkg/shim/proc/proc.go
+++ b/pkg/shim/proc/proc.go
@@ -17,23 +17,5 @@
// the sandbox process running the container.
package proc
-import (
- "fmt"
-)
-
// RunscRoot is the path to the root runsc state directory.
const RunscRoot = "/run/containerd/runsc"
-
-func stateName(v interface{}) string {
- switch v.(type) {
- case *runningState, *execRunningState:
- return "running"
- case *createdState, *execCreatedState:
- return "created"
- case *deletedState:
- return "deleted"
- case *stoppedState:
- return "stopped"
- }
- panic(fmt.Errorf("invalid state %v", v))
-}
diff --git a/pkg/shim/runsc/runsc.go b/pkg/shim/runsc/runsc.go
index ff0521d73..888cb0bcb 100644
--- a/pkg/shim/runsc/runsc.go
+++ b/pkg/shim/runsc/runsc.go
@@ -17,6 +17,7 @@
package runsc
import (
+ "bytes"
"context"
"encoding/json"
"fmt"
@@ -73,9 +74,9 @@ type Runsc struct {
// List returns all containers created inside the provided runsc root directory.
func (r *Runsc) List(context context.Context) ([]*runc.Container, error) {
- data, err := cmdOutput(r.command(context, "list", "--format=json"), false)
+ data, stderr, err := cmdOutput(r.command(context, "list", "--format=json"), false)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("%w: %s", err, stderr)
}
var out []*runc.Container
if err := json.Unmarshal(data, &out); err != nil {
@@ -86,9 +87,9 @@ func (r *Runsc) List(context context.Context) ([]*runc.Container, error) {
// State returns the state for the container provided by id.
func (r *Runsc) State(context context.Context, id string) (*runc.Container, error) {
- data, err := cmdOutput(r.command(context, "state", id), true)
+ data, stderr, err := cmdOutput(r.command(context, "state", id), false)
if err != nil {
- return nil, fmt.Errorf("%s: %s", err, data)
+ return nil, fmt.Errorf("%w: %s", err, stderr)
}
var c runc.Container
if err := json.Unmarshal(data, &c); err != nil {
@@ -142,9 +143,9 @@ func (r *Runsc) Create(context context.Context, id, bundle string, opts *CreateO
}
if cmd.Stdout == nil && cmd.Stderr == nil {
- data, err := cmdOutput(cmd, true)
+ out, _, err := cmdOutput(cmd, true)
if err != nil {
- return fmt.Errorf("%s: %s", err, data)
+ return fmt.Errorf("%w: %s", err, out)
}
return nil
}
@@ -168,15 +169,15 @@ func (r *Runsc) Create(context context.Context, id, bundle string, opts *CreateO
}
func (r *Runsc) Pause(context context.Context, id string) error {
- if _, err := cmdOutput(r.command(context, "pause", id), true); err != nil {
- return fmt.Errorf("unable to pause: %w", err)
+ if out, _, err := cmdOutput(r.command(context, "pause", id), true); err != nil {
+ return fmt.Errorf("unable to pause: %w: %s", err, out)
}
return nil
}
func (r *Runsc) Resume(context context.Context, id string) error {
- if _, err := cmdOutput(r.command(context, "resume", id), true); err != nil {
- return fmt.Errorf("unable to resume: %w", err)
+ if out, _, err := cmdOutput(r.command(context, "resume", id), true); err != nil {
+ return fmt.Errorf("unable to resume: %w: %s", err, out)
}
return nil
}
@@ -189,9 +190,9 @@ func (r *Runsc) Start(context context.Context, id string, cio runc.IO) error {
}
if cmd.Stdout == nil && cmd.Stderr == nil {
- data, err := cmdOutput(cmd, true)
+ out, _, err := cmdOutput(cmd, true)
if err != nil {
- return fmt.Errorf("%s: %s", err, data)
+ return fmt.Errorf("%w: %s", err, out)
}
return nil
}
@@ -221,12 +222,10 @@ type waitResult struct {
}
// Wait will wait for a running container, and return its exit status.
-//
-// TODO(random-liu): Add exec process support.
func (r *Runsc) Wait(context context.Context, id string) (int, error) {
- data, err := cmdOutput(r.command(context, "wait", id), true)
+ data, stderr, err := cmdOutput(r.command(context, "wait", id), false)
if err != nil {
- return 0, fmt.Errorf("%s: %s", err, data)
+ return 0, fmt.Errorf("%w: %s", err, stderr)
}
var res waitResult
if err := json.Unmarshal(data, &res); err != nil {
@@ -294,9 +293,9 @@ func (r *Runsc) Exec(context context.Context, id string, spec specs.Process, opt
opts.Set(cmd)
}
if cmd.Stdout == nil && cmd.Stderr == nil {
- data, err := cmdOutput(cmd, true)
+ out, _, err := cmdOutput(cmd, true)
if err != nil {
- return fmt.Errorf("%s: %s", err, data)
+ return fmt.Errorf("%w: %s", err, out)
}
return nil
}
@@ -391,20 +390,12 @@ func (r *Runsc) Kill(context context.Context, id string, sig int, opts *KillOpts
// Stats return the stats for a container like cpu, memory, and I/O.
func (r *Runsc) Stats(context context.Context, id string) (*runc.Stats, error) {
cmd := r.command(context, "events", "--stats", id)
- rd, err := cmd.StdoutPipe()
- if err != nil {
- return nil, err
- }
- ec, err := Monitor.Start(cmd)
+ data, stderr, err := cmdOutput(cmd, false)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("%w: %s", err, stderr)
}
- defer func() {
- rd.Close()
- Monitor.Wait(cmd, ec)
- }()
var e runc.Event
- if err := json.NewDecoder(rd).Decode(&e); err != nil {
+ if err := json.Unmarshal(data, &e); err != nil {
log.L.Debugf("Parsing events error: %v", err)
return nil, err
}
@@ -459,9 +450,9 @@ func (r *Runsc) Events(context context.Context, id string, interval time.Duratio
// Ps lists all the processes inside the container returning their pids.
func (r *Runsc) Ps(context context.Context, id string) ([]int, error) {
- data, err := cmdOutput(r.command(context, "ps", "--format", "json", id), true)
+ data, stderr, err := cmdOutput(r.command(context, "ps", "--format", "json", id), false)
if err != nil {
- return nil, fmt.Errorf("%s: %s", err, data)
+ return nil, fmt.Errorf("%w: %s", err, stderr)
}
var pids []int
if err := json.Unmarshal(data, &pids); err != nil {
@@ -472,9 +463,9 @@ func (r *Runsc) Ps(context context.Context, id string) ([]int, error) {
// Top lists all the processes inside the container returning the full ps data.
func (r *Runsc) Top(context context.Context, id string) (*runc.TopResults, error) {
- data, err := cmdOutput(r.command(context, "ps", "--format", "table", id), true)
+ data, stderr, err := cmdOutput(r.command(context, "ps", "--format", "table", id), false)
if err != nil {
- return nil, fmt.Errorf("%s: %s", err, data)
+ return nil, fmt.Errorf("%w: %s", err, stderr)
}
topResults, err := runc.ParsePSOutput(data)
@@ -517,9 +508,9 @@ func (r *Runsc) runOrError(cmd *exec.Cmd) error {
}
return err
}
- data, err := cmdOutput(cmd, true)
+ out, _, err := cmdOutput(cmd, true)
if err != nil {
- return fmt.Errorf("%s: %s", err, data)
+ return fmt.Errorf("%w: %s", err, out)
}
return nil
}
@@ -540,23 +531,29 @@ func (r *Runsc) command(context context.Context, args ...string) *exec.Cmd {
return cmd
}
-func cmdOutput(cmd *exec.Cmd, combined bool) ([]byte, error) {
- b := getBuf()
- defer putBuf(b)
+func cmdOutput(cmd *exec.Cmd, combined bool) ([]byte, []byte, error) {
+ stdout := getBuf()
+ defer putBuf(stdout)
+ cmd.Stdout = stdout
+ cmd.Stderr = stdout
- cmd.Stdout = b
- if combined {
- cmd.Stderr = b
+ var stderr *bytes.Buffer
+ if !combined {
+ stderr = getBuf()
+ defer putBuf(stderr)
+ cmd.Stderr = stderr
}
ec, err := Monitor.Start(cmd)
if err != nil {
- return nil, err
+ return nil, nil, err
}
status, err := Monitor.Wait(cmd, ec)
if err == nil && status != 0 {
- err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0])
+ err = fmt.Errorf("%q did not terminate sucessfully", cmd.Args[0])
}
-
- return b.Bytes(), err
+ if stderr == nil {
+ return stdout.Bytes(), nil, err
+ }
+ return stdout.Bytes(), stderr.Bytes(), err
}
diff --git a/pkg/shim/service.go b/pkg/shim/service.go
index 1f9adcb65..24e3b7a82 100644
--- a/pkg/shim/service.go
+++ b/pkg/shim/service.go
@@ -81,8 +81,6 @@ const (
// New returns a new shim service that can be used via GRPC.
func New(ctx context.Context, id string, publisher shim.Publisher, cancel func()) (shim.Shim, error) {
- log.L.Debugf("service.New, id: %s", id)
-
var opts shim.Opts
if ctxOpts := ctx.Value(shim.OptsKey{}); ctxOpts != nil {
opts = ctxOpts.(shim.Opts)
@@ -304,8 +302,6 @@ func (s *service) Cleanup(ctx context.Context) (*taskAPI.DeleteResponse, error)
// Create creates a new initial process and container with the underlying OCI
// runtime.
func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*taskAPI.CreateTaskResponse, error) {
- log.L.Debugf("Create, id: %s, bundle: %q", r.ID, r.Bundle)
-
s.mu.Lock()
defer s.mu.Unlock()
@@ -396,6 +392,9 @@ func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*ta
log.L.Debugf("stdout: %s", r.Stdout)
log.L.Debugf("stderr: %s", r.Stderr)
log.L.Debugf("***************************")
+ if log.L.Logger.IsLevelEnabled(logrus.DebugLevel) {
+ setDebugSigHandler()
+ }
}
// Save state before any action is taken to ensure Cleanup() will have all
@@ -453,10 +452,10 @@ func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*ta
}
process, err := newInit(r.Bundle, filepath.Join(r.Bundle, "work"), ns, s.platform, config, &s.opts, st.Rootfs)
if err != nil {
- return nil, errdefs.ToGRPC(err)
+ return nil, utils.ErrToGRPC(err)
}
if err := process.Create(ctx, config); err != nil {
- return nil, errdefs.ToGRPC(err)
+ return nil, utils.ErrToGRPC(err)
}
// Set up OOM notification on the sandbox's cgroup. This is done on
@@ -506,9 +505,6 @@ func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAP
if err != nil {
return nil, err
}
- if p == nil {
- return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
- }
if err := p.Delete(ctx); err != nil {
return nil, err
}
@@ -534,10 +530,10 @@ func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (*typ
p := s.processes[r.ExecID]
s.mu.Unlock()
if p != nil {
- return nil, errdefs.ToGRPCf(errdefs.ErrAlreadyExists, "id %s", r.ExecID)
+ return nil, utils.ErrToGRPCf(errdefs.ErrAlreadyExists, "id %s", r.ExecID)
}
if s.task == nil {
- return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
+ return nil, utils.ErrToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}
process, err := s.task.Exec(ctx, s.bundle, &proc.ExecConfig{
ID: r.ExecID,
@@ -548,7 +544,7 @@ func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (*typ
Spec: r.Spec,
})
if err != nil {
- return nil, errdefs.ToGRPC(err)
+ return nil, utils.ErrToGRPC(err)
}
s.mu.Lock()
s.processes[r.ExecID] = process
@@ -569,7 +565,7 @@ func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*
Height: uint16(r.Height),
}
if err := p.Resize(ws); err != nil {
- return nil, errdefs.ToGRPC(err)
+ return nil, utils.ErrToGRPC(err)
}
return empty, nil
}
@@ -580,10 +576,12 @@ func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.
p, err := s.getProcess(r.ExecID)
if err != nil {
+ log.L.Debugf("State failed to find process: %v", err)
return nil, err
}
st, err := p.Status(ctx)
if err != nil {
+ log.L.Debugf("State failed: %v", err)
return nil, err
}
status := task.StatusUnknown
@@ -596,7 +594,7 @@ func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.
status = task.StatusStopped
}
sio := p.Stdio()
- return &taskAPI.StateResponse{
+ res := &taskAPI.StateResponse{
ID: p.ID(),
Bundle: s.bundle,
Pid: uint32(p.Pid()),
@@ -607,7 +605,9 @@ func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.
Terminal: sio.Terminal,
ExitStatus: uint32(p.ExitStatus()),
ExitedAt: p.ExitedAt(),
- }, nil
+ }
+ log.L.Debugf("State succeeded, response: %+v", res)
+ return res, nil
}
// Pause the container.
@@ -615,7 +615,7 @@ func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*types.Em
log.L.Debugf("Pause, id: %s", r.ID)
if s.task == nil {
log.L.Debugf("Pause error, id: %s: container not created", r.ID)
- return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
+ return nil, utils.ErrToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}
err := s.task.Runtime().Pause(ctx, r.ID)
if err != nil {
@@ -629,7 +629,7 @@ func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*types.
log.L.Debugf("Resume, id: %s", r.ID)
if s.task == nil {
log.L.Debugf("Resume error, id: %s: container not created", r.ID)
- return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
+ return nil, utils.ErrToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}
err := s.task.Runtime().Resume(ctx, r.ID)
if err != nil {
@@ -646,12 +646,11 @@ func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*types.Empt
if err != nil {
return nil, err
}
- if p == nil {
- return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
- }
if err := p.Kill(ctx, r.Signal, r.All); err != nil {
- return nil, errdefs.ToGRPC(err)
+ log.L.Debugf("Kill failed: %v", err)
+ return nil, utils.ErrToGRPC(err)
}
+ log.L.Debugf("Kill succeeded")
return empty, nil
}
@@ -661,7 +660,7 @@ func (s *service) Pids(ctx context.Context, r *taskAPI.PidsRequest) (*taskAPI.Pi
pids, err := s.getContainerPids(ctx, r.ID)
if err != nil {
- return nil, errdefs.ToGRPC(err)
+ return nil, utils.ErrToGRPC(err)
}
var processes []*task.ProcessInfo
for _, pid := range pids {
@@ -707,7 +706,7 @@ func (s *service) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (*type
// Checkpoint checkpoints the container.
func (s *service) Checkpoint(ctx context.Context, r *taskAPI.CheckpointTaskRequest) (*types.Empty, error) {
log.L.Debugf("Checkpoint, id: %s", r.ID)
- return empty, errdefs.ToGRPC(errdefs.ErrNotImplemented)
+ return empty, utils.ErrToGRPC(errdefs.ErrNotImplemented)
}
// Connect returns shim information such as the shim's pid.
@@ -738,9 +737,9 @@ func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.
log.L.Debugf("Stats, id: %s", r.ID)
if s.task == nil {
log.L.Debugf("Stats error, id: %s: container not created", r.ID)
- return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
+ return nil, utils.ErrToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}
- stats, err := s.task.Runtime().Stats(ctx, s.id)
+ stats, err := s.task.Stats(ctx, s.id)
if err != nil {
log.L.Debugf("Stats error, id: %s: %v", r.ID, err)
return nil, err
@@ -812,7 +811,7 @@ func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.
// Update updates a running container.
func (s *service) Update(ctx context.Context, r *taskAPI.UpdateTaskRequest) (*types.Empty, error) {
- return empty, errdefs.ToGRPC(errdefs.ErrNotImplemented)
+ return empty, utils.ErrToGRPC(errdefs.ErrNotImplemented)
}
// Wait waits for a process to exit.
@@ -821,17 +820,17 @@ func (s *service) Wait(ctx context.Context, r *taskAPI.WaitRequest) (*taskAPI.Wa
p, err := s.getProcess(r.ExecID)
if err != nil {
+ log.L.Debugf("Wait failed to find process: %v", err)
return nil, err
}
- if p == nil {
- return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
- }
p.Wait()
- return &taskAPI.WaitResponse{
+ res := &taskAPI.WaitResponse{
ExitStatus: uint32(p.ExitStatus()),
ExitedAt: p.ExitedAt(),
- }, nil
+ }
+ log.L.Debugf("Wait succeeded, response: %+v", res)
+ return res, nil
}
func (s *service) processExits(ctx context.Context) {
@@ -848,10 +847,7 @@ func (s *service) checkProcesses(ctx context.Context, e proc.Exit) {
if ip, ok := p.(*proc.Init); ok {
// Ensure all children are killed.
log.L.Debugf("Container init process exited, killing all container processes")
- if err := ip.KillAll(ctx); err != nil {
- log.G(ctx).WithError(err).WithField("id", ip.ID()).
- Error("failed to kill init's children")
- }
+ ip.KillAll(ctx)
}
p.SetExited(e.Status)
s.events <- &events.TaskExit{
@@ -909,12 +905,17 @@ func (s *service) forward(ctx context.Context, publisher shim.Publisher) {
func (s *service) getProcess(execID string) (process.Process, error) {
s.mu.Lock()
defer s.mu.Unlock()
+
if execID == "" {
+ if s.task == nil {
+ return nil, utils.ErrToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
+ }
return s.task, nil
}
+
p := s.processes[execID]
if p == nil {
- return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process does not exist %s", execID)
+ return nil, utils.ErrToGRPCf(errdefs.ErrNotFound, "process does not exist %s", execID)
}
return p, nil
}
diff --git a/pkg/shim/utils/BUILD b/pkg/shim/utils/BUILD
index 54a0aabb7..2eb82f63c 100644
--- a/pkg/shim/utils/BUILD
+++ b/pkg/shim/utils/BUILD
@@ -6,6 +6,7 @@ go_library(
name = "utils",
srcs = [
"annotations.go",
+ "errors.go",
"utils.go",
"volumes.go",
],
@@ -14,14 +15,23 @@ go_library(
"//shim:__subpackages__",
],
deps = [
+ "@com_github_containerd_containerd//errdefs:go_default_library",
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
+ "@org_golang_google_grpc//codes:go_default_library",
+ "@org_golang_google_grpc//status:go_default_library",
],
)
go_test(
name = "utils_test",
size = "small",
- srcs = ["volumes_test.go"],
+ srcs = [
+ "errors_test.go",
+ "volumes_test.go",
+ ],
library = ":utils",
- deps = ["@com_github_opencontainers_runtime_spec//specs-go:go_default_library"],
+ deps = [
+ "@com_github_containerd_containerd//errdefs:go_default_library",
+ "@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
+ ],
)
diff --git a/pkg/shim/utils/errors.go b/pkg/shim/utils/errors.go
new file mode 100644
index 000000000..971d68c36
--- /dev/null
+++ b/pkg/shim/utils/errors.go
@@ -0,0 +1,74 @@
+// Copyright 2021 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
+//
+// https://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 utils
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "github.com/containerd/containerd/errdefs"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+// ErrToGRPC wraps containerd's ToGRPC error mapper which depends on
+// github.com/pkg/errors to work correctly. Once we upgrade to containerd v1.4,
+// this function can go away and we can use errdefs.ToGRPC directly instead.
+//
+// TODO(gvisor.dev/issue/6232): Remove after upgrading to containerd v1.4
+func ErrToGRPC(err error) error {
+ return errToGRPCMsg(err, err.Error())
+}
+
+// ErrToGRPCf maps the error to grpc error codes, assembling the formatting
+// string and combining it with the target error string.
+//
+// TODO(gvisor.dev/issue/6232): Remove after upgrading to containerd v1.4
+func ErrToGRPCf(err error, format string, args ...interface{}) error {
+ formatted := fmt.Sprintf(format, args...)
+ msg := fmt.Sprintf("%s: %s", formatted, err.Error())
+ return errToGRPCMsg(err, msg)
+}
+
+func errToGRPCMsg(err error, msg string) error {
+ if err == nil {
+ return nil
+ }
+ if _, ok := status.FromError(err); ok {
+ return err
+ }
+
+ switch {
+ case errors.Is(err, errdefs.ErrInvalidArgument):
+ return status.Errorf(codes.InvalidArgument, msg)
+ case errors.Is(err, errdefs.ErrNotFound):
+ return status.Errorf(codes.NotFound, msg)
+ case errors.Is(err, errdefs.ErrAlreadyExists):
+ return status.Errorf(codes.AlreadyExists, msg)
+ case errors.Is(err, errdefs.ErrFailedPrecondition):
+ return status.Errorf(codes.FailedPrecondition, msg)
+ case errors.Is(err, errdefs.ErrUnavailable):
+ return status.Errorf(codes.Unavailable, msg)
+ case errors.Is(err, errdefs.ErrNotImplemented):
+ return status.Errorf(codes.Unimplemented, msg)
+ case errors.Is(err, context.Canceled):
+ return status.Errorf(codes.Canceled, msg)
+ case errors.Is(err, context.DeadlineExceeded):
+ return status.Errorf(codes.DeadlineExceeded, msg)
+ }
+
+ return errdefs.ToGRPC(err)
+}
diff --git a/pkg/shim/utils/errors_test.go b/pkg/shim/utils/errors_test.go
new file mode 100644
index 000000000..0a8fe34c8
--- /dev/null
+++ b/pkg/shim/utils/errors_test.go
@@ -0,0 +1,50 @@
+// Copyright 2021 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
+//
+// https://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 utils
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/containerd/containerd/errdefs"
+)
+
+func TestGRPCRoundTripsErrors(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ err error
+ test func(err error) bool
+ }{
+ {
+ name: "passthrough",
+ err: errdefs.ErrNotFound,
+ test: errdefs.IsNotFound,
+ },
+ {
+ name: "wrapped",
+ err: fmt.Errorf("oh no: %w", errdefs.ErrNotFound),
+ test: errdefs.IsNotFound,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ if err := errdefs.FromGRPC(ErrToGRPC(tc.err)); !tc.test(err) {
+ t.Errorf("errToGRPC got %+v", err)
+ }
+ if err := errdefs.FromGRPC(ErrToGRPCf(tc.err, "testing %s", "123")); !tc.test(err) {
+ t.Errorf("errToGRPCf got %+v", err)
+ }
+ })
+ }
+}
diff --git a/pkg/sync/BUILD b/pkg/sync/BUILD
index 8b3a11c64..73791b456 100644
--- a/pkg/sync/BUILD
+++ b/pkg/sync/BUILD
@@ -1,5 +1,4 @@
load("//tools:defs.bzl", "go_library", "go_test")
-load("//tools/go_generics:defs.bzl", "go_template")
package(
default_visibility = ["//:sandbox"],
@@ -8,45 +7,6 @@ package(
exports_files(["LICENSE"])
-go_template(
- name = "generic_atomicptr",
- srcs = ["generic_atomicptr_unsafe.go"],
- types = [
- "Value",
- ],
-)
-
-go_template(
- name = "generic_atomicptrmap",
- srcs = ["generic_atomicptrmap_unsafe.go"],
- opt_consts = [
- "ShardOrder",
- ],
- opt_types = [
- "Hasher",
- ],
- types = [
- "Key",
- "Value",
- ],
- deps = [
- ":sync",
- "//pkg/gohacks",
- ],
-)
-
-go_template(
- name = "generic_seqatomic",
- srcs = ["generic_seqatomic_unsafe.go"],
- types = [
- "Value",
- ],
- deps = [
- ":sync",
- "//pkg/gohacks",
- ],
-)
-
go_library(
name = "sync",
srcs = [
diff --git a/pkg/sync/atomicptrtest/BUILD b/pkg/sync/atomicptr/BUILD
index e97553254..a6a7f01ac 100644
--- a/pkg/sync/atomicptrtest/BUILD
+++ b/pkg/sync/atomicptr/BUILD
@@ -1,14 +1,23 @@
load("//tools:defs.bzl", "go_library", "go_test")
-load("//tools/go_generics:defs.bzl", "go_template_instance")
+load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance")
package(licenses = ["notice"])
+go_template(
+ name = "generic_atomicptr",
+ srcs = ["generic_atomicptr_unsafe.go"],
+ types = [
+ "Value",
+ ],
+ visibility = ["//:sandbox"],
+)
+
go_template_instance(
name = "atomicptr_int",
out = "atomicptr_int_unsafe.go",
package = "atomicptr",
suffix = "Int",
- template = "//pkg/sync:generic_atomicptr",
+ template = ":generic_atomicptr",
types = {
"Value": "int",
},
diff --git a/pkg/sync/atomicptrtest/atomicptr_test.go b/pkg/sync/atomicptr/atomicptr_test.go
index 8fdc5112e..8fdc5112e 100644
--- a/pkg/sync/atomicptrtest/atomicptr_test.go
+++ b/pkg/sync/atomicptr/atomicptr_test.go
diff --git a/pkg/sync/generic_atomicptr_unsafe.go b/pkg/sync/atomicptr/generic_atomicptr_unsafe.go
index 82b6df18c..82b6df18c 100644
--- a/pkg/sync/generic_atomicptr_unsafe.go
+++ b/pkg/sync/atomicptr/generic_atomicptr_unsafe.go
diff --git a/pkg/sync/atomicptrmaptest/BUILD b/pkg/sync/atomicptrmap/BUILD
index 3f71ae97d..b0e218c79 100644
--- a/pkg/sync/atomicptrmaptest/BUILD
+++ b/pkg/sync/atomicptrmap/BUILD
@@ -1,17 +1,36 @@
load("//tools:defs.bzl", "go_library", "go_test")
-load("//tools/go_generics:defs.bzl", "go_template_instance")
+load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance")
package(
default_visibility = ["//visibility:private"],
licenses = ["notice"],
)
+go_template(
+ name = "generic_atomicptrmap",
+ srcs = ["generic_atomicptrmap_unsafe.go"],
+ opt_consts = [
+ "ShardOrder",
+ ],
+ opt_types = [
+ "Hasher",
+ ],
+ types = [
+ "Key",
+ "Value",
+ ],
+ deps = [
+ "//pkg/gohacks",
+ "//pkg/sync",
+ ],
+)
+
go_template_instance(
name = "test_atomicptrmap",
out = "test_atomicptrmap_unsafe.go",
package = "atomicptrmap",
prefix = "test",
- template = "//pkg/sync:generic_atomicptrmap",
+ template = ":generic_atomicptrmap",
types = {
"Key": "int64",
"Value": "testValue",
@@ -27,7 +46,7 @@ go_template_instance(
package = "atomicptrmap",
prefix = "test",
suffix = "Sharded",
- template = "//pkg/sync:generic_atomicptrmap",
+ template = ":generic_atomicptrmap",
types = {
"Key": "int64",
"Value": "testValue",
diff --git a/pkg/sync/atomicptrmaptest/atomicptrmap.go b/pkg/sync/atomicptrmap/atomicptrmap.go
index 867821ce9..867821ce9 100644
--- a/pkg/sync/atomicptrmaptest/atomicptrmap.go
+++ b/pkg/sync/atomicptrmap/atomicptrmap.go
diff --git a/pkg/sync/atomicptrmaptest/atomicptrmap_test.go b/pkg/sync/atomicptrmap/atomicptrmap_test.go
index 75a9997ef..75a9997ef 100644
--- a/pkg/sync/atomicptrmaptest/atomicptrmap_test.go
+++ b/pkg/sync/atomicptrmap/atomicptrmap_test.go
diff --git a/pkg/sync/generic_atomicptrmap_unsafe.go b/pkg/sync/atomicptrmap/generic_atomicptrmap_unsafe.go
index 3e98cb309..3e98cb309 100644
--- a/pkg/sync/generic_atomicptrmap_unsafe.go
+++ b/pkg/sync/atomicptrmap/generic_atomicptrmap_unsafe.go
diff --git a/pkg/sync/mutex_test.go b/pkg/sync/mutex_test.go
index 4fb51a8ab..9e4e3f0b2 100644
--- a/pkg/sync/mutex_test.go
+++ b/pkg/sync/mutex_test.go
@@ -64,7 +64,7 @@ func TestTryLockUnlock(t *testing.T) {
if !m.TryLock() {
t.Fatal("failed to aquire lock")
}
- m.Unlock()
+ m.Unlock() // +checklocksforce
if !m.TryLock() {
t.Fatal("failed to aquire lock after unlock")
}
diff --git a/pkg/sync/mutex_unsafe.go b/pkg/sync/mutex_unsafe.go
index 411a80a8a..b829765d9 100644
--- a/pkg/sync/mutex_unsafe.go
+++ b/pkg/sync/mutex_unsafe.go
@@ -32,6 +32,18 @@ func (m *CrossGoroutineMutex) state() *int32 {
return &(*syncMutex)(unsafe.Pointer(&m.Mutex)).state
}
+// Lock locks the underlying Mutex.
+// +checklocksignore
+func (m *CrossGoroutineMutex) Lock() {
+ m.Mutex.Lock()
+}
+
+// Unlock unlocks the underlying Mutex.
+// +checklocksignore
+func (m *CrossGoroutineMutex) Unlock() {
+ m.Mutex.Unlock()
+}
+
const (
mutexUnlocked = 0
mutexLocked = 1
@@ -62,6 +74,7 @@ type Mutex struct {
// Lock locks m. If the lock is already in use, the calling goroutine blocks
// until the mutex is available.
+// +checklocksignore
func (m *Mutex) Lock() {
noteLock(unsafe.Pointer(m))
m.m.Lock()
@@ -80,6 +93,7 @@ func (m *Mutex) Unlock() {
// TryLock tries to acquire the mutex. It returns true if it succeeds and false
// otherwise. TryLock does not block.
+// +checklocksignore
func (m *Mutex) TryLock() bool {
// Note lock first to enforce proper locking even if unsuccessful.
noteLock(unsafe.Pointer(m))
diff --git a/pkg/sync/rwmutex_test.go b/pkg/sync/rwmutex_test.go
index 5ca96d12b..56a88e712 100644
--- a/pkg/sync/rwmutex_test.go
+++ b/pkg/sync/rwmutex_test.go
@@ -172,7 +172,7 @@ func TestRWTryLockUnlock(t *testing.T) {
if !rwm.TryLock() {
t.Fatal("failed to aquire lock")
}
- rwm.Unlock()
+ rwm.Unlock() // +checklocksforce
if !rwm.TryLock() {
t.Fatal("failed to aquire lock after unlock")
}
diff --git a/pkg/sync/rwmutex_unsafe.go b/pkg/sync/rwmutex_unsafe.go
index 892d3e641..7829b06db 100644
--- a/pkg/sync/rwmutex_unsafe.go
+++ b/pkg/sync/rwmutex_unsafe.go
@@ -37,6 +37,7 @@ const rwmutexMaxReaders = 1 << 30
// TryRLock locks rw for reading. It returns true if it succeeds and false
// otherwise. It does not block.
+// +checklocksignore
func (rw *CrossGoroutineRWMutex) TryRLock() bool {
if RaceEnabled {
RaceDisable()
@@ -65,6 +66,7 @@ func (rw *CrossGoroutineRWMutex) TryRLock() bool {
// It should not be used for recursive read locking; a blocked Lock call
// excludes new readers from acquiring the lock. See the documentation on the
// RWMutex type.
+// +checklocksignore
func (rw *CrossGoroutineRWMutex) RLock() {
if RaceEnabled {
RaceDisable()
@@ -83,6 +85,7 @@ func (rw *CrossGoroutineRWMutex) RLock() {
//
// Preconditions:
// * rw is locked for reading.
+// +checklocksignore
func (rw *CrossGoroutineRWMutex) RUnlock() {
if RaceEnabled {
RaceReleaseMerge(unsafe.Pointer(&rw.writerSem))
@@ -134,6 +137,7 @@ func (rw *CrossGoroutineRWMutex) TryLock() bool {
// Lock locks rw for writing. If the lock is already locked for reading or
// writing, Lock blocks until the lock is available.
+// +checklocksignore
func (rw *CrossGoroutineRWMutex) Lock() {
if RaceEnabled {
RaceDisable()
@@ -228,6 +232,7 @@ type RWMutex struct {
// TryRLock locks rw for reading. It returns true if it succeeds and false
// otherwise. It does not block.
+// +checklocksignore
func (rw *RWMutex) TryRLock() bool {
// Note lock first to enforce proper locking even if unsuccessful.
noteLock(unsafe.Pointer(rw))
@@ -243,6 +248,7 @@ func (rw *RWMutex) TryRLock() bool {
// It should not be used for recursive read locking; a blocked Lock call
// excludes new readers from acquiring the lock. See the documentation on the
// RWMutex type.
+// +checklocksignore
func (rw *RWMutex) RLock() {
noteLock(unsafe.Pointer(rw))
rw.m.RLock()
@@ -261,6 +267,7 @@ func (rw *RWMutex) RUnlock() {
// TryLock locks rw for writing. It returns true if it succeeds and false
// otherwise. It does not block.
+// +checklocksignore
func (rw *RWMutex) TryLock() bool {
// Note lock first to enforce proper locking even if unsuccessful.
noteLock(unsafe.Pointer(rw))
@@ -273,6 +280,7 @@ func (rw *RWMutex) TryLock() bool {
// Lock locks rw for writing. If the lock is already locked for reading or
// writing, Lock blocks until the lock is available.
+// +checklocksignore
func (rw *RWMutex) Lock() {
noteLock(unsafe.Pointer(rw))
rw.m.Lock()
diff --git a/pkg/sync/seqatomictest/BUILD b/pkg/sync/seqatomic/BUILD
index 5f9164117..60f79ab54 100644
--- a/pkg/sync/seqatomictest/BUILD
+++ b/pkg/sync/seqatomic/BUILD
@@ -1,14 +1,27 @@
load("//tools:defs.bzl", "go_library", "go_test")
-load("//tools/go_generics:defs.bzl", "go_template_instance")
+load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance")
package(licenses = ["notice"])
+go_template(
+ name = "generic_seqatomic",
+ srcs = ["generic_seqatomic_unsafe.go"],
+ types = [
+ "Value",
+ ],
+ visibility = ["//:sandbox"],
+ deps = [
+ ":sync",
+ "//pkg/gohacks",
+ ],
+)
+
go_template_instance(
name = "seqatomic_int",
out = "seqatomic_int_unsafe.go",
package = "seqatomic",
suffix = "Int",
- template = "//pkg/sync:generic_seqatomic",
+ template = ":generic_seqatomic",
types = {
"Value": "int",
},
diff --git a/pkg/sync/generic_seqatomic_unsafe.go b/pkg/sync/seqatomic/generic_seqatomic_unsafe.go
index 9578c9c52..9578c9c52 100644
--- a/pkg/sync/generic_seqatomic_unsafe.go
+++ b/pkg/sync/seqatomic/generic_seqatomic_unsafe.go
diff --git a/pkg/sync/seqatomictest/seqatomic_test.go b/pkg/sync/seqatomic/seqatomic_test.go
index 2c4568b07..2c4568b07 100644
--- a/pkg/sync/seqatomictest/seqatomic_test.go
+++ b/pkg/sync/seqatomic/seqatomic_test.go
diff --git a/pkg/syserr/BUILD b/pkg/syserr/BUILD
index 9cc9e3bf2..ceee494fc 100644
--- a/pkg/syserr/BUILD
+++ b/pkg/syserr/BUILD
@@ -11,7 +11,9 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
- "//pkg/abi/linux",
+ "//pkg/abi/linux/errno",
+ "//pkg/errors",
+ "//pkg/errors/linuxerr",
"//pkg/syserror",
"//pkg/tcpip",
"@org_golang_x_sys//unix:go_default_library",
diff --git a/pkg/syserr/netstack.go b/pkg/syserr/netstack.go
index 90be24e15..eb44f1254 100644
--- a/pkg/syserr/netstack.go
+++ b/pkg/syserr/netstack.go
@@ -17,7 +17,7 @@ package syserr
import (
"fmt"
- "gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
"gvisor.dev/gvisor/pkg/tcpip"
)
@@ -25,33 +25,33 @@ import (
// Mapping for tcpip.Error types.
var (
- ErrUnknownProtocol = New((&tcpip.ErrUnknownProtocol{}).String(), linux.EINVAL)
- ErrUnknownNICID = New((&tcpip.ErrUnknownNICID{}).String(), linux.ENODEV)
- ErrUnknownDevice = New((&tcpip.ErrUnknownDevice{}).String(), linux.ENODEV)
- ErrUnknownProtocolOption = New((&tcpip.ErrUnknownProtocolOption{}).String(), linux.ENOPROTOOPT)
- ErrDuplicateNICID = New((&tcpip.ErrDuplicateNICID{}).String(), linux.EEXIST)
- ErrDuplicateAddress = New((&tcpip.ErrDuplicateAddress{}).String(), linux.EEXIST)
- ErrAlreadyBound = New((&tcpip.ErrAlreadyBound{}).String(), linux.EINVAL)
- ErrInvalidEndpointState = New((&tcpip.ErrInvalidEndpointState{}).String(), linux.EINVAL)
- ErrAlreadyConnecting = New((&tcpip.ErrAlreadyConnecting{}).String(), linux.EALREADY)
- ErrNoPortAvailable = New((&tcpip.ErrNoPortAvailable{}).String(), linux.EAGAIN)
- ErrPortInUse = New((&tcpip.ErrPortInUse{}).String(), linux.EADDRINUSE)
- ErrBadLocalAddress = New((&tcpip.ErrBadLocalAddress{}).String(), linux.EADDRNOTAVAIL)
- ErrClosedForSend = New((&tcpip.ErrClosedForSend{}).String(), linux.EPIPE)
- ErrClosedForReceive = New((&tcpip.ErrClosedForReceive{}).String(), linux.NOERRNO)
- ErrTimeout = New((&tcpip.ErrTimeout{}).String(), linux.ETIMEDOUT)
- ErrAborted = New((&tcpip.ErrAborted{}).String(), linux.EPIPE)
- ErrConnectStarted = New((&tcpip.ErrConnectStarted{}).String(), linux.EINPROGRESS)
- ErrDestinationRequired = New((&tcpip.ErrDestinationRequired{}).String(), linux.EDESTADDRREQ)
- ErrNotSupported = New((&tcpip.ErrNotSupported{}).String(), linux.EOPNOTSUPP)
- ErrQueueSizeNotSupported = New((&tcpip.ErrQueueSizeNotSupported{}).String(), linux.ENOTTY)
- ErrNoSuchFile = New((&tcpip.ErrNoSuchFile{}).String(), linux.ENOENT)
- ErrInvalidOptionValue = New((&tcpip.ErrInvalidOptionValue{}).String(), linux.EINVAL)
- ErrBroadcastDisabled = New((&tcpip.ErrBroadcastDisabled{}).String(), linux.EACCES)
- ErrNotPermittedNet = New((&tcpip.ErrNotPermitted{}).String(), linux.EPERM)
- ErrBadBuffer = New((&tcpip.ErrBadBuffer{}).String(), linux.EFAULT)
- ErrMalformedHeader = New((&tcpip.ErrMalformedHeader{}).String(), linux.EINVAL)
- ErrInvalidPortRange = New((&tcpip.ErrInvalidPortRange{}).String(), linux.EINVAL)
+ ErrUnknownProtocol = New((&tcpip.ErrUnknownProtocol{}).String(), errno.EINVAL)
+ ErrUnknownNICID = New((&tcpip.ErrUnknownNICID{}).String(), errno.ENODEV)
+ ErrUnknownDevice = New((&tcpip.ErrUnknownDevice{}).String(), errno.ENODEV)
+ ErrUnknownProtocolOption = New((&tcpip.ErrUnknownProtocolOption{}).String(), errno.ENOPROTOOPT)
+ ErrDuplicateNICID = New((&tcpip.ErrDuplicateNICID{}).String(), errno.EEXIST)
+ ErrDuplicateAddress = New((&tcpip.ErrDuplicateAddress{}).String(), errno.EEXIST)
+ ErrAlreadyBound = New((&tcpip.ErrAlreadyBound{}).String(), errno.EINVAL)
+ ErrInvalidEndpointState = New((&tcpip.ErrInvalidEndpointState{}).String(), errno.EINVAL)
+ ErrAlreadyConnecting = New((&tcpip.ErrAlreadyConnecting{}).String(), errno.EALREADY)
+ ErrNoPortAvailable = New((&tcpip.ErrNoPortAvailable{}).String(), errno.EAGAIN)
+ ErrPortInUse = New((&tcpip.ErrPortInUse{}).String(), errno.EADDRINUSE)
+ ErrBadLocalAddress = New((&tcpip.ErrBadLocalAddress{}).String(), errno.EADDRNOTAVAIL)
+ ErrClosedForSend = New((&tcpip.ErrClosedForSend{}).String(), errno.EPIPE)
+ ErrClosedForReceive = New((&tcpip.ErrClosedForReceive{}).String(), errno.NOERRNO)
+ ErrTimeout = New((&tcpip.ErrTimeout{}).String(), errno.ETIMEDOUT)
+ ErrAborted = New((&tcpip.ErrAborted{}).String(), errno.EPIPE)
+ ErrConnectStarted = New((&tcpip.ErrConnectStarted{}).String(), errno.EINPROGRESS)
+ ErrDestinationRequired = New((&tcpip.ErrDestinationRequired{}).String(), errno.EDESTADDRREQ)
+ ErrNotSupported = New((&tcpip.ErrNotSupported{}).String(), errno.EOPNOTSUPP)
+ ErrQueueSizeNotSupported = New((&tcpip.ErrQueueSizeNotSupported{}).String(), errno.ENOTTY)
+ ErrNoSuchFile = New((&tcpip.ErrNoSuchFile{}).String(), errno.ENOENT)
+ ErrInvalidOptionValue = New((&tcpip.ErrInvalidOptionValue{}).String(), errno.EINVAL)
+ ErrBroadcastDisabled = New((&tcpip.ErrBroadcastDisabled{}).String(), errno.EACCES)
+ ErrNotPermittedNet = New((&tcpip.ErrNotPermitted{}).String(), errno.EPERM)
+ ErrBadBuffer = New((&tcpip.ErrBadBuffer{}).String(), errno.EFAULT)
+ ErrMalformedHeader = New((&tcpip.ErrMalformedHeader{}).String(), errno.EINVAL)
+ ErrInvalidPortRange = New((&tcpip.ErrInvalidPortRange{}).String(), errno.EINVAL)
)
// TranslateNetstackError converts an error from the tcpip package to a sentry
diff --git a/pkg/syserr/syserr.go b/pkg/syserr/syserr.go
index d70521f32..558240008 100644
--- a/pkg/syserr/syserr.go
+++ b/pkg/syserr/syserr.go
@@ -21,7 +21,9 @@ import (
"fmt"
"golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/abi/linux/errno"
+ "gvisor.dev/gvisor/pkg/errors"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/syserror"
)
@@ -31,34 +33,33 @@ type Error struct {
message string
// noTranslation indicates that this Error cannot be translated to a
- // linux.Errno.
+ // errno.Errno.
noTranslation bool
- // errno is the linux.Errno this Error should be translated to.
- errno linux.Errno
+ // errno is the errno.Errno this Error should be translated to.
+ errno errno.Errno
}
// New creates a new Error and adds a translation for it.
//
// New must only be called at init.
-func New(message string, linuxTranslation linux.Errno) *Error {
+func New(message string, linuxTranslation errno.Errno) *Error {
err := &Error{message: message, errno: linuxTranslation}
// TODO(b/34162363): Remove this.
- errno := linuxTranslation
- if errno < 0 || int(errno) >= len(linuxBackwardsTranslations) {
- panic(fmt.Sprint("invalid errno: ", errno))
+ if int(err.errno) >= len(linuxBackwardsTranslations) {
+ panic(fmt.Sprint("invalid errno: ", err.errno))
}
- e := error(unix.Errno(errno))
- // syserror.ErrWouldBlock gets translated to syserror.EWOULDBLOCK and
+ e := error(unix.Errno(err.errno))
+ // syserror.ErrWouldBlock gets translated to linuxerr.EWOULDBLOCK and
// enables proper blocking semantics. This should temporary address the
// class of blocking bugs that keep popping up with the current state of
// the error space.
- if e == syserror.EWOULDBLOCK {
+ if err.errno == linuxerr.EWOULDBLOCK.Errno() {
e = syserror.ErrWouldBlock
}
- linuxBackwardsTranslations[errno] = linuxBackwardsTranslation{err: e, ok: true}
+ linuxBackwardsTranslations[err.errno] = linuxBackwardsTranslation{err: e, ok: true}
return err
}
@@ -69,7 +70,7 @@ func New(message string, linuxTranslation linux.Errno) *Error {
// NewDynamic should only be used sparingly and not be used for static error
// messages. Errors with static error messages should be declared with New as
// global variables.
-func NewDynamic(message string, linuxTranslation linux.Errno) *Error {
+func NewDynamic(message string, linuxTranslation errno.Errno) *Error {
return &Error{message: message, errno: linuxTranslation}
}
@@ -82,7 +83,7 @@ func NewWithoutTranslation(message string) *Error {
return &Error{message: message, noTranslation: true}
}
-func newWithHost(message string, linuxTranslation linux.Errno, hostErrno unix.Errno) *Error {
+func newWithHost(message string, linuxTranslation errno.Errno, hostErrno unix.Errno) *Error {
e := New(message, linuxTranslation)
addLinuxHostTranslation(hostErrno, e)
return e
@@ -114,19 +115,19 @@ func (e *Error) ToError() error {
if e.noTranslation {
panic(fmt.Sprintf("error %q does not support translation", e.message))
}
- errno := int(e.errno)
- if errno == linux.NOERRNO {
+ err := int(e.errno)
+ if err == errno.NOERRNO {
return nil
}
- if errno <= 0 || errno >= len(linuxBackwardsTranslations) || !linuxBackwardsTranslations[errno].ok {
- panic(fmt.Sprintf("unknown error %q (%d)", e.message, errno))
+ if err >= len(linuxBackwardsTranslations) || !linuxBackwardsTranslations[err].ok {
+ panic(fmt.Sprintf("unknown error %q (%d)", e.message, err))
}
- return linuxBackwardsTranslations[errno].err
+ return linuxBackwardsTranslations[err].err
}
// ToLinux converts the Error to a Linux ABI error that can be returned to the
// application.
-func (e *Error) ToLinux() linux.Errno {
+func (e *Error) ToLinux() errno.Errno {
if e.noTranslation {
panic(fmt.Sprintf("No Linux ABI translation available for %q", e.message))
}
@@ -138,137 +139,137 @@ func (e *Error) ToLinux() linux.Errno {
// Some of the errors should be replaced with package specific errors and
// others should be removed entirely.
var (
- ErrNotPermitted = newWithHost("operation not permitted", linux.EPERM, unix.EPERM)
- ErrNoFileOrDir = newWithHost("no such file or directory", linux.ENOENT, unix.ENOENT)
- ErrNoProcess = newWithHost("no such process", linux.ESRCH, unix.ESRCH)
- ErrInterrupted = newWithHost("interrupted system call", linux.EINTR, unix.EINTR)
- ErrIO = newWithHost("I/O error", linux.EIO, unix.EIO)
- ErrDeviceOrAddress = newWithHost("no such device or address", linux.ENXIO, unix.ENXIO)
- ErrTooManyArgs = newWithHost("argument list too long", linux.E2BIG, unix.E2BIG)
- ErrEcec = newWithHost("exec format error", linux.ENOEXEC, unix.ENOEXEC)
- ErrBadFD = newWithHost("bad file number", linux.EBADF, unix.EBADF)
- ErrNoChild = newWithHost("no child processes", linux.ECHILD, unix.ECHILD)
- ErrTryAgain = newWithHost("try again", linux.EAGAIN, unix.EAGAIN)
- ErrNoMemory = newWithHost("out of memory", linux.ENOMEM, unix.ENOMEM)
- ErrPermissionDenied = newWithHost("permission denied", linux.EACCES, unix.EACCES)
- ErrBadAddress = newWithHost("bad address", linux.EFAULT, unix.EFAULT)
- ErrNotBlockDevice = newWithHost("block device required", linux.ENOTBLK, unix.ENOTBLK)
- ErrBusy = newWithHost("device or resource busy", linux.EBUSY, unix.EBUSY)
- ErrExists = newWithHost("file exists", linux.EEXIST, unix.EEXIST)
- ErrCrossDeviceLink = newWithHost("cross-device link", linux.EXDEV, unix.EXDEV)
- ErrNoDevice = newWithHost("no such device", linux.ENODEV, unix.ENODEV)
- ErrNotDir = newWithHost("not a directory", linux.ENOTDIR, unix.ENOTDIR)
- ErrIsDir = newWithHost("is a directory", linux.EISDIR, unix.EISDIR)
- ErrInvalidArgument = newWithHost("invalid argument", linux.EINVAL, unix.EINVAL)
- ErrFileTableOverflow = newWithHost("file table overflow", linux.ENFILE, unix.ENFILE)
- ErrTooManyOpenFiles = newWithHost("too many open files", linux.EMFILE, unix.EMFILE)
- ErrNotTTY = newWithHost("not a typewriter", linux.ENOTTY, unix.ENOTTY)
- ErrTestFileBusy = newWithHost("text file busy", linux.ETXTBSY, unix.ETXTBSY)
- ErrFileTooBig = newWithHost("file too large", linux.EFBIG, unix.EFBIG)
- ErrNoSpace = newWithHost("no space left on device", linux.ENOSPC, unix.ENOSPC)
- ErrIllegalSeek = newWithHost("illegal seek", linux.ESPIPE, unix.ESPIPE)
- ErrReadOnlyFS = newWithHost("read-only file system", linux.EROFS, unix.EROFS)
- ErrTooManyLinks = newWithHost("too many links", linux.EMLINK, unix.EMLINK)
- ErrBrokenPipe = newWithHost("broken pipe", linux.EPIPE, unix.EPIPE)
- ErrDomain = newWithHost("math argument out of domain of func", linux.EDOM, unix.EDOM)
- ErrRange = newWithHost("math result not representable", linux.ERANGE, unix.ERANGE)
- ErrDeadlock = newWithHost("resource deadlock would occur", linux.EDEADLOCK, unix.EDEADLOCK)
- ErrNameTooLong = newWithHost("file name too long", linux.ENAMETOOLONG, unix.ENAMETOOLONG)
- ErrNoLocksAvailable = newWithHost("no record locks available", linux.ENOLCK, unix.ENOLCK)
- ErrInvalidSyscall = newWithHost("invalid system call number", linux.ENOSYS, unix.ENOSYS)
- ErrDirNotEmpty = newWithHost("directory not empty", linux.ENOTEMPTY, unix.ENOTEMPTY)
- ErrLinkLoop = newWithHost("too many symbolic links encountered", linux.ELOOP, unix.ELOOP)
- ErrNoMessage = newWithHost("no message of desired type", linux.ENOMSG, unix.ENOMSG)
- ErrIdentifierRemoved = newWithHost("identifier removed", linux.EIDRM, unix.EIDRM)
- ErrChannelOutOfRange = newWithHost("channel number out of range", linux.ECHRNG, unix.ECHRNG)
- ErrLevelTwoNotSynced = newWithHost("level 2 not synchronized", linux.EL2NSYNC, unix.EL2NSYNC)
- ErrLevelThreeHalted = newWithHost("level 3 halted", linux.EL3HLT, unix.EL3HLT)
- ErrLevelThreeReset = newWithHost("level 3 reset", linux.EL3RST, unix.EL3RST)
- ErrLinkNumberOutOfRange = newWithHost("link number out of range", linux.ELNRNG, unix.ELNRNG)
- ErrProtocolDriverNotAttached = newWithHost("protocol driver not attached", linux.EUNATCH, unix.EUNATCH)
- ErrNoCSIAvailable = newWithHost("no CSI structure available", linux.ENOCSI, unix.ENOCSI)
- ErrLevelTwoHalted = newWithHost("level 2 halted", linux.EL2HLT, unix.EL2HLT)
- ErrInvalidExchange = newWithHost("invalid exchange", linux.EBADE, unix.EBADE)
- ErrInvalidRequestDescriptor = newWithHost("invalid request descriptor", linux.EBADR, unix.EBADR)
- ErrExchangeFull = newWithHost("exchange full", linux.EXFULL, unix.EXFULL)
- ErrNoAnode = newWithHost("no anode", linux.ENOANO, unix.ENOANO)
- ErrInvalidRequestCode = newWithHost("invalid request code", linux.EBADRQC, unix.EBADRQC)
- ErrInvalidSlot = newWithHost("invalid slot", linux.EBADSLT, unix.EBADSLT)
- ErrBadFontFile = newWithHost("bad font file format", linux.EBFONT, unix.EBFONT)
- ErrNotStream = newWithHost("device not a stream", linux.ENOSTR, unix.ENOSTR)
- ErrNoDataAvailable = newWithHost("no data available", linux.ENODATA, unix.ENODATA)
- ErrTimerExpired = newWithHost("timer expired", linux.ETIME, unix.ETIME)
- ErrStreamsResourceDepleted = newWithHost("out of streams resources", linux.ENOSR, unix.ENOSR)
- ErrMachineNotOnNetwork = newWithHost("machine is not on the network", linux.ENONET, unix.ENONET)
- ErrPackageNotInstalled = newWithHost("package not installed", linux.ENOPKG, unix.ENOPKG)
- ErrIsRemote = newWithHost("object is remote", linux.EREMOTE, unix.EREMOTE)
- ErrNoLink = newWithHost("link has been severed", linux.ENOLINK, unix.ENOLINK)
- ErrAdvertise = newWithHost("advertise error", linux.EADV, unix.EADV)
- ErrSRMount = newWithHost("srmount error", linux.ESRMNT, unix.ESRMNT)
- ErrSendCommunication = newWithHost("communication error on send", linux.ECOMM, unix.ECOMM)
- ErrProtocol = newWithHost("protocol error", linux.EPROTO, unix.EPROTO)
- ErrMultihopAttempted = newWithHost("multihop attempted", linux.EMULTIHOP, unix.EMULTIHOP)
- ErrRFS = newWithHost("RFS specific error", linux.EDOTDOT, unix.EDOTDOT)
- ErrInvalidDataMessage = newWithHost("not a data message", linux.EBADMSG, unix.EBADMSG)
- ErrOverflow = newWithHost("value too large for defined data type", linux.EOVERFLOW, unix.EOVERFLOW)
- ErrNetworkNameNotUnique = newWithHost("name not unique on network", linux.ENOTUNIQ, unix.ENOTUNIQ)
- ErrFDInBadState = newWithHost("file descriptor in bad state", linux.EBADFD, unix.EBADFD)
- ErrRemoteAddressChanged = newWithHost("remote address changed", linux.EREMCHG, unix.EREMCHG)
- ErrSharedLibraryInaccessible = newWithHost("can not access a needed shared library", linux.ELIBACC, unix.ELIBACC)
- ErrCorruptedSharedLibrary = newWithHost("accessing a corrupted shared library", linux.ELIBBAD, unix.ELIBBAD)
- ErrLibSectionCorrupted = newWithHost(".lib section in a.out corrupted", linux.ELIBSCN, unix.ELIBSCN)
- ErrTooManySharedLibraries = newWithHost("attempting to link in too many shared libraries", linux.ELIBMAX, unix.ELIBMAX)
- ErrSharedLibraryExeced = newWithHost("cannot exec a shared library directly", linux.ELIBEXEC, unix.ELIBEXEC)
- ErrIllegalByteSequence = newWithHost("illegal byte sequence", linux.EILSEQ, unix.EILSEQ)
- ErrShouldRestart = newWithHost("interrupted system call should be restarted", linux.ERESTART, unix.ERESTART)
- ErrStreamPipe = newWithHost("streams pipe error", linux.ESTRPIPE, unix.ESTRPIPE)
- ErrTooManyUsers = newWithHost("too many users", linux.EUSERS, unix.EUSERS)
- ErrNotASocket = newWithHost("socket operation on non-socket", linux.ENOTSOCK, unix.ENOTSOCK)
- ErrDestinationAddressRequired = newWithHost("destination address required", linux.EDESTADDRREQ, unix.EDESTADDRREQ)
- ErrMessageTooLong = newWithHost("message too long", linux.EMSGSIZE, unix.EMSGSIZE)
- ErrWrongProtocolForSocket = newWithHost("protocol wrong type for socket", linux.EPROTOTYPE, unix.EPROTOTYPE)
- ErrProtocolNotAvailable = newWithHost("protocol not available", linux.ENOPROTOOPT, unix.ENOPROTOOPT)
- ErrProtocolNotSupported = newWithHost("protocol not supported", linux.EPROTONOSUPPORT, unix.EPROTONOSUPPORT)
- ErrSocketNotSupported = newWithHost("socket type not supported", linux.ESOCKTNOSUPPORT, unix.ESOCKTNOSUPPORT)
- ErrEndpointOperation = newWithHost("operation not supported on transport endpoint", linux.EOPNOTSUPP, unix.EOPNOTSUPP)
- ErrProtocolFamilyNotSupported = newWithHost("protocol family not supported", linux.EPFNOSUPPORT, unix.EPFNOSUPPORT)
- ErrAddressFamilyNotSupported = newWithHost("address family not supported by protocol", linux.EAFNOSUPPORT, unix.EAFNOSUPPORT)
- ErrAddressInUse = newWithHost("address already in use", linux.EADDRINUSE, unix.EADDRINUSE)
- ErrAddressNotAvailable = newWithHost("cannot assign requested address", linux.EADDRNOTAVAIL, unix.EADDRNOTAVAIL)
- ErrNetworkDown = newWithHost("network is down", linux.ENETDOWN, unix.ENETDOWN)
- ErrNetworkUnreachable = newWithHost("network is unreachable", linux.ENETUNREACH, unix.ENETUNREACH)
- ErrNetworkReset = newWithHost("network dropped connection because of reset", linux.ENETRESET, unix.ENETRESET)
- ErrConnectionAborted = newWithHost("software caused connection abort", linux.ECONNABORTED, unix.ECONNABORTED)
- ErrConnectionReset = newWithHost("connection reset by peer", linux.ECONNRESET, unix.ECONNRESET)
- ErrNoBufferSpace = newWithHost("no buffer space available", linux.ENOBUFS, unix.ENOBUFS)
- ErrAlreadyConnected = newWithHost("transport endpoint is already connected", linux.EISCONN, unix.EISCONN)
- ErrNotConnected = newWithHost("transport endpoint is not connected", linux.ENOTCONN, unix.ENOTCONN)
- ErrShutdown = newWithHost("cannot send after transport endpoint shutdown", linux.ESHUTDOWN, unix.ESHUTDOWN)
- ErrTooManyRefs = newWithHost("too many references: cannot splice", linux.ETOOMANYREFS, unix.ETOOMANYREFS)
- ErrTimedOut = newWithHost("connection timed out", linux.ETIMEDOUT, unix.ETIMEDOUT)
- ErrConnectionRefused = newWithHost("connection refused", linux.ECONNREFUSED, unix.ECONNREFUSED)
- ErrHostDown = newWithHost("host is down", linux.EHOSTDOWN, unix.EHOSTDOWN)
- ErrNoRoute = newWithHost("no route to host", linux.EHOSTUNREACH, unix.EHOSTUNREACH)
- ErrAlreadyInProgress = newWithHost("operation already in progress", linux.EALREADY, unix.EALREADY)
- ErrInProgress = newWithHost("operation now in progress", linux.EINPROGRESS, unix.EINPROGRESS)
- ErrStaleFileHandle = newWithHost("stale file handle", linux.ESTALE, unix.ESTALE)
- ErrStructureNeedsCleaning = newWithHost("structure needs cleaning", linux.EUCLEAN, unix.EUCLEAN)
- ErrIsNamedFile = newWithHost("is a named type file", linux.ENOTNAM, unix.ENOTNAM)
- ErrRemoteIO = newWithHost("remote I/O error", linux.EREMOTEIO, unix.EREMOTEIO)
- ErrQuotaExceeded = newWithHost("quota exceeded", linux.EDQUOT, unix.EDQUOT)
- ErrNoMedium = newWithHost("no medium found", linux.ENOMEDIUM, unix.ENOMEDIUM)
- ErrWrongMediumType = newWithHost("wrong medium type", linux.EMEDIUMTYPE, unix.EMEDIUMTYPE)
- ErrCanceled = newWithHost("operation canceled", linux.ECANCELED, unix.ECANCELED)
- ErrNoKey = newWithHost("required key not available", linux.ENOKEY, unix.ENOKEY)
- ErrKeyExpired = newWithHost("key has expired", linux.EKEYEXPIRED, unix.EKEYEXPIRED)
- ErrKeyRevoked = newWithHost("key has been revoked", linux.EKEYREVOKED, unix.EKEYREVOKED)
- ErrKeyRejected = newWithHost("key was rejected by service", linux.EKEYREJECTED, unix.EKEYREJECTED)
- ErrOwnerDied = newWithHost("owner died", linux.EOWNERDEAD, unix.EOWNERDEAD)
- ErrNotRecoverable = newWithHost("state not recoverable", linux.ENOTRECOVERABLE, unix.ENOTRECOVERABLE)
+ ErrNotPermitted = newWithHost("operation not permitted", errno.EPERM, unix.EPERM)
+ ErrNoFileOrDir = newWithHost("no such file or directory", errno.ENOENT, unix.ENOENT)
+ ErrNoProcess = newWithHost("no such process", errno.ESRCH, unix.ESRCH)
+ ErrInterrupted = newWithHost("interrupted system call", errno.EINTR, unix.EINTR)
+ ErrIO = newWithHost("I/O error", errno.EIO, unix.EIO)
+ ErrDeviceOrAddress = newWithHost("no such device or address", errno.ENXIO, unix.ENXIO)
+ ErrTooManyArgs = newWithHost("argument list too long", errno.E2BIG, unix.E2BIG)
+ ErrEcec = newWithHost("exec format error", errno.ENOEXEC, unix.ENOEXEC)
+ ErrBadFD = newWithHost("bad file number", errno.EBADF, unix.EBADF)
+ ErrNoChild = newWithHost("no child processes", errno.ECHILD, unix.ECHILD)
+ ErrTryAgain = newWithHost("try again", errno.EAGAIN, unix.EAGAIN)
+ ErrNoMemory = newWithHost("out of memory", errno.ENOMEM, unix.ENOMEM)
+ ErrPermissionDenied = newWithHost("permission denied", errno.EACCES, unix.EACCES)
+ ErrBadAddress = newWithHost("bad address", errno.EFAULT, unix.EFAULT)
+ ErrNotBlockDevice = newWithHost("block device required", errno.ENOTBLK, unix.ENOTBLK)
+ ErrBusy = newWithHost("device or resource busy", errno.EBUSY, unix.EBUSY)
+ ErrExists = newWithHost("file exists", errno.EEXIST, unix.EEXIST)
+ ErrCrossDeviceLink = newWithHost("cross-device link", errno.EXDEV, unix.EXDEV)
+ ErrNoDevice = newWithHost("no such device", errno.ENODEV, unix.ENODEV)
+ ErrNotDir = newWithHost("not a directory", errno.ENOTDIR, unix.ENOTDIR)
+ ErrIsDir = newWithHost("is a directory", errno.EISDIR, unix.EISDIR)
+ ErrInvalidArgument = newWithHost("invalid argument", errno.EINVAL, unix.EINVAL)
+ ErrFileTableOverflow = newWithHost("file table overflow", errno.ENFILE, unix.ENFILE)
+ ErrTooManyOpenFiles = newWithHost("too many open files", errno.EMFILE, unix.EMFILE)
+ ErrNotTTY = newWithHost("not a typewriter", errno.ENOTTY, unix.ENOTTY)
+ ErrTestFileBusy = newWithHost("text file busy", errno.ETXTBSY, unix.ETXTBSY)
+ ErrFileTooBig = newWithHost("file too large", errno.EFBIG, unix.EFBIG)
+ ErrNoSpace = newWithHost("no space left on device", errno.ENOSPC, unix.ENOSPC)
+ ErrIllegalSeek = newWithHost("illegal seek", errno.ESPIPE, unix.ESPIPE)
+ ErrReadOnlyFS = newWithHost("read-only file system", errno.EROFS, unix.EROFS)
+ ErrTooManyLinks = newWithHost("too many links", errno.EMLINK, unix.EMLINK)
+ ErrBrokenPipe = newWithHost("broken pipe", errno.EPIPE, unix.EPIPE)
+ ErrDomain = newWithHost("math argument out of domain of func", errno.EDOM, unix.EDOM)
+ ErrRange = newWithHost("math result not representable", errno.ERANGE, unix.ERANGE)
+ ErrDeadlock = newWithHost("resource deadlock would occur", errno.EDEADLOCK, unix.EDEADLOCK)
+ ErrNameTooLong = newWithHost("file name too long", errno.ENAMETOOLONG, unix.ENAMETOOLONG)
+ ErrNoLocksAvailable = newWithHost("no record locks available", errno.ENOLCK, unix.ENOLCK)
+ ErrInvalidSyscall = newWithHost("invalid system call number", errno.ENOSYS, unix.ENOSYS)
+ ErrDirNotEmpty = newWithHost("directory not empty", errno.ENOTEMPTY, unix.ENOTEMPTY)
+ ErrLinkLoop = newWithHost("too many symbolic links encountered", errno.ELOOP, unix.ELOOP)
+ ErrNoMessage = newWithHost("no message of desired type", errno.ENOMSG, unix.ENOMSG)
+ ErrIdentifierRemoved = newWithHost("identifier removed", errno.EIDRM, unix.EIDRM)
+ ErrChannelOutOfRange = newWithHost("channel number out of range", errno.ECHRNG, unix.ECHRNG)
+ ErrLevelTwoNotSynced = newWithHost("level 2 not synchronized", errno.EL2NSYNC, unix.EL2NSYNC)
+ ErrLevelThreeHalted = newWithHost("level 3 halted", errno.EL3HLT, unix.EL3HLT)
+ ErrLevelThreeReset = newWithHost("level 3 reset", errno.EL3RST, unix.EL3RST)
+ ErrLinkNumberOutOfRange = newWithHost("link number out of range", errno.ELNRNG, unix.ELNRNG)
+ ErrProtocolDriverNotAttached = newWithHost("protocol driver not attached", errno.EUNATCH, unix.EUNATCH)
+ ErrNoCSIAvailable = newWithHost("no CSI structure available", errno.ENOCSI, unix.ENOCSI)
+ ErrLevelTwoHalted = newWithHost("level 2 halted", errno.EL2HLT, unix.EL2HLT)
+ ErrInvalidExchange = newWithHost("invalid exchange", errno.EBADE, unix.EBADE)
+ ErrInvalidRequestDescriptor = newWithHost("invalid request descriptor", errno.EBADR, unix.EBADR)
+ ErrExchangeFull = newWithHost("exchange full", errno.EXFULL, unix.EXFULL)
+ ErrNoAnode = newWithHost("no anode", errno.ENOANO, unix.ENOANO)
+ ErrInvalidRequestCode = newWithHost("invalid request code", errno.EBADRQC, unix.EBADRQC)
+ ErrInvalidSlot = newWithHost("invalid slot", errno.EBADSLT, unix.EBADSLT)
+ ErrBadFontFile = newWithHost("bad font file format", errno.EBFONT, unix.EBFONT)
+ ErrNotStream = newWithHost("device not a stream", errno.ENOSTR, unix.ENOSTR)
+ ErrNoDataAvailable = newWithHost("no data available", errno.ENODATA, unix.ENODATA)
+ ErrTimerExpired = newWithHost("timer expired", errno.ETIME, unix.ETIME)
+ ErrStreamsResourceDepleted = newWithHost("out of streams resources", errno.ENOSR, unix.ENOSR)
+ ErrMachineNotOnNetwork = newWithHost("machine is not on the network", errno.ENONET, unix.ENONET)
+ ErrPackageNotInstalled = newWithHost("package not installed", errno.ENOPKG, unix.ENOPKG)
+ ErrIsRemote = newWithHost("object is remote", errno.EREMOTE, unix.EREMOTE)
+ ErrNoLink = newWithHost("link has been severed", errno.ENOLINK, unix.ENOLINK)
+ ErrAdvertise = newWithHost("advertise error", errno.EADV, unix.EADV)
+ ErrSRMount = newWithHost("srmount error", errno.ESRMNT, unix.ESRMNT)
+ ErrSendCommunication = newWithHost("communication error on send", errno.ECOMM, unix.ECOMM)
+ ErrProtocol = newWithHost("protocol error", errno.EPROTO, unix.EPROTO)
+ ErrMultihopAttempted = newWithHost("multihop attempted", errno.EMULTIHOP, unix.EMULTIHOP)
+ ErrRFS = newWithHost("RFS specific error", errno.EDOTDOT, unix.EDOTDOT)
+ ErrInvalidDataMessage = newWithHost("not a data message", errno.EBADMSG, unix.EBADMSG)
+ ErrOverflow = newWithHost("value too large for defined data type", errno.EOVERFLOW, unix.EOVERFLOW)
+ ErrNetworkNameNotUnique = newWithHost("name not unique on network", errno.ENOTUNIQ, unix.ENOTUNIQ)
+ ErrFDInBadState = newWithHost("file descriptor in bad state", errno.EBADFD, unix.EBADFD)
+ ErrRemoteAddressChanged = newWithHost("remote address changed", errno.EREMCHG, unix.EREMCHG)
+ ErrSharedLibraryInaccessible = newWithHost("can not access a needed shared library", errno.ELIBACC, unix.ELIBACC)
+ ErrCorruptedSharedLibrary = newWithHost("accessing a corrupted shared library", errno.ELIBBAD, unix.ELIBBAD)
+ ErrLibSectionCorrupted = newWithHost(".lib section in a.out corrupted", errno.ELIBSCN, unix.ELIBSCN)
+ ErrTooManySharedLibraries = newWithHost("attempting to link in too many shared libraries", errno.ELIBMAX, unix.ELIBMAX)
+ ErrSharedLibraryExeced = newWithHost("cannot exec a shared library directly", errno.ELIBEXEC, unix.ELIBEXEC)
+ ErrIllegalByteSequence = newWithHost("illegal byte sequence", errno.EILSEQ, unix.EILSEQ)
+ ErrShouldRestart = newWithHost("interrupted system call should be restarted", errno.ERESTART, unix.ERESTART)
+ ErrStreamPipe = newWithHost("streams pipe error", errno.ESTRPIPE, unix.ESTRPIPE)
+ ErrTooManyUsers = newWithHost("too many users", errno.EUSERS, unix.EUSERS)
+ ErrNotASocket = newWithHost("socket operation on non-socket", errno.ENOTSOCK, unix.ENOTSOCK)
+ ErrDestinationAddressRequired = newWithHost("destination address required", errno.EDESTADDRREQ, unix.EDESTADDRREQ)
+ ErrMessageTooLong = newWithHost("message too long", errno.EMSGSIZE, unix.EMSGSIZE)
+ ErrWrongProtocolForSocket = newWithHost("protocol wrong type for socket", errno.EPROTOTYPE, unix.EPROTOTYPE)
+ ErrProtocolNotAvailable = newWithHost("protocol not available", errno.ENOPROTOOPT, unix.ENOPROTOOPT)
+ ErrProtocolNotSupported = newWithHost("protocol not supported", errno.EPROTONOSUPPORT, unix.EPROTONOSUPPORT)
+ ErrSocketNotSupported = newWithHost("socket type not supported", errno.ESOCKTNOSUPPORT, unix.ESOCKTNOSUPPORT)
+ ErrEndpointOperation = newWithHost("operation not supported on transport endpoint", errno.EOPNOTSUPP, unix.EOPNOTSUPP)
+ ErrProtocolFamilyNotSupported = newWithHost("protocol family not supported", errno.EPFNOSUPPORT, unix.EPFNOSUPPORT)
+ ErrAddressFamilyNotSupported = newWithHost("address family not supported by protocol", errno.EAFNOSUPPORT, unix.EAFNOSUPPORT)
+ ErrAddressInUse = newWithHost("address already in use", errno.EADDRINUSE, unix.EADDRINUSE)
+ ErrAddressNotAvailable = newWithHost("cannot assign requested address", errno.EADDRNOTAVAIL, unix.EADDRNOTAVAIL)
+ ErrNetworkDown = newWithHost("network is down", errno.ENETDOWN, unix.ENETDOWN)
+ ErrNetworkUnreachable = newWithHost("network is unreachable", errno.ENETUNREACH, unix.ENETUNREACH)
+ ErrNetworkReset = newWithHost("network dropped connection because of reset", errno.ENETRESET, unix.ENETRESET)
+ ErrConnectionAborted = newWithHost("software caused connection abort", errno.ECONNABORTED, unix.ECONNABORTED)
+ ErrConnectionReset = newWithHost("connection reset by peer", errno.ECONNRESET, unix.ECONNRESET)
+ ErrNoBufferSpace = newWithHost("no buffer space available", errno.ENOBUFS, unix.ENOBUFS)
+ ErrAlreadyConnected = newWithHost("transport endpoint is already connected", errno.EISCONN, unix.EISCONN)
+ ErrNotConnected = newWithHost("transport endpoint is not connected", errno.ENOTCONN, unix.ENOTCONN)
+ ErrShutdown = newWithHost("cannot send after transport endpoint shutdown", errno.ESHUTDOWN, unix.ESHUTDOWN)
+ ErrTooManyRefs = newWithHost("too many references: cannot splice", errno.ETOOMANYREFS, unix.ETOOMANYREFS)
+ ErrTimedOut = newWithHost("connection timed out", errno.ETIMEDOUT, unix.ETIMEDOUT)
+ ErrConnectionRefused = newWithHost("connection refused", errno.ECONNREFUSED, unix.ECONNREFUSED)
+ ErrHostDown = newWithHost("host is down", errno.EHOSTDOWN, unix.EHOSTDOWN)
+ ErrNoRoute = newWithHost("no route to host", errno.EHOSTUNREACH, unix.EHOSTUNREACH)
+ ErrAlreadyInProgress = newWithHost("operation already in progress", errno.EALREADY, unix.EALREADY)
+ ErrInProgress = newWithHost("operation now in progress", errno.EINPROGRESS, unix.EINPROGRESS)
+ ErrStaleFileHandle = newWithHost("stale file handle", errno.ESTALE, unix.ESTALE)
+ ErrStructureNeedsCleaning = newWithHost("structure needs cleaning", errno.EUCLEAN, unix.EUCLEAN)
+ ErrIsNamedFile = newWithHost("is a named type file", errno.ENOTNAM, unix.ENOTNAM)
+ ErrRemoteIO = newWithHost("remote I/O error", errno.EREMOTEIO, unix.EREMOTEIO)
+ ErrQuotaExceeded = newWithHost("quota exceeded", errno.EDQUOT, unix.EDQUOT)
+ ErrNoMedium = newWithHost("no medium found", errno.ENOMEDIUM, unix.ENOMEDIUM)
+ ErrWrongMediumType = newWithHost("wrong medium type", errno.EMEDIUMTYPE, unix.EMEDIUMTYPE)
+ ErrCanceled = newWithHost("operation canceled", errno.ECANCELED, unix.ECANCELED)
+ ErrNoKey = newWithHost("required key not available", errno.ENOKEY, unix.ENOKEY)
+ ErrKeyExpired = newWithHost("key has expired", errno.EKEYEXPIRED, unix.EKEYEXPIRED)
+ ErrKeyRevoked = newWithHost("key has been revoked", errno.EKEYREVOKED, unix.EKEYREVOKED)
+ ErrKeyRejected = newWithHost("key was rejected by service", errno.EKEYREJECTED, unix.EKEYREJECTED)
+ ErrOwnerDied = newWithHost("owner died", errno.EOWNERDEAD, unix.EOWNERDEAD)
+ ErrNotRecoverable = newWithHost("state not recoverable", errno.ENOTRECOVERABLE, unix.ENOTRECOVERABLE)
// ErrWouldBlock translates to EWOULDBLOCK which is the same as EAGAIN
// on Linux.
- ErrWouldBlock = New("operation would block", linux.EWOULDBLOCK)
+ ErrWouldBlock = New("operation would block", errno.EWOULDBLOCK)
)
// FromError converts a generic error to an *Error.
@@ -281,6 +282,11 @@ func FromError(err error) *Error {
if errno, ok := err.(unix.Errno); ok {
return FromHost(errno)
}
+
+ if linuxErr, ok := err.(*errors.Error); ok {
+ return FromHost(unix.Errno(linuxErr.Errno()))
+ }
+
if errno, ok := syserror.TranslateError(err); ok {
return FromHost(errno)
}
diff --git a/pkg/syserror/syserror.go b/pkg/syserror/syserror.go
index 56b621357..7fc6f8a7a 100644
--- a/pkg/syserror/syserror.go
+++ b/pkg/syserror/syserror.go
@@ -26,63 +26,23 @@ import (
// The following variables have the same meaning as their syscall equivalent.
var (
- E2BIG = error(unix.E2BIG)
- EACCES = error(unix.EACCES)
- EADDRINUSE = error(unix.EADDRINUSE)
- EAGAIN = error(unix.EAGAIN)
- EBADF = error(unix.EBADF)
- EBADFD = error(unix.EBADFD)
- EBUSY = error(unix.EBUSY)
- ECHILD = error(unix.ECHILD)
- ECONNABORTED = error(unix.ECONNABORTED)
- ECONNREFUSED = error(unix.ECONNREFUSED)
- ECONNRESET = error(unix.ECONNRESET)
- EDEADLK = error(unix.EDEADLK)
- EEXIST = error(unix.EEXIST)
- EFAULT = error(unix.EFAULT)
- EFBIG = error(unix.EFBIG)
- EIDRM = error(unix.EIDRM)
- EINTR = error(unix.EINTR)
- EINVAL = error(unix.EINVAL)
- EIO = error(unix.EIO)
- EISDIR = error(unix.EISDIR)
- ELIBBAD = error(unix.ELIBBAD)
- ELOOP = error(unix.ELOOP)
- EMFILE = error(unix.EMFILE)
- EMLINK = error(unix.EMLINK)
- EMSGSIZE = error(unix.EMSGSIZE)
- ENAMETOOLONG = error(unix.ENAMETOOLONG)
- ENOATTR = ENODATA
- ENOBUFS = error(unix.ENOBUFS)
- ENODATA = error(unix.ENODATA)
- ENODEV = error(unix.ENODEV)
- ENOENT = error(unix.ENOENT)
- ENOEXEC = error(unix.ENOEXEC)
- ENOLCK = error(unix.ENOLCK)
- ENOLINK = error(unix.ENOLINK)
- ENOMEM = error(unix.ENOMEM)
- ENOSPC = error(unix.ENOSPC)
- ENOSYS = error(unix.ENOSYS)
- ENOTCONN = error(unix.ENOTCONN)
- ENOTDIR = error(unix.ENOTDIR)
- ENOTEMPTY = error(unix.ENOTEMPTY)
- ENOTSOCK = error(unix.ENOTSOCK)
- ENOTSUP = error(unix.ENOTSUP)
- ENOTTY = error(unix.ENOTTY)
- ENXIO = error(unix.ENXIO)
- EOPNOTSUPP = error(unix.EOPNOTSUPP)
- EOVERFLOW = error(unix.EOVERFLOW)
- EPERM = error(unix.EPERM)
- EPIPE = error(unix.EPIPE)
- ERANGE = error(unix.ERANGE)
- EREMOTE = error(unix.EREMOTE)
- EROFS = error(unix.EROFS)
- ESPIPE = error(unix.ESPIPE)
- ESRCH = error(unix.ESRCH)
- ETIMEDOUT = error(unix.ETIMEDOUT)
- EUSERS = error(unix.EUSERS)
- EWOULDBLOCK = error(unix.EWOULDBLOCK)
- EXDEV = error(unix.EXDEV)
+ EEXIST = error(unix.EEXIST)
+ EFAULT = error(unix.EFAULT)
+ EIDRM = error(unix.EIDRM)
+ EINTR = error(unix.EINTR)
+ EIO = error(unix.EIO)
+ EISDIR = error(unix.EISDIR)
+ ENOENT = error(unix.ENOENT)
+ ENOEXEC = error(unix.ENOEXEC)
+ ENOMEM = error(unix.ENOMEM)
+ ENOTSOCK = error(unix.ENOTSOCK)
+ ENOSPC = error(unix.ENOSPC)
+ ENOSYS = error(unix.ENOSYS)
+ ENOTDIR = error(unix.ENOTDIR)
+ ENOTTY = error(unix.ENOTTY)
+ EOPNOTSUPP = error(unix.EOPNOTSUPP)
+ ERANGE = error(unix.ERANGE)
+ ESRCH = error(unix.ESRCH)
)
var (
diff --git a/pkg/tcpip/BUILD b/pkg/tcpip/BUILD
index ed4d7e958..f00cfd0f5 100644
--- a/pkg/tcpip/BUILD
+++ b/pkg/tcpip/BUILD
@@ -46,7 +46,6 @@ deps_test(
"//pkg/gohacks",
"//pkg/goid",
"//pkg/ilist",
- "//pkg/iovec",
"//pkg/linewriter",
"//pkg/log",
"//pkg/rand",
diff --git a/pkg/tcpip/header/checksum.go b/pkg/tcpip/header/checksum.go
index 6aa9acfa8..e2c85e220 100644
--- a/pkg/tcpip/header/checksum.go
+++ b/pkg/tcpip/header/checksum.go
@@ -18,6 +18,7 @@ package header
import (
"encoding/binary"
+ "fmt"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
@@ -234,3 +235,64 @@ func PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, srcAddr tcpip.
return Checksum([]byte{0, uint8(protocol)}, xsum)
}
+
+// checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated
+// checksum.
+//
+// The value MUST begin at a 2-byte boundary in the original buffer.
+func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 {
+ // As per RFC 1071 page 4,
+ // (4) Incremental Update
+ //
+ // ...
+ //
+ // To update the checksum, simply add the differences of the
+ // sixteen bit integers that have been changed. To see why this
+ // works, observe that every 16-bit integer has an additive inverse
+ // and that addition is associative. From this it follows that
+ // given the original value m, the new value m', and the old
+ // checksum C, the new checksum C' is:
+ //
+ // C' = C + (-m) + m' = C + (m' - m)
+ return ChecksumCombine(xsum, ChecksumCombine(new, ^old))
+}
+
+// checksumUpdate2ByteAlignedAddress updates an address in a calculated
+// checksum.
+//
+// The addresses must have the same length and must contain an even number
+// of bytes. The address MUST begin at a 2-byte boundary in the original buffer.
+func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new tcpip.Address) uint16 {
+ const uint16Bytes = 2
+
+ if len(old) != len(new) {
+ panic(fmt.Sprintf("buffer lengths are different; old = %d, new = %d", len(old), len(new)))
+ }
+
+ if len(old)%uint16Bytes != 0 {
+ panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", len(old)))
+ }
+
+ // As per RFC 1071 page 4,
+ // (4) Incremental Update
+ //
+ // ...
+ //
+ // To update the checksum, simply add the differences of the
+ // sixteen bit integers that have been changed. To see why this
+ // works, observe that every 16-bit integer has an additive inverse
+ // and that addition is associative. From this it follows that
+ // given the original value m, the new value m', and the old
+ // checksum C, the new checksum C' is:
+ //
+ // C' = C + (-m) + m' = C + (m' - m)
+ for len(old) != 0 {
+ // Convert the 2 byte sequences to uint16 values then apply the increment
+ // update.
+ xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(old[0])<<8)+uint16(old[1]), (uint16(new[0])<<8)+uint16(new[1]))
+ old = old[uint16Bytes:]
+ new = new[uint16Bytes:]
+ }
+
+ return xsum
+}
diff --git a/pkg/tcpip/header/checksum_test.go b/pkg/tcpip/header/checksum_test.go
index d267dabd0..3445511f4 100644
--- a/pkg/tcpip/header/checksum_test.go
+++ b/pkg/tcpip/header/checksum_test.go
@@ -23,6 +23,7 @@ import (
"sync"
"testing"
+ "gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
@@ -256,3 +257,205 @@ func TestICMPv6Checksum(t *testing.T) {
})
}, want, fmt.Sprintf("header: {% x} data {% x}", h, vv.ToView()))
}
+
+func randomAddress(size int) tcpip.Address {
+ s := make([]byte, size)
+ for i := 0; i < size; i++ {
+ s[i] = byte(rand.Uint32())
+ }
+ return tcpip.Address(s)
+}
+
+func TestChecksummableNetworkUpdateAddress(t *testing.T) {
+ tests := []struct {
+ name string
+ update func(header.IPv4, tcpip.Address)
+ }{
+ {
+ name: "SetSourceAddressWithChecksumUpdate",
+ update: header.IPv4.SetSourceAddressWithChecksumUpdate,
+ },
+ {
+ name: "SetDestinationAddressWithChecksumUpdate",
+ update: header.IPv4.SetDestinationAddressWithChecksumUpdate,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ for i := 0; i < 1000; i++ {
+ var origBytes [header.IPv4MinimumSize]byte
+ header.IPv4(origBytes[:]).Encode(&header.IPv4Fields{
+ TOS: 1,
+ TotalLength: header.IPv4MinimumSize,
+ ID: 2,
+ Flags: 3,
+ FragmentOffset: 4,
+ TTL: 5,
+ Protocol: 6,
+ Checksum: 0,
+ SrcAddr: randomAddress(header.IPv4AddressSize),
+ DstAddr: randomAddress(header.IPv4AddressSize),
+ })
+
+ addr := randomAddress(header.IPv4AddressSize)
+
+ bytesCopy := origBytes
+ h := header.IPv4(bytesCopy[:])
+ origXSum := h.CalculateChecksum()
+ h.SetChecksum(^origXSum)
+
+ test.update(h, addr)
+ got := ^h.Checksum()
+ h.SetChecksum(0)
+ want := h.CalculateChecksum()
+ if got != want {
+ t.Errorf("got h.Checksum() = 0x%x, want = 0x%x; originalBytes = 0x%x, new addr = %s", got, want, origBytes, addr)
+ }
+ }
+ })
+ }
+}
+
+func TestChecksummableTransportUpdatePort(t *testing.T) {
+ // The fields in the pseudo header is not tested here so we just use 0.
+ const pseudoHeaderXSum = 0
+
+ tests := []struct {
+ name string
+ transportHdr func(_, _ uint16) (header.ChecksummableTransport, func(uint16) uint16)
+ proto tcpip.TransportProtocolNumber
+ }{
+ {
+ name: "TCP",
+ transportHdr: func(src, dst uint16) (header.ChecksummableTransport, func(uint16) uint16) {
+ h := header.TCP(make([]byte, header.TCPMinimumSize))
+ h.Encode(&header.TCPFields{
+ SrcPort: src,
+ DstPort: dst,
+ SeqNum: 1,
+ AckNum: 2,
+ DataOffset: header.TCPMinimumSize,
+ Flags: 3,
+ WindowSize: 4,
+ Checksum: 0,
+ UrgentPointer: 5,
+ })
+ h.SetChecksum(^h.CalculateChecksum(pseudoHeaderXSum))
+ return h, h.CalculateChecksum
+ },
+ proto: header.TCPProtocolNumber,
+ },
+ {
+ name: "UDP",
+ transportHdr: func(src, dst uint16) (header.ChecksummableTransport, func(uint16) uint16) {
+ h := header.UDP(make([]byte, header.UDPMinimumSize))
+ h.Encode(&header.UDPFields{
+ SrcPort: src,
+ DstPort: dst,
+ Length: 0,
+ Checksum: 0,
+ })
+ h.SetChecksum(^h.CalculateChecksum(pseudoHeaderXSum))
+ return h, h.CalculateChecksum
+ },
+ proto: header.UDPProtocolNumber,
+ },
+ }
+
+ for i := 0; i < 1000; i++ {
+ origSrcPort := uint16(rand.Uint32())
+ origDstPort := uint16(rand.Uint32())
+ newPort := uint16(rand.Uint32())
+
+ t.Run(fmt.Sprintf("OrigSrcPort=%d,OrigDstPort=%d,NewPort=%d", origSrcPort, origDstPort, newPort), func(*testing.T) {
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ for _, subTest := range []struct {
+ name string
+ update func(header.ChecksummableTransport)
+ }{
+ {
+ name: "Source port",
+ update: func(h header.ChecksummableTransport) { h.SetSourcePortWithChecksumUpdate(newPort) },
+ },
+ {
+ name: "Destination port",
+ update: func(h header.ChecksummableTransport) { h.SetDestinationPortWithChecksumUpdate(newPort) },
+ },
+ } {
+ t.Run(subTest.name, func(t *testing.T) {
+ h, calcXSum := test.transportHdr(origSrcPort, origDstPort)
+ subTest.update(h)
+ // TCP and UDP hold the 1s complement of the fully calculated
+ // checksum.
+ got := ^h.Checksum()
+ h.SetChecksum(0)
+
+ if want := calcXSum(pseudoHeaderXSum); got != want {
+ h, _ := test.transportHdr(origSrcPort, origDstPort)
+ t.Errorf("got Checksum() = 0x%x, want = 0x%x; originalBytes = %#v, new port = %d", got, want, h, newPort)
+ }
+ })
+ }
+ })
+ }
+ })
+ }
+}
+
+func TestChecksummableTransportUpdatePseudoHeaderAddress(t *testing.T) {
+ const addressSize = 6
+
+ tests := []struct {
+ name string
+ transportHdr func() header.ChecksummableTransport
+ proto tcpip.TransportProtocolNumber
+ }{
+ {
+ name: "TCP",
+ transportHdr: func() header.ChecksummableTransport { return header.TCP(make([]byte, header.TCPMinimumSize)) },
+ proto: header.TCPProtocolNumber,
+ },
+ {
+ name: "UDP",
+ transportHdr: func() header.ChecksummableTransport { return header.UDP(make([]byte, header.UDPMinimumSize)) },
+ proto: header.UDPProtocolNumber,
+ },
+ }
+
+ for i := 0; i < 1000; i++ {
+ permanent := randomAddress(addressSize)
+ old := randomAddress(addressSize)
+ new := randomAddress(addressSize)
+
+ t.Run(fmt.Sprintf("Permanent=%q,Old=%q,New=%q", permanent, old, new), func(t *testing.T) {
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ for _, fullChecksum := range []bool{true, false} {
+ t.Run(fmt.Sprintf("FullChecksum=%t", fullChecksum), func(t *testing.T) {
+ initialXSum := header.PseudoHeaderChecksum(test.proto, permanent, old, 0)
+ if fullChecksum {
+ // TCP and UDP hold the 1s complement of the fully calculated
+ // checksum.
+ initialXSum = ^initialXSum
+ }
+
+ h := test.transportHdr()
+ h.SetChecksum(initialXSum)
+ h.UpdateChecksumPseudoHeaderAddress(old, new, fullChecksum)
+
+ got := h.Checksum()
+ if fullChecksum {
+ got = ^got
+ }
+ if want := header.PseudoHeaderChecksum(test.proto, permanent, new, 0); got != want {
+ t.Errorf("got Checksum() = 0x%x, want = 0x%x; h = %#v", got, want, h)
+ }
+ })
+ }
+ })
+ }
+ })
+ }
+}
diff --git a/pkg/tcpip/header/interfaces.go b/pkg/tcpip/header/interfaces.go
index 861cbbb70..3a41adfc4 100644
--- a/pkg/tcpip/header/interfaces.go
+++ b/pkg/tcpip/header/interfaces.go
@@ -53,6 +53,31 @@ type Transport interface {
Payload() []byte
}
+// ChecksummableTransport is a Transport that supports checksumming.
+type ChecksummableTransport interface {
+ Transport
+
+ // SetSourcePortWithChecksumUpdate sets the source port and updates
+ // the checksum.
+ //
+ // The receiver's checksum must be a fully calculated checksum.
+ SetSourcePortWithChecksumUpdate(port uint16)
+
+ // SetDestinationPortWithChecksumUpdate sets the destination port and updates
+ // the checksum.
+ //
+ // The receiver's checksum must be a fully calculated checksum.
+ SetDestinationPortWithChecksumUpdate(port uint16)
+
+ // UpdateChecksumPseudoHeaderAddress updates the checksum to reflect an
+ // updated address in the pseudo header.
+ //
+ // If fullChecksum is true, the receiver's checksum field is assumed to hold a
+ // fully calculated checksum. Otherwise, it is assumed to hold a partially
+ // calculated checksum which only reflects the pseudo header.
+ UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool)
+}
+
// Network offers generic methods to query and/or update the fields of the
// header of a network protocol buffer.
type Network interface {
@@ -90,3 +115,16 @@ type Network interface {
// SetTOS sets the values of the "type of service" and "flow label" fields.
SetTOS(t uint8, l uint32)
}
+
+// ChecksummableNetwork is a Network that supports checksumming.
+type ChecksummableNetwork interface {
+ Network
+
+ // SetSourceAddressAndChecksum sets the source address and updates the
+ // checksum to reflect the new address.
+ SetSourceAddressWithChecksumUpdate(tcpip.Address)
+
+ // SetDestinationAddressAndChecksum sets the destination address and
+ // updates the checksum to reflect the new address.
+ SetDestinationAddressWithChecksumUpdate(tcpip.Address)
+}
diff --git a/pkg/tcpip/header/ipv4.go b/pkg/tcpip/header/ipv4.go
index e9abbb709..dcc549c7b 100644
--- a/pkg/tcpip/header/ipv4.go
+++ b/pkg/tcpip/header/ipv4.go
@@ -305,6 +305,18 @@ func (b IPv4) DestinationAddress() tcpip.Address {
return tcpip.Address(b[dstAddr : dstAddr+IPv4AddressSize])
}
+// SetSourceAddressWithChecksumUpdate implements ChecksummableNetwork.
+func (b IPv4) SetSourceAddressWithChecksumUpdate(new tcpip.Address) {
+ b.SetChecksum(^checksumUpdate2ByteAlignedAddress(^b.Checksum(), b.SourceAddress(), new))
+ b.SetSourceAddress(new)
+}
+
+// SetDestinationAddressWithChecksumUpdate implements ChecksummableNetwork.
+func (b IPv4) SetDestinationAddressWithChecksumUpdate(new tcpip.Address) {
+ b.SetChecksum(^checksumUpdate2ByteAlignedAddress(^b.Checksum(), b.DestinationAddress(), new))
+ b.SetDestinationAddress(new)
+}
+
// padIPv4OptionsLength returns the total length for IPv4 options of length l
// after applying padding according to RFC 791:
// The internet header padding is used to ensure that the internet
diff --git a/pkg/tcpip/header/ndp_options.go b/pkg/tcpip/header/ndp_options.go
index d6cad3a94..a647ea968 100644
--- a/pkg/tcpip/header/ndp_options.go
+++ b/pkg/tcpip/header/ndp_options.go
@@ -148,15 +148,10 @@ const (
// NDP option. That is, the length field for NDP options is in units of
// 8 octets, as per RFC 4861 section 4.6.
lengthByteUnits = 8
-)
-var (
// NDPInfiniteLifetime is a value that represents infinity for the
// 4-byte lifetime fields found in various NDP options. Its value is
// (2^32 - 1)s = 4294967295s.
- //
- // This is a variable instead of a constant so that tests can change
- // this value to a smaller value. It should only be modified by tests.
NDPInfiniteLifetime = time.Second * math.MaxUint32
)
@@ -238,6 +233,17 @@ func (i *NDPOptionIterator) Next() (NDPOption, bool, error) {
case ndpNonceOptionType:
return NDPNonceOption(body), false, nil
+ case ndpRouteInformationType:
+ if numBodyBytes > ndpRouteInformationMaxLength {
+ return nil, true, fmt.Errorf("got %d bytes for NDP Route Information option's body, expected at max %d bytes: %w", numBodyBytes, ndpRouteInformationMaxLength, ErrNDPOptMalformedBody)
+ }
+ opt := NDPRouteInformation(body)
+ if err := opt.hasError(); err != nil {
+ return nil, true, err
+ }
+
+ return opt, false, nil
+
case ndpPrefixInformationType:
// Make sure the length of a Prefix Information option
// body is ndpPrefixInformationLength, as per RFC 4861
@@ -935,3 +941,137 @@ func isUpperLetter(b byte) bool {
func isDigit(b byte) bool {
return b >= '0' && b <= '9'
}
+
+// As per RFC 4191 section 2.3,
+//
+// 2.3. Route Information Option
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Type | Length | Prefix Length |Resvd|Prf|Resvd|
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Route Lifetime |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Prefix (Variable Length) |
+// . .
+// . .
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// Fields:
+//
+// Type 24
+//
+//
+// Length 8-bit unsigned integer. The length of the option
+// (including the Type and Length fields) in units of 8
+// octets. The Length field is 1, 2, or 3 depending on the
+// Prefix Length. If Prefix Length is greater than 64, then
+// Length must be 3. If Prefix Length is greater than 0,
+// then Length must be 2 or 3. If Prefix Length is zero,
+// then Length must be 1, 2, or 3.
+const (
+ ndpRouteInformationType = ndpOptionIdentifier(24)
+ ndpRouteInformationMaxLength = 22
+
+ ndpRouteInformationPrefixLengthIdx = 0
+ ndpRouteInformationFlagsIdx = 1
+ ndpRouteInformationPrfShift = 3
+ ndpRouteInformationPrfMask = 3 << ndpRouteInformationPrfShift
+ ndpRouteInformationRouteLifetimeIdx = 2
+ ndpRouteInformationRoutePrefixIdx = 6
+)
+
+// NDPRouteInformation is the NDP Router Information option, as defined by
+// RFC 4191 section 2.3.
+type NDPRouteInformation []byte
+
+func (NDPRouteInformation) kind() ndpOptionIdentifier {
+ return ndpRouteInformationType
+}
+
+func (o NDPRouteInformation) length() int {
+ return len(o)
+}
+
+func (o NDPRouteInformation) serializeInto(b []byte) int {
+ return copy(b, o)
+}
+
+// String implements fmt.Stringer.
+func (o NDPRouteInformation) String() string {
+ return fmt.Sprintf("%T", o)
+}
+
+// PrefixLength returns the length of the prefix.
+func (o NDPRouteInformation) PrefixLength() uint8 {
+ return o[ndpRouteInformationPrefixLengthIdx]
+}
+
+// RoutePreference returns the preference of the route over other routes to the
+// same destination but through a different router.
+func (o NDPRouteInformation) RoutePreference() NDPRoutePreference {
+ return NDPRoutePreference((o[ndpRouteInformationFlagsIdx] & ndpRouteInformationPrfMask) >> ndpRouteInformationPrfShift)
+}
+
+// RouteLifetime returns the lifetime of the route.
+//
+// Note, a value of 0 implies the route is now invalid and a value of
+// infinity/forever is represented by NDPInfiniteLifetime.
+func (o NDPRouteInformation) RouteLifetime() time.Duration {
+ return time.Second * time.Duration(binary.BigEndian.Uint32(o[ndpRouteInformationRouteLifetimeIdx:]))
+}
+
+// Prefix returns the prefix of the destination subnet this route is for.
+func (o NDPRouteInformation) Prefix() (tcpip.Subnet, error) {
+ prefixLength := int(o.PrefixLength())
+ if max := IPv6AddressSize * 8; prefixLength > max {
+ return tcpip.Subnet{}, fmt.Errorf("got prefix length = %d, want <= %d", prefixLength, max)
+ }
+
+ prefix := o[ndpRouteInformationRoutePrefixIdx:]
+ var addrBytes [IPv6AddressSize]byte
+ if n := copy(addrBytes[:], prefix); n != len(prefix) {
+ panic(fmt.Sprintf("got copy(addrBytes, prefix) = %d, want = %d", n, len(prefix)))
+ }
+
+ return tcpip.AddressWithPrefix{
+ Address: tcpip.Address(addrBytes[:]),
+ PrefixLen: prefixLength,
+ }.Subnet(), nil
+}
+
+func (o NDPRouteInformation) hasError() error {
+ l := len(o)
+ if l < ndpRouteInformationRoutePrefixIdx {
+ return fmt.Errorf("%T too small, got = %d bytes: %w", o, l, ErrNDPOptMalformedBody)
+ }
+
+ prefixLength := int(o.PrefixLength())
+ if max := IPv6AddressSize * 8; prefixLength > max {
+ return fmt.Errorf("got prefix length = %d, want <= %d: %w", prefixLength, max, ErrNDPOptMalformedBody)
+ }
+
+ // Length 8-bit unsigned integer. The length of the option
+ // (including the Type and Length fields) in units of 8
+ // octets. The Length field is 1, 2, or 3 depending on the
+ // Prefix Length. If Prefix Length is greater than 64, then
+ // Length must be 3. If Prefix Length is greater than 0,
+ // then Length must be 2 or 3. If Prefix Length is zero,
+ // then Length must be 1, 2, or 3.
+ l += 2 // Add 2 bytes for the type and length bytes.
+ lengthField := l / lengthByteUnits
+ if prefixLength > 64 {
+ if lengthField != 3 {
+ return fmt.Errorf("Length field must be 3 when Prefix Length (%d) is > 64 (got = %d): %w", prefixLength, lengthField, ErrNDPOptMalformedBody)
+ }
+ } else if prefixLength > 0 {
+ if lengthField != 2 && lengthField != 3 {
+ return fmt.Errorf("Length field must be 2 or 3 when Prefix Length (%d) is between 0 and 64 (got = %d): %w", prefixLength, lengthField, ErrNDPOptMalformedBody)
+ }
+ } else if lengthField == 0 || lengthField > 3 {
+ return fmt.Errorf("Length field must be 1, 2, or 3 when Prefix Length is zero (got = %d): %w", lengthField, ErrNDPOptMalformedBody)
+ }
+
+ return nil
+}
diff --git a/pkg/tcpip/header/ndp_router_advert.go b/pkg/tcpip/header/ndp_router_advert.go
index bf7610863..7d6efa083 100644
--- a/pkg/tcpip/header/ndp_router_advert.go
+++ b/pkg/tcpip/header/ndp_router_advert.go
@@ -16,15 +16,94 @@ package header
import (
"encoding/binary"
+ "fmt"
"time"
)
+var _ fmt.Stringer = NDPRoutePreference(0)
+
+// NDPRoutePreference is the preference values for default routers or
+// more-specific routes.
+//
+// As per RFC 4191 section 2.1,
+//
+// Default router preferences and preferences for more-specific routes
+// are encoded the same way.
+//
+// Preference values are encoded as a two-bit signed integer, as
+// follows:
+//
+// 01 High
+// 00 Medium (default)
+// 11 Low
+// 10 Reserved - MUST NOT be sent
+//
+// Note that implementations can treat the value as a two-bit signed
+// integer.
+//
+// Having just three values reinforces that they are not metrics and
+// more values do not appear to be necessary for reasonable scenarios.
+type NDPRoutePreference uint8
+
+const (
+ // HighRoutePreference indicates a high preference, as per
+ // RFC 4191 section 2.1.
+ HighRoutePreference NDPRoutePreference = 0b01
+
+ // MediumRoutePreference indicates a medium preference, as per
+ // RFC 4191 section 2.1.
+ //
+ // This is the default preference value.
+ MediumRoutePreference = 0b00
+
+ // LowRoutePreference indicates a low preference, as per
+ // RFC 4191 section 2.1.
+ LowRoutePreference = 0b11
+
+ // ReservedRoutePreference is a reserved preference value, as per
+ // RFC 4191 section 2.1.
+ //
+ // It MUST NOT be sent.
+ ReservedRoutePreference = 0b10
+)
+
+// String implements fmt.Stringer.
+func (p NDPRoutePreference) String() string {
+ switch p {
+ case HighRoutePreference:
+ return "HighRoutePreference"
+ case MediumRoutePreference:
+ return "MediumRoutePreference"
+ case LowRoutePreference:
+ return "LowRoutePreference"
+ case ReservedRoutePreference:
+ return "ReservedRoutePreference"
+ default:
+ return fmt.Sprintf("NDPRoutePreference(%d)", p)
+ }
+}
+
// NDPRouterAdvert is an NDP Router Advertisement message. It will only contain
// the body of an ICMPv6 packet.
//
-// See RFC 4861 section 4.2 for more details.
+// See RFC 4861 section 4.2 and RFC 4191 section 2.2 for more details.
type NDPRouterAdvert []byte
+// As per RFC 4191 section 2.2,
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Type | Code | Checksum |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Cur Hop Limit |M|O|H|Prf|Resvd| Router Lifetime |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Reachable Time |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Retrans Timer |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Options ...
+// +-+-+-+-+-+-+-+-+-+-+-+-
const (
// NDPRAMinimumSize is the minimum size of a valid NDP Router
// Advertisement message (body of an ICMPv6 packet).
@@ -47,6 +126,14 @@ const (
// within the bit-field/flags byte of an NDPRouterAdvert.
ndpRAOtherConfFlagMask = (1 << 6)
+ // ndpDefaultRouterPreferenceShift is the shift of the Prf (Default Router
+ // Preference) field within the flags byte of an NDPRouterAdvert.
+ ndpDefaultRouterPreferenceShift = 3
+
+ // ndpDefaultRouterPreferenceMask is the mask of the Prf (Default Router
+ // Preference) field within the flags byte of an NDPRouterAdvert.
+ ndpDefaultRouterPreferenceMask = (0b11 << ndpDefaultRouterPreferenceShift)
+
// ndpRARouterLifetimeOffset is the start of the 2-byte Router Lifetime
// field within an NDPRouterAdvert.
ndpRARouterLifetimeOffset = 2
@@ -80,6 +167,11 @@ func (b NDPRouterAdvert) OtherConfFlag() bool {
return b[ndpRAFlagsOffset]&ndpRAOtherConfFlagMask != 0
}
+// DefaultRouterPreference returns the Default Router Preference field.
+func (b NDPRouterAdvert) DefaultRouterPreference() NDPRoutePreference {
+ return NDPRoutePreference((b[ndpRAFlagsOffset] & ndpDefaultRouterPreferenceMask) >> ndpDefaultRouterPreferenceShift)
+}
+
// RouterLifetime returns the lifetime associated with the default router. A
// value of 0 means the source of the Router Advertisement is not a default
// router and SHOULD NOT appear on the default router list. Note, a value of 0
diff --git a/pkg/tcpip/header/ndp_test.go b/pkg/tcpip/header/ndp_test.go
index 1b5093e58..2a897e938 100644
--- a/pkg/tcpip/header/ndp_test.go
+++ b/pkg/tcpip/header/ndp_test.go
@@ -21,6 +21,7 @@ import (
"fmt"
"io"
"regexp"
+ "strings"
"testing"
"time"
@@ -58,6 +59,224 @@ func TestNDPNeighborSolicit(t *testing.T) {
}
}
+func TestNDPRouteInformationOption(t *testing.T) {
+ tests := []struct {
+ name string
+
+ length uint8
+ prefixLength uint8
+ prf NDPRoutePreference
+ lifetimeS uint32
+ prefixBytes []byte
+ expectedPrefix tcpip.Subnet
+
+ expectedErr error
+ }{
+ {
+ name: "Length=1 with Prefix Length = 0",
+ length: 1,
+ prefixLength: 0,
+ prf: MediumRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: IPv6EmptySubnet,
+ },
+ {
+ name: "Length=1 but Prefix Length > 0",
+ length: 1,
+ prefixLength: 1,
+ prf: MediumRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedErr: ErrNDPOptMalformedBody,
+ },
+ {
+ name: "Length=2 with Prefix Length = 0",
+ length: 2,
+ prefixLength: 0,
+ prf: MediumRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: IPv6EmptySubnet,
+ },
+ {
+ name: "Length=2 with Prefix Length in [1, 64] (1)",
+ length: 2,
+ prefixLength: 1,
+ prf: LowRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: tcpip.AddressWithPrefix{
+ Address: tcpip.Address(strings.Repeat("\x00", IPv6AddressSize)),
+ PrefixLen: 1,
+ }.Subnet(),
+ },
+ {
+ name: "Length=2 with Prefix Length in [1, 64] (64)",
+ length: 2,
+ prefixLength: 64,
+ prf: HighRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: tcpip.AddressWithPrefix{
+ Address: tcpip.Address(strings.Repeat("\x00", IPv6AddressSize)),
+ PrefixLen: 64,
+ }.Subnet(),
+ },
+ {
+ name: "Length=2 with Prefix Length > 64",
+ length: 2,
+ prefixLength: 65,
+ prf: HighRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedErr: ErrNDPOptMalformedBody,
+ },
+ {
+ name: "Length=3 with Prefix Length = 0",
+ length: 3,
+ prefixLength: 0,
+ prf: MediumRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: IPv6EmptySubnet,
+ },
+ {
+ name: "Length=3 with Prefix Length in [1, 64] (1)",
+ length: 3,
+ prefixLength: 1,
+ prf: LowRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: tcpip.AddressWithPrefix{
+ Address: tcpip.Address(strings.Repeat("\x00", IPv6AddressSize)),
+ PrefixLen: 1,
+ }.Subnet(),
+ },
+ {
+ name: "Length=3 with Prefix Length in [1, 64] (64)",
+ length: 3,
+ prefixLength: 64,
+ prf: HighRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: tcpip.AddressWithPrefix{
+ Address: tcpip.Address(strings.Repeat("\x00", IPv6AddressSize)),
+ PrefixLen: 64,
+ }.Subnet(),
+ },
+ {
+ name: "Length=3 with Prefix Length in [65, 128] (65)",
+ length: 3,
+ prefixLength: 65,
+ prf: HighRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: tcpip.AddressWithPrefix{
+ Address: tcpip.Address(strings.Repeat("\x00", IPv6AddressSize)),
+ PrefixLen: 65,
+ }.Subnet(),
+ },
+ {
+ name: "Length=3 with Prefix Length in [65, 128] (128)",
+ length: 3,
+ prefixLength: 128,
+ prf: HighRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedPrefix: tcpip.AddressWithPrefix{
+ Address: tcpip.Address(strings.Repeat("\x00", IPv6AddressSize)),
+ PrefixLen: 128,
+ }.Subnet(),
+ },
+ {
+ name: "Length=3 with (invalid) Prefix Length > 128",
+ length: 3,
+ prefixLength: 129,
+ prf: HighRoutePreference,
+ lifetimeS: 1,
+ prefixBytes: nil,
+ expectedErr: ErrNDPOptMalformedBody,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ expectedRouteInformationBytes := [...]byte{
+ // Type, Length
+ 24, test.length,
+
+ // Prefix Length, Prf
+ uint8(test.prefixLength), uint8(test.prf) << 3,
+
+ // Route Lifetime
+ 0, 0, 0, 0,
+
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ }
+ binary.BigEndian.PutUint32(expectedRouteInformationBytes[4:], test.lifetimeS)
+ _ = copy(expectedRouteInformationBytes[8:], test.prefixBytes)
+
+ opts := NDPOptions(expectedRouteInformationBytes[:test.length*lengthByteUnits])
+ it, err := opts.Iter(false)
+ if err != nil {
+ t.Fatalf("got Iter(false) = (_, %s), want = (_, nil)", err)
+ }
+ opt, done, err := it.Next()
+ if !errors.Is(err, test.expectedErr) {
+ t.Fatalf("got Next() = (_, _, %s), want = (_, _, %s)", err, test.expectedErr)
+ }
+ if want := test.expectedErr != nil; done != want {
+ t.Fatalf("got Next() = (_, %t, _), want = (_, %t, _)", done, want)
+ }
+ if test.expectedErr != nil {
+ return
+ }
+
+ if got := opt.kind(); got != ndpRouteInformationType {
+ t.Errorf("got kind() = %d, want = %d", got, ndpRouteInformationType)
+ }
+
+ ri, ok := opt.(NDPRouteInformation)
+ if !ok {
+ t.Fatalf("got opt = %T, want = NDPRouteInformation", opt)
+ }
+
+ if got := ri.PrefixLength(); got != test.prefixLength {
+ t.Errorf("got PrefixLength() = %d, want = %d", got, test.prefixLength)
+ }
+ if got := ri.RoutePreference(); got != test.prf {
+ t.Errorf("got RoutePreference() = %d, want = %d", got, test.prf)
+ }
+ if got, want := ri.RouteLifetime(), time.Duration(test.lifetimeS)*time.Second; got != want {
+ t.Errorf("got RouteLifetime() = %s, want = %s", got, want)
+ }
+ if got, err := ri.Prefix(); err != nil {
+ t.Errorf("Prefix(): %s", err)
+ } else if got != test.expectedPrefix {
+ t.Errorf("got Prefix() = %s, want = %s", got, test.expectedPrefix)
+ }
+
+ // Iterator should not return anything else.
+ {
+ next, done, err := it.Next()
+ if err != nil {
+ t.Errorf("got Next() = (_, _, %s), want = (_, _, nil)", err)
+ }
+ if !done {
+ t.Error("got Next() = (_, false, _), want = (_, true, _)")
+ }
+ if next != nil {
+ t.Errorf("got Next() = (%x, _, _), want = (nil, _, _)", next)
+ }
+ }
+ })
+ }
+}
+
// TestNDPNeighborAdvert tests the functions of NDPNeighborAdvert.
func TestNDPNeighborAdvert(t *testing.T) {
b := []byte{
@@ -126,36 +345,83 @@ func TestNDPNeighborAdvert(t *testing.T) {
}
func TestNDPRouterAdvert(t *testing.T) {
- b := []byte{
- 64, 128, 1, 2,
- 3, 4, 5, 6,
- 7, 8, 9, 10,
+ tests := []struct {
+ hopLimit uint8
+ managedFlag, otherConfFlag bool
+ prf NDPRoutePreference
+ routerLifetimeS uint16
+ reachableTimeMS, retransTimerMS uint32
+ }{
+ {
+ hopLimit: 1,
+ managedFlag: false,
+ otherConfFlag: true,
+ prf: HighRoutePreference,
+ routerLifetimeS: 2,
+ reachableTimeMS: 3,
+ retransTimerMS: 4,
+ },
+ {
+ hopLimit: 64,
+ managedFlag: true,
+ otherConfFlag: false,
+ prf: LowRoutePreference,
+ routerLifetimeS: 258,
+ reachableTimeMS: 78492,
+ retransTimerMS: 13213,
+ },
}
- ra := NDPRouterAdvert(b)
+ for i, test := range tests {
+ t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+ flags := uint8(0)
+ if test.managedFlag {
+ flags |= 1 << 7
+ }
+ if test.otherConfFlag {
+ flags |= 1 << 6
+ }
+ flags |= uint8(test.prf) << 3
+
+ b := []byte{
+ test.hopLimit, flags, 1, 2,
+ 3, 4, 5, 6,
+ 7, 8, 9, 10,
+ }
+ binary.BigEndian.PutUint16(b[2:], test.routerLifetimeS)
+ binary.BigEndian.PutUint32(b[4:], test.reachableTimeMS)
+ binary.BigEndian.PutUint32(b[8:], test.retransTimerMS)
- if got := ra.CurrHopLimit(); got != 64 {
- t.Errorf("got ra.CurrHopLimit = %d, want = 64", got)
- }
+ ra := NDPRouterAdvert(b)
- if got := ra.ManagedAddrConfFlag(); !got {
- t.Errorf("got ManagedAddrConfFlag = false, want = true")
- }
+ if got := ra.CurrHopLimit(); got != test.hopLimit {
+ t.Errorf("got ra.CurrHopLimit() = %d, want = %d", got, test.hopLimit)
+ }
- if got := ra.OtherConfFlag(); got {
- t.Errorf("got OtherConfFlag = true, want = false")
- }
+ if got := ra.ManagedAddrConfFlag(); got != test.managedFlag {
+ t.Errorf("got ManagedAddrConfFlag() = %t, want = %t", got, test.managedFlag)
+ }
- if got, want := ra.RouterLifetime(), time.Second*258; got != want {
- t.Errorf("got ra.RouterLifetime = %d, want = %d", got, want)
- }
+ if got := ra.OtherConfFlag(); got != test.otherConfFlag {
+ t.Errorf("got OtherConfFlag() = %t, want = %t", got, test.otherConfFlag)
+ }
- if got, want := ra.ReachableTime(), time.Millisecond*50595078; got != want {
- t.Errorf("got ra.ReachableTime = %d, want = %d", got, want)
- }
+ if got := ra.DefaultRouterPreference(); got != test.prf {
+ t.Errorf("got DefaultRouterPreference() = %d, want = %d", got, test.prf)
+ }
+
+ if got, want := ra.RouterLifetime(), time.Second*time.Duration(test.routerLifetimeS); got != want {
+ t.Errorf("got ra.RouterLifetime() = %d, want = %d", got, want)
+ }
+
+ if got, want := ra.ReachableTime(), time.Millisecond*time.Duration(test.reachableTimeMS); got != want {
+ t.Errorf("got ra.ReachableTime() = %d, want = %d", got, want)
+ }
- if got, want := ra.RetransTimer(), time.Millisecond*117967114; got != want {
- t.Errorf("got ra.RetransTimer = %d, want = %d", got, want)
+ if got, want := ra.RetransTimer(), time.Millisecond*time.Duration(test.retransTimerMS); got != want {
+ t.Errorf("got ra.RetransTimer() = %d, want = %d", got, want)
+ }
+ })
}
}
@@ -1451,3 +1717,32 @@ func TestNDPOptionsIter(t *testing.T) {
t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next)
}
}
+
+func TestNDPRoutePreferenceStringer(t *testing.T) {
+ p := NDPRoutePreference(0)
+ for {
+ var wantStr string
+ switch p {
+ case 0b01:
+ wantStr = "HighRoutePreference"
+ case 0b00:
+ wantStr = "MediumRoutePreference"
+ case 0b11:
+ wantStr = "LowRoutePreference"
+ case 0b10:
+ wantStr = "ReservedRoutePreference"
+ default:
+ wantStr = fmt.Sprintf("NDPRoutePreference(%d)", p)
+ }
+
+ if gotStr := p.String(); gotStr != wantStr {
+ t.Errorf("got NDPRoutePreference(%d).String() = %s, want = %s", p, gotStr, wantStr)
+ }
+
+ p++
+ if p == 0 {
+ // Overflowed, we hit all values.
+ break
+ }
+ }
+}
diff --git a/pkg/tcpip/header/tcp.go b/pkg/tcpip/header/tcp.go
index 8dabe3354..a75e51a28 100644
--- a/pkg/tcpip/header/tcp.go
+++ b/pkg/tcpip/header/tcp.go
@@ -390,6 +390,35 @@ func (b TCP) EncodePartial(partialChecksum, length uint16, seqnum, acknum uint32
b.SetChecksum(^checksum)
}
+// SetSourcePortWithChecksumUpdate implements ChecksummableTransport.
+func (b TCP) SetSourcePortWithChecksumUpdate(new uint16) {
+ old := b.SourcePort()
+ b.SetSourcePort(new)
+ b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
+}
+
+// SetDestinationPortWithChecksumUpdate implements ChecksummableTransport.
+func (b TCP) SetDestinationPortWithChecksumUpdate(new uint16) {
+ old := b.DestinationPort()
+ b.SetDestinationPort(new)
+ b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
+}
+
+// UpdateChecksumPseudoHeaderAddress implements ChecksummableTransport.
+func (b TCP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool) {
+ xsum := b.Checksum()
+ if fullChecksum {
+ xsum = ^xsum
+ }
+
+ xsum = checksumUpdate2ByteAlignedAddress(xsum, old, new)
+ if fullChecksum {
+ xsum = ^xsum
+ }
+
+ b.SetChecksum(xsum)
+}
+
// ParseSynOptions parses the options received in a SYN segment and returns the
// relevant ones. opts should point to the option part of the TCP header.
func ParseSynOptions(opts []byte, isAck bool) TCPSynOptions {
diff --git a/pkg/tcpip/header/udp.go b/pkg/tcpip/header/udp.go
index ae9d167ff..f69d53314 100644
--- a/pkg/tcpip/header/udp.go
+++ b/pkg/tcpip/header/udp.go
@@ -130,3 +130,32 @@ func (b UDP) Encode(u *UDPFields) {
binary.BigEndian.PutUint16(b[udpLength:], u.Length)
binary.BigEndian.PutUint16(b[udpChecksum:], u.Checksum)
}
+
+// SetSourcePortWithChecksumUpdate implements ChecksummableTransport.
+func (b UDP) SetSourcePortWithChecksumUpdate(new uint16) {
+ old := b.SourcePort()
+ b.SetSourcePort(new)
+ b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
+}
+
+// SetDestinationPortWithChecksumUpdate implements ChecksummableTransport.
+func (b UDP) SetDestinationPortWithChecksumUpdate(new uint16) {
+ old := b.DestinationPort()
+ b.SetDestinationPort(new)
+ b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
+}
+
+// UpdateChecksumPseudoHeaderAddress implements ChecksummableTransport.
+func (b UDP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool) {
+ xsum := b.Checksum()
+ if fullChecksum {
+ xsum = ^xsum
+ }
+
+ xsum = checksumUpdate2ByteAlignedAddress(xsum, old, new)
+ if fullChecksum {
+ xsum = ^xsum
+ }
+
+ b.SetChecksum(xsum)
+}
diff --git a/pkg/tcpip/link/fdbased/BUILD b/pkg/tcpip/link/fdbased/BUILD
index d971194e6..1d0163823 100644
--- a/pkg/tcpip/link/fdbased/BUILD
+++ b/pkg/tcpip/link/fdbased/BUILD
@@ -14,7 +14,6 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
- "//pkg/iovec",
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
diff --git a/pkg/tcpip/link/fdbased/endpoint.go b/pkg/tcpip/link/fdbased/endpoint.go
index bddb1d0a2..1b56d2b72 100644
--- a/pkg/tcpip/link/fdbased/endpoint.go
+++ b/pkg/tcpip/link/fdbased/endpoint.go
@@ -41,11 +41,9 @@ package fdbased
import (
"fmt"
- "math"
"sync/atomic"
"golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/iovec"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
@@ -139,6 +137,20 @@ type endpoint struct {
// gsoKind is the supported kind of GSO.
gsoKind stack.SupportedGSO
+
+ // maxSyscallHeaderBytes has the same meaning as
+ // Options.MaxSyscallHeaderBytes.
+ maxSyscallHeaderBytes uintptr
+
+ // writevMaxIovs is the maximum number of iovecs that may be passed to
+ // rawfile.NonBlockingWriteIovec, as possibly limited by
+ // maxSyscallHeaderBytes. (No analogous limit is defined for
+ // rawfile.NonBlockingSendMMsg, since in that case the maximum number of
+ // iovecs also depends on the number of mmsghdrs. Instead, if sendBatch
+ // encounters a packet whose iovec count is limited by
+ // maxSyscallHeaderBytes, it falls back to writing the packet using writev
+ // via WritePacket.)
+ writevMaxIovs int
}
// Options specify the details about the fd-based endpoint to be created.
@@ -187,6 +199,11 @@ type Options struct {
// RXChecksumOffload if true, indicates that this endpoints capability
// set should include CapabilityRXChecksumOffload.
RXChecksumOffload bool
+
+ // If MaxSyscallHeaderBytes is non-zero, it is the maximum number of bytes
+ // of struct iovec, msghdr, and mmsghdr that may be passed by each host
+ // system call.
+ MaxSyscallHeaderBytes int
}
// fanoutID is used for AF_PACKET based endpoints to enable PACKET_FANOUT
@@ -196,8 +213,12 @@ type Options struct {
// option for an FD with a fanoutID already in use by another FD for a different
// NIC will return an EINVAL.
//
+// Since fanoutID must be unique within the network namespace, we start with
+// the PID to avoid collisions. The only way to be sure of avoiding collisions
+// is to run in a new network namespace.
+//
// Must be accessed using atomic operations.
-var fanoutID int32 = 0
+var fanoutID int32 = int32(unix.Getpid())
// New creates a new fd-based endpoint.
//
@@ -232,14 +253,25 @@ func New(opts *Options) (stack.LinkEndpoint, error) {
return nil, fmt.Errorf("opts.FD is empty, at least one FD must be specified")
}
+ if opts.MaxSyscallHeaderBytes < 0 {
+ return nil, fmt.Errorf("opts.MaxSyscallHeaderBytes is negative")
+ }
+
e := &endpoint{
- fds: opts.FDs,
- mtu: opts.MTU,
- caps: caps,
- closed: opts.ClosedFunc,
- addr: opts.Address,
- hdrSize: hdrSize,
- packetDispatchMode: opts.PacketDispatchMode,
+ fds: opts.FDs,
+ mtu: opts.MTU,
+ caps: caps,
+ closed: opts.ClosedFunc,
+ addr: opts.Address,
+ hdrSize: hdrSize,
+ packetDispatchMode: opts.PacketDispatchMode,
+ maxSyscallHeaderBytes: uintptr(opts.MaxSyscallHeaderBytes),
+ writevMaxIovs: rawfile.MaxIovs,
+ }
+ if e.maxSyscallHeaderBytes != 0 {
+ if max := int(e.maxSyscallHeaderBytes / rawfile.SizeofIovec); max < e.writevMaxIovs {
+ e.writevMaxIovs = max
+ }
}
// Increment fanoutID to ensure that we don't re-use the same fanoutID for
@@ -292,11 +324,6 @@ func createInboundDispatcher(e *endpoint, fd int, isSocket bool, fID int32) (lin
}
switch sa.(type) {
case *unix.SockaddrLinklayer:
- // See: PACKET_FANOUT_MAX in net/packet/internal.h
- const packetFanoutMax = 1 << 16
- if fID > packetFanoutMax {
- return nil, fmt.Errorf("host fanoutID limit exceeded, fanoutID must be <= %d", math.MaxUint16)
- }
// Enable PACKET_FANOUT mode if the underlying socket is of type
// AF_PACKET. We do not enable PACKET_FANOUT_FLAG_DEFRAG as that will
// prevent gvisor from receiving fragmented packets and the host does the
@@ -317,7 +344,7 @@ func createInboundDispatcher(e *endpoint, fd int, isSocket bool, fID int32) (lin
//
// See: https://github.com/torvalds/linux/blob/7acac4b3196caee5e21fb5ea53f8bc124e6a16fc/net/packet/af_packet.c#L3881
const fanoutType = unix.PACKET_FANOUT_HASH
- fanoutArg := int(fID) | fanoutType<<16
+ fanoutArg := (int(fID) & 0xffff) | fanoutType<<16
if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_FANOUT, fanoutArg); err != nil {
return nil, fmt.Errorf("failed to enable PACKET_FANOUT option: %v", err)
}
@@ -472,9 +499,8 @@ func (e *endpoint) WritePacket(r stack.RouteInfo, protocol tcpip.NetworkProtocol
e.AddHeader(r.LocalLinkAddress, r.RemoteLinkAddress, protocol, pkt)
}
- var builder iovec.Builder
-
fd := e.fds[pkt.Hash%uint32(len(e.fds))]
+ var vnetHdrBuf []byte
if e.gsoKind == stack.HWGSOSupported {
vnetHdr := virtioNetHdr{}
if pkt.GSOOptions.Type != stack.GSONone {
@@ -496,71 +522,123 @@ func (e *endpoint) WritePacket(r stack.RouteInfo, protocol tcpip.NetworkProtocol
vnetHdr.gsoSize = pkt.GSOOptions.MSS
}
}
+ vnetHdrBuf = vnetHdr.marshal()
+ }
- vnetHdrBuf := vnetHdr.marshal()
- builder.Add(vnetHdrBuf)
+ views := pkt.Views()
+ numIovecs := len(views)
+ if len(vnetHdrBuf) != 0 {
+ numIovecs++
+ }
+ if numIovecs > e.writevMaxIovs {
+ numIovecs = e.writevMaxIovs
}
- for _, v := range pkt.Views() {
- builder.Add(v)
+ // Allocate small iovec arrays on the stack.
+ var iovecsArr [8]unix.Iovec
+ iovecs := iovecsArr[:0]
+ if numIovecs > len(iovecsArr) {
+ iovecs = make([]unix.Iovec, 0, numIovecs)
}
- return rawfile.NonBlockingWriteIovec(fd, builder.Build())
+ iovecs = rawfile.AppendIovecFromBytes(iovecs, vnetHdrBuf, numIovecs)
+ for _, v := range views {
+ iovecs = rawfile.AppendIovecFromBytes(iovecs, v, numIovecs)
+ }
+ return rawfile.NonBlockingWriteIovec(fd, iovecs)
}
-func (e *endpoint) sendBatch(batchFD int, batch []*stack.PacketBuffer) (int, tcpip.Error) {
+func (e *endpoint) sendBatch(batchFD int, pkts []*stack.PacketBuffer) (int, tcpip.Error) {
// Send a batch of packets through batchFD.
- mmsgHdrs := make([]rawfile.MMsgHdr, 0, len(batch))
- for _, pkt := range batch {
- if e.hdrSize > 0 {
- e.AddHeader(pkt.EgressRoute.LocalLinkAddress, pkt.EgressRoute.RemoteLinkAddress, pkt.NetworkProtocolNumber, pkt)
- }
+ mmsgHdrsStorage := make([]rawfile.MMsgHdr, 0, len(pkts))
+ packets := 0
+ for packets < len(pkts) {
+ mmsgHdrs := mmsgHdrsStorage
+ batch := pkts[packets:]
+ syscallHeaderBytes := uintptr(0)
+ for _, pkt := range batch {
+ if e.hdrSize > 0 {
+ e.AddHeader(pkt.EgressRoute.LocalLinkAddress, pkt.EgressRoute.RemoteLinkAddress, pkt.NetworkProtocolNumber, pkt)
+ }
- var vnetHdrBuf []byte
- if e.gsoKind == stack.HWGSOSupported {
- vnetHdr := virtioNetHdr{}
- if pkt.GSOOptions.Type != stack.GSONone {
- vnetHdr.hdrLen = uint16(pkt.HeaderSize())
- if pkt.GSOOptions.NeedsCsum {
- vnetHdr.flags = _VIRTIO_NET_HDR_F_NEEDS_CSUM
- vnetHdr.csumStart = header.EthernetMinimumSize + pkt.GSOOptions.L3HdrLen
- vnetHdr.csumOffset = pkt.GSOOptions.CsumOffset
- }
- if pkt.GSOOptions.Type != stack.GSONone && uint16(pkt.Data().Size()) > pkt.GSOOptions.MSS {
- switch pkt.GSOOptions.Type {
- case stack.GSOTCPv4:
- vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV4
- case stack.GSOTCPv6:
- vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV6
- default:
- panic(fmt.Sprintf("Unknown gso type: %v", pkt.GSOOptions.Type))
+ var vnetHdrBuf []byte
+ if e.gsoKind == stack.HWGSOSupported {
+ vnetHdr := virtioNetHdr{}
+ if pkt.GSOOptions.Type != stack.GSONone {
+ vnetHdr.hdrLen = uint16(pkt.HeaderSize())
+ if pkt.GSOOptions.NeedsCsum {
+ vnetHdr.flags = _VIRTIO_NET_HDR_F_NEEDS_CSUM
+ vnetHdr.csumStart = header.EthernetMinimumSize + pkt.GSOOptions.L3HdrLen
+ vnetHdr.csumOffset = pkt.GSOOptions.CsumOffset
+ }
+ if pkt.GSOOptions.Type != stack.GSONone && uint16(pkt.Data().Size()) > pkt.GSOOptions.MSS {
+ switch pkt.GSOOptions.Type {
+ case stack.GSOTCPv4:
+ vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV4
+ case stack.GSOTCPv6:
+ vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV6
+ default:
+ panic(fmt.Sprintf("Unknown gso type: %v", pkt.GSOOptions.Type))
+ }
+ vnetHdr.gsoSize = pkt.GSOOptions.MSS
}
- vnetHdr.gsoSize = pkt.GSOOptions.MSS
}
+ vnetHdrBuf = vnetHdr.marshal()
}
- vnetHdrBuf = vnetHdr.marshal()
- }
- var builder iovec.Builder
- builder.Add(vnetHdrBuf)
- for _, v := range pkt.Views() {
- builder.Add(v)
- }
- iovecs := builder.Build()
+ views := pkt.Views()
+ numIovecs := len(views)
+ if len(vnetHdrBuf) != 0 {
+ numIovecs++
+ }
+ if numIovecs > rawfile.MaxIovs {
+ numIovecs = rawfile.MaxIovs
+ }
+ if e.maxSyscallHeaderBytes != 0 {
+ syscallHeaderBytes += rawfile.SizeofMMsgHdr + uintptr(numIovecs)*rawfile.SizeofIovec
+ if syscallHeaderBytes > e.maxSyscallHeaderBytes {
+ // We can't fit this packet into this call to sendmmsg().
+ // We could potentially do so if we reduced numIovecs
+ // further, but this might incur considerable extra
+ // copying. Leave it to the next batch instead.
+ break
+ }
+ }
- var mmsgHdr rawfile.MMsgHdr
- mmsgHdr.Msg.Iov = &iovecs[0]
- mmsgHdr.Msg.SetIovlen((len(iovecs)))
- mmsgHdrs = append(mmsgHdrs, mmsgHdr)
- }
+ // We can't easily allocate iovec arrays on the stack here since
+ // they will escape this loop iteration via mmsgHdrs.
+ iovecs := make([]unix.Iovec, 0, numIovecs)
+ iovecs = rawfile.AppendIovecFromBytes(iovecs, vnetHdrBuf, numIovecs)
+ for _, v := range views {
+ iovecs = rawfile.AppendIovecFromBytes(iovecs, v, numIovecs)
+ }
- packets := 0
- for len(mmsgHdrs) > 0 {
- sent, err := rawfile.NonBlockingSendMMsg(batchFD, mmsgHdrs)
- if err != nil {
- return packets, err
+ var mmsgHdr rawfile.MMsgHdr
+ mmsgHdr.Msg.Iov = &iovecs[0]
+ mmsgHdr.Msg.SetIovlen(len(iovecs))
+ mmsgHdrs = append(mmsgHdrs, mmsgHdr)
+ }
+
+ if len(mmsgHdrs) == 0 {
+ // We can't fit batch[0] into a mmsghdr while staying under
+ // e.maxSyscallHeaderBytes. Use WritePacket, which will avoid the
+ // mmsghdr (by using writev) and re-buffer iovecs more aggressively
+ // if necessary (by using e.writevMaxIovs instead of
+ // rawfile.MaxIovs).
+ pkt := batch[0]
+ if err := e.WritePacket(pkt.EgressRoute, pkt.NetworkProtocolNumber, pkt); err != nil {
+ return packets, err
+ }
+ packets++
+ } else {
+ for len(mmsgHdrs) > 0 {
+ sent, err := rawfile.NonBlockingSendMMsg(batchFD, mmsgHdrs)
+ if err != nil {
+ return packets, err
+ }
+ packets += sent
+ mmsgHdrs = mmsgHdrs[sent:]
+ }
}
- packets += sent
- mmsgHdrs = mmsgHdrs[sent:]
}
return packets, nil
@@ -678,8 +756,9 @@ func NewInjectable(fd int, mtu uint32, capabilities stack.LinkEndpointCapabiliti
unix.SetNonblock(fd, true)
return &InjectableEndpoint{endpoint: endpoint{
- fds: []int{fd},
- mtu: mtu,
- caps: capabilities,
+ fds: []int{fd},
+ mtu: mtu,
+ caps: capabilities,
+ writevMaxIovs: rawfile.MaxIovs,
}}
}
diff --git a/pkg/tcpip/link/rawfile/rawfile_unsafe.go b/pkg/tcpip/link/rawfile/rawfile_unsafe.go
index ba92aedbc..43fe57830 100644
--- a/pkg/tcpip/link/rawfile/rawfile_unsafe.go
+++ b/pkg/tcpip/link/rawfile/rawfile_unsafe.go
@@ -19,12 +19,66 @@
package rawfile
import (
+ "reflect"
"unsafe"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip"
)
+// SizeofIovec is the size of a unix.Iovec in bytes.
+const SizeofIovec = unsafe.Sizeof(unix.Iovec{})
+
+// MaxIovs is UIO_MAXIOV, the maximum number of iovecs that may be passed to a
+// host system call in a single array.
+const MaxIovs = 1024
+
+// IovecFromBytes returns a unix.Iovec representing bs.
+//
+// Preconditions: len(bs) > 0.
+func IovecFromBytes(bs []byte) unix.Iovec {
+ iov := unix.Iovec{
+ Base: &bs[0],
+ }
+ iov.SetLen(len(bs))
+ return iov
+}
+
+func bytesFromIovec(iov unix.Iovec) (bs []byte) {
+ sh := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
+ sh.Data = uintptr(unsafe.Pointer(iov.Base))
+ sh.Len = int(iov.Len)
+ sh.Cap = int(iov.Len)
+ return
+}
+
+// AppendIovecFromBytes returns append(iovs, IovecFromBytes(bs)). If len(bs) ==
+// 0, AppendIovecFromBytes returns iovs without modification. If len(iovs) >=
+// max, AppendIovecFromBytes replaces the final iovec in iovs with one that
+// also includes the contents of bs. Note that this implies that
+// AppendIovecFromBytes is only usable when the returned iovec slice is used as
+// the source of a write.
+func AppendIovecFromBytes(iovs []unix.Iovec, bs []byte, max int) []unix.Iovec {
+ if len(bs) == 0 {
+ return iovs
+ }
+ if len(iovs) < max {
+ return append(iovs, IovecFromBytes(bs))
+ }
+ iovs[len(iovs)-1] = IovecFromBytes(append(bytesFromIovec(iovs[len(iovs)-1]), bs...))
+ return iovs
+}
+
+// MMsgHdr represents the mmsg_hdr structure required by recvmmsg() on linux.
+type MMsgHdr struct {
+ Msg unix.Msghdr
+ Len uint32
+ _ [4]byte
+}
+
+// SizeofMMsgHdr is the size of a MMsgHdr in bytes.
+const SizeofMMsgHdr = unsafe.Sizeof(MMsgHdr{})
+
// GetMTU determines the MTU of a network interface device.
func GetMTU(name string) (uint32, error) {
fd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_DGRAM, 0)
@@ -137,13 +191,6 @@ func BlockingReadv(fd int, iovecs []unix.Iovec) (int, tcpip.Error) {
}
}
-// MMsgHdr represents the mmsg_hdr structure required by recvmmsg() on linux.
-type MMsgHdr struct {
- Msg unix.Msghdr
- Len uint32
- _ [4]byte
-}
-
// BlockingRecvMMsg reads from a file descriptor that is set up as non-blocking
// and stores the received messages in a slice of MMsgHdr structures. If no data
// is available, it will block in a poll() syscall until the file descriptor
diff --git a/pkg/tcpip/link/tun/BUILD b/pkg/tcpip/link/tun/BUILD
index 7656cca6a..4758a99ad 100644
--- a/pkg/tcpip/link/tun/BUILD
+++ b/pkg/tcpip/link/tun/BUILD
@@ -26,6 +26,7 @@ go_library(
deps = [
"//pkg/abi/linux",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/log",
"//pkg/refs",
"//pkg/refsvfs2",
diff --git a/pkg/tcpip/link/tun/device.go b/pkg/tcpip/link/tun/device.go
index 36af2a029..f3444e8b5 100644
--- a/pkg/tcpip/link/tun/device.go
+++ b/pkg/tcpip/link/tun/device.go
@@ -18,6 +18,7 @@ import (
"fmt"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -88,12 +89,12 @@ func (d *Device) SetIff(s *stack.Stack, name string, flags Flags) error {
defer d.mu.Unlock()
if d.endpoint != nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
// Input validation.
if flags.TAP && flags.TUN || !flags.TAP && !flags.TUN {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
prefix := "tun"
@@ -108,7 +109,7 @@ func (d *Device) SetIff(s *stack.Stack, name string, flags Flags) error {
endpoint, err := attachOrCreateNIC(s, name, prefix, linkCaps)
if err != nil {
- return syserror.EINVAL
+ return linuxerr.EINVAL
}
d.endpoint = endpoint
@@ -159,7 +160,7 @@ func attachOrCreateNIC(s *stack.Stack, name, prefix string, linkCaps stack.LinkE
// Race detected: A NIC has been created in between.
continue
default:
- return nil, syserror.EINVAL
+ return nil, linuxerr.EINVAL
}
}
}
@@ -170,7 +171,7 @@ func (d *Device) Write(data []byte) (int64, error) {
endpoint := d.endpoint
d.mu.RUnlock()
if endpoint == nil {
- return 0, syserror.EBADFD
+ return 0, linuxerr.EBADFD
}
if !endpoint.IsAttached() {
return 0, syserror.EIO
@@ -207,6 +208,15 @@ func (d *Device) Write(data []byte) (int64, error) {
protocol = pktInfoHdr.Protocol()
case ethHdr != nil:
protocol = ethHdr.Type()
+ case d.flags.TUN:
+ // TUN interface with IFF_NO_PI enabled, thus
+ // we need to determine protocol from version field
+ version := data[0] >> 4
+ if version == 4 {
+ protocol = header.IPv4ProtocolNumber
+ } else if version == 6 {
+ protocol = header.IPv6ProtocolNumber
+ }
}
// Try to determine remote link address, default zero.
@@ -233,7 +243,7 @@ func (d *Device) Read() ([]byte, error) {
endpoint := d.endpoint
d.mu.RUnlock()
if endpoint == nil {
- return nil, syserror.EBADFD
+ return nil, linuxerr.EBADFD
}
for {
@@ -264,13 +274,6 @@ func (d *Device) encodePkt(info *channel.PacketInfo) (buffer.View, bool) {
vv.AppendView(buffer.View(hdr))
}
- // If the packet does not already have link layer header, and the route
- // does not exist, we can't compute it. This is possibly a raw packet, tun
- // device doesn't support this at the moment.
- if info.Pkt.LinkHeader().View().IsEmpty() && len(info.Route.RemoteLinkAddress) == 0 {
- return nil, false
- }
-
// Ethernet header (TAP only).
if d.flags.TAP {
// Add ethernet header if not provided.
diff --git a/pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go b/pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go
index 0b51563cd..1261ad414 100644
--- a/pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go
+++ b/pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go
@@ -126,7 +126,7 @@ func (m *mockMulticastGroupProtocol) sendQueuedReports() {
// Precondition: m.mu must be read locked.
func (m *mockMulticastGroupProtocol) Enabled() bool {
if m.mu.TryLock() {
- m.mu.Unlock()
+ m.mu.Unlock() // +checklocksforce: TryLock.
m.t.Fatal("got write lock, expected to not take the lock; generic multicast protocol must take the read or write lock before calling Enabled")
}
@@ -138,11 +138,11 @@ func (m *mockMulticastGroupProtocol) Enabled() bool {
// Precondition: m.mu must be locked.
func (m *mockMulticastGroupProtocol) SendReport(groupAddress tcpip.Address) (bool, tcpip.Error) {
if m.mu.TryLock() {
- m.mu.Unlock()
+ m.mu.Unlock() // +checklocksforce: TryLock.
m.t.Fatalf("got write lock, expected to not take the lock; generic multicast protocol must take the write lock before sending report for %s", groupAddress)
}
if m.mu.TryRLock() {
- m.mu.RUnlock()
+ m.mu.RUnlock() // +checklocksforce: TryLock.
m.t.Fatalf("got read lock, expected to not take the lock; generic multicast protocol must take the write lock before sending report for %s", groupAddress)
}
@@ -155,11 +155,11 @@ func (m *mockMulticastGroupProtocol) SendReport(groupAddress tcpip.Address) (boo
// Precondition: m.mu must be locked.
func (m *mockMulticastGroupProtocol) SendLeave(groupAddress tcpip.Address) tcpip.Error {
if m.mu.TryLock() {
- m.mu.Unlock()
+ m.mu.Unlock() // +checklocksforce: TryLock.
m.t.Fatalf("got write lock, expected to not take the lock; generic multicast protocol must take the write lock before sending leave for %s", groupAddress)
}
if m.mu.TryRLock() {
- m.mu.RUnlock()
+ m.mu.RUnlock() // +checklocksforce: TryLock.
m.t.Fatalf("got read lock, expected to not take the lock; generic multicast protocol must take the write lock before sending leave for %s", groupAddress)
}
diff --git a/pkg/tcpip/network/ip_test.go b/pkg/tcpip/network/ip_test.go
index bd63e0289..771b9173a 100644
--- a/pkg/tcpip/network/ip_test.go
+++ b/pkg/tcpip/network/ip_test.go
@@ -88,6 +88,7 @@ type testObject struct {
dataCalls int
controlCalls int
+ rawCalls int
}
// checkValues verifies that the transport protocol, data contents, src & dst
@@ -148,6 +149,10 @@ func (t *testObject) DeliverTransportError(local, remote tcpip.Address, net tcpi
t.controlCalls++
}
+func (t *testObject) DeliverRawPacket(tcpip.TransportProtocolNumber, *stack.PacketBuffer) {
+ t.rawCalls++
+}
+
// Attach is only implemented to satisfy the LinkEndpoint interface.
func (*testObject) Attach(stack.NetworkDispatcher) {}
@@ -717,7 +722,10 @@ func TestReceive(t *testing.T) {
}
test.handlePacket(t, ep, &nic)
if nic.testObject.dataCalls != 1 {
- t.Errorf("Bad number of data calls: got %x, want 1", nic.testObject.dataCalls)
+ t.Errorf("Bad number of data calls: got %d, want 1", nic.testObject.dataCalls)
+ }
+ if nic.testObject.rawCalls != 1 {
+ t.Errorf("Bad number of raw calls: got %d, want 1", nic.testObject.rawCalls)
}
if got := stat.Value(); got != 1 {
t.Errorf("got s.Stats().IP.PacketsReceived.Value() = %d, want = 1", got)
@@ -968,7 +976,10 @@ func TestIPv4FragmentationReceive(t *testing.T) {
ep.HandlePacket(pkt)
if nic.testObject.dataCalls != 0 {
- t.Fatalf("Bad number of data calls: got %x, want 0", nic.testObject.dataCalls)
+ t.Fatalf("Bad number of data calls: got %d, want 0", nic.testObject.dataCalls)
+ }
+ if nic.testObject.rawCalls != 0 {
+ t.Errorf("Bad number of raw calls: got %d, want 0", nic.testObject.rawCalls)
}
// Send second segment.
@@ -977,7 +988,10 @@ func TestIPv4FragmentationReceive(t *testing.T) {
})
ep.HandlePacket(pkt)
if nic.testObject.dataCalls != 1 {
- t.Fatalf("Bad number of data calls: got %x, want 1", nic.testObject.dataCalls)
+ t.Fatalf("Bad number of data calls: got %d, want 1", nic.testObject.dataCalls)
+ }
+ if nic.testObject.rawCalls != 1 {
+ t.Errorf("Bad number of raw calls: got %d, want 1", nic.testObject.rawCalls)
}
}
@@ -1310,7 +1324,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv4Addr,
})
return hdr.View().ToVectorisedView()
},
@@ -1351,7 +1365,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv4Addr,
})
ip.SetHeaderLength(header.IPv4MinimumSize - 1)
return hdr.View().ToVectorisedView()
@@ -1370,7 +1384,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv4Addr,
})
return buffer.View(ip[:len(ip)-1]).ToVectorisedView()
},
@@ -1388,7 +1402,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv4Addr,
})
return buffer.View(ip).ToVectorisedView()
},
@@ -1430,7 +1444,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv4Addr,
Options: ipv4Options,
})
return hdr.View().ToVectorisedView()
@@ -1469,7 +1483,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
Protocol: transportProto,
TTL: ipv4.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv4Addr,
Options: ipv4Options,
})
vv := buffer.View(ip).ToVectorisedView()
@@ -1515,7 +1529,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
TransportProtocol: transportProto,
HopLimit: ipv6.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv6Addr,
})
return hdr.View().ToVectorisedView()
},
@@ -1560,7 +1574,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
TransportProtocol: tcpip.TransportProtocolNumber(header.IPv6FragmentExtHdrIdentifier),
HopLimit: ipv6.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv6Addr,
})
return hdr.View().ToVectorisedView()
},
@@ -1595,7 +1609,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
TransportProtocol: transportProto,
HopLimit: ipv6.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv6Addr,
})
return buffer.View(ip).ToVectorisedView()
},
@@ -1630,7 +1644,7 @@ func TestWriteHeaderIncludedPacket(t *testing.T) {
TransportProtocol: transportProto,
HopLimit: ipv6.DefaultTTL,
SrcAddr: src,
- DstAddr: header.IPv4Any,
+ DstAddr: remoteIPv4Addr,
})
return buffer.View(ip[:len(ip)-1]).ToVectorisedView()
},
diff --git a/pkg/tcpip/network/ipv4/icmp.go b/pkg/tcpip/network/ipv4/icmp.go
index 5f6b0c6af..2aa38eb98 100644
--- a/pkg/tcpip/network/ipv4/icmp.go
+++ b/pkg/tcpip/network/ipv4/icmp.go
@@ -173,9 +173,8 @@ func (e *endpoint) handleControl(errInfo stack.TransportError, pkt *stack.Packet
func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
received := e.stats.icmp.packetsReceived
- // TODO(gvisor.dev/issue/170): ICMP packets don't have their
- // TransportHeader fields set. See icmp/protocol.go:protocol.Parse for a
- // full explanation.
+ // ICMP packets don't have their TransportHeader fields set. See
+ // icmp/protocol.go:protocol.Parse for a full explanation.
v, ok := pkt.Data().PullUp(header.ICMPv4MinimumSize)
if !ok {
received.invalid.Increment()
diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go
index 6bee55634..44c85bdb8 100644
--- a/pkg/tcpip/network/ipv4/ipv4.go
+++ b/pkg/tcpip/network/ipv4/ipv4.go
@@ -429,9 +429,9 @@ func (e *endpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams,
// based on destination address and do not send the packet to link
// layer.
//
- // TODO(gvisor.dev/issue/170): We should do this for every
- // packet, rather than only NATted packets, but removing this check
- // short circuits broadcasts before they are sent out to other hosts.
+ // We should do this for every packet, rather than only NATted packets, but
+ // removing this check short circuits broadcasts before they are sent out to
+ // other hosts.
if pkt.NatDone {
netHeader := header.IPv4(pkt.NetworkHeader().View())
if ep := e.protocol.findEndpointWithAddress(netHeader.DestinationAddress()); ep != nil {
@@ -614,10 +614,6 @@ func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, pkt *stack.PacketBu
ipH.SetSourceAddress(r.LocalAddress())
}
- // Set the destination. If the packet already included a destination, it will
- // be part of the route anyways.
- ipH.SetDestinationAddress(r.RemoteAddress())
-
// Set the packet ID when zero.
if ipH.ID() == 0 {
// RFC 6864 section 4.3 mandates uniqueness of ID values for
@@ -720,7 +716,8 @@ func (e *endpoint) forwardPacket(pkt *stack.PacketBuffer) ip.ForwardingError {
return nil
}
- ep.handleValidatedPacket(h, pkt)
+ // The packet originally arrived on e so provide its NIC as the input NIC.
+ ep.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
return nil
}
@@ -836,7 +833,7 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
}
}
- e.handleValidatedPacket(h, pkt)
+ e.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
}
// handleLocalPacket is like HandlePacket except it does not perform the
@@ -855,10 +852,17 @@ func (e *endpoint) handleLocalPacket(pkt *stack.PacketBuffer, canSkipRXChecksum
return
}
- e.handleValidatedPacket(h, pkt)
+ e.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
}
-func (e *endpoint) handleValidatedPacket(h header.IPv4, pkt *stack.PacketBuffer) {
+func (e *endpoint) handleValidatedPacket(h header.IPv4, pkt *stack.PacketBuffer, inNICName string) {
+ // Raw socket packets are delivered based solely on the transport protocol
+ // number. We only require that the packet be valid IPv4, and that they not
+ // be fragmented.
+ if !h.More() && h.FragmentOffset() == 0 {
+ e.dispatcher.DeliverRawPacket(h.TransportProtocol(), pkt)
+ }
+
pkt.NICID = e.nic.ID()
stats := e.stats
stats.ip.ValidPacketsReceived.Increment()
@@ -920,8 +924,7 @@ func (e *endpoint) handleValidatedPacket(h header.IPv4, pkt *stack.PacketBuffer)
// iptables filtering. All packets that reach here are intended for
// this machine and will not be forwarded.
- inNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
- if ok := e.protocol.stack.IPTables().Check(stack.Input, pkt, nil, "" /* preroutingAddr */, inNicName, "" /* outNicName */); !ok {
+ if ok := e.protocol.stack.IPTables().Check(stack.Input, pkt, nil, "" /* preroutingAddr */, inNICName, "" /* outNicName */); !ok {
// iptables is telling us to drop the packet.
stats.ip.IPTablesInputDropped.Increment()
return
@@ -995,6 +998,9 @@ func (e *endpoint) handleValidatedPacket(h header.IPv4, pkt *stack.PacketBuffer)
// to do it here.
h.SetTotalLength(uint16(pkt.Data().Size() + len(h)))
h.SetFlagsFragmentOffset(0, 0)
+
+ // Now that the packet is reassembled, it can be sent to raw sockets.
+ e.dispatcher.DeliverRawPacket(h.TransportProtocol(), pkt)
}
stats.ip.PacketsDelivered.Increment()
diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go
index 23fc94303..94caaae6c 100644
--- a/pkg/tcpip/network/ipv6/icmp.go
+++ b/pkg/tcpip/network/ipv6/icmp.go
@@ -285,8 +285,8 @@ func isMLDValid(pkt *stack.PacketBuffer, iph header.IPv6, routerAlert *header.IP
func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, routerAlert *header.IPv6RouterAlertOption) {
sent := e.stats.icmp.packetsSent
received := e.stats.icmp.packetsReceived
- // TODO(gvisor.dev/issue/170): ICMP packets don't have their TransportHeader
- // fields set. See icmp/protocol.go:protocol.Parse for a full explanation.
+ // ICMP packets don't have their TransportHeader fields set. See
+ // icmp/protocol.go:protocol.Parse for a full explanation.
v, ok := pkt.Data().PullUp(header.ICMPv6HeaderSize)
if !ok {
received.invalid.Increment()
diff --git a/pkg/tcpip/network/ipv6/icmp_test.go b/pkg/tcpip/network/ipv6/icmp_test.go
index c2e9544c1..7c2a3e56b 100644
--- a/pkg/tcpip/network/ipv6/icmp_test.go
+++ b/pkg/tcpip/network/ipv6/icmp_test.go
@@ -90,6 +90,10 @@ func (*stubDispatcher) DeliverTransportPacket(tcpip.TransportProtocolNumber, *st
return stack.TransportPacketHandled
}
+func (*stubDispatcher) DeliverRawPacket(tcpip.TransportProtocolNumber, *stack.PacketBuffer) {
+ // No-op.
+}
+
var _ stack.NetworkInterface = (*testInterface)(nil)
type testInterface struct {
diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go
index 6103574f7..b1aec5312 100644
--- a/pkg/tcpip/network/ipv6/ipv6.go
+++ b/pkg/tcpip/network/ipv6/ipv6.go
@@ -344,7 +344,10 @@ func (e *endpoint) onAddressAssignedLocked(addr tcpip.Address) {
func (e *endpoint) InvalidateDefaultRouter(rtr tcpip.Address) {
e.mu.Lock()
defer e.mu.Unlock()
- e.mu.ndp.invalidateDefaultRouter(rtr)
+
+ // We represent default routers with a default (off-link) route through the
+ // router.
+ e.mu.ndp.invalidateOffLinkRoute(offLinkRoute{dest: header.IPv6EmptySubnet, router: rtr})
}
// SetNDPConfigurations implements NDPEndpoint.
@@ -755,9 +758,9 @@ func (e *endpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams,
// based on destination address and do not send the packet to link
// layer.
//
- // TODO(gvisor.dev/issue/170): We should do this for every
- // packet, rather than only NATted packets, but removing this check
- // short circuits broadcasts before they are sent out to other hosts.
+ // We should do this for every packet, rather than only NATted packets, but
+ // removing this check short circuits broadcasts before they are sent out to
+ // other hosts.
if pkt.NatDone {
netHeader := header.IPv6(pkt.NetworkHeader().View())
if ep := e.protocol.findEndpointWithAddress(netHeader.DestinationAddress()); ep != nil {
@@ -928,10 +931,6 @@ func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, pkt *stack.PacketBu
ipH.SetSourceAddress(r.LocalAddress())
}
- // Set the destination. If the packet already included a destination, it will
- // be part of the route anyways.
- ipH.SetDestinationAddress(r.RemoteAddress())
-
// Populate the packet buffer's network header and don't allow an invalid
// packet to be sent.
//
@@ -991,7 +990,8 @@ func (e *endpoint) forwardPacket(pkt *stack.PacketBuffer) ip.ForwardingError {
return nil
}
- ep.handleValidatedPacket(h, pkt)
+ // The packet originally arrived on e so provide its NIC as the input NIC.
+ ep.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
return nil
}
@@ -1104,7 +1104,7 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
}
}
- e.handleValidatedPacket(h, pkt)
+ e.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
}
// handleLocalPacket is like HandlePacket except it does not perform the
@@ -1123,10 +1123,14 @@ func (e *endpoint) handleLocalPacket(pkt *stack.PacketBuffer, canSkipRXChecksum
return
}
- e.handleValidatedPacket(h, pkt)
+ e.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
}
-func (e *endpoint) handleValidatedPacket(h header.IPv6, pkt *stack.PacketBuffer) {
+func (e *endpoint) handleValidatedPacket(h header.IPv6, pkt *stack.PacketBuffer, inNICName string) {
+ // Raw socket packets are delivered based solely on the transport protocol
+ // number. We only require that the packet be valid IPv6.
+ e.dispatcher.DeliverRawPacket(h.TransportProtocol(), pkt)
+
pkt.NICID = e.nic.ID()
stats := e.stats.ip
stats.ValidPacketsReceived.Increment()
@@ -1175,8 +1179,7 @@ func (e *endpoint) handleValidatedPacket(h header.IPv6, pkt *stack.PacketBuffer)
// iptables filtering. All packets that reach here are intended for
// this machine and need not be forwarded.
- inNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
- if ok := e.protocol.stack.IPTables().Check(stack.Input, pkt, nil, "" /* preroutingAddr */, inNicName, "" /* outNicName */); !ok {
+ if ok := e.protocol.stack.IPTables().Check(stack.Input, pkt, nil, "" /* preroutingAddr */, inNICName, "" /* outNicName */); !ok {
// iptables is telling us to drop the packet.
stats.IPTablesInputDropped.Increment()
return
@@ -1627,8 +1630,8 @@ func (e *endpoint) NetworkProtocolNumber() tcpip.NetworkProtocolNumber {
func (e *endpoint) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, peb stack.PrimaryEndpointBehavior, configType stack.AddressConfigType, deprecated bool) (stack.AddressEndpoint, tcpip.Error) {
// TODO(b/169350103): add checks here after making sure we no longer receive
// an empty address.
- e.mu.RLock()
- defer e.mu.RUnlock()
+ e.mu.Lock()
+ defer e.mu.Unlock()
return e.addAndAcquirePermanentAddressLocked(addr, peb, configType, deprecated)
}
@@ -1669,8 +1672,8 @@ func (e *endpoint) addAndAcquirePermanentAddressLocked(addr tcpip.AddressWithPre
// RemovePermanentAddress implements stack.AddressableEndpoint.
func (e *endpoint) RemovePermanentAddress(addr tcpip.Address) tcpip.Error {
- e.mu.RLock()
- defer e.mu.RUnlock()
+ e.mu.Lock()
+ defer e.mu.Unlock()
addressEndpoint := e.getAddressRLocked(addr)
if addressEndpoint == nil || !addressEndpoint.GetKind().IsPermanent() {
diff --git a/pkg/tcpip/network/ipv6/ndp.go b/pkg/tcpip/network/ipv6/ndp.go
index 851cd6e75..8837d66d8 100644
--- a/pkg/tcpip/network/ipv6/ndp.go
+++ b/pkg/tcpip/network/ipv6/ndp.go
@@ -54,6 +54,11 @@ const (
// Advertisements, as a host.
defaultDiscoverDefaultRouters = true
+ // defaultDiscoverMoreSpecificRoutes is the default configuration for
+ // whether or not to discover more-specific routes from incoming Router
+ // Advertisements, as a host.
+ defaultDiscoverMoreSpecificRoutes = true
+
// defaultDiscoverOnLinkPrefixes is the default configuration for
// whether or not to discover on-link prefixes from incoming Router
// Advertisements' Prefix Information option, as a host.
@@ -78,13 +83,13 @@ const (
// we cannot have a negative delay.
minimumMaxRtrSolicitationDelay = 0
- // MaxDiscoveredDefaultRouters is the maximum number of discovered
- // default routers. The stack should stop discovering new routers after
- // discovering MaxDiscoveredDefaultRouters routers.
+ // MaxDiscoveredOffLinkRoutes is the maximum number of discovered off-link
+ // routes. The stack should stop discovering new off-link routes after
+ // this limit is reached.
//
// This value MUST be at minimum 2 as per RFC 4861 section 6.3.4, and
// SHOULD be more.
- MaxDiscoveredDefaultRouters = 10
+ MaxDiscoveredOffLinkRoutes = 10
// MaxDiscoveredOnLinkPrefixes is the maximum number of discovered
// on-link prefixes. The stack should stop discovering new on-link
@@ -127,25 +132,17 @@ const (
// maxSLAACAddrLocalRegenAttempts is the maximum number of times to attempt
// SLAAC address regenerations in response to an IPv6 endpoint-local conflict.
maxSLAACAddrLocalRegenAttempts = 10
-)
-var (
// MinPrefixInformationValidLifetimeForUpdate is the minimum Valid
// Lifetime to update the valid lifetime of a generated address by
// SLAAC.
//
- // This is exported as a variable (instead of a constant) so tests
- // can update it to a smaller value.
- //
// Min = 2hrs.
MinPrefixInformationValidLifetimeForUpdate = 2 * time.Hour
// MaxDesyncFactor is the upper bound for the preferred lifetime's desync
// factor for temporary SLAAC addresses.
//
- // This is exported as a variable (instead of a constant) so tests
- // can update it to a smaller value.
- //
// Must be greater than 0.
//
// Max = 10m (from RFC 4941 section 5).
@@ -154,9 +151,6 @@ var (
// MinMaxTempAddrPreferredLifetime is the minimum value allowed for the
// maximum preferred lifetime for temporary SLAAC addresses.
//
- // This is exported as a variable (instead of a constant) so tests
- // can update it to a smaller value.
- //
// This value guarantees that a temporary address is preferred for at
// least 1hr if the SLAAC prefix is valid for at least that time.
MinMaxTempAddrPreferredLifetime = defaultRegenAdvanceDuration + MaxDesyncFactor + time.Hour
@@ -164,9 +158,6 @@ var (
// MinMaxTempAddrValidLifetime is the minimum value allowed for the
// maximum valid lifetime for temporary SLAAC addresses.
//
- // This is exported as a variable (instead of a constant) so tests
- // can update it to a smaller value.
- //
// This value guarantees that a temporary address is valid for at least
// 2hrs if the SLAAC prefix is valid for at least that time.
MinMaxTempAddrValidLifetime = 2 * time.Hour
@@ -214,28 +205,23 @@ type NDPDispatcher interface {
// is also not permitted to call into the stack.
OnDuplicateAddressDetectionResult(tcpip.NICID, tcpip.Address, stack.DADResult)
- // OnDefaultRouterDiscovered is called when a new default router is
- // discovered. Implementations must return true if the newly discovered
- // router should be remembered.
+ // OnOffLinkRouteUpdated is called when an off-link route is updated.
//
// This function is not permitted to block indefinitely. This function
// is also not permitted to call into the stack.
- OnDefaultRouterDiscovered(tcpip.NICID, tcpip.Address) bool
+ OnOffLinkRouteUpdated(tcpip.NICID, tcpip.Subnet, tcpip.Address, header.NDPRoutePreference)
- // OnDefaultRouterInvalidated is called when a discovered default router that
- // was remembered is invalidated.
+ // OnOffLinkRouteInvalidated is called when an off-link route is invalidated.
//
// This function is not permitted to block indefinitely. This function
// is also not permitted to call into the stack.
- OnDefaultRouterInvalidated(tcpip.NICID, tcpip.Address)
+ OnOffLinkRouteInvalidated(tcpip.NICID, tcpip.Subnet, tcpip.Address)
// OnOnLinkPrefixDiscovered is called when a new on-link prefix is discovered.
- // Implementations must return true if the newly discovered on-link prefix
- // should be remembered.
//
// This function is not permitted to block indefinitely. This function
// is also not permitted to call into the stack.
- OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) bool
+ OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet)
// OnOnLinkPrefixInvalidated is called when a discovered on-link prefix that
// was remembered is invalidated.
@@ -245,13 +231,11 @@ type NDPDispatcher interface {
OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet)
// OnAutoGenAddress is called when a new prefix with its autonomous address-
- // configuration flag set is received and SLAAC was performed. Implementations
- // may prevent the stack from assigning the address to the NIC by returning
- // false.
+ // configuration flag set is received and SLAAC was performed.
//
// This function is not permitted to block indefinitely. It must not
// call functions on the stack itself.
- OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) bool
+ OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix)
// OnAutoGenAddressDeprecated is called when an auto-generated address (SLAAC)
// is deprecated, but is still considered valid. Note, if an address is
@@ -373,12 +357,18 @@ type NDPConfigurations struct {
// DiscoverDefaultRouters determines whether or not default routers are
// discovered from Router Advertisements, as per RFC 4861 section 6. This
- // configuration is ignored if HandleRAs is false.
+ // configuration is ignored if RAs will not be processed (see HandleRAs).
DiscoverDefaultRouters bool
+ // DiscoverMoreSpecificRoutes determines whether or not more specific routes
+ // are discovered from Router Advertisements, as per RFC 4191. This
+ // configuration is ignored if RAs will not be processed (see HandleRAs).
+ DiscoverMoreSpecificRoutes bool
+
// DiscoverOnLinkPrefixes determines whether or not on-link prefixes are
// discovered from Router Advertisements' Prefix Information option, as per
- // RFC 4861 section 6. This configuration is ignored if HandleRAs is false.
+ // RFC 4861 section 6. This configuration is ignored if RAs will not be
+ // processed (see HandleRAs).
DiscoverOnLinkPrefixes bool
// AutoGenGlobalAddresses determines whether or not an IPv6 endpoint performs
@@ -429,6 +419,7 @@ func DefaultNDPConfigurations() NDPConfigurations {
MaxRtrSolicitationDelay: defaultMaxRtrSolicitationDelay,
HandleRAs: defaultHandleRAs,
DiscoverDefaultRouters: defaultDiscoverDefaultRouters,
+ DiscoverMoreSpecificRoutes: defaultDiscoverMoreSpecificRoutes,
DiscoverOnLinkPrefixes: defaultDiscoverOnLinkPrefixes,
AutoGenGlobalAddresses: defaultAutoGenGlobalAddresses,
AutoGenTempGlobalAddresses: defaultAutoGenTempGlobalAddresses,
@@ -469,6 +460,11 @@ type timer struct {
timer tcpip.Timer
}
+type offLinkRoute struct {
+ dest tcpip.Subnet
+ router tcpip.Address
+}
+
// ndpState is the per-Interface NDP state.
type ndpState struct {
// Do not allow overwriting this state.
@@ -483,8 +479,8 @@ type ndpState struct {
// The DAD timers to send the next NS message, or resolve the address.
dad ip.DAD
- // The default routers discovered through Router Advertisements.
- defaultRouters map[tcpip.Address]defaultRouterState
+ // The off-link routes discovered through Router Advertisements.
+ offLinkRoutes map[offLinkRoute]offLinkRouteState
// rtrSolicitTimer is the timer used to send the next router solicitation
// message.
@@ -512,10 +508,12 @@ type ndpState struct {
temporaryAddressDesyncFactor time.Duration
}
-// defaultRouterState holds data associated with a default router discovered by
+// offLinkRouteState holds data associated with an off-link route discovered by
// a Router Advertisement (RA).
-type defaultRouterState struct {
- // Job to invalidate the default router.
+type offLinkRouteState struct {
+ prf header.NDPRoutePreference
+
+ // Job to invalidate the route.
//
// Must not be nil.
invalidationJob *tcpip.Job
@@ -571,11 +569,11 @@ type slaacPrefixState struct {
// Must not be nil.
invalidationJob *tcpip.Job
- // Nonzero only when the address is not valid forever.
- validUntil tcpip.MonotonicTime
+ // nil iff the address is valid forever.
+ validUntil *tcpip.MonotonicTime
- // Nonzero only when the address is not preferred forever.
- preferredUntil tcpip.MonotonicTime
+ // nil iff the address is preferred forever.
+ preferredUntil *tcpip.MonotonicTime
// State associated with the stable address generated for the prefix.
stableAddr struct {
@@ -733,30 +731,22 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
// Is the IPv6 endpoint configured to discover default routers?
if ndp.configs.DiscoverDefaultRouters {
- rtr, ok := ndp.defaultRouters[ip]
- rl := ra.RouterLifetime()
- switch {
- case !ok && rl != 0:
- // This is a new default router we are discovering.
+ prf := ra.DefaultRouterPreference()
+ if prf == header.ReservedRoutePreference {
+ // As per RFC 4191 section 2.2,
//
- // Only remember it if we currently know about less than
- // MaxDiscoveredDefaultRouters routers.
- if len(ndp.defaultRouters) < MaxDiscoveredDefaultRouters {
- ndp.rememberDefaultRouter(ip, rl)
- }
-
- case ok && rl != 0:
- // This is an already discovered default router. Update
- // the invalidation job.
- rtr.invalidationJob.Cancel()
- rtr.invalidationJob.Schedule(rl)
- ndp.defaultRouters[ip] = rtr
-
- case ok && rl == 0:
- // We know about the router but it is no longer to be
- // used as a default router so invalidate it.
- ndp.invalidateDefaultRouter(ip)
+ // Prf (Default Router Preference)
+ //
+ // If the Reserved (10) value is received, the receiver MUST treat the
+ // value as if it were (00).
+ //
+ // Note that the value 00 is the medium (default) router preference value.
+ prf = header.MediumRoutePreference
}
+
+ // We represent default routers with a default (off-link) route through the
+ // router.
+ ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: header.IPv6EmptySubnet, router: ip}, ra.RouterLifetime(), prf)
}
// TODO(b/141556115): Do (RetransTimer, ReachableTime)) Parameter
@@ -808,61 +798,107 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
if opt.AutonomousAddressConfigurationFlag() {
ndp.handleAutonomousPrefixInformation(opt)
}
+
+ case header.NDPRouteInformation:
+ if !ndp.configs.DiscoverMoreSpecificRoutes {
+ continue
+ }
+
+ dest, err := opt.Prefix()
+ if err != nil {
+ panic(fmt.Sprintf("%T.Prefix(): %s", opt, err))
+ }
+
+ prf := opt.RoutePreference()
+ if prf == header.ReservedRoutePreference {
+ // As per RFC 4191 section 2.3,
+ //
+ // Prf (Route Preference)
+ // 2-bit signed integer. The Route Preference indicates
+ // whether to prefer the router associated with this prefix
+ // over others, when multiple identical prefixes (for
+ // different routers) have been received. If the Reserved
+ // (10) value is received, the Route Information Option MUST
+ // be ignored.
+ continue
+ }
+
+ ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: dest, router: ip}, opt.RouteLifetime(), prf)
}
// TODO(b/141556115): Do (MTU) Parameter Discovery.
}
}
-// invalidateDefaultRouter invalidates a discovered default router.
+// invalidateOffLinkRoute invalidates a discovered off-link route.
//
// The IPv6 endpoint that ndp belongs to MUST be locked.
-func (ndp *ndpState) invalidateDefaultRouter(ip tcpip.Address) {
- rtr, ok := ndp.defaultRouters[ip]
-
- // Is the router still discovered?
+func (ndp *ndpState) invalidateOffLinkRoute(route offLinkRoute) {
+ state, ok := ndp.offLinkRoutes[route]
if !ok {
- // ...Nope, do nothing further.
return
}
- rtr.invalidationJob.Cancel()
- delete(ndp.defaultRouters, ip)
+ state.invalidationJob.Cancel()
+ delete(ndp.offLinkRoutes, route)
- // Let the integrator know a discovered default router is invalidated.
+ // Let the integrator know a discovered off-link route is invalidated.
if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
- ndpDisp.OnDefaultRouterInvalidated(ndp.ep.nic.ID(), ip)
+ ndpDisp.OnOffLinkRouteInvalidated(ndp.ep.nic.ID(), route.dest, route.router)
}
}
-// rememberDefaultRouter remembers a newly discovered default router with IPv6
-// link-local address ip with lifetime rl.
+// handleOffLinkRouteDiscovery handles the discovery of an off-link route.
//
-// The router identified by ip MUST NOT already be known by the IPv6 endpoint.
-//
-// The IPv6 endpoint that ndp belongs to MUST be locked.
-func (ndp *ndpState) rememberDefaultRouter(ip tcpip.Address, rl time.Duration) {
+// Precondition: ndp.ep.mu must be locked.
+func (ndp *ndpState) handleOffLinkRouteDiscovery(route offLinkRoute, lifetime time.Duration, prf header.NDPRoutePreference) {
ndpDisp := ndp.ep.protocol.options.NDPDisp
if ndpDisp == nil {
return
}
- // Inform the integrator when we discovered a default router.
- if !ndpDisp.OnDefaultRouterDiscovered(ndp.ep.nic.ID(), ip) {
- // Informed by the integrator to not remember the router, do
- // nothing further.
- return
- }
+ state, ok := ndp.offLinkRoutes[route]
+ switch {
+ case !ok && lifetime != 0:
+ // This is a new route we are discovering.
+ //
+ // Only remember it if we currently know about less than
+ // MaxDiscoveredOffLinkRoutes routers.
+ if len(ndp.offLinkRoutes) < MaxDiscoveredOffLinkRoutes {
+ // Inform the integrator when we discovered an off-link route.
+ ndpDisp.OnOffLinkRouteUpdated(ndp.ep.nic.ID(), route.dest, route.router, prf)
+
+ state := offLinkRouteState{
+ prf: prf,
+ invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
+ ndp.invalidateOffLinkRoute(route)
+ }),
+ }
- state := defaultRouterState{
- invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
- ndp.invalidateDefaultRouter(ip)
- }),
- }
+ state.invalidationJob.Schedule(lifetime)
+
+ ndp.offLinkRoutes[route] = state
+ }
+
+ case ok && lifetime != 0:
+ // This is an already discovered off-link route. Update the lifetime.
+ state.invalidationJob.Cancel()
+ state.invalidationJob.Schedule(lifetime)
- state.invalidationJob.Schedule(rl)
+ if prf != state.prf {
+ state.prf = prf
+
+ // Inform the integrator about route preference updates.
+ ndpDisp.OnOffLinkRouteUpdated(ndp.ep.nic.ID(), route.dest, route.router, prf)
+ }
- ndp.defaultRouters[ip] = state
+ ndp.offLinkRoutes[route] = state
+
+ case ok && lifetime == 0:
+ // The already discovered off-link route is no longer considered valid so we
+ // invalidate it immediately.
+ ndp.invalidateOffLinkRoute(route)
+ }
}
// rememberOnLinkPrefix remembers a newly discovered on-link prefix with IPv6
@@ -878,11 +914,7 @@ func (ndp *ndpState) rememberOnLinkPrefix(prefix tcpip.Subnet, l time.Duration)
}
// Inform the integrator when we discovered an on-link prefix.
- if !ndpDisp.OnOnLinkPrefixDiscovered(ndp.ep.nic.ID(), prefix) {
- // Informed by the integrator to not remember the prefix, do
- // nothing further.
- return
- }
+ ndpDisp.OnOnLinkPrefixDiscovered(ndp.ep.nic.ID(), prefix)
state := onLinkPrefixState{
invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
@@ -1055,7 +1087,8 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
// The time an address is preferred until is needed to properly generate the
// address.
if pl < header.NDPInfiniteLifetime {
- state.preferredUntil = now.Add(pl)
+ t := now.Add(pl)
+ state.preferredUntil = &t
}
if !ndp.generateSLAACAddr(prefix, &state) {
@@ -1073,7 +1106,8 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
if vl < header.NDPInfiniteLifetime {
state.invalidationJob.Schedule(vl)
- state.validUntil = now.Add(vl)
+ t := now.Add(vl)
+ state.validUntil = &t
}
// If the address is assigned (DAD resolved), generate a temporary address.
@@ -1096,16 +1130,13 @@ func (ndp *ndpState) addAndAcquireSLAACAddr(addr tcpip.AddressWithPrefix, config
return nil
}
- if !ndpDisp.OnAutoGenAddress(ndp.ep.nic.ID(), addr) {
- // Informed by the integrator not to add the address.
- return nil
- }
-
addressEndpoint, err := ndp.ep.addAndAcquirePermanentAddressLocked(addr, stack.FirstPrimaryEndpoint, configType, deprecated)
if err != nil {
panic(fmt.Sprintf("ndp: error when adding SLAAC address %+v: %s", addr, err))
}
+ ndpDisp.OnAutoGenAddress(ndp.ep.nic.ID(), addr)
+
return addressEndpoint
}
@@ -1181,7 +1212,8 @@ func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixSt
state.stableAddr.localGenerationFailures++
}
- if addressEndpoint := ndp.addAndAcquireSLAACAddr(generatedAddr, stack.AddressConfigSlaac, ndp.ep.protocol.stack.Clock().NowMonotonic().Sub(state.preferredUntil) >= 0 /* deprecated */); addressEndpoint != nil {
+ deprecated := state.preferredUntil != nil && !state.preferredUntil.After(ndp.ep.protocol.stack.Clock().NowMonotonic())
+ if addressEndpoint := ndp.addAndAcquireSLAACAddr(generatedAddr, stack.AddressConfigSlaac, deprecated); addressEndpoint != nil {
state.stableAddr.addressEndpoint = addressEndpoint
state.generationAttempts++
return true
@@ -1242,7 +1274,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
// address is the lower of the valid lifetime of the stable address or the
// maximum temporary address valid lifetime.
vl := ndp.configs.MaxTempAddrValidLifetime
- if prefixState.validUntil != (tcpip.MonotonicTime{}) {
+ if prefixState.validUntil != nil {
if prefixVL := prefixState.validUntil.Sub(now); vl > prefixVL {
vl = prefixVL
}
@@ -1258,7 +1290,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
// maximum temporary address preferred lifetime - the temporary address desync
// factor.
pl := ndp.configs.MaxTempAddrPreferredLifetime - ndp.temporaryAddressDesyncFactor
- if prefixState.preferredUntil != (tcpip.MonotonicTime{}) {
+ if prefixState.preferredUntil != nil {
if prefixPL := prefixState.preferredUntil.Sub(now); pl > prefixPL {
// Respect the preferred lifetime of the prefix, as per RFC 4941 section
// 3.3 step 4.
@@ -1400,9 +1432,10 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
if !deprecated {
prefixState.deprecationJob.Schedule(pl)
}
- prefixState.preferredUntil = now.Add(pl)
+ t := now.Add(pl)
+ prefixState.preferredUntil = &t
} else {
- prefixState.preferredUntil = tcpip.MonotonicTime{}
+ prefixState.preferredUntil = nil
}
// As per RFC 4862 section 5.5.3.e, update the valid lifetime for prefix:
@@ -1420,14 +1453,14 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
// Handle the infinite valid lifetime separately as we do not schedule a
// job in this case.
prefixState.invalidationJob.Cancel()
- prefixState.validUntil = tcpip.MonotonicTime{}
+ prefixState.validUntil = nil
} else {
var effectiveVl time.Duration
var rl time.Duration
// If the prefix was originally set to be valid forever, assume the
// remaining time to be the maximum possible value.
- if prefixState.validUntil == (tcpip.MonotonicTime{}) {
+ if prefixState.validUntil == nil {
rl = header.NDPInfiniteLifetime
} else {
rl = prefixState.validUntil.Sub(now)
@@ -1442,7 +1475,8 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
if effectiveVl != 0 {
prefixState.invalidationJob.Cancel()
prefixState.invalidationJob.Schedule(effectiveVl)
- prefixState.validUntil = now.Add(effectiveVl)
+ t := now.Add(effectiveVl)
+ prefixState.validUntil = &t
}
}
@@ -1462,8 +1496,8 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
// maximum temporary address valid lifetime. Note, the valid lifetime of a
// temporary address is relative to the address's creation time.
validUntil := tempAddrState.createdAt.Add(ndp.configs.MaxTempAddrValidLifetime)
- if prefixState.validUntil != (tcpip.MonotonicTime{}) && validUntil.Sub(prefixState.validUntil) > 0 {
- validUntil = prefixState.validUntil
+ if prefixState.validUntil != nil && prefixState.validUntil.Before(validUntil) {
+ validUntil = *prefixState.validUntil
}
// If the address is no longer valid, invalidate it immediately. Otherwise,
@@ -1482,14 +1516,15 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
// desync factor. Note, the preferred lifetime of a temporary address is
// relative to the address's creation time.
preferredUntil := tempAddrState.createdAt.Add(ndp.configs.MaxTempAddrPreferredLifetime - ndp.temporaryAddressDesyncFactor)
- if prefixState.preferredUntil != (tcpip.MonotonicTime{}) && preferredUntil.Sub(prefixState.preferredUntil) > 0 {
- preferredUntil = prefixState.preferredUntil
+ if prefixState.preferredUntil != nil && prefixState.preferredUntil.Before(preferredUntil) {
+ preferredUntil = *prefixState.preferredUntil
}
// If the address is no longer preferred, deprecate it immediately.
// Otherwise, schedule the deprecation job again.
newPreferredLifetime := preferredUntil.Sub(now)
tempAddrState.deprecationJob.Cancel()
+
if newPreferredLifetime <= 0 {
ndp.deprecateSLAACAddress(tempAddrState.addressEndpoint)
} else {
@@ -1679,12 +1714,12 @@ func (ndp *ndpState) cleanupState() {
panic(fmt.Sprintf("ndp: still have discovered on-link prefixes after cleaning up; found = %d", got))
}
- for router := range ndp.defaultRouters {
- ndp.invalidateDefaultRouter(router)
+ for route := range ndp.offLinkRoutes {
+ ndp.invalidateOffLinkRoute(route)
}
- if got := len(ndp.defaultRouters); got != 0 {
- panic(fmt.Sprintf("ndp: still have discovered default routers after cleaning up; found = %d", got))
+ if got := len(ndp.offLinkRoutes); got != 0 {
+ panic(fmt.Sprintf("ndp: still have discovered off-link routes after cleaning up; found = %d", got))
}
ndp.dhcpv6Configuration = 0
@@ -1847,21 +1882,19 @@ func (ndp *ndpState) stopSolicitingRouters() {
}
func (ndp *ndpState) init(ep *endpoint, dadOptions ip.DADOptions) {
- if ndp.defaultRouters != nil {
+ if ndp.offLinkRoutes != nil {
panic("attempted to initialize NDP state twice")
}
ndp.ep = ep
ndp.configs = ep.protocol.options.NDPConfigs
ndp.dad.Init(&ndp.ep.mu, ep.protocol.options.DADConfigs, dadOptions)
- ndp.defaultRouters = make(map[tcpip.Address]defaultRouterState)
+ ndp.offLinkRoutes = make(map[offLinkRoute]offLinkRouteState)
ndp.onLinkPrefixes = make(map[tcpip.Subnet]onLinkPrefixState)
ndp.slaacPrefixes = make(map[tcpip.Subnet]slaacPrefixState)
header.InitialTempIID(ndp.temporaryIIDHistory[:], ndp.ep.protocol.options.TempIIDSeed, ndp.ep.nic.ID())
- if MaxDesyncFactor != 0 {
- ndp.temporaryAddressDesyncFactor = time.Duration(ep.protocol.stack.Rand().Int63n(int64(MaxDesyncFactor)))
- }
+ ndp.temporaryAddressDesyncFactor = time.Duration(ep.protocol.stack.Rand().Int63n(int64(MaxDesyncFactor)))
}
func (ndp *ndpState) SendDADMessage(addr tcpip.Address, nonce []byte) tcpip.Error {
diff --git a/pkg/tcpip/network/ipv6/ndp_test.go b/pkg/tcpip/network/ipv6/ndp_test.go
index 3438deb79..f0186c64e 100644
--- a/pkg/tcpip/network/ipv6/ndp_test.go
+++ b/pkg/tcpip/network/ipv6/ndp_test.go
@@ -42,24 +42,21 @@ type testNDPDispatcher struct {
func (*testNDPDispatcher) OnDuplicateAddressDetectionResult(tcpip.NICID, tcpip.Address, stack.DADResult) {
}
-func (t *testNDPDispatcher) OnDefaultRouterDiscovered(_ tcpip.NICID, addr tcpip.Address) bool {
+func (t *testNDPDispatcher) OnOffLinkRouteUpdated(_ tcpip.NICID, _ tcpip.Subnet, addr tcpip.Address, _ header.NDPRoutePreference) {
t.addr = addr
- return true
}
-func (t *testNDPDispatcher) OnDefaultRouterInvalidated(_ tcpip.NICID, addr tcpip.Address) {
+func (t *testNDPDispatcher) OnOffLinkRouteInvalidated(_ tcpip.NICID, _ tcpip.Subnet, addr tcpip.Address) {
t.addr = addr
}
-func (*testNDPDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) bool {
- return false
+func (*testNDPDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) {
}
func (*testNDPDispatcher) OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet) {
}
-func (*testNDPDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) bool {
- return false
+func (*testNDPDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) {
}
func (*testNDPDispatcher) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) {
@@ -96,7 +93,7 @@ func TestStackNDPEndpointInvalidateDefaultRouter(t *testing.T) {
ipv6EP := ep.(*endpoint)
ipv6EP.mu.Lock()
- ipv6EP.mu.ndp.rememberDefaultRouter(lladdr1, time.Hour)
+ ipv6EP.mu.ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: header.IPv6EmptySubnet, router: lladdr1}, time.Hour, header.MediumRoutePreference)
ipv6EP.mu.Unlock()
if ndpDisp.addr != lladdr1 {
diff --git a/pkg/tcpip/ports/BUILD b/pkg/tcpip/ports/BUILD
index b7f6d52ae..fe98a52af 100644
--- a/pkg/tcpip/ports/BUILD
+++ b/pkg/tcpip/ports/BUILD
@@ -12,6 +12,7 @@ go_library(
deps = [
"//pkg/sync",
"//pkg/tcpip",
+ "//pkg/tcpip/header",
],
)
diff --git a/pkg/tcpip/ports/ports.go b/pkg/tcpip/ports/ports.go
index 854d6a6ba..fb8ef1ee2 100644
--- a/pkg/tcpip/ports/ports.go
+++ b/pkg/tcpip/ports/ports.go
@@ -23,6 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
)
const (
@@ -122,7 +123,7 @@ type deviceToDest map[tcpip.NICID]destToCounter
// If either of the port reuse flags is enabled on any of the nodes, all nodes
// sharing a port must share at least one reuse flag. This matches Linux's
// behavior.
-func (dd deviceToDest) isAvailable(res Reservation) bool {
+func (dd deviceToDest) isAvailable(res Reservation, portSpecified bool) bool {
flagBits := res.Flags.Bits()
if res.BindToDevice == 0 {
intersection := FlagMask
@@ -138,6 +139,9 @@ func (dd deviceToDest) isAvailable(res Reservation) bool {
return false
}
}
+ if !portSpecified && res.Transport == header.TCPProtocolNumber {
+ return false
+ }
return true
}
@@ -146,16 +150,26 @@ func (dd deviceToDest) isAvailable(res Reservation) bool {
if dests, ok := dd[0]; ok {
var count int
intersection, count = dests.intersectionFlags(res)
- if count > 0 && intersection&flagBits == 0 {
- return false
+ if count > 0 {
+ if intersection&flagBits == 0 {
+ return false
+ }
+ if !portSpecified && res.Transport == header.TCPProtocolNumber {
+ return false
+ }
}
}
if dests, ok := dd[res.BindToDevice]; ok {
flags, count := dests.intersectionFlags(res)
intersection &= flags
- if count > 0 && intersection&flagBits == 0 {
- return false
+ if count > 0 {
+ if intersection&flagBits == 0 {
+ return false
+ }
+ if !portSpecified && res.Transport == header.TCPProtocolNumber {
+ return false
+ }
}
}
@@ -168,12 +182,12 @@ type addrToDevice map[tcpip.Address]deviceToDest
// isAvailable checks whether an IP address is available to bind to. If the
// address is the "any" address, check all other addresses. Otherwise, just
// check against the "any" address and the provided address.
-func (ad addrToDevice) isAvailable(res Reservation) bool {
+func (ad addrToDevice) isAvailable(res Reservation, portSpecified bool) bool {
if res.Addr == anyIPAddress {
// If binding to the "any" address then check that there are no
// conflicts with all addresses.
for _, devices := range ad {
- if !devices.isAvailable(res) {
+ if !devices.isAvailable(res, portSpecified) {
return false
}
}
@@ -182,14 +196,14 @@ func (ad addrToDevice) isAvailable(res Reservation) bool {
// Check that there is no conflict with the "any" address.
if devices, ok := ad[anyIPAddress]; ok {
- if !devices.isAvailable(res) {
+ if !devices.isAvailable(res, portSpecified) {
return false
}
}
// Check that this is no conflict with the provided address.
if devices, ok := ad[res.Addr]; ok {
- if !devices.isAvailable(res) {
+ if !devices.isAvailable(res, portSpecified) {
return false
}
}
@@ -310,7 +324,7 @@ func (pm *PortManager) ReservePort(rng *rand.Rand, res Reservation, testPort Por
// If a port is specified, just try to reserve it for all network
// protocols.
if res.Port != 0 {
- if !pm.reserveSpecificPortLocked(res) {
+ if !pm.reserveSpecificPortLocked(res, true /* portSpecified */) {
return 0, &tcpip.ErrPortInUse{}
}
if testPort != nil {
@@ -330,7 +344,7 @@ func (pm *PortManager) ReservePort(rng *rand.Rand, res Reservation, testPort Por
// A port wasn't specified, so try to find one.
return pm.PickEphemeralPort(rng, func(p uint16) (bool, tcpip.Error) {
res.Port = p
- if !pm.reserveSpecificPortLocked(res) {
+ if !pm.reserveSpecificPortLocked(res, false /* portSpecified */) {
return false, nil
}
if testPort != nil {
@@ -350,12 +364,12 @@ func (pm *PortManager) ReservePort(rng *rand.Rand, res Reservation, testPort Por
// reserveSpecificPortLocked tries to reserve the given port on all given
// protocols.
-func (pm *PortManager) reserveSpecificPortLocked(res Reservation) bool {
+func (pm *PortManager) reserveSpecificPortLocked(res Reservation, portSpecified bool) bool {
// Make sure the port is available.
for _, network := range res.Networks {
desc := portDescriptor{network, res.Transport, res.Port}
if addrs, ok := pm.allocatedPorts[desc]; ok {
- if !addrs.isAvailable(res) {
+ if !addrs.isAvailable(res, portSpecified) {
return false
}
}
diff --git a/pkg/tcpip/stack/BUILD b/pkg/tcpip/stack/BUILD
index 395ff9a07..e0847e58a 100644
--- a/pkg/tcpip/stack/BUILD
+++ b/pkg/tcpip/stack/BUILD
@@ -95,7 +95,7 @@ go_library(
go_test(
name = "stack_x_test",
- size = "medium",
+ size = "small",
srcs = [
"addressable_endpoint_state_test.go",
"ndp_test.go",
diff --git a/pkg/tcpip/stack/addressable_endpoint_state.go b/pkg/tcpip/stack/addressable_endpoint_state.go
index ce9cebdaa..ae0bb4ace 100644
--- a/pkg/tcpip/stack/addressable_endpoint_state.go
+++ b/pkg/tcpip/stack/addressable_endpoint_state.go
@@ -249,7 +249,7 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
// or we are adding a new temporary or permanent address.
//
// The address MUST be write locked at this point.
- defer addrState.mu.Unlock()
+ defer addrState.mu.Unlock() // +checklocksforce
if permanent {
if addrState.mu.kind.IsPermanent() {
diff --git a/pkg/tcpip/stack/conntrack.go b/pkg/tcpip/stack/conntrack.go
index f7fbcbaa7..068dab7ce 100644
--- a/pkg/tcpip/stack/conntrack.go
+++ b/pkg/tcpip/stack/conntrack.go
@@ -35,7 +35,6 @@ import (
// Currently, only TCP tracking is supported.
// Our hash table has 16K buckets.
-// TODO(gvisor.dev/issue/170): These should be tunable.
const numBuckets = 1 << 14
// Direction of the tuple.
@@ -165,8 +164,6 @@ func (cn *conn) updateLocked(tcpHeader header.TCP, hook Hook) {
// Update the state of tcb. tcb assumes it's always initialized on the
// client. However, we only need to know whether the connection is
// established or not, so the client/server distinction isn't important.
- // TODO(gvisor.dev/issue/170): Add support in tcpconntrack to handle
- // other tcp states.
if cn.tcb.IsEmpty() {
cn.tcb.Init(tcpHeader)
} else if hook == cn.tcbHook {
@@ -246,8 +243,7 @@ func (ct *ConnTrack) init() {
// connFor gets the conn for pkt if it exists, or returns nil
// if it does not. It returns an error when pkt does not contain a valid TCP
// header.
-// TODO(gvisor.dev/issue/170): Only TCP packets are supported. Need to support
-// other transport protocols.
+// TODO(gvisor.dev/issue/6168): Support UDP.
func (ct *ConnTrack) connFor(pkt *PacketBuffer) (*conn, direction) {
tid, err := packetToTupleID(pkt)
if err != nil {
@@ -367,7 +363,7 @@ func (ct *ConnTrack) insertConn(conn *conn) {
// Unlocking can happen in any order.
ct.buckets[tupleBucket].mu.Unlock()
if tupleBucket != replyBucket {
- ct.buckets[replyBucket].mu.Unlock()
+ ct.buckets[replyBucket].mu.Unlock() // +checklocksforce
}
}
@@ -385,7 +381,7 @@ func (ct *ConnTrack) handlePacket(pkt *PacketBuffer, hook Hook, r *Route) bool {
return false
}
- // TODO(gvisor.dev/issue/170): Support other transport protocols.
+ // TODO(gvisor.dev/issue/6168): Support UDP.
if pkt.Network().TransportProtocol() != header.TCPProtocolNumber {
return false
}
@@ -409,16 +405,23 @@ func (ct *ConnTrack) handlePacket(pkt *PacketBuffer, hook Hook, r *Route) bool {
// validated if checksum offloading is off. It may require IP defrag if the
// packets are fragmented.
+ var newAddr tcpip.Address
+ var newPort uint16
+
+ updateSRCFields := false
+
switch hook {
case Prerouting, Output:
if conn.manip == manipDestination {
switch dir {
case dirOriginal:
- tcpHeader.SetDestinationPort(conn.reply.srcPort)
- netHeader.SetDestinationAddress(conn.reply.srcAddr)
+ newPort = conn.reply.srcPort
+ newAddr = conn.reply.srcAddr
case dirReply:
- tcpHeader.SetSourcePort(conn.original.dstPort)
- netHeader.SetSourceAddress(conn.original.dstAddr)
+ newPort = conn.original.dstPort
+ newAddr = conn.original.dstAddr
+
+ updateSRCFields = true
}
pkt.NatDone = true
}
@@ -426,11 +429,13 @@ func (ct *ConnTrack) handlePacket(pkt *PacketBuffer, hook Hook, r *Route) bool {
if conn.manip == manipSource {
switch dir {
case dirOriginal:
- tcpHeader.SetSourcePort(conn.reply.dstPort)
- netHeader.SetSourceAddress(conn.reply.dstAddr)
+ newPort = conn.reply.dstPort
+ newAddr = conn.reply.dstAddr
+
+ updateSRCFields = true
case dirReply:
- tcpHeader.SetDestinationPort(conn.original.srcPort)
- netHeader.SetDestinationAddress(conn.original.srcAddr)
+ newPort = conn.original.srcPort
+ newAddr = conn.original.srcAddr
}
pkt.NatDone = true
}
@@ -441,33 +446,33 @@ func (ct *ConnTrack) handlePacket(pkt *PacketBuffer, hook Hook, r *Route) bool {
return false
}
+ fullChecksum := false
+ updatePseudoHeader := false
switch hook {
case Prerouting, Input:
case Output, Postrouting:
// Calculate the TCP checksum and set it.
- tcpHeader.SetChecksum(0)
- length := uint16(len(tcpHeader) + pkt.Data().Size())
- xsum := header.PseudoHeaderChecksum(header.TCPProtocolNumber, netHeader.SourceAddress(), netHeader.DestinationAddress(), length)
if pkt.GSOOptions.Type != GSONone && pkt.GSOOptions.NeedsCsum {
- tcpHeader.SetChecksum(xsum)
+ updatePseudoHeader = true
} else if r.RequiresTXTransportChecksum() {
- xsum = header.ChecksumCombine(xsum, pkt.Data().AsRange().Checksum())
- tcpHeader.SetChecksum(^tcpHeader.CalculateChecksum(xsum))
+ fullChecksum = true
+ updatePseudoHeader = true
}
default:
panic(fmt.Sprintf("unrecognized hook = %s", hook))
}
- // After modification, IPv4 packets need a valid checksum.
- if pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber {
- netHeader := header.IPv4(pkt.NetworkHeader().View())
- netHeader.SetChecksum(0)
- netHeader.SetChecksum(^netHeader.CalculateChecksum())
- }
+ rewritePacket(
+ netHeader,
+ tcpHeader,
+ updateSRCFields,
+ fullChecksum,
+ updatePseudoHeader,
+ newPort,
+ newAddr,
+ )
// Update the state of tcb.
- // TODO(gvisor.dev/issue/170): Add support in tcpcontrack to handle
- // other tcp states.
conn.mu.Lock()
defer conn.mu.Unlock()
@@ -544,8 +549,6 @@ func (ct *ConnTrack) bucket(id tupleID) int {
// reapUnused returns the next bucket that should be checked and the time after
// which it should be called again.
func (ct *ConnTrack) reapUnused(start int, prevInterval time.Duration) (int, time.Duration) {
- // TODO(gvisor.dev/issue/170): This can be more finely controlled, as
- // it is in Linux via sysctl.
const fractionPerReaping = 128
const maxExpiredPct = 50
const maxFullTraversal = 60 * time.Second
@@ -623,7 +626,7 @@ func (ct *ConnTrack) reapTupleLocked(tuple *tuple, bucket int, now time.Time) bo
// Don't re-unlock if both tuples are in the same bucket.
if differentBuckets {
- ct.buckets[replyBucket].mu.Unlock()
+ ct.buckets[replyBucket].mu.Unlock() // +checklocksforce
}
return true
diff --git a/pkg/tcpip/stack/iptables.go b/pkg/tcpip/stack/iptables.go
index 0a26f6dd8..f152c0d83 100644
--- a/pkg/tcpip/stack/iptables.go
+++ b/pkg/tcpip/stack/iptables.go
@@ -268,10 +268,6 @@ const (
// should continue traversing the network stack and false when it should be
// dropped.
//
-// TODO(gvisor.dev/issue/170): PacketBuffer should hold the route, from
-// which address can be gathered. Currently, address is only needed for
-// prerouting.
-//
// Precondition: pkt.NetworkHeader is set.
func (it *IPTables) Check(hook Hook, pkt *PacketBuffer, r *Route, preroutingAddr tcpip.Address, inNicName, outNicName string) bool {
if pkt.NetworkProtocolNumber != header.IPv4ProtocolNumber && pkt.NetworkProtocolNumber != header.IPv6ProtocolNumber {
diff --git a/pkg/tcpip/stack/iptables_targets.go b/pkg/tcpip/stack/iptables_targets.go
index 2812c89aa..96cc899bb 100644
--- a/pkg/tcpip/stack/iptables_targets.go
+++ b/pkg/tcpip/stack/iptables_targets.go
@@ -87,9 +87,6 @@ func (*ReturnTarget) Action(*PacketBuffer, *ConnTrack, Hook, *Route, tcpip.Addre
// destination port/IP. Outgoing packets are redirected to the loopback device,
// and incoming packets are redirected to the incoming interface (rather than
// forwarded).
-//
-// TODO(gvisor.dev/issue/170): Other flags need to be added after we support
-// them.
type RedirectTarget struct {
// Port indicates port used to redirect. It is immutable.
Port uint16
@@ -100,9 +97,6 @@ type RedirectTarget struct {
}
// Action implements Target.Action.
-// TODO(gvisor.dev/issue/170): Parse headers without copying. The current
-// implementation only works for Prerouting and calls pkt.Clone(), neither
-// of which should be the case.
func (rt *RedirectTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Route, address tcpip.Address) (RuleVerdict, int) {
// Sanity check.
if rt.NetworkProtocol != pkt.NetworkProtocolNumber {
@@ -136,34 +130,26 @@ func (rt *RedirectTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r
panic("redirect target is supported only on output and prerouting hooks")
}
- // TODO(gvisor.dev/issue/170): Check Flags in RedirectTarget if
- // we need to change dest address (for OUTPUT chain) or ports.
switch protocol := pkt.TransportProtocolNumber; protocol {
case header.UDPProtocolNumber:
udpHeader := header.UDP(pkt.TransportHeader().View())
- udpHeader.SetDestinationPort(rt.Port)
- // Calculate UDP checksum and set it.
if hook == Output {
- udpHeader.SetChecksum(0)
- netHeader := pkt.Network()
- netHeader.SetDestinationAddress(address)
-
// Only calculate the checksum if offloading isn't supported.
- if r.RequiresTXTransportChecksum() {
- length := uint16(pkt.Size()) - uint16(len(pkt.NetworkHeader().View()))
- xsum := header.PseudoHeaderChecksum(protocol, netHeader.SourceAddress(), netHeader.DestinationAddress(), length)
- xsum = header.ChecksumCombine(xsum, pkt.Data().AsRange().Checksum())
- udpHeader.SetChecksum(^udpHeader.CalculateChecksum(xsum))
- }
+ requiresChecksum := r.RequiresTXTransportChecksum()
+ rewritePacket(
+ pkt.Network(),
+ udpHeader,
+ false, /* updateSRCFields */
+ requiresChecksum,
+ requiresChecksum,
+ rt.Port,
+ address,
+ )
+ } else {
+ udpHeader.SetDestinationPort(rt.Port)
}
- // After modification, IPv4 packets need a valid checksum.
- if pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber {
- netHeader := header.IPv4(pkt.NetworkHeader().View())
- netHeader.SetChecksum(0)
- netHeader.SetChecksum(^netHeader.CalculateChecksum())
- }
pkt.NatDone = true
case header.TCPProtocolNumber:
if ct == nil {
@@ -222,26 +208,18 @@ func (st *SNATTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Rou
switch protocol := pkt.TransportProtocolNumber; protocol {
case header.UDPProtocolNumber:
- udpHeader := header.UDP(pkt.TransportHeader().View())
- udpHeader.SetChecksum(0)
- udpHeader.SetSourcePort(st.Port)
- netHeader := pkt.Network()
- netHeader.SetSourceAddress(st.Addr)
-
// Only calculate the checksum if offloading isn't supported.
- if r.RequiresTXTransportChecksum() {
- length := uint16(pkt.Size()) - uint16(len(pkt.NetworkHeader().View()))
- xsum := header.PseudoHeaderChecksum(protocol, netHeader.SourceAddress(), netHeader.DestinationAddress(), length)
- xsum = header.ChecksumCombine(xsum, pkt.Data().AsRange().Checksum())
- udpHeader.SetChecksum(^udpHeader.CalculateChecksum(xsum))
- }
+ requiresChecksum := r.RequiresTXTransportChecksum()
+ rewritePacket(
+ pkt.Network(),
+ header.UDP(pkt.TransportHeader().View()),
+ true, /* updateSRCFields */
+ requiresChecksum,
+ requiresChecksum,
+ st.Port,
+ st.Addr,
+ )
- // After modification, IPv4 packets need a valid checksum.
- if pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber {
- netHeader := header.IPv4(pkt.NetworkHeader().View())
- netHeader.SetChecksum(0)
- netHeader.SetChecksum(^netHeader.CalculateChecksum())
- }
pkt.NatDone = true
case header.TCPProtocolNumber:
if ct == nil {
@@ -260,3 +238,42 @@ func (st *SNATTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, r *Rou
return RuleAccept, 0
}
+
+func rewritePacket(n header.Network, t header.ChecksummableTransport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPort uint16, newAddr tcpip.Address) {
+ if updateSRCFields {
+ if fullChecksum {
+ t.SetSourcePortWithChecksumUpdate(newPort)
+ } else {
+ t.SetSourcePort(newPort)
+ }
+ } else {
+ if fullChecksum {
+ t.SetDestinationPortWithChecksumUpdate(newPort)
+ } else {
+ t.SetDestinationPort(newPort)
+ }
+ }
+
+ if updatePseudoHeader {
+ var oldAddr tcpip.Address
+ if updateSRCFields {
+ oldAddr = n.SourceAddress()
+ } else {
+ oldAddr = n.DestinationAddress()
+ }
+
+ t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr, fullChecksum)
+ }
+
+ if checksummableNetHeader, ok := n.(header.ChecksummableNetwork); ok {
+ if updateSRCFields {
+ checksummableNetHeader.SetSourceAddressWithChecksumUpdate(newAddr)
+ } else {
+ checksummableNetHeader.SetDestinationAddressWithChecksumUpdate(newAddr)
+ }
+ } else if updateSRCFields {
+ n.SetSourceAddress(newAddr)
+ } else {
+ n.SetDestinationAddress(newAddr)
+ }
+}
diff --git a/pkg/tcpip/stack/iptables_types.go b/pkg/tcpip/stack/iptables_types.go
index 93592e7f5..66e5f22ac 100644
--- a/pkg/tcpip/stack/iptables_types.go
+++ b/pkg/tcpip/stack/iptables_types.go
@@ -242,7 +242,6 @@ type IPHeaderFilter struct {
func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicName string) bool {
// Extract header fields.
var (
- // TODO(gvisor.dev/issue/170): Support other filter fields.
transProto tcpip.TransportProtocolNumber
dstAddr tcpip.Address
srcAddr tcpip.Address
@@ -291,7 +290,6 @@ func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicNa
return true
case Postrouting:
- // TODO(gvisor.dev/issue/170): Add the check for POSTROUTING.
return true
default:
panic(fmt.Sprintf("unknown hook: %d", hook))
diff --git a/pkg/tcpip/stack/ndp_test.go b/pkg/tcpip/stack/ndp_test.go
index 133bacdd0..ca2250ad6 100644
--- a/pkg/tcpip/stack/ndp_test.go
+++ b/pkg/tcpip/stack/ndp_test.go
@@ -52,17 +52,6 @@ const (
linkAddr4 = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x09")
defaultPrefixLen = 128
-
- // Extra time to use when waiting for an async event to occur.
- defaultAsyncPositiveEventTimeout = 10 * time.Second
-
- // Extra time to use when waiting for an async event to not occur.
- //
- // Since a negative check is used to make sure an event did not happen, it is
- // okay to use a smaller timeout compared to the positive case since execution
- // stall in regards to the monotonic clock will not affect the expected
- // outcome.
- defaultAsyncNegativeEventTimeout = time.Second
)
var (
@@ -112,11 +101,13 @@ type ndpDADEvent struct {
res stack.DADResult
}
-type ndpRouterEvent struct {
- nicID tcpip.NICID
- addr tcpip.Address
- // true if router was discovered, false if invalidated.
- discovered bool
+type ndpOffLinkRouteEvent struct {
+ nicID tcpip.NICID
+ subnet tcpip.Subnet
+ router tcpip.Address
+ prf header.NDPRoutePreference
+ // true if route was updated, false if invalidated.
+ updated bool
}
type ndpPrefixEvent struct {
@@ -140,6 +131,10 @@ type ndpAutoGenAddrEvent struct {
eventType ndpAutoGenAddrEventType
}
+func (e ndpAutoGenAddrEvent) String() string {
+ return fmt.Sprintf("%T{nicID=%d addr=%s eventType=%d}", e, e.nicID, e.addr, e.eventType)
+}
+
type ndpRDNSS struct {
addrs []tcpip.Address
lifetime time.Duration
@@ -167,10 +162,8 @@ var _ ipv6.NDPDispatcher = (*ndpDispatcher)(nil)
// related events happen for test purposes.
type ndpDispatcher struct {
dadC chan ndpDADEvent
- routerC chan ndpRouterEvent
- rememberRouter bool
+ offLinkRouteC chan ndpOffLinkRouteEvent
prefixC chan ndpPrefixEvent
- rememberPrefix bool
autoGenAddrC chan ndpAutoGenAddrEvent
rdnssC chan ndpRDNSSEvent
dnsslC chan ndpDNSSLEvent
@@ -189,32 +182,35 @@ func (n *ndpDispatcher) OnDuplicateAddressDetectionResult(nicID tcpip.NICID, add
}
}
-// Implements ipv6.NDPDispatcher.OnDefaultRouterDiscovered.
-func (n *ndpDispatcher) OnDefaultRouterDiscovered(nicID tcpip.NICID, addr tcpip.Address) bool {
- if c := n.routerC; c != nil {
- c <- ndpRouterEvent{
+// Implements ipv6.NDPDispatcher.OnOffLinkRouteUpdated.
+func (n *ndpDispatcher) OnOffLinkRouteUpdated(nicID tcpip.NICID, subnet tcpip.Subnet, router tcpip.Address, prf header.NDPRoutePreference) {
+ if c := n.offLinkRouteC; c != nil {
+ c <- ndpOffLinkRouteEvent{
nicID,
- addr,
+ subnet,
+ router,
+ prf,
true,
}
}
-
- return n.rememberRouter
}
-// Implements ipv6.NDPDispatcher.OnDefaultRouterInvalidated.
-func (n *ndpDispatcher) OnDefaultRouterInvalidated(nicID tcpip.NICID, addr tcpip.Address) {
- if c := n.routerC; c != nil {
- c <- ndpRouterEvent{
+// Implements ipv6.NDPDispatcher.OnOffLinkRouteInvalidated.
+func (n *ndpDispatcher) OnOffLinkRouteInvalidated(nicID tcpip.NICID, subnet tcpip.Subnet, router tcpip.Address) {
+ if c := n.offLinkRouteC; c != nil {
+ var prf header.NDPRoutePreference
+ c <- ndpOffLinkRouteEvent{
nicID,
- addr,
+ subnet,
+ router,
+ prf,
false,
}
}
}
// Implements ipv6.NDPDispatcher.OnOnLinkPrefixDiscovered.
-func (n *ndpDispatcher) OnOnLinkPrefixDiscovered(nicID tcpip.NICID, prefix tcpip.Subnet) bool {
+func (n *ndpDispatcher) OnOnLinkPrefixDiscovered(nicID tcpip.NICID, prefix tcpip.Subnet) {
if c := n.prefixC; c != nil {
c <- ndpPrefixEvent{
nicID,
@@ -222,8 +218,6 @@ func (n *ndpDispatcher) OnOnLinkPrefixDiscovered(nicID tcpip.NICID, prefix tcpip
true,
}
}
-
- return n.rememberPrefix
}
// Implements ipv6.NDPDispatcher.OnOnLinkPrefixInvalidated.
@@ -237,7 +231,7 @@ func (n *ndpDispatcher) OnOnLinkPrefixInvalidated(nicID tcpip.NICID, prefix tcpi
}
}
-func (n *ndpDispatcher) OnAutoGenAddress(nicID tcpip.NICID, addr tcpip.AddressWithPrefix) bool {
+func (n *ndpDispatcher) OnAutoGenAddress(nicID tcpip.NICID, addr tcpip.AddressWithPrefix) {
if c := n.autoGenAddrC; c != nil {
c <- ndpAutoGenAddrEvent{
nicID,
@@ -245,7 +239,6 @@ func (n *ndpDispatcher) OnAutoGenAddress(nicID tcpip.NICID, addr tcpip.AddressWi
newAddr,
}
}
- return true
}
func (n *ndpDispatcher) OnAutoGenAddressDeprecated(nicID tcpip.NICID, addr tcpip.AddressWithPrefix) {
@@ -1039,9 +1032,12 @@ func TestSetNDPConfigurations(t *testing.T) {
}
}
-// raBufWithOptsAndDHCPv6 returns a valid NDP Router Advertisement with options
-// and DHCPv6 configurations specified.
-func raBufWithOptsAndDHCPv6(ip tcpip.Address, rl uint16, managedAddress, otherConfigurations bool, optSer header.NDPOptionsSerializer) *stack.PacketBuffer {
+// raBuf returns a valid NDP Router Advertisement with options, router
+// preference and DHCPv6 configurations specified.
+func raBuf(ip tcpip.Address, rl uint16, managedAddress, otherConfigurations bool, prf header.NDPRoutePreference, optSer header.NDPOptionsSerializer) *stack.PacketBuffer {
+ const flagsByte = 1
+ const routerLifetimeOffset = 2
+
icmpSize := header.ICMPv6HeaderSize + header.NDPRAMinimumSize + optSer.Length()
hdr := buffer.NewPrependable(header.IPv6MinimumSize + icmpSize)
pkt := header.ICMPv6(hdr.Prepend(icmpSize))
@@ -1050,19 +1046,19 @@ func raBufWithOptsAndDHCPv6(ip tcpip.Address, rl uint16, managedAddress, otherCo
raPayload := pkt.MessageBody()
ra := header.NDPRouterAdvert(raPayload)
// Populate the Router Lifetime.
- binary.BigEndian.PutUint16(raPayload[2:], rl)
+ binary.BigEndian.PutUint16(raPayload[routerLifetimeOffset:], rl)
// Populate the Managed Address flag field.
if managedAddress {
- // The Managed Addresses flag field is the 7th bit of byte #1 (0-indexing)
- // of the RA payload.
- raPayload[1] |= 1 << 7
+ // The Managed Addresses flag field is the 7th bit of the flags byte.
+ raPayload[flagsByte] |= 1 << 7
}
// Populate the Other Configurations flag field.
if otherConfigurations {
- // The Other Configurations flag field is the 6th bit of byte #1
- // (0-indexing) of the RA payload.
- raPayload[1] |= 1 << 6
+ // The Other Configurations flag field is the 6th bit of the flags byte.
+ raPayload[flagsByte] |= 1 << 6
}
+ // The Prf field is held in the flags byte.
+ raPayload[flagsByte] |= byte(prf) << 3
opts := ra.Options()
opts.Serialize(optSer)
pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
@@ -1090,7 +1086,7 @@ func raBufWithOptsAndDHCPv6(ip tcpip.Address, rl uint16, managedAddress, otherCo
// Note, raBufWithOpts does not populate any of the RA fields other than the
// Router Lifetime.
func raBufWithOpts(ip tcpip.Address, rl uint16, optSer header.NDPOptionsSerializer) *stack.PacketBuffer {
- return raBufWithOptsAndDHCPv6(ip, rl, false, false, optSer)
+ return raBuf(ip, rl, false /* managedAddress */, false /* otherConfigurations */, 0 /* prf */, optSer)
}
// raBufWithDHCPv6 returns a valid NDP Router Advertisement with DHCPv6 related
@@ -1098,18 +1094,26 @@ func raBufWithOpts(ip tcpip.Address, rl uint16, optSer header.NDPOptionsSerializ
//
// Note, raBufWithDHCPv6 does not populate any of the RA fields other than the
// DHCPv6 related ones.
-func raBufWithDHCPv6(ip tcpip.Address, managedAddresses, otherConfiguratiosns bool) *stack.PacketBuffer {
- return raBufWithOptsAndDHCPv6(ip, 0, managedAddresses, otherConfiguratiosns, header.NDPOptionsSerializer{})
+func raBufWithDHCPv6(ip tcpip.Address, managedAddresses, otherConfigurations bool) *stack.PacketBuffer {
+ return raBuf(ip, 0, managedAddresses, otherConfigurations, 0 /* prf */, header.NDPOptionsSerializer{})
}
// raBuf returns a valid NDP Router Advertisement.
//
// Note, raBuf does not populate any of the RA fields other than the
// Router Lifetime.
-func raBuf(ip tcpip.Address, rl uint16) *stack.PacketBuffer {
+func raBufSimple(ip tcpip.Address, rl uint16) *stack.PacketBuffer {
return raBufWithOpts(ip, rl, header.NDPOptionsSerializer{})
}
+// raBufWithPrf returns a valid NDP Router Advertisement with a preference.
+//
+// Note, raBufWithPrf does not populate any of the RA fields other than the
+// Router Lifetime and Default Router Preference fields.
+func raBufWithPrf(ip tcpip.Address, rl uint16, prf header.NDPRoutePreference) *stack.PacketBuffer {
+ return raBuf(ip, rl, false /* managedAddress */, false /* otherConfigurations */, prf, header.NDPOptionsSerializer{})
+}
+
// raBufWithPI returns a valid NDP Router Advertisement with a single Prefix
// Information option.
//
@@ -1148,6 +1152,39 @@ func raBufWithPI(ip tcpip.Address, rl uint16, prefix tcpip.AddressWithPrefix, on
})
}
+// raBufWithRIO returns a valid NDP Router Advertisement with a single Route
+// Information option.
+//
+// All fields in the RA will be zero except the RIO option.
+func raBufWithRIO(t *testing.T, ip tcpip.Address, prefix tcpip.AddressWithPrefix, lifetimeSeconds uint32, prf header.NDPRoutePreference) *stack.PacketBuffer {
+ // buf will hold the route information option after the Type and Length
+ // fields.
+ //
+ // 2.3. Route Information Option
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Type | Length | Prefix Length |Resvd|Prf|Resvd|
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Route Lifetime |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Prefix (Variable Length) |
+ // . .
+ // . .
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ var buf [22]byte
+ buf[0] = uint8(prefix.PrefixLen)
+ buf[1] = byte(prf) << 3
+ binary.BigEndian.PutUint32(buf[2:], lifetimeSeconds)
+ if n := copy(buf[6:], prefix.Address); n != len(prefix.Address) {
+ t.Fatalf("got copy(...) = %d, want = %d", n, len(prefix.Address))
+ }
+ return raBufWithOpts(ip, 0 /* router lifetime */, header.NDPOptionsSerializer{
+ header.NDPRouteInformation(buf[:]),
+ })
+}
+
func TestDynamicConfigurationsDisabled(t *testing.T) {
const (
nicID = 1
@@ -1169,7 +1206,7 @@ func TestDynamicConfigurationsDisabled(t *testing.T) {
config: func(enable bool) ipv6.NDPConfigurations {
return ipv6.NDPConfigurations{DiscoverDefaultRouters: enable}
},
- ra: raBuf(llAddr2, 1000),
+ ra: raBufSimple(llAddr2, 1000),
},
{
name: "No Prefix Discovery",
@@ -1205,9 +1242,9 @@ func TestDynamicConfigurationsDisabled(t *testing.T) {
t.Run(fmt.Sprintf("HandleRAs(%s), Forwarding(%t), Enabled(%t)", handle, forwarding, enable), func(t *testing.T) {
ndpDisp := ndpDispatcher{
- routerC: make(chan ndpRouterEvent, 1),
- prefixC: make(chan ndpPrefixEvent, 1),
- autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
+ offLinkRouteC: make(chan ndpOffLinkRouteEvent, 1),
+ prefixC: make(chan ndpPrefixEvent, 1),
+ autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
}
ndpConfigs := test.config(enable)
ndpConfigs.HandleRAs = handle
@@ -1277,8 +1314,8 @@ func TestDynamicConfigurationsDisabled(t *testing.T) {
t.Errorf("got v6Stats.UnhandledRouterAdvertisements.Value() = %d, want = %d", got, want)
}
select {
- case e := <-ndpDisp.routerC:
- t.Errorf("unexpectedly discovered a router when configured not to: %#v", e)
+ case e := <-ndpDisp.offLinkRouteC:
+ t.Errorf("unexpectedly updated an off-link route when configured not to: %#v", e)
default:
}
select {
@@ -1304,10 +1341,8 @@ func boolToUint64(v bool) uint64 {
return 0
}
-// Check e to make sure that the event is for addr on nic with ID 1, and the
-// discovered flag set to discovered.
-func checkRouterEvent(e ndpRouterEvent, addr tcpip.Address, discovered bool) string {
- return cmp.Diff(ndpRouterEvent{nicID: 1, addr: addr, discovered: discovered}, e, cmp.AllowUnexported(e))
+func checkOffLinkRouteEvent(e ndpOffLinkRouteEvent, nicID tcpip.NICID, subnet tcpip.Subnet, router tcpip.Address, prf header.NDPRoutePreference, updated bool) string {
+ return cmp.Diff(ndpOffLinkRouteEvent{nicID: nicID, subnet: subnet, router: router, prf: prf, updated: updated}, e, cmp.AllowUnexported(e))
}
func testWithRAs(t *testing.T, f func(*testing.T, ipv6.HandleRAsConfiguration, bool)) {
@@ -1340,167 +1375,176 @@ func testWithRAs(t *testing.T, f func(*testing.T, ipv6.HandleRAsConfiguration, b
}
}
-// TestRouterDiscoveryDispatcherNoRemember tests that the stack does not
-// remember a discovered router when the dispatcher asks it not to.
-func TestRouterDiscoveryDispatcherNoRemember(t *testing.T) {
- ndpDisp := ndpDispatcher{
- routerC: make(chan ndpRouterEvent, 1),
- }
- e := channel.New(0, 1280, linkAddr1)
- clock := faketime.NewManualClock()
- s := stack.New(stack.Options{
- NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
- NDPConfigs: ipv6.NDPConfigurations{
- HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
- DiscoverDefaultRouters: true,
- },
- NDPDisp: &ndpDisp,
- })},
- Clock: clock,
- })
+func TestOffLinkRouteDiscovery(t *testing.T) {
+ const nicID = 1
- if err := s.CreateNIC(1, e); err != nil {
- t.Fatalf("CreateNIC(1) = %s", err)
- }
+ moreSpecificPrefix := tcpip.AddressWithPrefix{Address: testutil.MustParse6("a00::"), PrefixLen: 16}
+ tests := []struct {
+ name string
- // Receive an RA for a router we should not remember.
- const lifetimeSeconds = 1
- e.InjectInbound(header.IPv6ProtocolNumber, raBuf(llAddr2, lifetimeSeconds))
- select {
- case e := <-ndpDisp.routerC:
- if diff := checkRouterEvent(e, llAddr2, true); diff != "" {
- t.Errorf("router event mismatch (-want +got):\n%s", diff)
- }
- default:
- t.Fatal("expected router discovery event")
- }
+ discoverDefaultRouters bool
+ discoverMoreSpecificRoutes bool
- // Wait for the invalidation time plus some buffer to make sure we do
- // not actually receive any invalidation events as we should not have
- // remembered the router in the first place.
- clock.Advance(lifetimeSeconds * time.Second)
- select {
- case <-ndpDisp.routerC:
- t.Fatal("should not have received any router events")
- default:
+ dest tcpip.Subnet
+ ra func(*testing.T, tcpip.Address, uint16, header.NDPRoutePreference) *stack.PacketBuffer
+ }{
+ {
+ name: "Default router discovery",
+ discoverDefaultRouters: true,
+ discoverMoreSpecificRoutes: false,
+ dest: header.IPv6EmptySubnet,
+ ra: func(_ *testing.T, router tcpip.Address, lifetimeSeconds uint16, prf header.NDPRoutePreference) *stack.PacketBuffer {
+ return raBufWithPrf(router, lifetimeSeconds, prf)
+ },
+ },
+ {
+ name: "More-specific route discovery",
+ discoverDefaultRouters: false,
+ discoverMoreSpecificRoutes: true,
+ dest: moreSpecificPrefix.Subnet(),
+ ra: func(t *testing.T, router tcpip.Address, lifetimeSeconds uint16, prf header.NDPRoutePreference) *stack.PacketBuffer {
+ return raBufWithRIO(t, router, moreSpecificPrefix, uint32(lifetimeSeconds), prf)
+ },
+ },
}
-}
-func TestRouterDiscovery(t *testing.T) {
- testWithRAs(t, func(t *testing.T, handleRAs ipv6.HandleRAsConfiguration, forwarding bool) {
- ndpDisp := ndpDispatcher{
- routerC: make(chan ndpRouterEvent, 1),
- rememberRouter: true,
- }
- e := channel.New(0, 1280, linkAddr1)
- clock := faketime.NewManualClock()
- s := stack.New(stack.Options{
- NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
- NDPConfigs: ipv6.NDPConfigurations{
- HandleRAs: handleRAs,
- DiscoverDefaultRouters: true,
- },
- NDPDisp: &ndpDisp,
- })},
- Clock: clock,
- })
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ testWithRAs(t, func(t *testing.T, handleRAs ipv6.HandleRAsConfiguration, forwarding bool) {
+ ndpDisp := ndpDispatcher{
+ offLinkRouteC: make(chan ndpOffLinkRouteEvent, 1),
+ }
+ e := channel.New(0, 1280, linkAddr1)
+ clock := faketime.NewManualClock()
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
+ NDPConfigs: ipv6.NDPConfigurations{
+ HandleRAs: handleRAs,
+ DiscoverDefaultRouters: test.discoverDefaultRouters,
+ DiscoverMoreSpecificRoutes: test.discoverMoreSpecificRoutes,
+ },
+ NDPDisp: &ndpDisp,
+ })},
+ Clock: clock,
+ })
- expectRouterEvent := func(addr tcpip.Address, discovered bool) {
- t.Helper()
+ expectOffLinkRouteEvent := func(addr tcpip.Address, prf header.NDPRoutePreference, updated bool) {
+ t.Helper()
- select {
- case e := <-ndpDisp.routerC:
- if diff := checkRouterEvent(e, addr, discovered); diff != "" {
- t.Errorf("router event mismatch (-want +got):\n%s", diff)
+ select {
+ case e := <-ndpDisp.offLinkRouteC:
+ if diff := checkOffLinkRouteEvent(e, nicID, test.dest, addr, prf, updated); diff != "" {
+ t.Errorf("off-link route event mismatch (-want +got):\n%s", diff)
+ }
+ default:
+ t.Fatal("expected router discovery event")
+ }
}
- default:
- t.Fatal("expected router discovery event")
- }
- }
- expectAsyncRouterInvalidationEvent := func(addr tcpip.Address, timeout time.Duration) {
- t.Helper()
+ expectAsyncOffLinkRouteInvalidationEvent := func(addr tcpip.Address, timeout time.Duration) {
+ t.Helper()
- clock.Advance(timeout)
- select {
- case e := <-ndpDisp.routerC:
- if diff := checkRouterEvent(e, addr, false); diff != "" {
- t.Errorf("router event mismatch (-want +got):\n%s", diff)
+ clock.Advance(timeout)
+ select {
+ case e := <-ndpDisp.offLinkRouteC:
+ var prf header.NDPRoutePreference
+ if diff := checkOffLinkRouteEvent(e, nicID, test.dest, addr, prf, false); diff != "" {
+ t.Errorf("off-link route event mismatch (-want +got):\n%s", diff)
+ }
+ default:
+ t.Fatal("timed out waiting for router discovery event")
+ }
}
- default:
- t.Fatal("timed out waiting for router discovery event")
- }
- }
- if err := s.SetForwardingDefaultAndAllNICs(ipv6.ProtocolNumber, forwarding); err != nil {
- t.Fatalf("SetForwardingDefaultAndAllNICs(%d, %t): %s", ipv6.ProtocolNumber, forwarding, err)
- }
-
- if err := s.CreateNIC(1, e); err != nil {
- t.Fatalf("CreateNIC(1) = %s", err)
- }
+ if err := s.SetForwardingDefaultAndAllNICs(ipv6.ProtocolNumber, forwarding); err != nil {
+ t.Fatalf("SetForwardingDefaultAndAllNICs(%d, %t): %s", ipv6.ProtocolNumber, forwarding, err)
+ }
- // Rx an RA from lladdr2 with zero lifetime. It should not be
- // remembered.
- e.InjectInbound(header.IPv6ProtocolNumber, raBuf(llAddr2, 0))
- select {
- case <-ndpDisp.routerC:
- t.Fatal("unexpectedly discovered a router with 0 lifetime")
- default:
- }
+ if err := s.CreateNIC(nicID, e); err != nil {
+ t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
+ }
- // Rx an RA from lladdr2 with a huge lifetime.
- e.InjectInbound(header.IPv6ProtocolNumber, raBuf(llAddr2, 1000))
- expectRouterEvent(llAddr2, true)
+ // Rx an RA from lladdr2 with zero lifetime. It should not be
+ // remembered.
+ e.InjectInbound(header.IPv6ProtocolNumber, test.ra(t, llAddr2, 0, header.MediumRoutePreference))
+ select {
+ case <-ndpDisp.offLinkRouteC:
+ t.Fatal("unexpectedly updated an off-link route with 0 lifetime")
+ default:
+ }
- // Rx an RA from another router (lladdr3) with non-zero lifetime.
- const l3LifetimeSeconds = 6
- e.InjectInbound(header.IPv6ProtocolNumber, raBuf(llAddr3, l3LifetimeSeconds))
- expectRouterEvent(llAddr3, true)
+ // Discover an off-link route through llAddr2.
+ e.InjectInbound(header.IPv6ProtocolNumber, test.ra(t, llAddr2, 1000, header.ReservedRoutePreference))
+ if test.discoverMoreSpecificRoutes {
+ // The reserved value is considered invalid with more-specific route
+ // discovery so we inject the same packet but with the default
+ // (medium) preference value.
+ select {
+ case <-ndpDisp.offLinkRouteC:
+ t.Fatal("unexpectedly updated an off-link route with a reserved preference value")
+ default:
+ }
+ e.InjectInbound(header.IPv6ProtocolNumber, test.ra(t, llAddr2, 1000, header.MediumRoutePreference))
+ }
+ expectOffLinkRouteEvent(llAddr2, header.MediumRoutePreference, true)
+
+ // Rx an RA from another router (lladdr3) with non-zero lifetime and
+ // non-default preference value.
+ const l3LifetimeSeconds = 6
+ e.InjectInbound(header.IPv6ProtocolNumber, test.ra(t, llAddr3, l3LifetimeSeconds, header.HighRoutePreference))
+ expectOffLinkRouteEvent(llAddr3, header.HighRoutePreference, true)
+
+ // Rx an RA from lladdr2 with lesser lifetime and default (medium)
+ // preference value.
+ const l2LifetimeSeconds = 2
+ e.InjectInbound(header.IPv6ProtocolNumber, test.ra(t, llAddr2, l2LifetimeSeconds, header.MediumRoutePreference))
+ select {
+ case <-ndpDisp.offLinkRouteC:
+ t.Fatal("should not receive a off-link route event when updating lifetimes for known routers")
+ default:
+ }
- // Rx an RA from lladdr2 with lesser lifetime.
- const l2LifetimeSeconds = 2
- e.InjectInbound(header.IPv6ProtocolNumber, raBuf(llAddr2, l2LifetimeSeconds))
- select {
- case <-ndpDisp.routerC:
- t.Fatal("Should not receive a router event when updating lifetimes for known routers")
- default:
- }
+ // Rx an RA from lladdr2 with a different preference.
+ e.InjectInbound(header.IPv6ProtocolNumber, test.ra(t, llAddr2, l2LifetimeSeconds, header.LowRoutePreference))
+ expectOffLinkRouteEvent(llAddr2, header.LowRoutePreference, true)
- // Wait for lladdr2's router invalidation job to execute. The lifetime
- // of the router should have been updated to the most recent (smaller)
- // lifetime.
- //
- // Wait for the normal lifetime plus an extra bit for the
- // router to get invalidated. If we don't get an invalidation
- // event after this time, then something is wrong.
- expectAsyncRouterInvalidationEvent(llAddr2, l2LifetimeSeconds*time.Second)
-
- // Rx an RA from lladdr2 with huge lifetime.
- e.InjectInbound(header.IPv6ProtocolNumber, raBuf(llAddr2, 1000))
- expectRouterEvent(llAddr2, true)
-
- // Rx an RA from lladdr2 with zero lifetime. It should be invalidated.
- e.InjectInbound(header.IPv6ProtocolNumber, raBuf(llAddr2, 0))
- expectRouterEvent(llAddr2, false)
-
- // Wait for lladdr3's router invalidation job to execute. The lifetime
- // of the router should have been updated to the most recent (smaller)
- // lifetime.
- //
- // Wait for the normal lifetime plus an extra bit for the
- // router to get invalidated. If we don't get an invalidation
- // event after this time, then something is wrong.
- expectAsyncRouterInvalidationEvent(llAddr3, l3LifetimeSeconds*time.Second)
- })
+ // Wait for lladdr2's router invalidation job to execute. The lifetime
+ // of the router should have been updated to the most recent (smaller)
+ // lifetime.
+ //
+ // Wait for the normal lifetime plus an extra bit for the
+ // router to get invalidated. If we don't get an invalidation
+ // event after this time, then something is wrong.
+ expectAsyncOffLinkRouteInvalidationEvent(llAddr2, l2LifetimeSeconds*time.Second)
+
+ // Rx an RA from lladdr2 with huge lifetime.
+ e.InjectInbound(header.IPv6ProtocolNumber, test.ra(t, llAddr2, 1000, header.MediumRoutePreference))
+ expectOffLinkRouteEvent(llAddr2, header.MediumRoutePreference, true)
+
+ // Rx an RA from lladdr2 with zero lifetime. It should be invalidated.
+ e.InjectInbound(header.IPv6ProtocolNumber, test.ra(t, llAddr2, 0, header.MediumRoutePreference))
+ expectOffLinkRouteEvent(llAddr2, header.MediumRoutePreference, false)
+
+ // Wait for lladdr3's router invalidation job to execute. The lifetime
+ // of the router should have been updated to the most recent (smaller)
+ // lifetime.
+ //
+ // Wait for the normal lifetime plus an extra bit for the
+ // router to get invalidated. If we don't get an invalidation
+ // event after this time, then something is wrong.
+ expectAsyncOffLinkRouteInvalidationEvent(llAddr3, l3LifetimeSeconds*time.Second)
+ })
+ })
+ }
}
// TestRouterDiscoveryMaxRouters tests that only
-// ipv6.MaxDiscoveredDefaultRouters discovered routers are remembered.
+// ipv6.MaxDiscoveredOffLinkRoutes discovered routers are remembered.
func TestRouterDiscoveryMaxRouters(t *testing.T) {
+ const nicID = 1
+
ndpDisp := ndpDispatcher{
- routerC: make(chan ndpRouterEvent, 1),
- rememberRouter: true,
+ offLinkRouteC: make(chan ndpOffLinkRouteEvent, 1),
}
e := channel.New(0, 1280, linkAddr1)
s := stack.New(stack.Options{
@@ -1513,23 +1557,23 @@ func TestRouterDiscoveryMaxRouters(t *testing.T) {
})},
})
- if err := s.CreateNIC(1, e); err != nil {
- t.Fatalf("CreateNIC(1) = %s", err)
+ if err := s.CreateNIC(nicID, e); err != nil {
+ t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
}
// Receive an RA from 2 more than the max number of discovered routers.
- for i := 1; i <= ipv6.MaxDiscoveredDefaultRouters+2; i++ {
+ for i := 1; i <= ipv6.MaxDiscoveredOffLinkRoutes+2; i++ {
linkAddr := []byte{2, 2, 3, 4, 5, 0}
linkAddr[5] = byte(i)
llAddr := header.LinkLocalAddr(tcpip.LinkAddress(linkAddr))
- e.InjectInbound(header.IPv6ProtocolNumber, raBuf(llAddr, 5))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufSimple(llAddr, 5))
- if i <= ipv6.MaxDiscoveredDefaultRouters {
+ if i <= ipv6.MaxDiscoveredOffLinkRoutes {
select {
- case e := <-ndpDisp.routerC:
- if diff := checkRouterEvent(e, llAddr, true); diff != "" {
- t.Errorf("router event mismatch (-want +got):\n%s", diff)
+ case e := <-ndpDisp.offLinkRouteC:
+ if diff := checkOffLinkRouteEvent(e, nicID, header.IPv6EmptySubnet, llAddr, header.MediumRoutePreference, true); diff != "" {
+ t.Errorf("off-link route event mismatch (-want +got):\n%s", diff)
}
default:
t.Fatal("expected router discovery event")
@@ -1537,7 +1581,7 @@ func TestRouterDiscoveryMaxRouters(t *testing.T) {
} else {
select {
- case <-ndpDisp.routerC:
+ case <-ndpDisp.offLinkRouteC:
t.Fatal("should not have discovered a new router after we already discovered the max number of routers")
default:
}
@@ -1551,54 +1595,6 @@ func checkPrefixEvent(e ndpPrefixEvent, prefix tcpip.Subnet, discovered bool) st
return cmp.Diff(ndpPrefixEvent{nicID: 1, prefix: prefix, discovered: discovered}, e, cmp.AllowUnexported(e))
}
-// TestPrefixDiscoveryDispatcherNoRemember tests that the stack does not
-// remember a discovered on-link prefix when the dispatcher asks it not to.
-func TestPrefixDiscoveryDispatcherNoRemember(t *testing.T) {
- prefix, subnet, _ := prefixSubnetAddr(0, "")
-
- ndpDisp := ndpDispatcher{
- prefixC: make(chan ndpPrefixEvent, 1),
- }
- e := channel.New(0, 1280, linkAddr1)
- clock := faketime.NewManualClock()
- s := stack.New(stack.Options{
- NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
- NDPConfigs: ipv6.NDPConfigurations{
- HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
- DiscoverOnLinkPrefixes: true,
- },
- NDPDisp: &ndpDisp,
- })},
- Clock: clock,
- })
-
- if err := s.CreateNIC(1, e); err != nil {
- t.Fatalf("CreateNIC(1) = %s", err)
- }
-
- // Receive an RA with prefix that we should not remember.
- const lifetimeSeconds = 1
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, lifetimeSeconds, 0))
- select {
- case e := <-ndpDisp.prefixC:
- if diff := checkPrefixEvent(e, subnet, true); diff != "" {
- t.Errorf("prefix event mismatch (-want +got):\n%s", diff)
- }
- default:
- t.Fatal("expected prefix discovery event")
- }
-
- // Wait for the invalidation time plus some buffer to make sure we do
- // not actually receive any invalidation events as we should not have
- // remembered the prefix in the first place.
- clock.Advance(lifetimeSeconds * time.Second)
- select {
- case <-ndpDisp.prefixC:
- t.Fatal("should not have received any prefix events")
- default:
- }
-}
-
func TestPrefixDiscovery(t *testing.T) {
prefix1, subnet1, _ := prefixSubnetAddr(0, "")
prefix2, subnet2, _ := prefixSubnetAddr(1, "")
@@ -1606,8 +1602,7 @@ func TestPrefixDiscovery(t *testing.T) {
testWithRAs(t, func(t *testing.T, handleRAs ipv6.HandleRAsConfiguration, forwarding bool) {
ndpDisp := ndpDispatcher{
- prefixC: make(chan ndpPrefixEvent, 1),
- rememberPrefix: true,
+ prefixC: make(chan ndpPrefixEvent, 1),
}
e := channel.New(0, 1280, linkAddr1)
clock := faketime.NewManualClock()
@@ -1697,17 +1692,6 @@ func TestPrefixDiscovery(t *testing.T) {
}
func TestPrefixDiscoveryWithInfiniteLifetime(t *testing.T) {
- // Update the infinite lifetime value to a smaller value so we can test
- // that when we receive a PI with such a lifetime value, we do not
- // invalidate the prefix.
- const testInfiniteLifetimeSeconds = 2
- const testInfiniteLifetime = testInfiniteLifetimeSeconds * time.Second
- saved := header.NDPInfiniteLifetime
- header.NDPInfiniteLifetime = testInfiniteLifetime
- defer func() {
- header.NDPInfiniteLifetime = saved
- }()
-
prefix := tcpip.AddressWithPrefix{
Address: testutil.MustParse6("102:304:506:708::"),
PrefixLen: 64,
@@ -1715,8 +1699,7 @@ func TestPrefixDiscoveryWithInfiniteLifetime(t *testing.T) {
subnet := prefix.Subnet()
ndpDisp := ndpDispatcher{
- prefixC: make(chan ndpPrefixEvent, 1),
- rememberPrefix: true,
+ prefixC: make(chan ndpPrefixEvent, 1),
}
e := channel.New(0, 1280, linkAddr1)
clock := faketime.NewManualClock()
@@ -1750,9 +1733,9 @@ func TestPrefixDiscoveryWithInfiniteLifetime(t *testing.T) {
// Receive an RA with prefix in an NDP Prefix Information option (PI)
// with infinite valid lifetime which should not get invalidated.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, testInfiniteLifetimeSeconds, 0))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, infiniteLifetimeSeconds, 0))
expectPrefixEvent(subnet, true)
- clock.Advance(testInfiniteLifetime)
+ clock.Advance(header.NDPInfiniteLifetime)
select {
case <-ndpDisp.prefixC:
t.Fatal("unexpectedly invalidated a prefix with infinite lifetime")
@@ -1760,9 +1743,8 @@ func TestPrefixDiscoveryWithInfiniteLifetime(t *testing.T) {
}
// Receive an RA with finite lifetime.
- // The prefix should get invalidated after 1s.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, testInfiniteLifetimeSeconds-1, 0))
- clock.Advance(testInfiniteLifetime)
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, infiniteLifetimeSeconds-1, 0))
+ clock.Advance(header.NDPInfiniteLifetime - time.Second)
select {
case e := <-ndpDisp.prefixC:
if diff := checkPrefixEvent(e, subnet, false); diff != "" {
@@ -1773,23 +1755,13 @@ func TestPrefixDiscoveryWithInfiniteLifetime(t *testing.T) {
}
// Receive an RA with finite lifetime.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, testInfiniteLifetimeSeconds-1, 0))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, infiniteLifetimeSeconds-1, 0))
expectPrefixEvent(subnet, true)
// Receive an RA with prefix with an infinite lifetime.
// The prefix should not be invalidated.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, testInfiniteLifetimeSeconds, 0))
- clock.Advance(testInfiniteLifetime)
- select {
- case <-ndpDisp.prefixC:
- t.Fatal("unexpectedly invalidated a prefix with infinite lifetime")
- default:
- }
-
- // Receive an RA with a prefix with a lifetime value greater than the
- // set infinite lifetime value.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, testInfiniteLifetimeSeconds+1, 0))
- clock.Advance((testInfiniteLifetimeSeconds + 1) * time.Second)
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, false, infiniteLifetimeSeconds, 0))
+ clock.Advance(header.NDPInfiniteLifetime)
select {
case <-ndpDisp.prefixC:
t.Fatal("unexpectedly invalidated a prefix with infinite lifetime")
@@ -1806,8 +1778,7 @@ func TestPrefixDiscoveryWithInfiniteLifetime(t *testing.T) {
// ipv6.MaxDiscoveredOnLinkPrefixes discovered on-link prefixes are remembered.
func TestPrefixDiscoveryMaxOnLinkPrefixes(t *testing.T) {
ndpDisp := ndpDispatcher{
- prefixC: make(chan ndpPrefixEvent, ipv6.MaxDiscoveredOnLinkPrefixes+3),
- rememberPrefix: true,
+ prefixC: make(chan ndpPrefixEvent, ipv6.MaxDiscoveredOnLinkPrefixes+3),
}
e := channel.New(0, 1280, linkAddr1)
s := stack.New(stack.Options{
@@ -1884,17 +1855,12 @@ func checkAutoGenAddrEvent(e ndpAutoGenAddrEvent, addr tcpip.AddressWithPrefix,
return cmp.Diff(ndpAutoGenAddrEvent{nicID: 1, addr: addr, eventType: eventType}, e, cmp.AllowUnexported(e))
}
+const minVLSeconds = uint32(ipv6.MinPrefixInformationValidLifetimeForUpdate / time.Second)
+const infiniteLifetimeSeconds = uint32(header.NDPInfiniteLifetime / time.Second)
+
// TestAutoGenAddr tests that an address is properly generated and invalidated
// when configured to do so.
func TestAutoGenAddr(t *testing.T) {
- const newMinVL = 2
- newMinVLDuration := newMinVL * time.Second
- saved := ipv6.MinPrefixInformationValidLifetimeForUpdate
- defer func() {
- ipv6.MinPrefixInformationValidLifetimeForUpdate = saved
- }()
- ipv6.MinPrefixInformationValidLifetimeForUpdate = newMinVLDuration
-
prefix1, _, addr1 := prefixSubnetAddr(0, linkAddr1)
prefix2, _, addr2 := prefixSubnetAddr(1, linkAddr1)
@@ -1903,6 +1869,7 @@ func TestAutoGenAddr(t *testing.T) {
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
}
e := channel.New(0, 1280, linkAddr1)
+ clock := faketime.NewManualClock()
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
NDPConfigs: ipv6.NDPConfigurations{
@@ -1911,6 +1878,7 @@ func TestAutoGenAddr(t *testing.T) {
},
NDPDisp: &ndpDisp,
})},
+ Clock: clock,
})
if err := s.SetForwardingDefaultAndAllNICs(ipv6.ProtocolNumber, forwarding); err != nil {
@@ -1960,8 +1928,9 @@ func TestAutoGenAddr(t *testing.T) {
default:
}
- // Receive an RA with prefix2 in a PI.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, 100, 0))
+ // Receive an RA with prefix2 in a PI with a valid lifetime that exceeds
+ // the minimum.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, minVLSeconds+1, 0))
expectAutoGenAddrEvent(addr2, newAddr)
if !containsV6Addr(s.NICInfo()[1].ProtocolAddresses, addr1) {
t.Fatalf("Should have %s in the list of addresses", addr1)
@@ -1971,7 +1940,7 @@ func TestAutoGenAddr(t *testing.T) {
}
// Refresh valid lifetime for addr of prefix1.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, newMinVL, 0))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 0))
select {
case <-ndpDisp.autoGenAddrC:
t.Fatal("unexpectedly auto-generated an address when we already have an address for a prefix")
@@ -1979,12 +1948,13 @@ func TestAutoGenAddr(t *testing.T) {
}
// Wait for addr of prefix1 to be invalidated.
+ clock.Advance(ipv6.MinPrefixInformationValidLifetimeForUpdate)
select {
case e := <-ndpDisp.autoGenAddrC:
if diff := checkAutoGenAddrEvent(e, addr1, invalidatedAddr); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(newMinVLDuration + defaultAsyncPositiveEventTimeout):
+ default:
t.Fatal("timed out waiting for addr auto gen event")
}
if containsV6Addr(s.NICInfo()[1].ProtocolAddresses, addr1) {
@@ -2014,20 +1984,7 @@ func addressCheck(addrs []tcpip.ProtocolAddress, containList, notContainList []t
// TestAutoGenTempAddr tests that temporary SLAAC addresses are generated when
// configured to do so as part of IPv6 Privacy Extensions.
func TestAutoGenTempAddr(t *testing.T) {
- const (
- nicID = 1
- newMinVL = 5
- newMinVLDuration = newMinVL * time.Second
- )
-
- savedMinPrefixInformationValidLifetimeForUpdate := ipv6.MinPrefixInformationValidLifetimeForUpdate
- savedMaxDesync := ipv6.MaxDesyncFactor
- defer func() {
- ipv6.MinPrefixInformationValidLifetimeForUpdate = savedMinPrefixInformationValidLifetimeForUpdate
- ipv6.MaxDesyncFactor = savedMaxDesync
- }()
- ipv6.MinPrefixInformationValidLifetimeForUpdate = newMinVLDuration
- ipv6.MaxDesyncFactor = time.Nanosecond
+ const nicID = 1
prefix1, _, addr1 := prefixSubnetAddr(0, linkAddr1)
prefix2, _, addr2 := prefixSubnetAddr(1, linkAddr1)
@@ -2047,218 +2004,211 @@ func TestAutoGenTempAddr(t *testing.T) {
},
}
- // This Run will not return until the parallel tests finish.
- //
- // We need this because we need to do some teardown work after the
- // parallel tests complete.
- //
- // See https://godoc.org/testing#hdr-Subtests_and_Sub_benchmarks for
- // more details.
- t.Run("group", func(t *testing.T) {
- for i, test := range tests {
- i := i
- test := test
-
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
-
- seed := []byte{uint8(i)}
- var tempIIDHistory [header.IIDSize]byte
- header.InitialTempIID(tempIIDHistory[:], seed, nicID)
- newTempAddr := func(stableAddr tcpip.Address) tcpip.AddressWithPrefix {
- return header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], stableAddr)
- }
-
- ndpDisp := ndpDispatcher{
- dadC: make(chan ndpDADEvent, 2),
- autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
- }
- e := channel.New(0, 1280, linkAddr1)
- s := stack.New(stack.Options{
- NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
- DADConfigs: stack.DADConfigurations{
- DupAddrDetectTransmits: test.dupAddrTransmits,
- RetransmitTimer: test.retransmitTimer,
- },
- NDPConfigs: ipv6.NDPConfigurations{
- HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
- AutoGenGlobalAddresses: true,
- AutoGenTempGlobalAddresses: true,
- },
- NDPDisp: &ndpDisp,
- TempIIDSeed: seed,
- })},
- })
-
- if err := s.CreateNIC(nicID, e); err != nil {
- t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
- }
-
- expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
- t.Helper()
-
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- default:
- t.Fatal("expected addr auto gen event")
- }
- }
-
- expectAutoGenAddrEventAsync := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
- t.Helper()
+ for i, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ seed := []byte{uint8(i)}
+ var tempIIDHistory [header.IIDSize]byte
+ header.InitialTempIID(tempIIDHistory[:], seed, nicID)
+ newTempAddr := func(stableAddr tcpip.Address) tcpip.AddressWithPrefix {
+ return header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], stableAddr)
+ }
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- case <-time.After(defaultAsyncPositiveEventTimeout):
- t.Fatal("timed out waiting for addr auto gen event")
- }
- }
+ ndpDisp := ndpDispatcher{
+ dadC: make(chan ndpDADEvent, 2),
+ autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
+ }
+ e := channel.New(0, 1280, linkAddr1)
+ clock := faketime.NewManualClock()
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
+ DADConfigs: stack.DADConfigurations{
+ DupAddrDetectTransmits: test.dupAddrTransmits,
+ RetransmitTimer: test.retransmitTimer,
+ },
+ NDPConfigs: ipv6.NDPConfigurations{
+ HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
+ AutoGenGlobalAddresses: true,
+ AutoGenTempGlobalAddresses: true,
+ MaxTempAddrValidLifetime: 2 * ipv6.MinPrefixInformationValidLifetimeForUpdate,
+ MaxTempAddrPreferredLifetime: 2 * ipv6.MinPrefixInformationValidLifetimeForUpdate,
+ },
+ NDPDisp: &ndpDisp,
+ TempIIDSeed: seed,
+ })},
+ Clock: clock,
+ })
- expectDADEventAsync := func(addr tcpip.Address) {
- t.Helper()
+ if err := s.CreateNIC(nicID, e); err != nil {
+ t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
+ }
- select {
- case e := <-ndpDisp.dadC:
- if diff := checkDADEvent(e, nicID, addr, &stack.DADSucceeded{}); diff != "" {
- t.Errorf("DAD event mismatch (-want +got):\n%s", diff)
- }
- case <-time.After(time.Duration(test.dupAddrTransmits)*test.retransmitTimer + defaultAsyncPositiveEventTimeout):
- t.Fatal("timed out waiting for DAD event")
- }
- }
+ expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
+ t.Helper()
- // Receive an RA with prefix1 in an NDP Prefix Information option (PI)
- // with zero valid lifetime.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 0, 0))
select {
case e := <-ndpDisp.autoGenAddrC:
- t.Fatalf("unexpectedly auto-generated an address with 0 lifetime; event = %+v", e)
+ if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
+ }
default:
+ t.Fatal("expected addr auto gen event")
}
+ }
+
+ expectAutoGenAddrEventAsync := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
+ t.Helper()
- // Receive an RA with prefix1 in an NDP Prefix Information option (PI)
- // with non-zero valid lifetime.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 0))
- expectAutoGenAddrEvent(addr1, newAddr)
- expectDADEventAsync(addr1.Address)
+ clock.RunImmediatelyScheduledJobs()
select {
case e := <-ndpDisp.autoGenAddrC:
- t.Fatalf("unexpectedly got an auto gen addr event = %+v", e)
+ if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
+ }
default:
+ t.Fatal("timed out waiting for addr auto gen event")
}
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1}, nil); mismatch != "" {
- t.Fatal(mismatch)
- }
+ }
- // Receive an RA with prefix1 in an NDP Prefix Information option (PI)
- // with non-zero valid & preferred lifetimes.
- tempAddr1 := newTempAddr(addr1.Address)
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 100))
- expectAutoGenAddrEvent(tempAddr1, newAddr)
- expectDADEventAsync(tempAddr1.Address)
- if mismatch := addressCheck(s.NICInfo()[1].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1}, nil); mismatch != "" {
- t.Fatal(mismatch)
- }
+ expectDADEventAsync := func(addr tcpip.Address) {
+ t.Helper()
- // Receive an RA with prefix2 in an NDP Prefix Information option (PI)
- // with preferred lifetime > valid lifetime
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 5, 6))
+ clock.Advance(time.Duration(test.dupAddrTransmits) * test.retransmitTimer)
select {
- case e := <-ndpDisp.autoGenAddrC:
- t.Fatalf("unexpectedly auto-generated an address with preferred lifetime > valid lifetime; event = %+v", e)
+ case e := <-ndpDisp.dadC:
+ if diff := checkDADEvent(e, nicID, addr, &stack.DADSucceeded{}); diff != "" {
+ t.Errorf("DAD event mismatch (-want +got):\n%s", diff)
+ }
default:
+ t.Fatal("timed out waiting for DAD event")
}
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1}, nil); mismatch != "" {
- t.Fatal(mismatch)
- }
+ }
- // Receive an RA with prefix2 in a PI w/ non-zero valid and preferred
- // lifetimes.
- tempAddr2 := newTempAddr(addr2.Address)
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, 100, 100))
- expectAutoGenAddrEvent(addr2, newAddr)
- expectDADEventAsync(addr2.Address)
- expectAutoGenAddrEventAsync(tempAddr2, newAddr)
- expectDADEventAsync(tempAddr2.Address)
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1, addr2, tempAddr2}, nil); mismatch != "" {
- t.Fatal(mismatch)
- }
+ // Receive an RA with prefix1 in an NDP Prefix Information option (PI)
+ // with zero valid lifetime.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 0, 0))
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ t.Fatalf("unexpectedly auto-generated an address with 0 lifetime; event = %+v", e)
+ default:
+ }
- // Deprecate prefix1.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 0))
- expectAutoGenAddrEvent(addr1, deprecatedAddr)
- expectAutoGenAddrEvent(tempAddr1, deprecatedAddr)
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1, addr2, tempAddr2}, nil); mismatch != "" {
- t.Fatal(mismatch)
- }
+ // Receive an RA with prefix1 in an NDP Prefix Information option (PI)
+ // with non-zero valid lifetime.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 0))
+ expectAutoGenAddrEvent(addr1, newAddr)
+ expectDADEventAsync(addr1.Address)
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ t.Fatalf("unexpectedly got an auto gen addr event = %+v", e)
+ default:
+ }
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1}, nil); mismatch != "" {
+ t.Fatal(mismatch)
+ }
- // Refresh lifetimes for prefix1.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 100))
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1, addr2, tempAddr2}, nil); mismatch != "" {
- t.Fatal(mismatch)
- }
+ // Receive an RA with prefix1 in an NDP Prefix Information option (PI)
+ // with non-zero valid & preferred lifetimes.
+ tempAddr1 := newTempAddr(addr1.Address)
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 100))
+ expectAutoGenAddrEvent(tempAddr1, newAddr)
+ expectDADEventAsync(tempAddr1.Address)
+ if mismatch := addressCheck(s.NICInfo()[1].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1}, nil); mismatch != "" {
+ t.Fatal(mismatch)
+ }
- // Reduce valid lifetime and deprecate addresses of prefix1.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, newMinVL, 0))
- expectAutoGenAddrEvent(addr1, deprecatedAddr)
- expectAutoGenAddrEvent(tempAddr1, deprecatedAddr)
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1, addr2, tempAddr2}, nil); mismatch != "" {
- t.Fatal(mismatch)
- }
+ // Receive an RA with prefix2 in an NDP Prefix Information option (PI)
+ // with preferred lifetime > valid lifetime
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 5, 6))
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ t.Fatalf("unexpectedly auto-generated an address with preferred lifetime > valid lifetime; event = %+v", e)
+ default:
+ }
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1}, nil); mismatch != "" {
+ t.Fatal(mismatch)
+ }
- // Wait for addrs of prefix1 to be invalidated. They should be
- // invalidated at the same time.
- select {
- case e := <-ndpDisp.autoGenAddrC:
- var nextAddr tcpip.AddressWithPrefix
- if e.addr == addr1 {
- if diff := checkAutoGenAddrEvent(e, addr1, invalidatedAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- nextAddr = tempAddr1
- } else {
- if diff := checkAutoGenAddrEvent(e, tempAddr1, invalidatedAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- nextAddr = addr1
- }
+ // Receive an RA with prefix2 in a PI with a valid lifetime that exceeds
+ // the minimum and won't be reached in this test.
+ tempAddr2 := newTempAddr(addr2.Address)
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, 2*minVLSeconds, 2*minVLSeconds))
+ expectAutoGenAddrEvent(addr2, newAddr)
+ expectDADEventAsync(addr2.Address)
+ expectAutoGenAddrEventAsync(tempAddr2, newAddr)
+ expectDADEventAsync(tempAddr2.Address)
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1, addr2, tempAddr2}, nil); mismatch != "" {
+ t.Fatal(mismatch)
+ }
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, nextAddr, invalidatedAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- case <-time.After(defaultAsyncPositiveEventTimeout):
- t.Fatal("timed out waiting for addr auto gen event")
+ // Deprecate prefix1.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 0))
+ expectAutoGenAddrEvent(addr1, deprecatedAddr)
+ expectAutoGenAddrEvent(tempAddr1, deprecatedAddr)
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1, addr2, tempAddr2}, nil); mismatch != "" {
+ t.Fatal(mismatch)
+ }
+
+ // Refresh lifetimes for prefix1.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, 100, 100))
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1, addr2, tempAddr2}, nil); mismatch != "" {
+ t.Fatal(mismatch)
+ }
+
+ // Reduce valid lifetime and deprecate addresses of prefix1.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, minVLSeconds, 0))
+ expectAutoGenAddrEvent(addr1, deprecatedAddr)
+ expectAutoGenAddrEvent(tempAddr1, deprecatedAddr)
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr1, tempAddr1, addr2, tempAddr2}, nil); mismatch != "" {
+ t.Fatal(mismatch)
+ }
+
+ // Wait for addrs of prefix1 to be invalidated. They should be
+ // invalidated at the same time.
+ clock.Advance(ipv6.MinPrefixInformationValidLifetimeForUpdate)
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ var nextAddr tcpip.AddressWithPrefix
+ if e.addr == addr1 {
+ if diff := checkAutoGenAddrEvent(e, addr1, invalidatedAddr); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(newMinVLDuration + defaultAsyncPositiveEventTimeout):
- t.Fatal("timed out waiting for addr auto gen event")
- }
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr2, tempAddr2}, []tcpip.AddressWithPrefix{addr1, tempAddr1}); mismatch != "" {
- t.Fatal(mismatch)
+ nextAddr = tempAddr1
+ } else {
+ if diff := checkAutoGenAddrEvent(e, tempAddr1, invalidatedAddr); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
+ }
+ nextAddr = addr1
}
- // Receive an RA with prefix2 in a PI w/ 0 lifetimes.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, 0, 0))
- expectAutoGenAddrEvent(addr2, deprecatedAddr)
- expectAutoGenAddrEvent(tempAddr2, deprecatedAddr)
select {
case e := <-ndpDisp.autoGenAddrC:
- t.Errorf("got unexpected auto gen addr event = %+v", e)
+ if diff := checkAutoGenAddrEvent(e, nextAddr, invalidatedAddr); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
+ }
default:
+ t.Fatal("timed out waiting for addr auto gen event")
}
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr2, tempAddr2}, []tcpip.AddressWithPrefix{addr1, tempAddr1}); mismatch != "" {
- t.Fatal(mismatch)
- }
- })
- }
- })
+ default:
+ t.Fatal("timed out waiting for addr auto gen event")
+ }
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr2, tempAddr2}, []tcpip.AddressWithPrefix{addr1, tempAddr1}); mismatch != "" {
+ t.Fatal(mismatch)
+ }
+
+ // Receive an RA with prefix2 in a PI w/ 0 lifetimes.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, 0, 0))
+ expectAutoGenAddrEvent(addr2, deprecatedAddr)
+ expectAutoGenAddrEvent(tempAddr2, deprecatedAddr)
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ t.Errorf("got unexpected auto gen addr event = %+v", e)
+ default:
+ }
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr2, tempAddr2}, []tcpip.AddressWithPrefix{addr1, tempAddr1}); mismatch != "" {
+ t.Fatal(mismatch)
+ }
+ })
+ }
}
// TestNoAutoGenTempAddrForLinkLocal test that temporary SLAAC addresses are not
@@ -2266,12 +2216,6 @@ func TestAutoGenTempAddr(t *testing.T) {
func TestNoAutoGenTempAddrForLinkLocal(t *testing.T) {
const nicID = 1
- savedMaxDesyncFactor := ipv6.MaxDesyncFactor
- defer func() {
- ipv6.MaxDesyncFactor = savedMaxDesyncFactor
- }()
- ipv6.MaxDesyncFactor = time.Nanosecond
-
tests := []struct {
name string
dupAddrTransmits uint8
@@ -2287,66 +2231,56 @@ func TestNoAutoGenTempAddrForLinkLocal(t *testing.T) {
},
}
- // This Run will not return until the parallel tests finish.
- //
- // We need this because we need to do some teardown work after the
- // parallel tests complete.
- //
- // See https://godoc.org/testing#hdr-Subtests_and_Sub_benchmarks for
- // more details.
- t.Run("group", func(t *testing.T) {
- for _, test := range tests {
- test := test
-
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
-
- ndpDisp := ndpDispatcher{
- dadC: make(chan ndpDADEvent, 1),
- autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
- }
- e := channel.New(0, 1280, linkAddr1)
- s := stack.New(stack.Options{
- NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
- NDPConfigs: ipv6.NDPConfigurations{
- AutoGenTempGlobalAddresses: true,
- },
- NDPDisp: &ndpDisp,
- AutoGenLinkLocal: true,
- })},
- })
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ ndpDisp := ndpDispatcher{
+ dadC: make(chan ndpDADEvent, 1),
+ autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
+ }
+ e := channel.New(0, 1280, linkAddr1)
+ clock := faketime.NewManualClock()
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
+ NDPConfigs: ipv6.NDPConfigurations{
+ AutoGenTempGlobalAddresses: true,
+ },
+ NDPDisp: &ndpDisp,
+ AutoGenLinkLocal: true,
+ })},
+ Clock: clock,
+ })
- if err := s.CreateNIC(nicID, e); err != nil {
- t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
- }
+ if err := s.CreateNIC(nicID, e); err != nil {
+ t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
+ }
- // The stable link-local address should auto-generate and resolve DAD.
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, tcpip.AddressWithPrefix{Address: llAddr1, PrefixLen: header.IIDOffsetInIPv6Address * 8}, newAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- default:
- t.Fatal("expected addr auto gen event")
+ // The stable link-local address should auto-generate and resolve DAD.
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ if diff := checkAutoGenAddrEvent(e, tcpip.AddressWithPrefix{Address: llAddr1, PrefixLen: header.IIDOffsetInIPv6Address * 8}, newAddr); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- select {
- case e := <-ndpDisp.dadC:
- if diff := checkDADEvent(e, nicID, llAddr1, &stack.DADSucceeded{}); diff != "" {
- t.Errorf("DAD event mismatch (-want +got):\n%s", diff)
- }
- case <-time.After(time.Duration(test.dupAddrTransmits)*test.retransmitTimer + defaultAsyncPositiveEventTimeout):
- t.Fatal("timed out waiting for DAD event")
+ default:
+ t.Fatal("expected addr auto gen event")
+ }
+ clock.Advance(time.Duration(test.dupAddrTransmits) * test.retransmitTimer)
+ select {
+ case e := <-ndpDisp.dadC:
+ if diff := checkDADEvent(e, nicID, llAddr1, &stack.DADSucceeded{}); diff != "" {
+ t.Errorf("DAD event mismatch (-want +got):\n%s", diff)
}
+ default:
+ t.Fatal("timed out waiting for DAD event")
+ }
- // No new addresses should be generated.
- select {
- case e := <-ndpDisp.autoGenAddrC:
- t.Errorf("got unxpected auto gen addr event = %+v", e)
- case <-time.After(defaultAsyncNegativeEventTimeout):
- }
- })
- }
- })
+ // No new addresses should be generated.
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ t.Errorf("got unxpected auto gen addr event = %+v", e)
+ default:
+ }
+ })
+ }
}
// TestNoAutoGenTempAddrWithoutStableAddr tests that a temporary SLAAC address
@@ -2359,12 +2293,6 @@ func TestNoAutoGenTempAddrWithoutStableAddr(t *testing.T) {
retransmitTimer = 2 * time.Second
)
- savedMaxDesyncFactor := ipv6.MaxDesyncFactor
- defer func() {
- ipv6.MaxDesyncFactor = savedMaxDesyncFactor
- }()
- ipv6.MaxDesyncFactor = 0
-
prefix, _, addr := prefixSubnetAddr(0, linkAddr1)
var tempIIDHistory [header.IIDSize]byte
header.InitialTempIID(tempIIDHistory[:], nil, nicID)
@@ -2375,6 +2303,7 @@ func TestNoAutoGenTempAddrWithoutStableAddr(t *testing.T) {
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
}
e := channel.New(0, 1280, linkAddr1)
+ clock := faketime.NewManualClock()
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
DADConfigs: stack.DADConfigurations{
@@ -2388,6 +2317,7 @@ func TestNoAutoGenTempAddrWithoutStableAddr(t *testing.T) {
},
NDPDisp: &ndpDisp,
})},
+ Clock: clock,
})
if err := s.CreateNIC(nicID, e); err != nil {
@@ -2417,12 +2347,13 @@ func TestNoAutoGenTempAddrWithoutStableAddr(t *testing.T) {
// Wait for DAD to complete for the stable address then expect the temporary
// address to be generated.
+ clock.Advance(dadTransmits * retransmitTimer)
select {
case e := <-ndpDisp.dadC:
if diff := checkDADEvent(e, nicID, addr.Address, &stack.DADSucceeded{}); diff != "" {
t.Errorf("DAD event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(dadTransmits*retransmitTimer + defaultAsyncPositiveEventTimeout):
+ default:
t.Fatal("timed out waiting for DAD event")
}
select {
@@ -2430,7 +2361,7 @@ func TestNoAutoGenTempAddrWithoutStableAddr(t *testing.T) {
if diff := checkAutoGenAddrEvent(e, tempAddr, newAddr); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(defaultAsyncPositiveEventTimeout):
+ default:
t.Fatal("timed out waiting for addr auto gen event")
}
}
@@ -2439,46 +2370,44 @@ func TestNoAutoGenTempAddrWithoutStableAddr(t *testing.T) {
// regenerated.
func TestAutoGenTempAddrRegen(t *testing.T) {
const (
- nicID = 1
- regenAfter = 2 * time.Second
- newMinVL = 10
- newMinVLDuration = newMinVL * time.Second
- )
+ nicID = 1
+ regenAdv = 2 * time.Second
- savedMaxDesyncFactor := ipv6.MaxDesyncFactor
- savedMinMaxTempAddrPreferredLifetime := ipv6.MinMaxTempAddrPreferredLifetime
- savedMinMaxTempAddrValidLifetime := ipv6.MinMaxTempAddrValidLifetime
- defer func() {
- ipv6.MaxDesyncFactor = savedMaxDesyncFactor
- ipv6.MinMaxTempAddrPreferredLifetime = savedMinMaxTempAddrPreferredLifetime
- ipv6.MinMaxTempAddrValidLifetime = savedMinMaxTempAddrValidLifetime
- }()
- ipv6.MaxDesyncFactor = 0
- ipv6.MinMaxTempAddrPreferredLifetime = newMinVLDuration
- ipv6.MinMaxTempAddrValidLifetime = newMinVLDuration
+ numTempAddrs = 3
+ maxTempAddrValidLifetime = numTempAddrs * ipv6.MinPrefixInformationValidLifetimeForUpdate
+ )
prefix, _, addr := prefixSubnetAddr(0, linkAddr1)
var tempIIDHistory [header.IIDSize]byte
header.InitialTempIID(tempIIDHistory[:], nil, nicID)
- tempAddr1 := header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], addr.Address)
- tempAddr2 := header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], addr.Address)
- tempAddr3 := header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], addr.Address)
+ var tempAddrs [numTempAddrs]tcpip.AddressWithPrefix
+ for i := 0; i < len(tempAddrs); i++ {
+ tempAddrs[i] = header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], addr.Address)
+ }
ndpDisp := ndpDispatcher{
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
}
e := channel.New(0, 1280, linkAddr1)
ndpConfigs := ipv6.NDPConfigurations{
- HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
- AutoGenGlobalAddresses: true,
- AutoGenTempGlobalAddresses: true,
- RegenAdvanceDuration: newMinVLDuration - regenAfter,
+ HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
+ AutoGenGlobalAddresses: true,
+ AutoGenTempGlobalAddresses: true,
+ RegenAdvanceDuration: regenAdv,
+ MaxTempAddrValidLifetime: maxTempAddrValidLifetime,
+ MaxTempAddrPreferredLifetime: ipv6.MinPrefixInformationValidLifetimeForUpdate,
+ }
+ clock := faketime.NewManualClock()
+ randSource := savingRandSource{
+ s: rand.NewSource(time.Now().UnixNano()),
}
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
NDPConfigs: ndpConfigs,
NDPDisp: &ndpDisp,
})},
+ Clock: clock,
+ RandSource: &randSource,
})
if err := s.CreateNIC(nicID, e); err != nil {
@@ -2501,36 +2430,43 @@ func TestAutoGenTempAddrRegen(t *testing.T) {
expectAutoGenAddrEventAsync := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType, timeout time.Duration) {
t.Helper()
+ clock.Advance(timeout)
select {
case e := <-ndpDisp.autoGenAddrC:
if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(timeout):
+ default:
t.Fatal("timed out waiting for addr auto gen event")
}
}
+ tempDesyncFactor := time.Duration(randSource.lastInt63) % ipv6.MaxDesyncFactor
+ effectiveMaxTempAddrPL := ipv6.MinPrefixInformationValidLifetimeForUpdate - tempDesyncFactor
+ // The time since the last regeneration before a new temporary address is
+ // generated.
+ tempAddrRegenenerationTime := effectiveMaxTempAddrPL - regenAdv
+
// Receive an RA with prefix1 in an NDP Prefix Information option (PI)
// with non-zero valid & preferred lifetimes.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, 100, 100))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, minVLSeconds, minVLSeconds))
expectAutoGenAddrEvent(addr, newAddr)
- expectAutoGenAddrEvent(tempAddr1, newAddr)
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr, tempAddr1}, nil); mismatch != "" {
+ expectAutoGenAddrEvent(tempAddrs[0], newAddr)
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr, tempAddrs[0]}, nil); mismatch != "" {
t.Fatal(mismatch)
}
// Wait for regeneration
- expectAutoGenAddrEventAsync(tempAddr2, newAddr, regenAfter+defaultAsyncPositiveEventTimeout)
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr, tempAddr1, tempAddr2}, nil); mismatch != "" {
+ expectAutoGenAddrEventAsync(tempAddrs[1], newAddr, tempAddrRegenenerationTime)
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, minVLSeconds, minVLSeconds))
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr, tempAddrs[0], tempAddrs[1]}, nil); mismatch != "" {
t.Fatal(mismatch)
}
+ expectAutoGenAddrEventAsync(tempAddrs[0], deprecatedAddr, regenAdv)
// Wait for regeneration
- expectAutoGenAddrEventAsync(tempAddr3, newAddr, regenAfter+defaultAsyncPositiveEventTimeout)
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr, tempAddr1, tempAddr2, tempAddr3}, nil); mismatch != "" {
- t.Fatal(mismatch)
- }
+ expectAutoGenAddrEventAsync(tempAddrs[2], newAddr, tempAddrRegenenerationTime-regenAdv)
+ expectAutoGenAddrEventAsync(tempAddrs[1], deprecatedAddr, regenAdv)
// Stop generating temporary addresses
ndpConfigs.AutoGenTempGlobalAddresses = false
@@ -2541,45 +2477,24 @@ func TestAutoGenTempAddrRegen(t *testing.T) {
ndpEP.SetNDPConfigurations(ndpConfigs)
}
+ // Refresh lifetimes and wait for the last temporary address to be deprecated.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, minVLSeconds, minVLSeconds))
+ expectAutoGenAddrEventAsync(tempAddrs[2], deprecatedAddr, effectiveMaxTempAddrPL-regenAdv)
+
+ // Refresh lifetimes such that the prefix is valid and preferred forever.
+ //
+ // This should not affect the lifetimes of temporary addresses because they
+ // are capped by the maximum valid and preferred lifetimes for temporary
+ // addresses.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, infiniteLifetimeSeconds, infiniteLifetimeSeconds))
+
// Wait for all the temporary addresses to get invalidated.
- tempAddrs := []tcpip.AddressWithPrefix{tempAddr1, tempAddr2, tempAddr3}
- invalidateAfter := newMinVLDuration - 2*regenAfter
+ invalidateAfter := maxTempAddrValidLifetime - clock.NowMonotonic().Sub(tcpip.MonotonicTime{})
for _, addr := range tempAddrs {
- // Wait for a deprecation then invalidation event, or just an invalidation
- // event. We need to cover both cases but cannot deterministically hit both
- // cases because the deprecation and invalidation jobs could execute in any
- // order.
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, addr, deprecatedAddr); diff == "" {
- // If we get a deprecation event first, we should get an invalidation
- // event almost immediately after.
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, addr, invalidatedAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- case <-time.After(defaultAsyncPositiveEventTimeout):
- t.Fatal("timed out waiting for addr auto gen event")
- }
- } else if diff := checkAutoGenAddrEvent(e, addr, invalidatedAddr); diff == "" {
- // If we get an invalidation event first, we shouldn't get a deprecation
- // event after.
- select {
- case e := <-ndpDisp.autoGenAddrC:
- t.Fatalf("unexpectedly got an auto-generated event = %+v", e)
- case <-time.After(defaultAsyncNegativeEventTimeout):
- }
- } else {
- t.Fatalf("got unexpected auto-generated event = %+v", e)
- }
- case <-time.After(invalidateAfter + defaultAsyncPositiveEventTimeout):
- t.Fatal("timed out waiting for addr auto gen event")
- }
-
- invalidateAfter = regenAfter
+ expectAutoGenAddrEventAsync(addr, invalidatedAddr, invalidateAfter)
+ invalidateAfter = tempAddrRegenenerationTime
}
- if mismatch := addressCheck(s.NICInfo()[1].ProtocolAddresses, []tcpip.AddressWithPrefix{addr}, tempAddrs); mismatch != "" {
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr}, tempAddrs[:]); mismatch != "" {
t.Fatal(mismatch)
}
}
@@ -2588,52 +2503,54 @@ func TestAutoGenTempAddrRegen(t *testing.T) {
// regeneration job gets updated when refreshing the address's lifetimes.
func TestAutoGenTempAddrRegenJobUpdates(t *testing.T) {
const (
- nicID = 1
- regenAfter = 2 * time.Second
- newMinVL = 10
- newMinVLDuration = newMinVL * time.Second
- )
+ nicID = 1
+ regenAdv = 2 * time.Second
- savedMaxDesyncFactor := ipv6.MaxDesyncFactor
- savedMinMaxTempAddrPreferredLifetime := ipv6.MinMaxTempAddrPreferredLifetime
- savedMinMaxTempAddrValidLifetime := ipv6.MinMaxTempAddrValidLifetime
- defer func() {
- ipv6.MaxDesyncFactor = savedMaxDesyncFactor
- ipv6.MinMaxTempAddrPreferredLifetime = savedMinMaxTempAddrPreferredLifetime
- ipv6.MinMaxTempAddrValidLifetime = savedMinMaxTempAddrValidLifetime
- }()
- ipv6.MaxDesyncFactor = 0
- ipv6.MinMaxTempAddrPreferredLifetime = newMinVLDuration
- ipv6.MinMaxTempAddrValidLifetime = newMinVLDuration
+ numTempAddrs = 3
+ maxTempAddrPreferredLifetime = ipv6.MinPrefixInformationValidLifetimeForUpdate
+ maxTempAddrPreferredLifetimeSeconds = uint32(maxTempAddrPreferredLifetime / time.Second)
+ )
prefix, _, addr := prefixSubnetAddr(0, linkAddr1)
var tempIIDHistory [header.IIDSize]byte
header.InitialTempIID(tempIIDHistory[:], nil, nicID)
- tempAddr1 := header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], addr.Address)
- tempAddr2 := header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], addr.Address)
- tempAddr3 := header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], addr.Address)
+ var tempAddrs [numTempAddrs]tcpip.AddressWithPrefix
+ for i := 0; i < len(tempAddrs); i++ {
+ tempAddrs[i] = header.GenerateTempIPv6SLAACAddr(tempIIDHistory[:], addr.Address)
+ }
ndpDisp := ndpDispatcher{
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
}
e := channel.New(0, 1280, linkAddr1)
ndpConfigs := ipv6.NDPConfigurations{
- HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
- AutoGenGlobalAddresses: true,
- AutoGenTempGlobalAddresses: true,
- RegenAdvanceDuration: newMinVLDuration - regenAfter,
+ HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
+ AutoGenGlobalAddresses: true,
+ AutoGenTempGlobalAddresses: true,
+ RegenAdvanceDuration: regenAdv,
+ MaxTempAddrPreferredLifetime: maxTempAddrPreferredLifetime,
+ MaxTempAddrValidLifetime: maxTempAddrPreferredLifetime * 2,
+ }
+ clock := faketime.NewManualClock()
+ initialTime := clock.NowMonotonic()
+ randSource := savingRandSource{
+ s: rand.NewSource(time.Now().UnixNano()),
}
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
NDPConfigs: ndpConfigs,
NDPDisp: &ndpDisp,
})},
+ Clock: clock,
+ RandSource: &randSource,
})
if err := s.CreateNIC(nicID, e); err != nil {
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
}
+ tempDesyncFactor := time.Duration(randSource.lastInt63) % ipv6.MaxDesyncFactor
+
expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
t.Helper()
@@ -2650,22 +2567,23 @@ func TestAutoGenTempAddrRegenJobUpdates(t *testing.T) {
expectAutoGenAddrEventAsync := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType, timeout time.Duration) {
t.Helper()
+ clock.Advance(timeout)
select {
case e := <-ndpDisp.autoGenAddrC:
if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(timeout):
+ default:
t.Fatal("timed out waiting for addr auto gen event")
}
}
// Receive an RA with prefix1 in an NDP Prefix Information option (PI)
// with non-zero valid & preferred lifetimes.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, 100, 100))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, maxTempAddrPreferredLifetimeSeconds, maxTempAddrPreferredLifetimeSeconds))
expectAutoGenAddrEvent(addr, newAddr)
- expectAutoGenAddrEvent(tempAddr1, newAddr)
- if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr, tempAddr1}, nil); mismatch != "" {
+ expectAutoGenAddrEvent(tempAddrs[0], newAddr)
+ if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, []tcpip.AddressWithPrefix{addr, tempAddrs[0]}, nil); mismatch != "" {
t.Fatal(mismatch)
}
@@ -2673,13 +2591,27 @@ func TestAutoGenTempAddrRegenJobUpdates(t *testing.T) {
//
// A new temporary address should be generated after the regeneration
// time has passed since the prefix is deprecated.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, 100, 0))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, maxTempAddrPreferredLifetimeSeconds, 0))
expectAutoGenAddrEvent(addr, deprecatedAddr)
- expectAutoGenAddrEvent(tempAddr1, deprecatedAddr)
+ expectAutoGenAddrEvent(tempAddrs[0], deprecatedAddr)
select {
case e := <-ndpDisp.autoGenAddrC:
- t.Fatalf("unexpected auto gen addr event = %+v", e)
- case <-time.After(regenAfter + defaultAsyncNegativeEventTimeout):
+ t.Fatalf("unexpected auto gen addr event = %#v", e)
+ default:
+ }
+
+ effectiveMaxTempAddrPL := maxTempAddrPreferredLifetime - tempDesyncFactor
+ // The time since the last regeneration before a new temporary address is
+ // generated.
+ tempAddrRegenenerationTime := effectiveMaxTempAddrPL - regenAdv
+
+ // Advance the clock by the regeneration time but don't expect a new temporary
+ // address as the prefix is deprecated.
+ clock.Advance(tempAddrRegenenerationTime)
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ t.Fatalf("unexpected auto gen addr event = %#v", e)
+ default:
}
// Prefer the prefix again.
@@ -2687,8 +2619,15 @@ func TestAutoGenTempAddrRegenJobUpdates(t *testing.T) {
// A new temporary address should immediately be generated since the
// regeneration time has already passed since the last address was generated
// - this regeneration does not depend on a job.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, 100, 100))
- expectAutoGenAddrEvent(tempAddr2, newAddr)
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, maxTempAddrPreferredLifetimeSeconds, maxTempAddrPreferredLifetimeSeconds))
+ expectAutoGenAddrEvent(tempAddrs[1], newAddr)
+ // Wait for the first temporary address to be deprecated.
+ expectAutoGenAddrEventAsync(tempAddrs[0], deprecatedAddr, regenAdv)
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ t.Fatalf("unexpected auto gen addr event = %s", e)
+ default:
+ }
// Increase the maximum lifetimes for temporary addresses to large values
// then refresh the lifetimes of the prefix.
@@ -2699,34 +2638,30 @@ func TestAutoGenTempAddrRegenJobUpdates(t *testing.T) {
// regenerate a new temporary address. Note, new addresses are only
// regenerated after the preferred lifetime - the regenerate advance duration
// as paased.
- ndpConfigs.MaxTempAddrValidLifetime = 100 * time.Second
- ndpConfigs.MaxTempAddrPreferredLifetime = 100 * time.Second
+ const largeLifetimeSeconds = minVLSeconds * 2
+ const largeLifetime = time.Duration(largeLifetimeSeconds) * time.Second
+ ndpConfigs.MaxTempAddrValidLifetime = 2 * largeLifetime
+ ndpConfigs.MaxTempAddrPreferredLifetime = largeLifetime
ipv6Ep, err := s.GetNetworkEndpoint(nicID, header.IPv6ProtocolNumber)
if err != nil {
t.Fatalf("s.GetNetworkEndpoint(%d, %d): %s", nicID, header.IPv6ProtocolNumber, err)
}
ndpEP := ipv6Ep.(ipv6.NDPEndpoint)
ndpEP.SetNDPConfigurations(ndpConfigs)
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, 100, 100))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, largeLifetimeSeconds, largeLifetimeSeconds))
+ timeSinceInitialTime := clock.NowMonotonic().Sub(initialTime)
+ clock.Advance(largeLifetime - timeSinceInitialTime)
+ expectAutoGenAddrEvent(tempAddrs[0], deprecatedAddr)
+ // to offset the advement of time to test the first temporary address's
+ // deprecation after the second was generated
+ advLess := regenAdv
+ expectAutoGenAddrEventAsync(tempAddrs[2], newAddr, timeSinceInitialTime-advLess-(tempDesyncFactor+regenAdv))
+ expectAutoGenAddrEventAsync(tempAddrs[1], deprecatedAddr, regenAdv)
select {
case e := <-ndpDisp.autoGenAddrC:
t.Fatalf("unexpected auto gen addr event = %+v", e)
- case <-time.After(regenAfter + defaultAsyncNegativeEventTimeout):
+ default:
}
-
- // Set the maximum lifetimes for temporary addresses such that on the next
- // RA, the regeneration job gets scheduled again.
- //
- // The maximum lifetime is the sum of the minimum lifetimes for temporary
- // addresses + the time that has already passed since the last address was
- // generated so that the regeneration job is needed to generate the next
- // address.
- newLifetimes := newMinVLDuration + regenAfter + defaultAsyncNegativeEventTimeout
- ndpConfigs.MaxTempAddrValidLifetime = newLifetimes
- ndpConfigs.MaxTempAddrPreferredLifetime = newLifetimes
- ndpEP.SetNDPConfigurations(ndpConfigs)
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, 100, 100))
- expectAutoGenAddrEventAsync(tempAddr3, newAddr, regenAfter+defaultAsyncPositiveEventTimeout)
}
// TestMixedSLAACAddrConflictRegen tests SLAAC address regeneration in response
@@ -2954,13 +2889,14 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) {
// stack.Stack will have a default route through the router (llAddr3) installed
// and a static link-address (linkAddr3) added to the link address cache for the
// router.
-func stackAndNdpDispatcherWithDefaultRoute(t *testing.T, nicID tcpip.NICID) (*ndpDispatcher, *channel.Endpoint, *stack.Stack) {
+func stackAndNdpDispatcherWithDefaultRoute(t *testing.T, nicID tcpip.NICID) (*ndpDispatcher, *channel.Endpoint, *stack.Stack, *faketime.ManualClock) {
t.Helper()
ndpDisp := &ndpDispatcher{
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
}
e := channel.New(0, 1280, linkAddr1)
e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
+ clock := faketime.NewManualClock()
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
NDPConfigs: ipv6.NDPConfigurations{
@@ -2970,6 +2906,7 @@ func stackAndNdpDispatcherWithDefaultRoute(t *testing.T, nicID tcpip.NICID) (*nd
NDPDisp: ndpDisp,
})},
TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol},
+ Clock: clock,
})
if err := s.CreateNIC(nicID, e); err != nil {
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
@@ -2983,7 +2920,7 @@ func stackAndNdpDispatcherWithDefaultRoute(t *testing.T, nicID tcpip.NICID) (*nd
if err := s.AddStaticNeighbor(nicID, ipv6.ProtocolNumber, llAddr3, linkAddr3); err != nil {
t.Fatalf("s.AddStaticNeighbor(%d, %d, %s, %s): %s", nicID, ipv6.ProtocolNumber, llAddr3, linkAddr3, err)
}
- return ndpDisp, e, s
+ return ndpDisp, e, s, clock
}
// addrForNewConnectionTo returns the local address used when creating a new
@@ -3057,7 +2994,7 @@ func TestAutoGenAddrDeprecateFromPI(t *testing.T) {
prefix1, _, addr1 := prefixSubnetAddr(0, linkAddr1)
prefix2, _, addr2 := prefixSubnetAddr(1, linkAddr1)
- ndpDisp, e, s := stackAndNdpDispatcherWithDefaultRoute(t, nicID)
+ ndpDisp, e, s, _ := stackAndNdpDispatcherWithDefaultRoute(t, nicID)
expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
t.Helper()
@@ -3160,19 +3097,11 @@ func TestAutoGenAddrDeprecateFromPI(t *testing.T) {
// when its preferred lifetime expires.
func TestAutoGenAddrJobDeprecation(t *testing.T) {
const nicID = 1
- const newMinVL = 2
- newMinVLDuration := newMinVL * time.Second
-
- saved := ipv6.MinPrefixInformationValidLifetimeForUpdate
- defer func() {
- ipv6.MinPrefixInformationValidLifetimeForUpdate = saved
- }()
- ipv6.MinPrefixInformationValidLifetimeForUpdate = newMinVLDuration
prefix1, _, addr1 := prefixSubnetAddr(0, linkAddr1)
prefix2, _, addr2 := prefixSubnetAddr(1, linkAddr1)
- ndpDisp, e, s := stackAndNdpDispatcherWithDefaultRoute(t, nicID)
+ ndpDisp, e, s, clock := stackAndNdpDispatcherWithDefaultRoute(t, nicID)
expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
t.Helper()
@@ -3190,12 +3119,13 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
expectAutoGenAddrEventAfter := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType, timeout time.Duration) {
t.Helper()
+ clock.Advance(timeout)
select {
case e := <-ndpDisp.autoGenAddrC:
if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(timeout):
+ default:
t.Fatal("timed out waiting for addr auto gen event")
}
}
@@ -3213,7 +3143,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
}
// Receive PI for prefix2.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, 100, 100))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, infiniteLifetimeSeconds, infiniteLifetimeSeconds))
expectAutoGenAddrEvent(addr2, newAddr)
if !containsV6Addr(s.NICInfo()[nicID].ProtocolAddresses, addr2) {
t.Fatalf("should have %s in the list of addresses", addr2)
@@ -3232,7 +3162,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
expectPrimaryAddr(addr1)
// Refresh lifetime for addr of prefix1.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, newMinVL, newMinVL-1))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, minVLSeconds, minVLSeconds-1))
select {
case <-ndpDisp.autoGenAddrC:
t.Fatal("unexpectedly got an auto-generated event")
@@ -3241,7 +3171,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
expectPrimaryAddr(addr1)
// Wait for addr of prefix1 to be deprecated.
- expectAutoGenAddrEventAfter(addr1, deprecatedAddr, newMinVLDuration-time.Second+defaultAsyncPositiveEventTimeout)
+ expectAutoGenAddrEventAfter(addr1, deprecatedAddr, ipv6.MinPrefixInformationValidLifetimeForUpdate-time.Second)
if !containsV6Addr(s.NICInfo()[nicID].ProtocolAddresses, addr1) {
t.Fatalf("should not have %s in the list of addresses", addr1)
}
@@ -3251,6 +3181,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
// addr2 should be the primary endpoint now since addr1 is deprecated but
// addr2 is not.
expectPrimaryAddr(addr2)
+
// addr1 is deprecated but if explicitly requested, it should be used.
fullAddr1 := tcpip.FullAddress{Addr: addr1.Address, NIC: nicID}
if got := addrForNewConnectionWithAddr(t, s, fullAddr1); got != addr1.Address {
@@ -3259,7 +3190,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
// Refresh valid lifetime for addr of prefix1, w/ 0 preferred lifetime to make
// sure we do not get a deprecation event again.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, newMinVL, 0))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, minVLSeconds, 0))
select {
case <-ndpDisp.autoGenAddrC:
t.Fatal("unexpectedly got an auto-generated event")
@@ -3271,7 +3202,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
}
// Refresh lifetimes for addr of prefix1.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, newMinVL, newMinVL-1))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix1, true, true, minVLSeconds, minVLSeconds-1))
select {
case <-ndpDisp.autoGenAddrC:
t.Fatal("unexpectedly got an auto-generated event")
@@ -3281,7 +3212,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
expectPrimaryAddr(addr1)
// Wait for addr of prefix1 to be deprecated.
- expectAutoGenAddrEventAfter(addr1, deprecatedAddr, newMinVLDuration-time.Second+defaultAsyncPositiveEventTimeout)
+ expectAutoGenAddrEventAfter(addr1, deprecatedAddr, ipv6.MinPrefixInformationValidLifetimeForUpdate-time.Second)
if !containsV6Addr(s.NICInfo()[nicID].ProtocolAddresses, addr1) {
t.Fatalf("should not have %s in the list of addresses", addr1)
}
@@ -3295,7 +3226,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
}
// Wait for addr of prefix1 to be invalidated.
- expectAutoGenAddrEventAfter(addr1, invalidatedAddr, time.Second+defaultAsyncPositiveEventTimeout)
+ expectAutoGenAddrEventAfter(addr1, invalidatedAddr, time.Second)
if containsV6Addr(s.NICInfo()[nicID].ProtocolAddresses, addr1) {
t.Fatalf("should not have %s in the list of addresses", addr1)
}
@@ -3305,7 +3236,7 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
expectPrimaryAddr(addr2)
// Refresh both lifetimes for addr of prefix2 to the same value.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, newMinVL, newMinVL))
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix2, true, true, minVLSeconds, minVLSeconds))
select {
case <-ndpDisp.autoGenAddrC:
t.Fatal("unexpectedly got an auto-generated event")
@@ -3317,6 +3248,17 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
// cases because the deprecation and invalidation handlers could be handled in
// either deprecation then invalidation, or invalidation then deprecation
// (which should be cancelled by the invalidation handler).
+ //
+ // Since we're about to cause both events to fire, we need the dispatcher
+ // channel to be able to hold both.
+ if got, want := len(ndpDisp.autoGenAddrC), 0; got != want {
+ t.Fatalf("got len(ndpDisp.autoGenAddrC) = %d, want %d", got, want)
+ }
+ if got, want := cap(ndpDisp.autoGenAddrC), 1; got != want {
+ t.Fatalf("got cap(ndpDisp.autoGenAddrC) = %d, want %d", got, want)
+ }
+ ndpDisp.autoGenAddrC = make(chan ndpAutoGenAddrEvent, 2)
+ clock.Advance(ipv6.MinPrefixInformationValidLifetimeForUpdate)
select {
case e := <-ndpDisp.autoGenAddrC:
if diff := checkAutoGenAddrEvent(e, addr2, deprecatedAddr); diff == "" {
@@ -3327,21 +3269,21 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
if diff := checkAutoGenAddrEvent(e, addr2, invalidatedAddr); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(defaultAsyncPositiveEventTimeout):
+ default:
t.Fatal("timed out waiting for addr auto gen event")
}
} else if diff := checkAutoGenAddrEvent(e, addr2, invalidatedAddr); diff == "" {
- // If we get an invalidation event first, we should not get a deprecation
+ // If we get an invalidation event first, we should not get a deprecation
// event after.
select {
case <-ndpDisp.autoGenAddrC:
t.Fatal("unexpectedly got an auto-generated event")
- case <-time.After(defaultAsyncNegativeEventTimeout):
+ default:
}
} else {
t.Fatalf("got unexpected auto-generated event")
}
- case <-time.After(newMinVLDuration + defaultAsyncPositiveEventTimeout):
+ default:
t.Fatal("timed out waiting for addr auto gen event")
}
if containsV6Addr(s.NICInfo()[nicID].ProtocolAddresses, addr1) {
@@ -3378,15 +3320,6 @@ func TestAutoGenAddrJobDeprecation(t *testing.T) {
// infinite values.
func TestAutoGenAddrFiniteToInfiniteToFiniteVL(t *testing.T) {
const infiniteVLSeconds = 2
- const minVLSeconds = 1
- savedIL := header.NDPInfiniteLifetime
- savedMinVL := ipv6.MinPrefixInformationValidLifetimeForUpdate
- defer func() {
- ipv6.MinPrefixInformationValidLifetimeForUpdate = savedMinVL
- header.NDPInfiniteLifetime = savedIL
- }()
- ipv6.MinPrefixInformationValidLifetimeForUpdate = minVLSeconds * time.Second
- header.NDPInfiniteLifetime = infiniteVLSeconds * time.Second
prefix, _, addr := prefixSubnetAddr(0, linkAddr1)
@@ -3410,68 +3343,58 @@ func TestAutoGenAddrFiniteToInfiniteToFiniteVL(t *testing.T) {
},
}
- // This Run will not return until the parallel tests finish.
- //
- // We need this because we need to do some teardown work after the
- // parallel tests complete.
- //
- // See https://godoc.org/testing#hdr-Subtests_and_Sub_benchmarks for
- // more details.
- t.Run("group", func(t *testing.T) {
- for _, test := range tests {
- test := test
-
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
-
- ndpDisp := ndpDispatcher{
- autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
- }
- e := channel.New(0, 1280, linkAddr1)
- s := stack.New(stack.Options{
- NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
- NDPConfigs: ipv6.NDPConfigurations{
- HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
- AutoGenGlobalAddresses: true,
- },
- NDPDisp: &ndpDisp,
- })},
- })
-
- if err := s.CreateNIC(1, e); err != nil {
- t.Fatalf("CreateNIC(1) = %s", err)
- }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ ndpDisp := ndpDispatcher{
+ autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
+ }
+ e := channel.New(0, 1280, linkAddr1)
+ clock := faketime.NewManualClock()
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
+ NDPConfigs: ipv6.NDPConfigurations{
+ HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
+ AutoGenGlobalAddresses: true,
+ },
+ NDPDisp: &ndpDisp,
+ })},
+ Clock: clock,
+ })
- // Receive an RA with finite prefix.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, minVLSeconds, 0))
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, addr, newAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
+ if err := s.CreateNIC(1, e); err != nil {
+ t.Fatalf("CreateNIC(1) = %s", err)
+ }
- default:
- t.Fatal("expected addr auto gen event")
+ // Receive an RA with finite prefix.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, minVLSeconds, 0))
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ if diff := checkAutoGenAddrEvent(e, addr, newAddr); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- // Receive an new RA with prefix with infinite VL.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, test.infiniteVL, 0))
+ default:
+ t.Fatal("expected addr auto gen event")
+ }
- // Receive a new RA with prefix with finite VL.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, minVLSeconds, 0))
+ // Receive an new RA with prefix with infinite VL.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, test.infiniteVL, 0))
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, addr, invalidatedAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
+ // Receive a new RA with prefix with finite VL.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, minVLSeconds, 0))
- case <-time.After(minVLSeconds*time.Second + defaultAsyncPositiveEventTimeout):
- t.Fatal("timeout waiting for addr auto gen event")
+ clock.Advance(ipv6.MinPrefixInformationValidLifetimeForUpdate)
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ if diff := checkAutoGenAddrEvent(e, addr, invalidatedAddr); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- })
- }
- })
+
+ default:
+ t.Fatal("timeout waiting for addr auto gen event")
+ }
+ })
+ }
}
// TestAutoGenAddrValidLifetimeUpdates tests that the valid lifetime of an
@@ -3479,12 +3402,6 @@ func TestAutoGenAddrFiniteToInfiniteToFiniteVL(t *testing.T) {
// RFC 4862 section 5.5.3.e.
func TestAutoGenAddrValidLifetimeUpdates(t *testing.T) {
const infiniteVL = 4294967295
- const newMinVL = 4
- saved := ipv6.MinPrefixInformationValidLifetimeForUpdate
- defer func() {
- ipv6.MinPrefixInformationValidLifetimeForUpdate = saved
- }()
- ipv6.MinPrefixInformationValidLifetimeForUpdate = newMinVL * time.Second
prefix, _, addr := prefixSubnetAddr(0, linkAddr1)
@@ -3495,137 +3412,129 @@ func TestAutoGenAddrValidLifetimeUpdates(t *testing.T) {
evl uint32
}{
// Should update the VL to the minimum VL for updating if the
- // new VL is less than newMinVL but was originally greater than
+ // new VL is less than minVLSeconds but was originally greater than
// it.
{
"LargeVLToVLLessThanMinVLForUpdate",
9999,
1,
- newMinVL,
+ minVLSeconds,
},
{
"LargeVLTo0",
9999,
0,
- newMinVL,
+ minVLSeconds,
},
{
"InfiniteVLToVLLessThanMinVLForUpdate",
infiniteVL,
1,
- newMinVL,
+ minVLSeconds,
},
{
"InfiniteVLTo0",
infiniteVL,
0,
- newMinVL,
+ minVLSeconds,
},
- // Should not update VL if original VL was less than newMinVL
- // and the new VL is also less than newMinVL.
+ // Should not update VL if original VL was less than minVLSeconds
+ // and the new VL is also less than minVLSeconds.
{
"ShouldNotUpdateWhenBothOldAndNewAreLessThanMinVLForUpdate",
- newMinVL - 1,
- newMinVL - 3,
- newMinVL - 1,
+ minVLSeconds - 1,
+ minVLSeconds - 3,
+ minVLSeconds - 1,
},
// Should take the new VL if the new VL is greater than the
- // remaining time or is greater than newMinVL.
+ // remaining time or is greater than minVLSeconds.
{
"MorethanMinVLToLesserButStillMoreThanMinVLForUpdate",
- newMinVL + 5,
- newMinVL + 3,
- newMinVL + 3,
+ minVLSeconds + 5,
+ minVLSeconds + 3,
+ minVLSeconds + 3,
},
{
"SmallVLToGreaterVLButStillLessThanMinVLForUpdate",
- newMinVL - 3,
- newMinVL - 1,
- newMinVL - 1,
+ minVLSeconds - 3,
+ minVLSeconds - 1,
+ minVLSeconds - 1,
},
{
"SmallVLToGreaterVLThatIsMoreThaMinVLForUpdate",
- newMinVL - 3,
- newMinVL + 1,
- newMinVL + 1,
+ minVLSeconds - 3,
+ minVLSeconds + 1,
+ minVLSeconds + 1,
},
}
- // This Run will not return until the parallel tests finish.
- //
- // We need this because we need to do some teardown work after the
- // parallel tests complete.
- //
- // See https://godoc.org/testing#hdr-Subtests_and_Sub_benchmarks for
- // more details.
- t.Run("group", func(t *testing.T) {
- for _, test := range tests {
- test := test
-
- t.Run(test.name, func(t *testing.T) {
- t.Parallel()
-
- ndpDisp := ndpDispatcher{
- autoGenAddrC: make(chan ndpAutoGenAddrEvent, 10),
- }
- e := channel.New(10, 1280, linkAddr1)
- s := stack.New(stack.Options{
- NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
- NDPConfigs: ipv6.NDPConfigurations{
- HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
- AutoGenGlobalAddresses: true,
- },
- NDPDisp: &ndpDisp,
- })},
- })
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ ndpDisp := ndpDispatcher{
+ autoGenAddrC: make(chan ndpAutoGenAddrEvent, 10),
+ }
+ e := channel.New(10, 1280, linkAddr1)
+ clock := faketime.NewManualClock()
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
+ NDPConfigs: ipv6.NDPConfigurations{
+ HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
+ AutoGenGlobalAddresses: true,
+ },
+ NDPDisp: &ndpDisp,
+ })},
+ Clock: clock,
+ })
- if err := s.CreateNIC(1, e); err != nil {
- t.Fatalf("CreateNIC(1) = %s", err)
- }
+ if err := s.CreateNIC(1, e); err != nil {
+ t.Fatalf("CreateNIC(1) = %s", err)
+ }
- // Receive an RA with prefix with initial VL,
- // test.ovl.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, test.ovl, 0))
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, addr, newAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- default:
- t.Fatal("expected addr auto gen event")
+ // Receive an RA with prefix with initial VL,
+ // test.ovl.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, test.ovl, 0))
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ if diff := checkAutoGenAddrEvent(e, addr, newAddr); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
+ default:
+ t.Fatal("expected addr auto gen event")
+ }
- // Receive an new RA with prefix with new VL,
- // test.nvl.
- e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, test.nvl, 0))
+ // Receive an new RA with prefix with new VL,
+ // test.nvl.
+ e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, test.nvl, 0))
- //
- // Validate that the VL for the address got set
- // to test.evl.
- //
+ //
+ // Validate that the VL for the address got set
+ // to test.evl.
+ //
- // The address should not be invalidated until the effective valid
- // lifetime has passed.
- select {
- case <-ndpDisp.autoGenAddrC:
- t.Fatal("unexpectedly received an auto gen addr event")
- case <-time.After(time.Duration(test.evl)*time.Second - defaultAsyncNegativeEventTimeout):
- }
+ // The address should not be invalidated until the effective valid
+ // lifetime has passed.
+ const delta = 1
+ clock.Advance(time.Duration(test.evl)*time.Second - delta)
+ select {
+ case <-ndpDisp.autoGenAddrC:
+ t.Fatal("unexpectedly received an auto gen addr event")
+ default:
+ }
- // Wait for the invalidation event.
- select {
- case e := <-ndpDisp.autoGenAddrC:
- if diff := checkAutoGenAddrEvent(e, addr, invalidatedAddr); diff != "" {
- t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
- }
- case <-time.After(defaultAsyncPositiveEventTimeout):
- t.Fatal("timeout waiting for addr auto gen event")
+ // Wait for the invalidation event.
+ clock.Advance(delta)
+ select {
+ case e := <-ndpDisp.autoGenAddrC:
+ if diff := checkAutoGenAddrEvent(e, addr, invalidatedAddr); diff != "" {
+ t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- })
- }
- })
+ default:
+ t.Fatal("timeout waiting for addr auto gen event")
+ }
+ })
+ }
}
// TestAutoGenAddrRemoval tests that when auto-generated addresses are removed
@@ -3696,7 +3605,7 @@ func TestAutoGenAddrAfterRemoval(t *testing.T) {
prefix1, _, addr1 := prefixSubnetAddr(0, linkAddr1)
prefix2, _, addr2 := prefixSubnetAddr(1, linkAddr1)
- ndpDisp, e, s := stackAndNdpDispatcherWithDefaultRoute(t, nicID)
+ ndpDisp, e, s, _ := stackAndNdpDispatcherWithDefaultRoute(t, nicID)
expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
t.Helper()
@@ -3976,13 +3885,6 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
const maxMaxRetries = 3
const lifetimeSeconds = 10
- // Needed for the temporary address sub test.
- savedMaxDesync := ipv6.MaxDesyncFactor
- defer func() {
- ipv6.MaxDesyncFactor = savedMaxDesync
- }()
- ipv6.MaxDesyncFactor = time.Nanosecond
-
secretKey := makeSecretKey(t)
prefix, subnet, _ := prefixSubnetAddr(0, linkAddr1)
@@ -4008,22 +3910,24 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
}
}
- expectAutoGenAddrEventAsync := func(t *testing.T, ndpDisp *ndpDispatcher, addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
+ expectAutoGenAddrEventAsync := func(t *testing.T, clock *faketime.ManualClock, ndpDisp *ndpDispatcher, addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
t.Helper()
+ clock.RunImmediatelyScheduledJobs()
select {
case e := <-ndpDisp.autoGenAddrC:
if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(defaultAsyncPositiveEventTimeout):
+ default:
t.Fatal("timed out waiting for addr auto gen event")
}
}
- expectDADEvent := func(t *testing.T, ndpDisp *ndpDispatcher, addr tcpip.Address, res stack.DADResult) {
+ expectDADEvent := func(t *testing.T, clock *faketime.ManualClock, ndpDisp *ndpDispatcher, addr tcpip.Address, res stack.DADResult) {
t.Helper()
+ clock.RunImmediatelyScheduledJobs()
select {
case e := <-ndpDisp.dadC:
if diff := checkDADEvent(e, nicID, addr, res); diff != "" {
@@ -4034,15 +3938,16 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
}
}
- expectDADEventAsync := func(t *testing.T, ndpDisp *ndpDispatcher, addr tcpip.Address, res stack.DADResult) {
+ expectDADEventAsync := func(t *testing.T, clock *faketime.ManualClock, ndpDisp *ndpDispatcher, addr tcpip.Address, res stack.DADResult) {
t.Helper()
+ clock.Advance(dadTransmits * retransmitTimer)
select {
case e := <-ndpDisp.dadC:
if diff := checkDADEvent(e, nicID, addr, res); diff != "" {
t.Errorf("DAD event mismatch (-want +got):\n%s", diff)
}
- case <-time.After(dadTransmits*retransmitTimer + defaultAsyncPositiveEventTimeout):
+ default:
t.Fatal("timed out waiting for DAD event")
}
}
@@ -4053,7 +3958,7 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
name string
ndpConfigs ipv6.NDPConfigurations
autoGenLinkLocal bool
- prepareFn func(t *testing.T, ndpDisp *ndpDispatcher, e *channel.Endpoint, tempIIDHistory []byte) []tcpip.AddressWithPrefix
+ prepareFn func(t *testing.T, clock *faketime.ManualClock, ndpDisp *ndpDispatcher, e *channel.Endpoint, tempIIDHistory []byte) []tcpip.AddressWithPrefix
addrGenFn func(dadCounter uint8, tempIIDHistory []byte) tcpip.AddressWithPrefix
}{
{
@@ -4062,7 +3967,7 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
HandleRAs: ipv6.HandlingRAsEnabledWhenForwardingDisabled,
AutoGenGlobalAddresses: true,
},
- prepareFn: func(_ *testing.T, _ *ndpDispatcher, e *channel.Endpoint, _ []byte) []tcpip.AddressWithPrefix {
+ prepareFn: func(_ *testing.T, _ *faketime.ManualClock, _ *ndpDispatcher, e *channel.Endpoint, _ []byte) []tcpip.AddressWithPrefix {
// Receive an RA with prefix1 in a PI.
e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, lifetimeSeconds, lifetimeSeconds))
return nil
@@ -4076,7 +3981,7 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
name: "LinkLocal address",
ndpConfigs: ipv6.NDPConfigurations{},
autoGenLinkLocal: true,
- prepareFn: func(*testing.T, *ndpDispatcher, *channel.Endpoint, []byte) []tcpip.AddressWithPrefix {
+ prepareFn: func(*testing.T, *faketime.ManualClock, *ndpDispatcher, *channel.Endpoint, []byte) []tcpip.AddressWithPrefix {
return nil
},
addrGenFn: func(dadCounter uint8, _ []byte) tcpip.AddressWithPrefix {
@@ -4090,14 +3995,14 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
AutoGenGlobalAddresses: true,
AutoGenTempGlobalAddresses: true,
},
- prepareFn: func(t *testing.T, ndpDisp *ndpDispatcher, e *channel.Endpoint, tempIIDHistory []byte) []tcpip.AddressWithPrefix {
+ prepareFn: func(t *testing.T, clock *faketime.ManualClock, ndpDisp *ndpDispatcher, e *channel.Endpoint, tempIIDHistory []byte) []tcpip.AddressWithPrefix {
header.InitialTempIID(tempIIDHistory, nil, nicID)
// Generate a stable SLAAC address so temporary addresses will be
// generated.
e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, 100, 100))
expectAutoGenAddrEvent(t, ndpDisp, stableAddrForTempAddrTest, newAddr)
- expectDADEventAsync(t, ndpDisp, stableAddrForTempAddrTest.Address, &stack.DADSucceeded{})
+ expectDADEventAsync(t, clock, ndpDisp, stableAddrForTempAddrTest.Address, &stack.DADSucceeded{})
// The stable address will be assigned throughout the test.
return []tcpip.AddressWithPrefix{stableAddrForTempAddrTest}
@@ -4109,14 +4014,6 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
}
for _, addrType := range addrTypes {
- // This Run will not return until the parallel tests finish.
- //
- // We need this because we need to do some teardown work after the parallel
- // tests complete and limit the number of parallel tests running at the same
- // time to reduce flakes.
- //
- // See https://godoc.org/testing#hdr-Subtests_and_Sub_benchmarks for
- // more details.
t.Run(addrType.name, func(t *testing.T) {
for maxRetries := uint8(0); maxRetries <= maxMaxRetries; maxRetries++ {
for numFailures := uint8(0); numFailures <= maxRetries+1; numFailures++ {
@@ -4125,8 +4022,6 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
addrType := addrType
t.Run(fmt.Sprintf("%d max retries and %d failures", maxRetries, numFailures), func(t *testing.T) {
- t.Parallel()
-
ndpDisp := ndpDispatcher{
dadC: make(chan ndpDADEvent, 1),
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
@@ -4134,6 +4029,7 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
e := channel.New(0, 1280, linkAddr1)
ndpConfigs := addrType.ndpConfigs
ndpConfigs.AutoGenAddressConflictRetries = maxRetries
+ clock := faketime.NewManualClock()
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
AutoGenLinkLocal: addrType.autoGenLinkLocal,
@@ -4150,6 +4046,7 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
SecretKey: secretKey,
},
})},
+ Clock: clock,
})
opts := stack.NICOptions{Name: nicName}
if err := s.CreateNICWithOptions(nicID, e, opts); err != nil {
@@ -4157,12 +4054,12 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
}
var tempIIDHistory [header.IIDSize]byte
- stableAddrs := addrType.prepareFn(t, &ndpDisp, e, tempIIDHistory[:])
+ stableAddrs := addrType.prepareFn(t, clock, &ndpDisp, e, tempIIDHistory[:])
// Simulate DAD conflicts so the address is regenerated.
for i := uint8(0); i < numFailures; i++ {
addr := addrType.addrGenFn(i, tempIIDHistory[:])
- expectAutoGenAddrEventAsync(t, &ndpDisp, addr, newAddr)
+ expectAutoGenAddrEventAsync(t, clock, &ndpDisp, addr, newAddr)
// Should not have any new addresses assigned to the NIC.
if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, stableAddrs, nil); mismatch != "" {
@@ -4172,7 +4069,7 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
// Simulate a DAD conflict.
rxNDPSolicit(e, addr.Address)
expectAutoGenAddrEvent(t, &ndpDisp, addr, invalidatedAddr)
- expectDADEvent(t, &ndpDisp, addr.Address, &stack.DADDupAddrDetected{})
+ expectDADEvent(t, clock, &ndpDisp, addr.Address, &stack.DADDupAddrDetected{})
// Attempting to add the address manually should not fail if the
// address's state was cleaned up when DAD failed.
@@ -4182,7 +4079,7 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
if err := s.RemoveAddress(nicID, addr.Address); err != nil {
t.Fatalf("RemoveAddress(%d, %s) = %s", nicID, addr.Address, err)
}
- expectDADEvent(t, &ndpDisp, addr.Address, &stack.DADAborted{})
+ expectDADEvent(t, clock, &ndpDisp, addr.Address, &stack.DADAborted{})
}
// Should not have any new addresses assigned to the NIC.
@@ -4194,8 +4091,8 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
// an address after DAD resolves.
if maxRetries+1 > numFailures {
addr := addrType.addrGenFn(numFailures, tempIIDHistory[:])
- expectAutoGenAddrEventAsync(t, &ndpDisp, addr, newAddr)
- expectDADEventAsync(t, &ndpDisp, addr.Address, &stack.DADSucceeded{})
+ expectAutoGenAddrEventAsync(t, clock, &ndpDisp, addr, newAddr)
+ expectDADEventAsync(t, clock, &ndpDisp, addr.Address, &stack.DADSucceeded{})
if mismatch := addressCheck(s.NICInfo()[nicID].ProtocolAddresses, append(stableAddrs, addr), nil); mismatch != "" {
t.Fatal(mismatch)
}
@@ -4205,7 +4102,7 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
select {
case e := <-ndpDisp.autoGenAddrC:
t.Fatalf("unexpectedly got an auto-generated address event = %+v", e)
- case <-time.After(defaultAsyncNegativeEventTimeout):
+ default:
}
})
}
@@ -4718,11 +4615,9 @@ func TestNoCleanupNDPStateWhenForwardingEnabled(t *testing.T) {
)
ndpDisp := ndpDispatcher{
- routerC: make(chan ndpRouterEvent, 1),
- rememberRouter: true,
- prefixC: make(chan ndpPrefixEvent, 1),
- rememberPrefix: true,
- autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
+ offLinkRouteC: make(chan ndpOffLinkRouteEvent, 1),
+ prefixC: make(chan ndpPrefixEvent, 1),
+ autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
}
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
@@ -4765,17 +4660,17 @@ func TestNoCleanupNDPStateWhenForwardingEnabled(t *testing.T) {
),
)
select {
- case e := <-ndpDisp.routerC:
- if diff := checkRouterEvent(e, llAddr3, true /* discovered */); diff != "" {
- t.Errorf("router event mismatch (-want +got):\n%s", diff)
+ case e := <-ndpDisp.offLinkRouteC:
+ if diff := checkOffLinkRouteEvent(e, nicID, header.IPv6EmptySubnet, llAddr3, header.MediumRoutePreference, true /* discovered */); diff != "" {
+ t.Errorf("off-link route event mismatch (-want +got):\n%s", diff)
}
default:
- t.Errorf("expected router event for %s on NIC(%d)", llAddr3, nicID)
+ t.Errorf("expected off-link route event for %s on NIC(%d)", llAddr3, nicID)
}
select {
case e := <-ndpDisp.prefixC:
if diff := checkPrefixEvent(e, subnet, true /* discovered */); diff != "" {
- t.Errorf("router event mismatch (-want +got):\n%s", diff)
+ t.Errorf("off-link route event mismatch (-want +got):\n%s", diff)
}
default:
t.Errorf("expected prefix event for %s on NIC(%d)", prefix, nicID)
@@ -4797,8 +4692,8 @@ func TestNoCleanupNDPStateWhenForwardingEnabled(t *testing.T) {
t.Fatalf("SetForwardingDefaultAndAllNICs(%d, %t): %s", ipv6.ProtocolNumber, forwarding, err)
}
select {
- case e := <-ndpDisp.routerC:
- t.Errorf("unexpected router event = %#v", e)
+ case e := <-ndpDisp.offLinkRouteC:
+ t.Errorf("unexpected off-link route event = %#v", e)
default:
}
select {
@@ -4884,11 +4779,9 @@ func TestCleanupNDPState(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ndpDisp := ndpDispatcher{
- routerC: make(chan ndpRouterEvent, maxRouterAndPrefixEvents),
- rememberRouter: true,
- prefixC: make(chan ndpPrefixEvent, maxRouterAndPrefixEvents),
- rememberPrefix: true,
- autoGenAddrC: make(chan ndpAutoGenAddrEvent, test.maxAutoGenAddrEvents),
+ offLinkRouteC: make(chan ndpOffLinkRouteEvent, maxRouterAndPrefixEvents),
+ prefixC: make(chan ndpPrefixEvent, maxRouterAndPrefixEvents),
+ autoGenAddrC: make(chan ndpAutoGenAddrEvent, test.maxAutoGenAddrEvents),
}
clock := faketime.NewManualClock()
s := stack.New(stack.Options{
@@ -4905,14 +4798,14 @@ func TestCleanupNDPState(t *testing.T) {
Clock: clock,
})
- expectRouterEvent := func() (bool, ndpRouterEvent) {
+ expectOffLinkRouteEvent := func() (bool, ndpOffLinkRouteEvent) {
select {
- case e := <-ndpDisp.routerC:
+ case e := <-ndpDisp.offLinkRouteC:
return true, e
default:
}
- return false, ndpRouterEvent{}
+ return false, ndpOffLinkRouteEvent{}
}
expectPrefixEvent := func() (bool, ndpPrefixEvent) {
@@ -4957,8 +4850,8 @@ func TestCleanupNDPState(t *testing.T) {
// multiple addresses.
e1.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr3, lifetimeSeconds, prefix1, true, true, lifetimeSeconds, lifetimeSeconds))
- if ok, _ := expectRouterEvent(); !ok {
- t.Errorf("expected router event for %s on NIC(%d)", llAddr3, nicID1)
+ if ok, _ := expectOffLinkRouteEvent(); !ok {
+ t.Errorf("expected off-link route event for %s on NIC(%d)", llAddr3, nicID1)
}
if ok, _ := expectPrefixEvent(); !ok {
t.Errorf("expected prefix event for %s on NIC(%d)", prefix1, nicID1)
@@ -4968,8 +4861,8 @@ func TestCleanupNDPState(t *testing.T) {
}
e1.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr4, lifetimeSeconds, prefix2, true, true, lifetimeSeconds, lifetimeSeconds))
- if ok, _ := expectRouterEvent(); !ok {
- t.Errorf("expected router event for %s on NIC(%d)", llAddr4, nicID1)
+ if ok, _ := expectOffLinkRouteEvent(); !ok {
+ t.Errorf("expected off-link route event for %s on NIC(%d)", llAddr4, nicID1)
}
if ok, _ := expectPrefixEvent(); !ok {
t.Errorf("expected prefix event for %s on NIC(%d)", prefix2, nicID1)
@@ -4979,8 +4872,8 @@ func TestCleanupNDPState(t *testing.T) {
}
e2.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr3, lifetimeSeconds, prefix1, true, true, lifetimeSeconds, lifetimeSeconds))
- if ok, _ := expectRouterEvent(); !ok {
- t.Errorf("expected router event for %s on NIC(%d)", llAddr3, nicID2)
+ if ok, _ := expectOffLinkRouteEvent(); !ok {
+ t.Errorf("expected off-link route event for %s on NIC(%d)", llAddr3, nicID2)
}
if ok, _ := expectPrefixEvent(); !ok {
t.Errorf("expected prefix event for %s on NIC(%d)", prefix1, nicID2)
@@ -4990,8 +4883,8 @@ func TestCleanupNDPState(t *testing.T) {
}
e2.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr4, lifetimeSeconds, prefix2, true, true, lifetimeSeconds, lifetimeSeconds))
- if ok, _ := expectRouterEvent(); !ok {
- t.Errorf("expected router event for %s on NIC(%d)", llAddr4, nicID2)
+ if ok, _ := expectOffLinkRouteEvent(); !ok {
+ t.Errorf("expected off-link route event for %s on NIC(%d)", llAddr4, nicID2)
}
if ok, _ := expectPrefixEvent(); !ok {
t.Errorf("expected prefix event for %s on NIC(%d)", prefix2, nicID2)
@@ -5032,14 +4925,14 @@ func TestCleanupNDPState(t *testing.T) {
test.cleanupFn(t, s)
// Collect invalidation events after having NDP state cleaned up.
- gotRouterEvents := make(map[ndpRouterEvent]int)
+ gotOffLinkRouteEvents := make(map[ndpOffLinkRouteEvent]int)
for i := 0; i < maxRouterAndPrefixEvents; i++ {
- ok, e := expectRouterEvent()
+ ok, e := expectOffLinkRouteEvent()
if !ok {
- t.Errorf("expected %d router events after becoming a router; got = %d", maxRouterAndPrefixEvents, i)
+ t.Errorf("expected %d off-link route events after becoming a router; got = %d", maxRouterAndPrefixEvents, i)
break
}
- gotRouterEvents[e]++
+ gotOffLinkRouteEvents[e]++
}
gotPrefixEvents := make(map[ndpPrefixEvent]int)
for i := 0; i < maxRouterAndPrefixEvents; i++ {
@@ -5066,14 +4959,14 @@ func TestCleanupNDPState(t *testing.T) {
t.FailNow()
}
- expectedRouterEvents := map[ndpRouterEvent]int{
- {nicID: nicID1, addr: llAddr3, discovered: false}: 1,
- {nicID: nicID1, addr: llAddr4, discovered: false}: 1,
- {nicID: nicID2, addr: llAddr3, discovered: false}: 1,
- {nicID: nicID2, addr: llAddr4, discovered: false}: 1,
+ expectedOffLinkRouteEvents := map[ndpOffLinkRouteEvent]int{
+ {nicID: nicID1, subnet: header.IPv6EmptySubnet, router: llAddr3, updated: false}: 1,
+ {nicID: nicID1, subnet: header.IPv6EmptySubnet, router: llAddr4, updated: false}: 1,
+ {nicID: nicID2, subnet: header.IPv6EmptySubnet, router: llAddr3, updated: false}: 1,
+ {nicID: nicID2, subnet: header.IPv6EmptySubnet, router: llAddr4, updated: false}: 1,
}
- if diff := cmp.Diff(expectedRouterEvents, gotRouterEvents); diff != "" {
- t.Errorf("router events mismatch (-want +got):\n%s", diff)
+ if diff := cmp.Diff(expectedOffLinkRouteEvents, gotOffLinkRouteEvents); diff != "" {
+ t.Errorf("off-link route events mismatch (-want +got):\n%s", diff)
}
expectedPrefixEvents := map[ndpPrefixEvent]int{
{nicID: nicID1, prefix: subnet1, discovered: false}: 1,
@@ -5137,8 +5030,8 @@ func TestCleanupNDPState(t *testing.T) {
// cancelled when the NDP state was cleaned up).
clock.Advance(lifetimeSeconds * time.Second)
select {
- case <-ndpDisp.routerC:
- t.Error("unexpected router event")
+ case <-ndpDisp.offLinkRouteC:
+ t.Error("unexpected off-link route event")
default:
}
select {
@@ -5163,7 +5056,6 @@ func TestDHCPv6ConfigurationFromNDPDA(t *testing.T) {
ndpDisp := ndpDispatcher{
dhcpv6ConfigurationC: make(chan ndpDHCPv6Event, 1),
- rememberRouter: true,
}
e := channel.New(0, 1280, linkAddr1)
s := stack.New(stack.Options{
diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go
index 378389db2..b854d868c 100644
--- a/pkg/tcpip/stack/nic.go
+++ b/pkg/tcpip/stack/nic.go
@@ -779,17 +779,11 @@ func (n *nic) DeliverTransportPacket(protocol tcpip.TransportProtocolNumber, pkt
transProto := state.proto
- // Raw socket packets are delivered based solely on the transport
- // protocol number. We do not inspect the payload to ensure it's
- // validly formed.
- n.stack.demux.deliverRawPacket(protocol, pkt)
-
// TransportHeader is empty only when pkt is an ICMP packet or was reassembled
// from fragments.
if pkt.TransportHeader().View().IsEmpty() {
- // TODO(gvisor.dev/issue/170): ICMP packets don't have their TransportHeader
- // fields set yet, parse it here. See icmp/protocol.go:protocol.Parse for a
- // full explanation.
+ // ICMP packets don't have their TransportHeader fields set yet, parse it
+ // here. See icmp/protocol.go:protocol.Parse for a full explanation.
if protocol == header.ICMPv4ProtocolNumber || protocol == header.ICMPv6ProtocolNumber {
// ICMP packets may be longer, but until icmp.Parse is implemented, here
// we parse it using the minimum size.
@@ -878,6 +872,17 @@ func (n *nic) DeliverTransportError(local, remote tcpip.Address, net tcpip.Netwo
}
}
+// DeliverRawPacket implements TransportDispatcher.
+func (n *nic) DeliverRawPacket(protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) {
+ // For ICMPv4 only we validate the header length for compatibility with
+ // raw(7) ICMP_FILTER. The same check is made in Linux here:
+ // https://github.com/torvalds/linux/blob/70585216/net/ipv4/raw.c#L189.
+ if protocol == header.ICMPv4ProtocolNumber && pkt.TransportHeader().View().Size()+pkt.Data().Size() < header.ICMPv4MinimumSize {
+ return
+ }
+ n.stack.demux.deliverRawPacket(protocol, pkt)
+}
+
// ID implements NetworkInterface.
func (n *nic) ID() tcpip.NICID {
return n.id
diff --git a/pkg/tcpip/stack/packet_buffer.go b/pkg/tcpip/stack/packet_buffer.go
index 4ca702121..9192d8433 100644
--- a/pkg/tcpip/stack/packet_buffer.go
+++ b/pkg/tcpip/stack/packet_buffer.go
@@ -134,7 +134,7 @@ type PacketBuffer struct {
// https://www.man7.org/linux/man-pages/man7/packet.7.html.
PktType tcpip.PacketType
- // NICID is the ID of the interface the network packet was received at.
+ // NICID is the ID of the last interface the network packet was handled at.
NICID tcpip.NICID
// RXTransportChecksumValidated indicates that transport checksum verification
diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go
index a038389e0..dfe2c886f 100644
--- a/pkg/tcpip/stack/registration.go
+++ b/pkg/tcpip/stack/registration.go
@@ -265,6 +265,11 @@ type TransportDispatcher interface {
//
// DeliverTransportError takes ownership of the packet buffer.
DeliverTransportError(local, remote tcpip.Address, _ tcpip.NetworkProtocolNumber, _ tcpip.TransportProtocolNumber, _ TransportError, _ *PacketBuffer)
+
+ // DeliverRawPacket delivers a packet to any subscribed raw sockets.
+ //
+ // DeliverRawPacket does NOT take ownership of the packet buffer.
+ DeliverRawPacket(tcpip.TransportProtocolNumber, *PacketBuffer)
}
// PacketLooping specifies where an outbound packet should be sent.
diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go
index 40d277312..81fabe29a 100644
--- a/pkg/tcpip/stack/stack.go
+++ b/pkg/tcpip/stack/stack.go
@@ -108,7 +108,7 @@ type Stack struct {
handleLocal bool
// tables are the iptables packet filtering and manipulation rules.
- // TODO(gvisor.dev/issue/170): S/R this field.
+ // TODO(gvisor.dev/issue/4595): S/R this field.
tables *IPTables
// resumableEndpoints is a list of endpoints that need to be resumed if the
@@ -1872,9 +1872,8 @@ const (
// ParsePacketBufferTransport parses the provided packet buffer's transport
// header.
func (s *Stack) ParsePacketBufferTransport(protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) ParseResult {
- // TODO(gvisor.dev/issue/170): ICMP packets don't have their TransportHeader
- // fields set yet, parse it here. See icmp/protocol.go:protocol.Parse for a
- // full explanation.
+ // ICMP packets don't have their TransportHeader fields set yet, parse it
+ // here. See icmp/protocol.go:protocol.Parse for a full explanation.
if protocol == header.ICMPv4ProtocolNumber || protocol == header.ICMPv6ProtocolNumber {
return ParsedOK
}
diff --git a/pkg/tcpip/stack/tcp.go b/pkg/tcpip/stack/tcp.go
index e90c1a770..90a8ba6cf 100644
--- a/pkg/tcpip/stack/tcp.go
+++ b/pkg/tcpip/stack/tcp.go
@@ -380,9 +380,6 @@ type TCPSndBufState struct {
// SndClosed indicates that the endpoint has been closed for sends.
SndClosed bool
- // SndBufInQueue is the number of bytes in the send queue.
- SndBufInQueue seqnum.Size
-
// PacketTooBigCount is used to notify the main protocol routine how
// many times a "packet too big" control packet is received.
PacketTooBigCount int
diff --git a/pkg/tcpip/stack/transport_demuxer.go b/pkg/tcpip/stack/transport_demuxer.go
index 8a8454a6a..dda57e225 100644
--- a/pkg/tcpip/stack/transport_demuxer.go
+++ b/pkg/tcpip/stack/transport_demuxer.go
@@ -16,6 +16,7 @@ package stack
import (
"fmt"
+
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/hash/jenkins"
@@ -215,10 +216,17 @@ func (epsByNIC *endpointsByNIC) registerEndpoint(d *transportDemuxer, netProto t
netProto: netProto,
transProto: transProto,
}
- epsByNIC.endpoints[bindToDevice] = multiPortEp
}
- return multiPortEp.singleRegisterEndpoint(t, flags)
+ if err := multiPortEp.singleRegisterEndpoint(t, flags); err != nil {
+ return err
+ }
+ // Only add this newly created multiportEndpoint if the singleRegisterEndpoint
+ // succeeded.
+ if !ok {
+ epsByNIC.endpoints[bindToDevice] = multiPortEp
+ }
+ return nil
}
func (epsByNIC *endpointsByNIC) checkEndpoint(flags ports.Flags, bindToDevice tcpip.NICID) tcpip.Error {
@@ -405,7 +413,6 @@ func (ep *multiPortEndpoint) handlePacketAll(id TransportEndpointID, pkt *Packet
func (ep *multiPortEndpoint) singleRegisterEndpoint(t TransportEndpoint, flags ports.Flags) tcpip.Error {
ep.mu.Lock()
defer ep.mu.Unlock()
-
bits := flags.Bits() & ports.MultiBindFlagMask
if len(ep.endpoints) != 0 {
@@ -468,17 +475,21 @@ func (d *transportDemuxer) singleRegisterEndpoint(netProto tcpip.NetworkProtocol
eps.mu.Lock()
defer eps.mu.Unlock()
-
epsByNIC, ok := eps.endpoints[id]
if !ok {
epsByNIC = &endpointsByNIC{
endpoints: make(map[tcpip.NICID]*multiPortEndpoint),
seed: d.stack.Seed(),
}
+ }
+ if err := epsByNIC.registerEndpoint(d, netProto, protocol, ep, flags, bindToDevice); err != nil {
+ return err
+ }
+ // Only add this newly created epsByNIC if registerEndpoint succeeded.
+ if !ok {
eps.endpoints[id] = epsByNIC
}
-
- return epsByNIC.registerEndpoint(d, netProto, protocol, ep, flags, bindToDevice)
+ return nil
}
func (d *transportDemuxer) singleCheckEndpoint(netProto tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, id TransportEndpointID, flags ports.Flags, bindToDevice tcpip.NICID) tcpip.Error {
diff --git a/pkg/tcpip/stack/transport_demuxer_test.go b/pkg/tcpip/stack/transport_demuxer_test.go
index 0972c94de..45b09110d 100644
--- a/pkg/tcpip/stack/transport_demuxer_test.go
+++ b/pkg/tcpip/stack/transport_demuxer_test.go
@@ -203,6 +203,56 @@ func TestTransportDemuxerRegister(t *testing.T) {
}
}
+func TestTransportDemuxerRegisterMultiple(t *testing.T) {
+ type test struct {
+ flags ports.Flags
+ want tcpip.Error
+ }
+ for _, subtest := range []struct {
+ name string
+ tests []test
+ }{
+ {"zeroFlags", []test{
+ {ports.Flags{}, nil},
+ {ports.Flags{}, &tcpip.ErrPortInUse{}},
+ }},
+ {"multibindFlags", []test{
+ // Allow multiple registrations same TransportEndpointID with multibind flags.
+ {ports.Flags{LoadBalanced: true, MostRecent: true}, nil},
+ {ports.Flags{LoadBalanced: true, MostRecent: true}, nil},
+ // Disallow registration w/same ID for a non-multibindflag.
+ {ports.Flags{TupleOnly: true}, &tcpip.ErrPortInUse{}},
+ }},
+ } {
+ t.Run(subtest.name, func(t *testing.T) {
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol},
+ TransportProtocols: []stack.TransportProtocolFactory{udp.NewProtocol},
+ })
+ var eps []tcpip.Endpoint
+ for idx, test := range subtest.tests {
+ var wq waiter.Queue
+ ep, err := s.NewEndpoint(udp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
+ if err != nil {
+ t.Fatal(err)
+ }
+ eps = append(eps, ep)
+ tEP, ok := ep.(stack.TransportEndpoint)
+ if !ok {
+ t.Fatalf("%T does not implement stack.TransportEndpoint", ep)
+ }
+ id := stack.TransportEndpointID{LocalPort: 1}
+ if got, want := s.RegisterTransportEndpoint([]tcpip.NetworkProtocolNumber{ipv4.ProtocolNumber}, udp.ProtocolNumber, id, tEP, test.flags, 0), test.want; got != want {
+ t.Fatalf("test index: %d, s.RegisterTransportEndpoint(ipv4.ProtocolNumber, udp.ProtocolNumber, _, _, %+v, 0) = %s, want %s", idx, test.flags, got, want)
+ }
+ }
+ for _, ep := range eps {
+ ep.Close()
+ }
+ })
+ }
+}
+
// TestBindToDeviceDistribution injects varied packets on input devices and checks that
// the distribution of packets received matches expectations.
func TestBindToDeviceDistribution(t *testing.T) {
diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go
index 91622fa4c..8f2658f64 100644
--- a/pkg/tcpip/tcpip.go
+++ b/pkg/tcpip/tcpip.go
@@ -465,11 +465,11 @@ type ControlMessages struct {
// PacketOwner is used to get UID and GID of the packet.
type PacketOwner interface {
- // UID returns UID of the packet.
- UID() uint32
+ // UID returns KUID of the packet.
+ KUID() uint32
- // GID returns GID of the packet.
- GID() uint32
+ // GID returns KGID of the packet.
+ KGID() uint32
}
// ReadOptions contains options for Endpoint.Read.
diff --git a/pkg/tcpip/tests/integration/iptables_test.go b/pkg/tcpip/tests/integration/iptables_test.go
index 07ba2b837..f9ab7d0af 100644
--- a/pkg/tcpip/tests/integration/iptables_test.go
+++ b/pkg/tcpip/tests/integration/iptables_test.go
@@ -166,7 +166,7 @@ func TestIPTablesStatsForInput(t *testing.T) {
// Make sure the packet is not dropped by the next rule.
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
if err := ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, true, err)
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, true, err)
}
},
genPacket: genPacketV6,
@@ -187,7 +187,7 @@ func TestIPTablesStatsForInput(t *testing.T) {
filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{nicName}}
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
if err := ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, false, err)
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, false, err)
}
},
genPacket: genPacketV4,
@@ -207,7 +207,7 @@ func TestIPTablesStatsForInput(t *testing.T) {
filter.Rules[ruleIdx].Target = &stack.DropTarget{}
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
if err := ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, true, err)
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, true, err)
}
},
genPacket: genPacketV6,
@@ -227,7 +227,7 @@ func TestIPTablesStatsForInput(t *testing.T) {
filter.Rules[ruleIdx].Target = &stack.DropTarget{}
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
if err := ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, false, err)
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, false, err)
}
},
genPacket: genPacketV4,
@@ -250,7 +250,7 @@ func TestIPTablesStatsForInput(t *testing.T) {
filter.Rules[ruleIdx].Target = &stack.DropTarget{}
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
if err := ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, true, err)
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, true, err)
}
},
genPacket: genPacketV6,
@@ -273,7 +273,7 @@ func TestIPTablesStatsForInput(t *testing.T) {
filter.Rules[ruleIdx].Target = &stack.DropTarget{}
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
if err := ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, false, err)
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, false, err)
}
},
genPacket: genPacketV4,
@@ -293,7 +293,7 @@ func TestIPTablesStatsForInput(t *testing.T) {
filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{anotherNicName}}
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
if err := ipt.ReplaceTable(stack.FilterID, filter, true /* ipv6 */); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, true, err)
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, true, err)
}
},
genPacket: genPacketV6,
@@ -313,7 +313,7 @@ func TestIPTablesStatsForInput(t *testing.T) {
filter.Rules[ruleIdx].Matchers = []stack.Matcher{&inputIfNameMatcher{anotherNicName}}
filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{}
if err := ipt.ReplaceTable(stack.FilterID, filter, false /* ipv6 */); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, false, err)
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, false, err)
}
},
genPacket: genPacketV4,
@@ -465,7 +465,7 @@ func TestIPTableWritePackets(t *testing.T) {
}
if err := s.IPTables().ReplaceTable(stack.FilterID, table, false /* ipv4 */); err != nil {
- t.Fatalf("RelaceTable(%d, _, false): %s", stack.FilterID, err)
+ t.Fatalf("ReplaceTable(%d, _, false): %s", stack.FilterID, err)
}
},
genPacket: func(r *stack.Route) stack.PacketBufferList {
@@ -556,7 +556,7 @@ func TestIPTableWritePackets(t *testing.T) {
}
if err := s.IPTables().ReplaceTable(stack.FilterID, table, true /* ipv6 */); err != nil {
- t.Fatalf("RelaceTable(%d, _, true): %s", stack.FilterID, err)
+ t.Fatalf("ReplaceTable(%d, _, true): %s", stack.FilterID, err)
}
},
genPacket: func(r *stack.Route) stack.PacketBufferList {
@@ -681,6 +681,32 @@ func forwardedICMPv6EchoReplyChecker(t *testing.T, b []byte, src, dst tcpip.Addr
checker.ICMPv6Type(header.ICMPv6EchoReply)))
}
+func boolToInt(v bool) uint64 {
+ if v {
+ return 1
+ }
+ return 0
+}
+
+func setupDropFilter(hook stack.Hook, f stack.IPHeaderFilter) func(*testing.T, *stack.Stack, tcpip.NetworkProtocolNumber) {
+ return func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber) {
+ t.Helper()
+
+ ipv6 := netProto == ipv6.ProtocolNumber
+
+ ipt := s.IPTables()
+ filter := ipt.GetTable(stack.FilterID, ipv6)
+ ruleIdx := filter.BuiltinChains[hook]
+ filter.Rules[ruleIdx].Filter = f
+ filter.Rules[ruleIdx].Target = &stack.DropTarget{NetworkProtocol: netProto}
+ // Make sure the packet is not dropped by the next rule.
+ filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{NetworkProtocol: netProto}
+ if err := ipt.ReplaceTable(stack.FilterID, filter, ipv6); err != nil {
+ t.Fatalf("ipt.ReplaceTable(%d, _, %t): %s", stack.FilterID, ipv6, err)
+ }
+ }
+}
+
func TestForwardingHook(t *testing.T) {
const (
nicID1 = 1
@@ -740,32 +766,6 @@ func TestForwardingHook(t *testing.T) {
},
}
- setupDropFilter := func(f stack.IPHeaderFilter) func(*testing.T, *stack.Stack, tcpip.NetworkProtocolNumber) {
- return func(t *testing.T, s *stack.Stack, netProto tcpip.NetworkProtocolNumber) {
- t.Helper()
-
- ipv6 := netProto == ipv6.ProtocolNumber
-
- ipt := s.IPTables()
- filter := ipt.GetTable(stack.FilterID, ipv6)
- ruleIdx := filter.BuiltinChains[stack.Forward]
- filter.Rules[ruleIdx].Filter = f
- filter.Rules[ruleIdx].Target = &stack.DropTarget{NetworkProtocol: netProto}
- // Make sure the packet is not dropped by the next rule.
- filter.Rules[ruleIdx+1].Target = &stack.AcceptTarget{NetworkProtocol: netProto}
- if err := ipt.ReplaceTable(stack.FilterID, filter, ipv6); err != nil {
- t.Fatalf("ipt.RelaceTable(%d, _, %t): %s", stack.FilterID, ipv6, err)
- }
- }
- }
-
- boolToInt := func(v bool) uint64 {
- if v {
- return 1
- }
- return 0
- }
-
subTests := []struct {
name string
setupFilter func(*testing.T, *stack.Stack, tcpip.NetworkProtocolNumber)
@@ -779,59 +779,59 @@ func TestForwardingHook(t *testing.T) {
{
name: "Drop",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{}),
expectForward: false,
},
{
name: "Drop with input NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{InputInterface: nic1Name}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{InputInterface: nic1Name}),
expectForward: false,
},
{
name: "Drop with output NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{OutputInterface: nic2Name}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{OutputInterface: nic2Name}),
expectForward: false,
},
{
name: "Drop with input and output NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{InputInterface: nic1Name, OutputInterface: nic2Name}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{InputInterface: nic1Name, OutputInterface: nic2Name}),
expectForward: false,
},
{
name: "Drop with other input NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{InputInterface: otherNICName}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{InputInterface: otherNICName}),
expectForward: true,
},
{
name: "Drop with other output NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{OutputInterface: otherNICName}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{OutputInterface: otherNICName}),
expectForward: true,
},
{
name: "Drop with other input and output NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{InputInterface: otherNICName, OutputInterface: nic2Name}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{InputInterface: otherNICName, OutputInterface: nic2Name}),
expectForward: true,
},
{
name: "Drop with input and other output NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{InputInterface: nic1Name, OutputInterface: otherNICName}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{InputInterface: nic1Name, OutputInterface: otherNICName}),
expectForward: true,
},
{
name: "Drop with other input and other output NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{InputInterface: otherNICName, OutputInterface: otherNICName}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{InputInterface: otherNICName, OutputInterface: otherNICName}),
expectForward: true,
},
{
name: "Drop with inverted input NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{InputInterface: nic1Name, InputInterfaceInvert: true}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{InputInterface: nic1Name, InputInterfaceInvert: true}),
expectForward: true,
},
{
name: "Drop with inverted output NIC filtering",
- setupFilter: setupDropFilter(stack.IPHeaderFilter{OutputInterface: nic2Name, OutputInterfaceInvert: true}),
+ setupFilter: setupDropFilter(stack.Forward, stack.IPHeaderFilter{OutputInterface: nic2Name, OutputInterfaceInvert: true}),
expectForward: true,
},
}
@@ -941,3 +941,194 @@ func TestForwardingHook(t *testing.T) {
})
}
}
+
+func TestInputHookWithLocalForwarding(t *testing.T) {
+ const (
+ nicID1 = 1
+ nicID2 = 2
+
+ nic1Name = "nic1"
+ nic2Name = "nic2"
+
+ otherNICName = "otherNIC"
+ )
+
+ tests := []struct {
+ name string
+ netProto tcpip.NetworkProtocolNumber
+ rx func(*channel.Endpoint)
+ checker func(*testing.T, []byte)
+ }{
+ {
+ name: "IPv4",
+ netProto: ipv4.ProtocolNumber,
+ rx: func(e *channel.Endpoint) {
+ utils.RxICMPv4EchoRequest(e, utils.RemoteIPv4Addr, utils.Ipv4Addr2.AddressWithPrefix.Address, ttl)
+ },
+ checker: func(t *testing.T, b []byte) {
+ checker.IPv4(t, b,
+ checker.SrcAddr(utils.Ipv4Addr2.AddressWithPrefix.Address),
+ checker.DstAddr(utils.RemoteIPv4Addr),
+ checker.ICMPv4(
+ checker.ICMPv4Type(header.ICMPv4EchoReply)))
+ },
+ },
+ {
+ name: "IPv6",
+ netProto: ipv6.ProtocolNumber,
+ rx: func(e *channel.Endpoint) {
+ utils.RxICMPv6EchoRequest(e, utils.RemoteIPv6Addr, utils.Ipv6Addr2.AddressWithPrefix.Address, ttl)
+ },
+ checker: func(t *testing.T, b []byte) {
+ checker.IPv6(t, b,
+ checker.SrcAddr(utils.Ipv6Addr2.AddressWithPrefix.Address),
+ checker.DstAddr(utils.RemoteIPv6Addr),
+ checker.ICMPv6(
+ checker.ICMPv6Type(header.ICMPv6EchoReply)))
+ },
+ },
+ }
+
+ subTests := []struct {
+ name string
+ setupFilter func(*testing.T, *stack.Stack, tcpip.NetworkProtocolNumber)
+ expectDrop bool
+ }{
+ {
+ name: "Accept",
+ setupFilter: func(*testing.T, *stack.Stack, tcpip.NetworkProtocolNumber) { /* no filter */ },
+ expectDrop: false,
+ },
+
+ {
+ name: "Drop",
+ setupFilter: setupDropFilter(stack.Input, stack.IPHeaderFilter{}),
+ expectDrop: true,
+ },
+ {
+ name: "Drop with input NIC filtering on arrival NIC",
+ setupFilter: setupDropFilter(stack.Input, stack.IPHeaderFilter{InputInterface: nic1Name}),
+ expectDrop: true,
+ },
+ {
+ name: "Drop with input NIC filtering on delivered NIC",
+ setupFilter: setupDropFilter(stack.Input, stack.IPHeaderFilter{InputInterface: nic2Name}),
+ expectDrop: false,
+ },
+
+ {
+ name: "Drop with input NIC filtering on other NIC",
+ setupFilter: setupDropFilter(stack.Input, stack.IPHeaderFilter{InputInterface: otherNICName}),
+ expectDrop: false,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ for _, subTest := range subTests {
+ t.Run(subTest.name, func(t *testing.T) {
+ s := stack.New(stack.Options{
+ NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
+ })
+
+ subTest.setupFilter(t, s, test.netProto)
+
+ e1 := channel.New(1, header.IPv6MinimumMTU, "")
+ if err := s.CreateNICWithOptions(nicID1, e1, stack.NICOptions{Name: nic1Name}); err != nil {
+ t.Fatalf("s.CreateNICWithOptions(%d, _, _): %s", nicID1, err)
+ }
+ if err := s.AddProtocolAddress(nicID1, utils.Ipv4Addr1); err != nil {
+ t.Fatalf("s.AddProtocolAddress(%d, %#v): %s", nicID1, utils.Ipv4Addr1, err)
+ }
+ if err := s.AddProtocolAddress(nicID1, utils.Ipv6Addr1); err != nil {
+ t.Fatalf("s.AddProtocolAddress(%d, %#v): %s", nicID1, utils.Ipv6Addr1, err)
+ }
+
+ e2 := channel.New(1, header.IPv6MinimumMTU, "")
+ if err := s.CreateNICWithOptions(nicID2, e2, stack.NICOptions{Name: nic2Name}); err != nil {
+ t.Fatalf("s.CreateNICWithOptions(%d, _, _): %s", nicID2, err)
+ }
+ if err := s.AddProtocolAddress(nicID2, utils.Ipv4Addr2); err != nil {
+ t.Fatalf("s.AddProtocolAddress(%d, %#v): %s", nicID2, utils.Ipv4Addr2, err)
+ }
+ if err := s.AddProtocolAddress(nicID2, utils.Ipv6Addr2); err != nil {
+ t.Fatalf("s.AddProtocolAddress(%d, %#v): %s", nicID2, utils.Ipv6Addr2, err)
+ }
+
+ if err := s.SetForwardingDefaultAndAllNICs(ipv4.ProtocolNumber, true); err != nil {
+ t.Fatalf("s.SetForwardingDefaultAndAllNICs(%d, true): %s", ipv4.ProtocolNumber, err)
+ }
+ if err := s.SetForwardingDefaultAndAllNICs(ipv6.ProtocolNumber, true); err != nil {
+ t.Fatalf("s.SetForwardingDefaultAndAllNICs(%d, true): %s", ipv6.ProtocolNumber, err)
+ }
+
+ s.SetRouteTable([]tcpip.Route{
+ {
+ Destination: header.IPv4EmptySubnet,
+ NIC: nicID1,
+ },
+ {
+ Destination: header.IPv6EmptySubnet,
+ NIC: nicID1,
+ },
+ })
+
+ test.rx(e1)
+
+ ep1, err := s.GetNetworkEndpoint(nicID1, test.netProto)
+ if err != nil {
+ t.Fatalf("s.GetNetworkEndpoint(%d, %d): %s", nicID1, test.netProto, err)
+ }
+ ep1Stats := ep1.Stats()
+ ipEP1Stats, ok := ep1Stats.(stack.IPNetworkEndpointStats)
+ if !ok {
+ t.Fatalf("got ep1Stats = %T, want = stack.IPNetworkEndpointStats", ep1Stats)
+ }
+ ip1Stats := ipEP1Stats.IPStats()
+
+ if got := ip1Stats.PacketsReceived.Value(); got != 1 {
+ t.Errorf("got ip1Stats.PacketsReceived.Value() = %d, want = 1", got)
+ }
+ if got := ip1Stats.ValidPacketsReceived.Value(); got != 1 {
+ t.Errorf("got ip1Stats.ValidPacketsReceived.Value() = %d, want = 1", got)
+ }
+ if got, want := ip1Stats.PacketsSent.Value(), boolToInt(!subTest.expectDrop); got != want {
+ t.Errorf("got ip1Stats.PacketsSent.Value() = %d, want = %d", got, want)
+ }
+
+ ep2, err := s.GetNetworkEndpoint(nicID2, test.netProto)
+ if err != nil {
+ t.Fatalf("s.GetNetworkEndpoint(%d, %d): %s", nicID2, test.netProto, err)
+ }
+ ep2Stats := ep2.Stats()
+ ipEP2Stats, ok := ep2Stats.(stack.IPNetworkEndpointStats)
+ if !ok {
+ t.Fatalf("got ep2Stats = %T, want = stack.IPNetworkEndpointStats", ep2Stats)
+ }
+ ip2Stats := ipEP2Stats.IPStats()
+ if got := ip2Stats.PacketsReceived.Value(); got != 0 {
+ t.Errorf("got ip2Stats.PacketsReceived.Value() = %d, want = 0", got)
+ }
+ if got := ip2Stats.ValidPacketsReceived.Value(); got != 1 {
+ t.Errorf("got ip2Stats.ValidPacketsReceived.Value() = %d, want = 1", got)
+ }
+ if got, want := ip2Stats.IPTablesInputDropped.Value(), boolToInt(subTest.expectDrop); got != want {
+ t.Errorf("got ip2Stats.IPTablesInputDropped.Value() = %d, want = %d", got, want)
+ }
+ if got := ip2Stats.PacketsSent.Value(); got != 0 {
+ t.Errorf("got ip2Stats.PacketsSent.Value() = %d, want = 0", got)
+ }
+
+ if p, ok := e1.Read(); ok == subTest.expectDrop {
+ t.Errorf("got e1.Read() = (%#v, %t), want = (_, %t)", p, ok, !subTest.expectDrop)
+ } else if !subTest.expectDrop {
+ test.checker(t, stack.PayloadSince(p.Pkt.NetworkHeader()))
+ }
+ if p, ok := e2.Read(); ok {
+ t.Errorf("got e1.Read() = (%#v, true), want = (_, false)", p)
+ }
+ })
+ }
+ })
+ }
+}
diff --git a/pkg/tcpip/tests/integration/loopback_test.go b/pkg/tcpip/tests/integration/loopback_test.go
index 87d36e1dd..b2008f0b2 100644
--- a/pkg/tcpip/tests/integration/loopback_test.go
+++ b/pkg/tcpip/tests/integration/loopback_test.go
@@ -44,20 +44,17 @@ type ndpDispatcher struct{}
func (*ndpDispatcher) OnDuplicateAddressDetectionResult(tcpip.NICID, tcpip.Address, stack.DADResult) {
}
-func (*ndpDispatcher) OnDefaultRouterDiscovered(tcpip.NICID, tcpip.Address) bool {
- return false
+func (*ndpDispatcher) OnOffLinkRouteUpdated(tcpip.NICID, tcpip.Subnet, tcpip.Address, header.NDPRoutePreference) {
}
-func (*ndpDispatcher) OnDefaultRouterInvalidated(tcpip.NICID, tcpip.Address) {}
+func (*ndpDispatcher) OnOffLinkRouteInvalidated(tcpip.NICID, tcpip.Subnet, tcpip.Address) {}
-func (*ndpDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) bool {
- return false
+func (*ndpDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) {
}
func (*ndpDispatcher) OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet) {}
-func (*ndpDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) bool {
- return true
+func (*ndpDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) {
}
func (*ndpDispatcher) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) {}
diff --git a/pkg/tcpip/transport/icmp/endpoint.go b/pkg/tcpip/transport/icmp/endpoint.go
index fb77febcf..f9a15efb2 100644
--- a/pkg/tcpip/transport/icmp/endpoint.go
+++ b/pkg/tcpip/transport/icmp/endpoint.go
@@ -213,6 +213,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
// reacquire the mutex in exclusive mode.
//
// Returns true for retry if preparation should be retried.
+// +checklocks:e.mu
func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err tcpip.Error) {
switch e.state {
case stateInitial:
@@ -229,10 +230,8 @@ func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err tcpip
}
e.mu.RUnlock()
- defer e.mu.RLock()
-
e.mu.Lock()
- defer e.mu.Unlock()
+ defer e.mu.DowngradeLock()
// The state changed when we released the shared locked and re-acquired
// it in exclusive mode. Try again.
@@ -758,8 +757,6 @@ func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketB
switch e.NetProto {
case header.IPv4ProtocolNumber:
h := header.ICMPv4(pkt.TransportHeader().View())
- // TODO(gvisor.dev/issue/170): Determine if len(h) check is still needed
- // after early parsing.
if len(h) < header.ICMPv4MinimumSize || h.Type() != header.ICMPv4EchoReply {
e.stack.Stats().DroppedPackets.Increment()
e.stats.ReceiveErrors.MalformedPacketsReceived.Increment()
@@ -767,8 +764,6 @@ func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketB
}
case header.IPv6ProtocolNumber:
h := header.ICMPv6(pkt.TransportHeader().View())
- // TODO(gvisor.dev/issue/170): Determine if len(h) check is still needed
- // after early parsing.
if len(h) < header.ICMPv6MinimumSize || h.Type() != header.ICMPv6EchoReply {
e.stack.Stats().DroppedPackets.Increment()
e.stats.ReceiveErrors.MalformedPacketsReceived.Increment()
diff --git a/pkg/tcpip/transport/icmp/protocol.go b/pkg/tcpip/transport/icmp/protocol.go
index 47f7dd1cb..fa82affc1 100644
--- a/pkg/tcpip/transport/icmp/protocol.go
+++ b/pkg/tcpip/transport/icmp/protocol.go
@@ -123,8 +123,6 @@ func (*protocol) Wait() {}
// Parse implements stack.TransportProtocol.Parse.
func (*protocol) Parse(pkt *stack.PacketBuffer) bool {
- // TODO(gvisor.dev/issue/170): Implement parsing of ICMP.
- //
// Right now, the Parse() method is tied to enabled protocols passed into
// stack.New. This works for UDP and TCP, but we handle ICMP traffic even
// when netstack users don't pass ICMP as a supported protocol.
diff --git a/pkg/tcpip/transport/packet/endpoint.go b/pkg/tcpip/transport/packet/endpoint.go
index cd8c99d41..8e7bb6c6e 100644
--- a/pkg/tcpip/transport/packet/endpoint.go
+++ b/pkg/tcpip/transport/packet/endpoint.go
@@ -208,7 +208,6 @@ func (ep *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResul
}
func (*endpoint) Write(tcpip.Payloader, tcpip.WriteOptions) (int64, tcpip.Error) {
- // TODO(gvisor.dev/issue/173): Implement.
return 0, &tcpip.ErrInvalidOptionValue{}
}
@@ -244,8 +243,6 @@ func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpi
// Bind implements tcpip.Endpoint.Bind.
func (ep *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
- // TODO(gvisor.dev/issue/173): Add Bind support.
-
// "By default, all packets of the specified protocol type are passed
// to a packet socket. To get packets only from a specific interface
// use bind(2) specifying an address in a struct sockaddr_ll to bind
@@ -385,7 +382,6 @@ func (ep *endpoint) HandlePacket(nicID tcpip.NICID, localAddr tcpip.LinkAddress,
// Push new packet into receive list and increment the buffer size.
var packet packet
- // TODO(gvisor.dev/issue/173): Return network protocol.
if !pkt.LinkHeader().View().IsEmpty() {
// Get info directly from the ethernet header.
hdr := header.Ethernet(pkt.LinkHeader().View())
@@ -424,7 +420,6 @@ func (ep *endpoint) HandlePacket(nicID tcpip.NICID, localAddr tcpip.LinkAddress,
default:
panic(fmt.Sprintf("unexpected PktType in pkt: %+v", pkt))
}
-
} else {
// Raw packets need their ethernet headers prepended before
// queueing.
diff --git a/pkg/tcpip/transport/raw/endpoint.go b/pkg/tcpip/transport/raw/endpoint.go
index 1bce2769a..b6687911a 100644
--- a/pkg/tcpip/transport/raw/endpoint.go
+++ b/pkg/tcpip/transport/raw/endpoint.go
@@ -286,26 +286,6 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcp
return nil, nil, nil, &tcpip.ErrBadBuffer{}
}
- // If this is an unassociated socket and callee provided a nonzero
- // destination address, route using that address.
- if e.ops.GetHeaderIncluded() {
- ip := header.IPv4(payloadBytes)
- if !ip.IsValid(len(payloadBytes)) {
- return nil, nil, nil, &tcpip.ErrInvalidOptionValue{}
- }
- dstAddr := ip.DestinationAddress()
- // Update dstAddr with the address in the IP header, unless
- // opts.To is set (e.g. if sendto specifies a specific
- // address).
- if dstAddr != tcpip.Address([]byte{0, 0, 0, 0}) && opts.To == nil {
- opts.To = &tcpip.FullAddress{
- NIC: 0, // NIC is unset.
- Addr: dstAddr, // The address from the payload.
- Port: 0, // There are no ports here.
- }
- }
- }
-
// Did the user caller provide a destination? If not, use the connected
// destination.
if opts.To == nil {
diff --git a/pkg/tcpip/transport/tcp/accept.go b/pkg/tcpip/transport/tcp/accept.go
index 2c65b737d..aa413ad05 100644
--- a/pkg/tcpip/transport/tcp/accept.go
+++ b/pkg/tcpip/transport/tcp/accept.go
@@ -330,7 +330,9 @@ func (l *listenContext) performHandshake(s *segment, opts *header.TCPSynOptions,
}
ep := h.ep
- if err := h.complete(); err != nil {
+ // N.B. the endpoint is generated above by startHandshake, and will be
+ // returned locked. This first call is forced.
+ if err := h.complete(); err != nil { // +checklocksforce
ep.stack.Stats().TCP.FailedConnectionAttempts.Increment()
ep.stats.FailedConnectionAttempts.Increment()
l.cleanupFailedHandshake(h)
@@ -364,6 +366,7 @@ func (l *listenContext) closeAllPendingEndpoints() {
}
// Precondition: h.ep.mu must be held.
+// +checklocks:h.ep.mu
func (l *listenContext) cleanupFailedHandshake(h *handshake) {
e := h.ep
e.mu.Unlock()
@@ -504,7 +507,9 @@ func (e *endpoint) handleSynSegment(ctx *listenContext, s *segment, opts *header
}
go func() {
- if err := h.complete(); err != nil {
+ // Note that startHandshake returns a locked endpoint. The
+ // force call here just makes it so.
+ if err := h.complete(); err != nil { // +checklocksforce
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
e.stats.FailedConnectionAttempts.Increment()
ctx.cleanupFailedHandshake(h)
@@ -560,6 +565,10 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Err
}
switch {
+ case s.flags.Contains(header.TCPFlagRst):
+ e.stack.Stats().DroppedPackets.Increment()
+ return nil
+
case s.flags == header.TCPFlagSyn:
if e.acceptQueueIsFull() {
e.stack.Stats().TCP.ListenOverflowSynDrop.Increment()
@@ -611,7 +620,7 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Err
e.stack.Stats().TCP.ListenOverflowSynCookieSent.Increment()
return nil
- case (s.flags & header.TCPFlagAck) != 0:
+ case s.flags.Contains(header.TCPFlagAck):
if e.acceptQueueIsFull() {
// Silently drop the ack as the application can't accept
// the connection at this point. The ack will be
@@ -736,6 +745,13 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Err
mss: rcvdSynOptions.MSS,
})
+ // Requeue the segment if the ACK completing the handshake has more info
+ // to be procesed by the newly established endpoint.
+ if (s.flags.Contains(header.TCPFlagFin) || s.data.Size() > 0) && n.enqueueSegment(s) {
+ s.incRef()
+ n.newSegmentWaker.Assert()
+ }
+
// Do the delivery in a separate goroutine so
// that we don't block the listen loop in case
// the application is slow to accept or stops
@@ -753,6 +769,7 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Err
return nil
default:
+ e.stack.Stats().DroppedPackets.Increment()
return nil
}
}
diff --git a/pkg/tcpip/transport/tcp/connect.go b/pkg/tcpip/transport/tcp/connect.go
index 570e5081c..93ed161f9 100644
--- a/pkg/tcpip/transport/tcp/connect.go
+++ b/pkg/tcpip/transport/tcp/connect.go
@@ -406,11 +406,11 @@ func (h *handshake) synRcvdState(s *segment) tcpip.Error {
h.ep.transitionToStateEstablishedLocked(h)
- // If the segment has data then requeue it for the receiver
- // to process it again once main loop is started.
- if s.data.Size() > 0 {
+ // Requeue the segment if the ACK completing the handshake has more info
+ // to be procesed by the newly established endpoint.
+ if (s.flags.Contains(header.TCPFlagFin) || s.data.Size() > 0) && h.ep.enqueueSegment(s) {
s.incRef()
- h.ep.enqueueSegment(s)
+ h.ep.newSegmentWaker.Assert()
}
return nil
}
@@ -511,6 +511,7 @@ func (h *handshake) start() {
}
// complete completes the TCP 3-way handshake initiated by h.start().
+// +checklocks:h.ep.mu
func (h *handshake) complete() tcpip.Error {
// Set up the wakers.
var s sleep.Sleeper
@@ -909,30 +910,13 @@ func (e *endpoint) sendRaw(data buffer.VectorisedView, flags header.TCPFlags, se
return err
}
-func (e *endpoint) handleWrite() {
- e.sndQueueInfo.sndQueueMu.Lock()
- next := e.drainSendQueueLocked()
- e.sndQueueInfo.sndQueueMu.Unlock()
-
- e.sendData(next)
-}
-
-// Move packets from send queue to send list.
-//
-// Precondition: e.sndBufMu must be locked.
-func (e *endpoint) drainSendQueueLocked() *segment {
- first := e.sndQueueInfo.sndQueue.Front()
- if first != nil {
- e.snd.writeList.PushBackList(&e.sndQueueInfo.sndQueue)
- e.sndQueueInfo.SndBufInQueue = 0
- }
- return first
-}
-
// Precondition: e.mu must be locked.
func (e *endpoint) sendData(next *segment) {
// Initialize the next segment to write if it's currently nil.
if e.snd.writeNext == nil {
+ if next == nil {
+ return
+ }
e.snd.writeNext = next
}
@@ -940,17 +924,6 @@ func (e *endpoint) sendData(next *segment) {
e.snd.sendData()
}
-func (e *endpoint) handleClose() {
- if !e.EndpointState().connected() {
- return
- }
- // Drain the send queue.
- e.handleWrite()
-
- // Mark send side as closed.
- e.snd.Closed = true
-}
-
// resetConnectionLocked puts the endpoint in an error state with the given
// error code and sends a RST if and only if the error is not ErrConnectionReset
// indicating that the connection is being reset due to receiving a RST. This
@@ -1130,7 +1103,7 @@ func (e *endpoint) handleReset(s *segment) (ok bool, err tcpip.Error) {
func (e *endpoint) handleSegmentsLocked(fastPath bool) tcpip.Error {
checkRequeue := true
for i := 0; i < maxSegmentsPerWake; i++ {
- if e.EndpointState().closed() {
+ if state := e.EndpointState(); state.closed() || state == StateTimeWait {
return nil
}
s := e.segmentQueue.dequeue()
@@ -1311,42 +1284,45 @@ func (e *endpoint) disableKeepaliveTimer() {
e.keepalive.Unlock()
}
-// protocolMainLoop is the main loop of the TCP protocol. It runs in its own
-// goroutine and is responsible for sending segments and handling received
-// segments.
-func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{}) tcpip.Error {
- e.mu.Lock()
- var closeTimer tcpip.Timer
- var closeWaker sleep.Waker
-
- epilogue := func() {
- // e.mu is expected to be hold upon entering this section.
- if e.snd != nil {
- e.snd.resendTimer.cleanup()
- e.snd.probeTimer.cleanup()
- e.snd.reorderTimer.cleanup()
- }
+// protocolMainLoopDone is called at the end of protocolMainLoop.
+// +checklocksrelease:e.mu
+func (e *endpoint) protocolMainLoopDone(closeTimer tcpip.Timer, closeWaker *sleep.Waker) {
+ if e.snd != nil {
+ e.snd.resendTimer.cleanup()
+ e.snd.probeTimer.cleanup()
+ e.snd.reorderTimer.cleanup()
+ }
- if closeTimer != nil {
- closeTimer.Stop()
- }
+ if closeTimer != nil {
+ closeTimer.Stop()
+ }
- e.completeWorkerLocked()
+ e.completeWorkerLocked()
- if e.drainDone != nil {
- close(e.drainDone)
- }
+ if e.drainDone != nil {
+ close(e.drainDone)
+ }
- e.mu.Unlock()
+ e.mu.Unlock()
- e.drainClosingSegmentQueue()
+ e.drainClosingSegmentQueue()
- // When the protocol loop exits we should wake up our waiters.
- e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
- }
+ // When the protocol loop exits we should wake up our waiters.
+ e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
+}
+// protocolMainLoop is the main loop of the TCP protocol. It runs in its own
+// goroutine and is responsible for sending segments and handling received
+// segments.
+func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{}) tcpip.Error {
+ var (
+ closeTimer tcpip.Timer
+ closeWaker sleep.Waker
+ )
+
+ e.mu.Lock()
if handshake {
- if err := e.h.complete(); err != nil {
+ if err := e.h.complete(); err != nil { // +checklocksforce
e.lastErrorMu.Lock()
e.lastError = err
e.lastErrorMu.Unlock()
@@ -1355,8 +1331,7 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
e.hardError = err
e.workerCleanup = true
- // Lock released below.
- epilogue()
+ e.protocolMainLoopDone(closeTimer, &closeWaker)
return err
}
}
@@ -1402,14 +1377,7 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
{
w: &e.sndQueueInfo.sndWaker,
f: func() tcpip.Error {
- e.handleWrite()
- return nil
- },
- },
- {
- w: &e.sndQueueInfo.sndCloseWaker,
- f: func() tcpip.Error {
- e.handleClose()
+ e.sendData(nil /* next */)
return nil
},
},
@@ -1474,11 +1442,19 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
return &tcpip.ErrConnectionReset{}
}
- if n&notifyClose != 0 && closeTimer == nil {
- if e.EndpointState() == StateFinWait2 && e.closed {
+ if n&notifyClose != 0 && e.closed {
+ switch e.EndpointState() {
+ case StateEstablished:
+ // Perform full shutdown if the endpoint is still
+ // established. This can occur when notifyClose
+ // was asserted just before becoming established.
+ e.shutdownLocked(tcpip.ShutdownWrite | tcpip.ShutdownRead)
+ case StateFinWait2:
// The socket has been closed and we are in FIN_WAIT2
// so start the FIN_WAIT2 timer.
- closeTimer = e.stack.Clock().AfterFunc(e.tcpLingerTimeout, closeWaker.Assert)
+ if closeTimer == nil {
+ closeTimer = e.stack.Clock().AfterFunc(e.tcpLingerTimeout, closeWaker.Assert)
+ }
}
}
@@ -1499,7 +1475,7 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
// Only block the worker if the endpoint
// is not in closed state or error state.
close(e.drainDone)
- e.mu.Unlock()
+ e.mu.Unlock() // +checklocksforce
<-e.undrain
e.mu.Lock()
}
@@ -1560,8 +1536,6 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
if err != nil {
e.resetConnectionLocked(err)
}
- // Lock released below.
- epilogue()
}
loop:
@@ -1585,6 +1559,7 @@ loop:
// just want to terminate the loop and cleanup the
// endpoint.
cleanupOnError(nil)
+ e.protocolMainLoopDone(closeTimer, &closeWaker)
return nil
case StateTimeWait:
fallthrough
@@ -1593,6 +1568,7 @@ loop:
default:
if err := funcs[v].f(); err != nil {
cleanupOnError(err)
+ e.protocolMainLoopDone(closeTimer, &closeWaker)
return nil
}
}
@@ -1616,13 +1592,13 @@ loop:
// Handle any StateError transition from StateTimeWait.
if e.EndpointState() == StateError {
cleanupOnError(nil)
+ e.protocolMainLoopDone(closeTimer, &closeWaker)
return nil
}
e.transitionToStateCloseLocked()
- // Lock released below.
- epilogue()
+ e.protocolMainLoopDone(closeTimer, &closeWaker)
// A new SYN was received during TIME_WAIT and we need to abort
// the timewait and redirect the segment to the listener queue
@@ -1692,6 +1668,7 @@ func (e *endpoint) handleTimeWaitSegments() (extendTimeWait bool, reuseTW func()
// should be executed after releasing the endpoint registrations. This is
// done in cases where a new SYN is received during TIME_WAIT that carries
// a sequence number larger than one see on the connection.
+// +checklocks:e.mu
func (e *endpoint) doTimeWait() (twReuse func()) {
// Trigger a 2 * MSL time wait state. During this period
// we will drop all incoming segments.
diff --git a/pkg/tcpip/transport/tcp/dispatcher.go b/pkg/tcpip/transport/tcp/dispatcher.go
index dff7cb89c..7d110516b 100644
--- a/pkg/tcpip/transport/tcp/dispatcher.go
+++ b/pkg/tcpip/transport/tcp/dispatcher.go
@@ -127,7 +127,7 @@ func (p *processor) start(wg *sync.WaitGroup) {
case !ep.segmentQueue.empty():
p.epQ.enqueue(ep)
}
- ep.mu.Unlock()
+ ep.mu.Unlock() // +checklocksforce
} else {
ep.newSegmentWaker.Assert()
}
diff --git a/pkg/tcpip/transport/tcp/endpoint.go b/pkg/tcpip/transport/tcp/endpoint.go
index a27e2110b..1ed4ba419 100644
--- a/pkg/tcpip/transport/tcp/endpoint.go
+++ b/pkg/tcpip/transport/tcp/endpoint.go
@@ -293,16 +293,9 @@ type sndQueueInfo struct {
sndQueueMu sync.Mutex `state:"nosave"`
stack.TCPSndBufState
- // sndQueue holds segments that are ready to be sent.
- sndQueue segmentList `state:"wait"`
-
- // sndWaker is used to signal the protocol goroutine when segments are
- // added to the `sndQueue`.
+ // sndWaker is used to signal the protocol goroutine when there may be
+ // segments that need to be sent.
sndWaker sleep.Waker `state:"manual"`
-
- // sndCloseWaker is used to notify the protocol goroutine when the send
- // side is closed.
- sndCloseWaker sleep.Waker `state:"manual"`
}
// rcvQueueInfo contains the endpoint's rcvQueue and associated metadata.
@@ -671,6 +664,7 @@ func calculateAdvertisedMSS(userMSS uint16, r *stack.Route) uint16 {
// The assumption behind spinning here being that background packet processing
// should not be holding the lock for long and spinning reduces latency as we
// avoid an expensive sleep/wakeup of of the syscall goroutine).
+// +checklocksacquire:e.mu
func (e *endpoint) LockUser() {
for {
// Try first if the sock is locked then check if it's owned
@@ -690,7 +684,7 @@ func (e *endpoint) LockUser() {
continue
}
atomic.StoreUint32(&e.ownedByUser, 1)
- return
+ return // +checklocksforce
}
}
@@ -707,7 +701,7 @@ func (e *endpoint) LockUser() {
// protocol goroutine altogether.
//
// Precondition: e.LockUser() must have been called before calling e.UnlockUser()
-// +checklocks:e.mu
+// +checklocksrelease:e.mu
func (e *endpoint) UnlockUser() {
// Lock segment queue before checking so that we avoid a race where
// segments can be queued between the time we check if queue is empty
@@ -743,12 +737,13 @@ func (e *endpoint) UnlockUser() {
}
// StopWork halts packet processing. Only to be used in tests.
+// +checklocksacquire:e.mu
func (e *endpoint) StopWork() {
e.mu.Lock()
}
// ResumeWork resumes packet processing. Only to be used in tests.
-// +checklocks:e.mu
+// +checklocksrelease:e.mu
func (e *endpoint) ResumeWork() {
e.mu.Unlock()
}
@@ -1487,87 +1482,95 @@ func (e *endpoint) isEndpointWritableLocked() (int, tcpip.Error) {
return avail, nil
}
-// Write writes data to the endpoint's peer.
-func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
- // Linux completely ignores any address passed to sendto(2) for TCP sockets
- // (without the MSG_FASTOPEN flag). Corking is unimplemented, so opts.More
- // and opts.EndOfRecord are also ignored.
+// readFromPayloader reads a slice from the Payloader.
+// +checklocks:e.mu
+// +checklocks:e.sndQueueInfo.sndQueueMu
+func (e *endpoint) readFromPayloader(p tcpip.Payloader, opts tcpip.WriteOptions, avail int) ([]byte, tcpip.Error) {
+ // We can release locks while copying data.
+ //
+ // This is not possible if atomic is set, because we can't allow the
+ // available buffer space to be consumed by some other caller while we
+ // are copying data in.
+ if !opts.Atomic {
+ e.sndQueueInfo.sndQueueMu.Unlock()
+ defer e.sndQueueInfo.sndQueueMu.Lock()
- e.LockUser()
- defer e.UnlockUser()
+ e.UnlockUser()
+ defer e.LockUser()
+ }
- nextSeg, n, err := func() (*segment, int, tcpip.Error) {
- e.sndQueueInfo.sndQueueMu.Lock()
- defer e.sndQueueInfo.sndQueueMu.Unlock()
+ // Fetch data.
+ if l := p.Len(); l < avail {
+ avail = l
+ }
+ if avail == 0 {
+ return nil, nil
+ }
+ v := make([]byte, avail)
+ n, err := p.Read(v)
+ if err != nil && err != io.EOF {
+ return nil, &tcpip.ErrBadBuffer{}
+ }
+ return v[:n], nil
+}
+// queueSegment reads data from the payloader and returns a segment to be sent.
+// +checklocks:e.mu
+func (e *endpoint) queueSegment(p tcpip.Payloader, opts tcpip.WriteOptions) (*segment, int, tcpip.Error) {
+ e.sndQueueInfo.sndQueueMu.Lock()
+ defer e.sndQueueInfo.sndQueueMu.Unlock()
+
+ avail, err := e.isEndpointWritableLocked()
+ if err != nil {
+ e.stats.WriteErrors.WriteClosed.Increment()
+ return nil, 0, err
+ }
+
+ v, err := e.readFromPayloader(p, opts, avail)
+ if err != nil {
+ return nil, 0, err
+ }
+ if !opts.Atomic {
+ // Since we released locks in between it's possible that the
+ // endpoint transitioned to a CLOSED/ERROR states so make
+ // sure endpoint is still writable before trying to write.
avail, err := e.isEndpointWritableLocked()
if err != nil {
e.stats.WriteErrors.WriteClosed.Increment()
return nil, 0, err
}
- v, err := func() ([]byte, tcpip.Error) {
- // We can release locks while copying data.
- //
- // This is not possible if atomic is set, because we can't allow the
- // available buffer space to be consumed by some other caller while we
- // are copying data in.
- if !opts.Atomic {
- e.sndQueueInfo.sndQueueMu.Unlock()
- defer e.sndQueueInfo.sndQueueMu.Lock()
-
- e.UnlockUser()
- defer e.LockUser()
- }
-
- // Fetch data.
- if l := p.Len(); l < avail {
- avail = l
- }
- if avail == 0 {
- return nil, nil
- }
- v := make([]byte, avail)
- n, err := p.Read(v)
- if err != nil && err != io.EOF {
- return nil, &tcpip.ErrBadBuffer{}
- }
- return v[:n], nil
- }()
- if len(v) == 0 || err != nil {
- return nil, 0, err
+ // Discard any excess data copied in due to avail being reduced due
+ // to a simultaneous write call to the socket.
+ if avail < len(v) {
+ v = v[:avail]
}
+ }
- if !opts.Atomic {
- // Since we released locks in between it's possible that the
- // endpoint transitioned to a CLOSED/ERROR states so make
- // sure endpoint is still writable before trying to write.
- avail, err := e.isEndpointWritableLocked()
- if err != nil {
- e.stats.WriteErrors.WriteClosed.Increment()
- return nil, 0, err
- }
+ // Add data to the send queue.
+ s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), v)
+ e.sndQueueInfo.SndBufUsed += len(v)
+ e.snd.writeList.PushBack(s)
- // Discard any excess data copied in due to avail being reduced due
- // to a simultaneous write call to the socket.
- if avail < len(v) {
- v = v[:avail]
- }
- }
+ return s, len(v), nil
+}
+
+// Write writes data to the endpoint's peer.
+func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
+ // Linux completely ignores any address passed to sendto(2) for TCP sockets
+ // (without the MSG_FASTOPEN flag). Corking is unimplemented, so opts.More
+ // and opts.EndOfRecord are also ignored.
- // Add data to the send queue.
- s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), v)
- e.sndQueueInfo.SndBufUsed += len(v)
- e.sndQueueInfo.SndBufInQueue += seqnum.Size(len(v))
- e.sndQueueInfo.sndQueue.PushBack(s)
+ e.LockUser()
+ defer e.UnlockUser()
- return e.drainSendQueueLocked(), len(v), nil
- }()
// Return if either we didn't queue anything or if an error occurred while
// attempting to queue data.
+ nextSeg, n, err := e.queueSegment(p, opts)
if n == 0 || err != nil {
return 0, err
}
+
e.sendData(nextSeg)
return int64(n), nil
}
@@ -2314,7 +2317,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) tcp
// connection setting here.
if !handshake {
e.segmentQueue.mu.Lock()
- for _, l := range []segmentList{e.segmentQueue.list, e.sndQueueInfo.sndQueue, e.snd.writeList} {
+ for _, l := range []segmentList{e.segmentQueue.list, e.snd.writeList} {
for s := l.Front(); s != nil; s = s.Next() {
s.id = e.TransportEndpointInfo.ID
e.sndQueueInfo.sndWaker.Assert()
@@ -2372,6 +2375,9 @@ func (e *endpoint) shutdownLocked(flags tcpip.ShutdownFlags) tcpip.Error {
e.notifyProtocolGoroutine(notifyTickleWorker)
return nil
}
+ // Wake up any readers that maybe waiting for the stream to become
+ // readable.
+ e.waiterQueue.Notify(waiter.ReadableEvents)
}
// Close for write.
@@ -2388,12 +2394,20 @@ func (e *endpoint) shutdownLocked(flags tcpip.ShutdownFlags) tcpip.Error {
// Queue fin segment.
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), nil)
- e.sndQueueInfo.sndQueue.PushBack(s)
- e.sndQueueInfo.SndBufInQueue++
+ e.snd.writeList.PushBack(s)
// Mark endpoint as closed.
e.sndQueueInfo.SndClosed = true
e.sndQueueInfo.sndQueueMu.Unlock()
- e.handleClose()
+
+ // Drain the send queue.
+ e.sendData(s)
+
+ // Mark send side as closed.
+ e.snd.Closed = true
+
+ // Wake up any writers that maybe waiting for the stream to become
+ // writable.
+ e.waiterQueue.Notify(waiter.WritableEvents)
}
return nil
@@ -2501,6 +2515,7 @@ func (e *endpoint) listen(backlog int) tcpip.Error {
// startAcceptedLoop sets up required state and starts a goroutine with the
// main loop for accepted connections.
+// +checklocksrelease:e.mu
func (e *endpoint) startAcceptedLoop() {
e.workerRunning = true
e.mu.Unlock()
diff --git a/pkg/tcpip/transport/tcp/forwarder.go b/pkg/tcpip/transport/tcp/forwarder.go
index 65c86823a..2e709ed78 100644
--- a/pkg/tcpip/transport/tcp/forwarder.go
+++ b/pkg/tcpip/transport/tcp/forwarder.go
@@ -164,8 +164,9 @@ func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint,
return nil, err
}
- // Start the protocol goroutine.
- ep.startAcceptedLoop()
+ // Start the protocol goroutine. Note that the endpoint is returned
+ // from performHandshake locked.
+ ep.startAcceptedLoop() // +checklocksforce
return ep, nil
}
diff --git a/pkg/tcpip/transport/tcp/rcv.go b/pkg/tcpip/transport/tcp/rcv.go
index 661ca604a..9ce8fcae9 100644
--- a/pkg/tcpip/transport/tcp/rcv.go
+++ b/pkg/tcpip/transport/tcp/rcv.go
@@ -559,7 +559,6 @@ func (r *receiver) handleTimeWaitSegment(s *segment) (resetTimeWait bool, newSyn
// (2) returns to TIME-WAIT state if the SYN turns out
// to be an old duplicate".
if s.flags.Contains(header.TCPFlagSyn) && r.RcvNxt.LessThan(segSeq) {
-
return false, true
}
diff --git a/pkg/tcpip/transport/tcp/tcp_test.go b/pkg/tcpip/transport/tcp/tcp_test.go
index e7ede7662..71c4aa85d 100644
--- a/pkg/tcpip/transport/tcp/tcp_test.go
+++ b/pkg/tcpip/transport/tcp/tcp_test.go
@@ -3451,17 +3451,13 @@ loop:
for {
switch _, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{}); err.(type) {
case *tcpip.ErrWouldBlock:
- select {
- case <-ch:
- // Expect the state to be StateError and subsequent Reads to fail with HardError.
- _, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{})
- if d := cmp.Diff(&tcpip.ErrConnectionReset{}, err); d != "" {
- t.Fatalf("c.EP.Read() mismatch (-want +got):\n%s", d)
- }
- break loop
- case <-time.After(1 * time.Second):
- t.Fatalf("Timed out waiting for reset to arrive")
+ <-ch
+ // Expect the state to be StateError and subsequent Reads to fail with HardError.
+ _, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{})
+ if d := cmp.Diff(&tcpip.ErrConnectionReset{}, err); d != "" {
+ t.Fatalf("c.EP.Read() mismatch (-want +got):\n%s", d)
}
+ break loop
case *tcpip.ErrConnectionReset:
break loop
default:
@@ -3472,14 +3468,27 @@ loop:
if tcp.EndpointState(c.EP.State()) != tcp.StateError {
t.Fatalf("got EP state is not StateError")
}
- if got := c.Stack().Stats().TCP.EstablishedResets.Value(); got != 1 {
- t.Errorf("got stats.TCP.EstablishedResets.Value() = %d, want = 1", got)
+
+ checkValid := func() []error {
+ var errors []error
+ if got := c.Stack().Stats().TCP.EstablishedResets.Value(); got != 1 {
+ errors = append(errors, fmt.Errorf("got stats.TCP.EstablishedResets.Value() = %d, want = 1", got))
+ }
+ if got := c.Stack().Stats().TCP.CurrentEstablished.Value(); got != 0 {
+ errors = append(errors, fmt.Errorf("got stats.TCP.CurrentEstablished.Value() = %d, want = 0", got))
+ }
+ if got := c.Stack().Stats().TCP.CurrentConnected.Value(); got != 0 {
+ errors = append(errors, fmt.Errorf("got stats.TCP.CurrentConnected.Value() = %d, want = 0", got))
+ }
+ return errors
}
- if got := c.Stack().Stats().TCP.CurrentEstablished.Value(); got != 0 {
- t.Errorf("got stats.TCP.CurrentEstablished.Value() = %d, want = 0", got)
+
+ start := time.Now()
+ for time.Since(start) < time.Minute && len(checkValid()) > 0 {
+ time.Sleep(50 * time.Millisecond)
}
- if got := c.Stack().Stats().TCP.CurrentConnected.Value(); got != 0 {
- t.Errorf("got stats.TCP.CurrentConnected.Value() = %d, want = 0", got)
+ for _, err := range checkValid() {
+ t.Error(err)
}
}
@@ -6068,6 +6077,11 @@ func TestSynRcvdBadSeqNumber(t *testing.T) {
// complete the connection to test that the large SEQ num
// did not change the state from SYN-RCVD.
+ // Get setup to be notified about connection establishment.
+ we, ch := waiter.NewChannelEntry(nil)
+ c.WQ.EventRegister(&we, waiter.ReadableEvents)
+ defer c.WQ.EventUnregister(&we)
+
// Send ACK to move to ESTABLISHED state.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
@@ -6078,32 +6092,12 @@ func TestSynRcvdBadSeqNumber(t *testing.T) {
RcvWnd: 30000,
})
+ <-ch
newEP, _, err := c.EP.Accept(nil)
- switch err.(type) {
- case nil, *tcpip.ErrWouldBlock:
- default:
+ if err != nil {
t.Fatalf("Accept failed: %s", err)
}
- if cmp.Equal(&tcpip.ErrWouldBlock{}, err) {
- // Try to accept the connections in the backlog.
- we, ch := waiter.NewChannelEntry(nil)
- c.WQ.EventRegister(&we, waiter.ReadableEvents)
- defer c.WQ.EventUnregister(&we)
-
- // Wait for connection to be established.
- select {
- case <-ch:
- newEP, _, err = c.EP.Accept(nil)
- if err != nil {
- t.Fatalf("Accept failed: %s", err)
- }
-
- case <-time.After(1 * time.Second):
- t.Fatalf("Timed out waiting for accept")
- }
- }
-
// Now verify that the TCP socket is usable and in a connected state.
data := "Don't panic"
var r strings.Reader
@@ -6209,12 +6203,26 @@ func TestPassiveFailedConnectionAttemptIncrement(t *testing.T) {
RcvWnd: 30000,
})
- time.Sleep(50 * time.Millisecond)
- if got := stats.TCP.ListenOverflowSynDrop.Value(); got != want {
- t.Errorf("got stats.TCP.ListenOverflowSynDrop.Value() = %d, want = %d", got, want)
+ checkValid := func() []error {
+ var errors []error
+ if got := stats.TCP.ListenOverflowSynDrop.Value(); got != want {
+ errors = append(errors, fmt.Errorf("got stats.TCP.ListenOverflowSynDrop.Value() = %d, want = %d", got, want))
+ }
+ if got := c.EP.Stats().(*tcp.Stats).ReceiveErrors.ListenOverflowSynDrop.Value(); got != want {
+ errors = append(errors, fmt.Errorf("got EP stats Stats.ReceiveErrors.ListenOverflowSynDrop = %d, want = %d", got, want))
+ }
+ return errors
}
- if got := c.EP.Stats().(*tcp.Stats).ReceiveErrors.ListenOverflowSynDrop.Value(); got != want {
- t.Errorf("got EP stats Stats.ReceiveErrors.ListenOverflowSynDrop = %d, want = %d", got, want)
+
+ start := time.Now()
+ for time.Since(start) < time.Minute && len(checkValid()) > 0 {
+ time.Sleep(50 * time.Millisecond)
+ }
+ for _, err := range checkValid() {
+ t.Error(err)
+ }
+ if t.Failed() {
+ t.FailNow()
}
we, ch := waiter.NewChannelEntry(nil)
@@ -6225,19 +6233,62 @@ func TestPassiveFailedConnectionAttemptIncrement(t *testing.T) {
_, _, err = c.EP.Accept(nil)
if cmp.Equal(&tcpip.ErrWouldBlock{}, err) {
// Wait for connection to be established.
- select {
- case <-ch:
- _, _, err = c.EP.Accept(nil)
- if err != nil {
- t.Fatalf("Accept failed: %s", err)
- }
-
- case <-time.After(1 * time.Second):
- t.Fatalf("Timed out waiting for accept")
+ <-ch
+ _, _, err = c.EP.Accept(nil)
+ if err != nil {
+ t.Fatalf("Accept failed: %s", err)
}
}
}
+func TestListenDropIncrement(t *testing.T) {
+ c := context.New(t, defaultMTU)
+ defer c.Cleanup()
+
+ stats := c.Stack().Stats()
+ c.Create(-1 /*epRcvBuf*/)
+
+ if err := c.EP.Bind(tcpip.FullAddress{Addr: context.StackAddr, Port: context.StackPort}); err != nil {
+ t.Fatalf("Bind failed: %s", err)
+ }
+ if err := c.EP.Listen(1 /*backlog*/); err != nil {
+ t.Fatalf("Listen failed: %s", err)
+ }
+
+ initialDropped := stats.DroppedPackets.Value()
+
+ // Send RST, FIN segments, that are expected to be dropped by the listener.
+ c.SendPacket(nil, &context.Headers{
+ SrcPort: context.TestPort,
+ DstPort: context.StackPort,
+ Flags: header.TCPFlagRst,
+ })
+ c.SendPacket(nil, &context.Headers{
+ SrcPort: context.TestPort,
+ DstPort: context.StackPort,
+ Flags: header.TCPFlagFin,
+ })
+
+ // To ensure that the RST, FIN sent earlier are indeed received and ignored
+ // by the listener, send a SYN and wait for the SYN to be ACKd.
+ irs := seqnum.Value(context.TestInitialSequenceNumber)
+ c.SendPacket(nil, &context.Headers{
+ SrcPort: context.TestPort,
+ DstPort: context.StackPort,
+ Flags: header.TCPFlagSyn,
+ SeqNum: irs,
+ })
+ checker.IPv4(t, c.GetPacket(), checker.TCP(checker.SrcPort(context.StackPort),
+ checker.DstPort(context.TestPort),
+ checker.TCPFlags(header.TCPFlagAck|header.TCPFlagSyn),
+ checker.TCPAckNum(uint32(irs)+1),
+ ))
+
+ if got, want := stats.DroppedPackets.Value(), initialDropped+2; got != want {
+ t.Fatalf("got stats.DroppedPackets.Value() = %d, want = %d", got, want)
+ }
+}
+
func TestEndpointBindListenAcceptState(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
@@ -7435,7 +7486,7 @@ func TestTCPUserTimeout(t *testing.T) {
select {
case <-notifyCh:
case <-time.After(2 * initRTO):
- t.Fatalf("connection still alive after %s, should have been closed after :%s", 2*initRTO, userTimeout)
+ t.Fatalf("connection still alive after %s, should have been closed after %s", 2*initRTO, userTimeout)
}
// No packet should be received as the connection should be silently
diff --git a/pkg/tcpip/transport/udp/endpoint.go b/pkg/tcpip/transport/udp/endpoint.go
index def9d7186..82a3f2287 100644
--- a/pkg/tcpip/transport/udp/endpoint.go
+++ b/pkg/tcpip/transport/udp/endpoint.go
@@ -364,6 +364,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
// reacquire the mutex in exclusive mode.
//
// Returns true for retry if preparation should be retried.
+// +checklocks:e.mu
func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err tcpip.Error) {
switch e.EndpointState() {
case StateInitial:
@@ -380,10 +381,8 @@ func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err tcpip
}
e.mu.RUnlock()
- defer e.mu.RLock()
-
e.mu.Lock()
- defer e.mu.Unlock()
+ defer e.mu.DowngradeLock()
// The state changed when we released the shared locked and re-acquired
// it in exclusive mode. Try again.
@@ -449,37 +448,20 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcp
return n, err
}
-func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
- if err := e.LastError(); err != nil {
- return 0, err
- }
-
- // MSG_MORE is unimplemented. (This also means that MSG_EOR is a no-op.)
- if opts.More {
- return 0, &tcpip.ErrInvalidOptionValue{}
- }
-
- to := opts.To
-
+func (e *endpoint) buildUDPPacketInfo(p tcpip.Payloader, opts tcpip.WriteOptions) (udpPacketInfo, tcpip.Error) {
e.mu.RLock()
- lockReleased := false
- defer func() {
- if lockReleased {
- return
- }
- e.mu.RUnlock()
- }()
+ defer e.mu.RUnlock()
// If we've shutdown with SHUT_WR we are in an invalid state for sending.
if e.shutdownFlags&tcpip.ShutdownWrite != 0 {
- return 0, &tcpip.ErrClosedForSend{}
+ return udpPacketInfo{}, &tcpip.ErrClosedForSend{}
}
// Prepare for write.
for {
- retry, err := e.prepareForWrite(to)
+ retry, err := e.prepareForWrite(opts.To)
if err != nil {
- return 0, err
+ return udpPacketInfo{}, err
}
if !retry {
@@ -489,34 +471,34 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcp
route := e.route
dstPort := e.dstPort
- if to != nil {
+ if opts.To != nil {
// Reject destination address if it goes through a different
// NIC than the endpoint was bound to.
- nicID := to.NIC
+ nicID := opts.To.NIC
if nicID == 0 {
nicID = tcpip.NICID(e.ops.GetBindToDevice())
}
if e.BindNICID != 0 {
if nicID != 0 && nicID != e.BindNICID {
- return 0, &tcpip.ErrNoRoute{}
+ return udpPacketInfo{}, &tcpip.ErrNoRoute{}
}
nicID = e.BindNICID
}
- if to.Port == 0 {
+ if opts.To.Port == 0 {
// Port 0 is an invalid port to send to.
- return 0, &tcpip.ErrInvalidEndpointState{}
+ return udpPacketInfo{}, &tcpip.ErrInvalidEndpointState{}
}
- dst, netProto, err := e.checkV4MappedLocked(*to)
+ dst, netProto, err := e.checkV4MappedLocked(*opts.To)
if err != nil {
- return 0, err
+ return udpPacketInfo{}, err
}
r, _, err := e.connectRoute(nicID, dst, netProto)
if err != nil {
- return 0, err
+ return udpPacketInfo{}, err
}
defer r.Release()
@@ -525,12 +507,12 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcp
}
if !e.ops.GetBroadcast() && route.IsOutboundBroadcast() {
- return 0, &tcpip.ErrBroadcastDisabled{}
+ return udpPacketInfo{}, &tcpip.ErrBroadcastDisabled{}
}
v := make([]byte, p.Len())
if _, err := io.ReadFull(p, v); err != nil {
- return 0, &tcpip.ErrBadBuffer{}
+ return udpPacketInfo{}, &tcpip.ErrBadBuffer{}
}
if len(v) > header.UDPMaximumPacketSize {
// Payload can't possibly fit in a packet.
@@ -548,24 +530,39 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcp
v,
)
}
- return 0, &tcpip.ErrMessageTooLong{}
+ return udpPacketInfo{}, &tcpip.ErrMessageTooLong{}
}
ttl := e.ttl
useDefaultTTL := ttl == 0
-
if header.IsV4MulticastAddress(route.RemoteAddress()) || header.IsV6MulticastAddress(route.RemoteAddress()) {
ttl = e.multicastTTL
// Multicast allows a 0 TTL.
useDefaultTTL = false
}
- localPort := e.ID.LocalPort
- sendTOS := e.sendTOS
- owner := e.owner
- noChecksum := e.SocketOptions().GetNoChecksum()
- lockReleased = true
- e.mu.RUnlock()
+ return udpPacketInfo{
+ route: route,
+ data: buffer.View(v),
+ localPort: e.ID.LocalPort,
+ remotePort: dstPort,
+ ttl: ttl,
+ useDefaultTTL: useDefaultTTL,
+ tos: e.sendTOS,
+ owner: e.owner,
+ noChecksum: e.SocketOptions().GetNoChecksum(),
+ }, nil
+}
+
+func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
+ if err := e.LastError(); err != nil {
+ return 0, err
+ }
+
+ // MSG_MORE is unimplemented. (This also means that MSG_EOR is a no-op.)
+ if opts.More {
+ return 0, &tcpip.ErrInvalidOptionValue{}
+ }
// Do not hold lock when sending as loopback is synchronous and if the UDP
// datagram ends up generating an ICMP response then it can result in a
@@ -577,10 +574,15 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcp
//
// See: https://golang.org/pkg/sync/#RWMutex for details on why recursive read
// locking is prohibited.
- if err := sendUDP(route, buffer.View(v).ToVectorisedView(), localPort, dstPort, ttl, useDefaultTTL, sendTOS, owner, noChecksum); err != nil {
+ u, err := e.buildUDPPacketInfo(p, opts)
+ if err != nil {
return 0, err
}
- return int64(len(v)), nil
+ n, err := u.send()
+ if err != nil {
+ return 0, err
+ }
+ return int64(n), nil
}
// OnReuseAddressSet implements tcpip.SocketOptionsHandler.
@@ -817,14 +819,30 @@ func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
return nil
}
-// sendUDP sends a UDP segment via the provided network endpoint and under the
-// provided identity.
-func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, ttl uint8, useDefaultTTL bool, tos uint8, owner tcpip.PacketOwner, noChecksum bool) tcpip.Error {
+// udpPacketInfo contains all information required to send a UDP packet.
+//
+// This should be used as a value-only type, which exists in order to simplify
+// return value syntax. It should not be exported or extended.
+type udpPacketInfo struct {
+ route *stack.Route
+ data buffer.View
+ localPort uint16
+ remotePort uint16
+ ttl uint8
+ useDefaultTTL bool
+ tos uint8
+ owner tcpip.PacketOwner
+ noChecksum bool
+}
+
+// send sends the given packet.
+func (u *udpPacketInfo) send() (int, tcpip.Error) {
+ vv := u.data.ToVectorisedView()
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
- ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()),
- Data: data,
+ ReserveHeaderBytes: header.UDPMinimumSize + int(u.route.MaxHeaderLength()),
+ Data: vv,
})
- pkt.Owner = owner
+ pkt.Owner = u.owner
// Initialize the UDP header.
udp := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize))
@@ -832,8 +850,8 @@ func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort u
length := uint16(pkt.Size())
udp.Encode(&header.UDPFields{
- SrcPort: localPort,
- DstPort: remotePort,
+ SrcPort: u.localPort,
+ DstPort: u.remotePort,
Length: length,
})
@@ -841,30 +859,30 @@ func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort u
// On IPv4, UDP checksum is optional, and a zero value indicates the
// transmitter skipped the checksum generation (RFC768).
// On IPv6, UDP checksum is not optional (RFC2460 Section 8.1).
- if r.RequiresTXTransportChecksum() &&
- (!noChecksum || r.NetProto() == header.IPv6ProtocolNumber) {
- xsum := r.PseudoHeaderChecksum(ProtocolNumber, length)
- for _, v := range data.Views() {
+ if u.route.RequiresTXTransportChecksum() &&
+ (!u.noChecksum || u.route.NetProto() == header.IPv6ProtocolNumber) {
+ xsum := u.route.PseudoHeaderChecksum(ProtocolNumber, length)
+ for _, v := range vv.Views() {
xsum = header.Checksum(v, xsum)
}
udp.SetChecksum(^udp.CalculateChecksum(xsum))
}
- if useDefaultTTL {
- ttl = r.DefaultTTL()
+ if u.useDefaultTTL {
+ u.ttl = u.route.DefaultTTL()
}
- if err := r.WritePacket(stack.NetworkHeaderParams{
+ if err := u.route.WritePacket(stack.NetworkHeaderParams{
Protocol: ProtocolNumber,
- TTL: ttl,
- TOS: tos,
+ TTL: u.ttl,
+ TOS: u.tos,
}, pkt); err != nil {
- r.Stats().UDP.PacketSendErrors.Increment()
- return err
+ u.route.Stats().UDP.PacketSendErrors.Increment()
+ return 0, err
}
// Track count of packets sent.
- r.Stats().UDP.PacketsSent.Increment()
- return nil
+ u.route.Stats().UDP.PacketsSent.Increment()
+ return len(u.data), nil
}
// checkV4MappedLocked determines the effective network protocol and converts
diff --git a/pkg/urpc/urpc.go b/pkg/urpc/urpc.go
index 0e9a829f6..0ef635a2f 100644
--- a/pkg/urpc/urpc.go
+++ b/pkg/urpc/urpc.go
@@ -27,6 +27,7 @@ import (
"os"
"reflect"
"runtime"
+ "time"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
@@ -458,29 +459,45 @@ func (s *Server) StartHandling(client *unet.Socket) {
// No new requests should be initiated after calling Stop. Existing clients
// will be closed after completing any pending RPCs. This method will block
// until all clients have disconnected.
-func (s *Server) Stop() {
- // Wait for all outstanding requests.
- defer s.wg.Wait()
-
+//
+// timeout is the time for clients to complete ongoing RPCs.
+func (s *Server) Stop(timeout time.Duration) {
// Call any Stop callbacks.
for _, stopper := range s.stoppers {
stopper.Stop()
}
- // Close all known clients.
- s.mu.Lock()
- defer s.mu.Unlock()
- for client, state := range s.clients {
- switch state {
- case idle:
- // Close connection now.
- client.Close()
- s.clients[client] = closed
- case processing:
- // Request close when done.
- s.clients[client] = closeRequested
+ done := make(chan bool, 1)
+ go func() {
+ if timeout != 0 {
+ timer := time.NewTicker(timeout)
+ defer timer.Stop()
+ select {
+ case <-done:
+ return
+ case <-timer.C:
+ }
}
- }
+
+ // Close all known clients.
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ for client, state := range s.clients {
+ switch state {
+ case idle:
+ // Close connection now.
+ client.Close()
+ s.clients[client] = closed
+ case processing:
+ // Request close when done.
+ s.clients[client] = closeRequested
+ }
+ }
+ }()
+
+ // Wait for all outstanding requests.
+ s.wg.Wait()
+ done <- true
}
// Client is a urpc client.
diff --git a/pkg/usermem/BUILD b/pkg/usermem/BUILD
index 3dba36f12..54674ee88 100644
--- a/pkg/usermem/BUILD
+++ b/pkg/usermem/BUILD
@@ -7,12 +7,14 @@ go_library(
srcs = [
"bytes_io.go",
"bytes_io_unsafe.go",
+ "marshal.go",
"usermem.go",
],
visibility = ["//:sandbox"],
deps = [
"//pkg/atomicbitops",
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/gohacks",
"//pkg/hostarch",
"//pkg/safemem",
@@ -29,6 +31,7 @@ go_test(
library = ":usermem",
deps = [
"//pkg/context",
+ "//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/safemem",
"//pkg/syserror",
diff --git a/pkg/usermem/bytes_io.go b/pkg/usermem/bytes_io.go
index 3da3c0294..4c97b9136 100644
--- a/pkg/usermem/bytes_io.go
+++ b/pkg/usermem/bytes_io.go
@@ -16,6 +16,7 @@ package usermem
import (
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/syserror"
@@ -51,7 +52,7 @@ func (b *BytesIO) CopyIn(ctx context.Context, addr hostarch.Addr, dst []byte, op
// ZeroOut implements IO.ZeroOut.
func (b *BytesIO) ZeroOut(ctx context.Context, addr hostarch.Addr, toZero int64, opts IOOpts) (int64, error) {
if toZero > int64(maxInt) {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
rngN, rngErr := b.rangeCheck(addr, int(toZero))
if rngN == 0 {
@@ -89,7 +90,7 @@ func (b *BytesIO) rangeCheck(addr hostarch.Addr, length int) (int, error) {
return 0, nil
}
if length < 0 {
- return 0, syserror.EINVAL
+ return 0, linuxerr.EINVAL
}
max := hostarch.Addr(len(b.Bytes))
if addr >= max {
diff --git a/pkg/usermem/marshal.go b/pkg/usermem/marshal.go
new file mode 100644
index 000000000..5b5a662dc
--- /dev/null
+++ b/pkg/usermem/marshal.go
@@ -0,0 +1,43 @@
+// Copyright 2021 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 usermem
+
+import (
+ "gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/hostarch"
+)
+
+// IOCopyContext wraps an object implementing hostarch.IO to implement
+// marshal.CopyContext.
+type IOCopyContext struct {
+ Ctx context.Context
+ IO IO
+ Opts IOOpts
+}
+
+// CopyScratchBuffer implements marshal.CopyContext.CopyScratchBuffer.
+func (i *IOCopyContext) CopyScratchBuffer(size int) []byte {
+ return make([]byte, size)
+}
+
+// CopyOutBytes implements marshal.CopyContext.CopyOutBytes.
+func (i *IOCopyContext) CopyOutBytes(addr hostarch.Addr, b []byte) (int, error) {
+ return i.IO.CopyOut(i.Ctx, addr, b, i.Opts)
+}
+
+// CopyInBytes implements marshal.CopyContext.CopyInBytes.
+func (i *IOCopyContext) CopyInBytes(addr hostarch.Addr, b []byte) (int, error) {
+ return i.IO.CopyIn(i.Ctx, addr, b, i.Opts)
+}
diff --git a/pkg/usermem/usermem.go b/pkg/usermem/usermem.go
index 0d6d25e50..ca852558e 100644
--- a/pkg/usermem/usermem.go
+++ b/pkg/usermem/usermem.go
@@ -22,11 +22,11 @@ import (
"strconv"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/gohacks"
+ "gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/syserror"
-
- "gvisor.dev/gvisor/pkg/hostarch"
)
// IO provides access to the contents of a virtual memory space.
@@ -244,7 +244,7 @@ func CopyStringIn(ctx context.Context, uio IO, addr hostarch.Addr, maxlen int, o
}
addr = end
}
- return gohacks.StringFromImmutableBytes(buf), syserror.ENAMETOOLONG
+ return gohacks.StringFromImmutableBytes(buf), linuxerr.ENAMETOOLONG
}
// CopyOutVec copies bytes from src to the memory mapped at ars in uio. The
@@ -382,7 +382,7 @@ func CopyInt32StringsInVec(ctx context.Context, uio IO, ars hostarch.AddrRangeSe
// Parse a single value.
val, err := strconv.ParseInt(string(buf[i:nextI]), 10, 32)
if err != nil {
- return int64(i), syserror.EINVAL
+ return int64(i), linuxerr.EINVAL
}
dsts[j] = int32(val)
@@ -398,7 +398,7 @@ func CopyInt32StringsInVec(ctx context.Context, uio IO, ars hostarch.AddrRangeSe
return int64(i), cperr
}
if j == 0 {
- return int64(i), syserror.EINVAL
+ return int64(i), linuxerr.EINVAL
}
return int64(i), nil
}
diff --git a/pkg/usermem/usermem_test.go b/pkg/usermem/usermem_test.go
index 9b697b593..597770ca7 100644
--- a/pkg/usermem/usermem_test.go
+++ b/pkg/usermem/usermem_test.go
@@ -22,6 +22,7 @@ import (
"testing"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/syserror"
@@ -213,7 +214,7 @@ func TestCopyStringInNoTerminatingZeroByte(t *testing.T) {
func TestCopyStringInTruncatedByMaxlen(t *testing.T) {
got, err := CopyStringIn(newContext(), newBytesIOString(strings.Repeat("A", 10)), 0, 5, IOOpts{})
- if want, wantErr := strings.Repeat("A", 5), syserror.ENAMETOOLONG; got != want || err != wantErr {
+ if want, wantErr := strings.Repeat("A", 5), linuxerr.ENAMETOOLONG; got != want || err != wantErr {
t.Errorf("CopyStringIn: got (%q, %v), wanted (%q, %v)", got, err, want, wantErr)
}
}
@@ -272,8 +273,8 @@ func TestCopyInt32StringsInVecRequiresOneValidValue(t *testing.T) {
src := BytesIOSequence([]byte(s))
initial := []int32{1, 2}
dsts := append([]int32(nil), initial...)
- if n, err := CopyInt32StringsInVec(newContext(), src.IO, src.Addrs, dsts, src.Opts); err != syserror.EINVAL {
- t.Errorf("CopyInt32StringsInVec: got (%d, %v), wanted (_, %v)", n, err, syserror.EINVAL)
+ if n, err := CopyInt32StringsInVec(newContext(), src.IO, src.Addrs, dsts, src.Opts); !linuxerr.Equals(linuxerr.EINVAL, err) {
+ t.Errorf("CopyInt32StringsInVec: got (%d, %v), wanted (_, %v)", n, err, linuxerr.EINVAL)
}
if !reflect.DeepEqual(dsts, initial) {
t.Errorf("dsts: got %v, wanted %v", dsts, initial)
diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD
index d51347fe1..c9d2b3eff 100644
--- a/runsc/boot/BUILD
+++ b/runsc/boot/BUILD
@@ -32,13 +32,13 @@ go_library(
"//pkg/control/server",
"//pkg/coverage",
"//pkg/cpuid",
+ "//pkg/errors/linuxerr",
"//pkg/eventchannel",
"//pkg/fd",
"//pkg/flipcall",
"//pkg/fspath",
"//pkg/log",
"//pkg/memutil",
- "//pkg/metric",
"//pkg/rand",
"//pkg/refs",
"//pkg/refsvfs2",
@@ -95,7 +95,6 @@ go_library(
"//pkg/sentry/vfs",
"//pkg/sentry/watchdog",
"//pkg/sync",
- "//pkg/syserror",
"//pkg/tcpip",
"//pkg/tcpip/link/fdbased",
"//pkg/tcpip/link/loopback",
diff --git a/runsc/boot/controller.go b/runsc/boot/controller.go
index 9b270cbf2..132973e6b 100644
--- a/runsc/boot/controller.go
+++ b/runsc/boot/controller.go
@@ -18,6 +18,7 @@ import (
"errors"
"fmt"
"os"
+ gtime "time"
specs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
@@ -165,8 +166,11 @@ func newController(fd int, l *Loader) (*controller, error) {
return ctrl, nil
}
+// stopRPCTimeout is the time for clients to complete ongoing RPCs.
+const stopRPCTimeout = 15 * gtime.Second
+
func (c *controller) stop() {
- c.srv.Stop()
+ c.srv.Stop(stopRPCTimeout)
}
// containerManager manages sandbox containers.
@@ -330,6 +334,11 @@ func (cm *containerManager) ExecuteAsync(args *control.ExecArgs, pid *int32) err
// Checkpoint pauses a sandbox and saves its state.
func (cm *containerManager) Checkpoint(o *control.SaveOpts, _ *struct{}) error {
log.Debugf("containerManager.Checkpoint")
+ // TODO(gvisor.dev/issues/6243): save/restore not supported w/ hostinet
+ if cm.l.root.conf.Network == config.NetworkHost {
+ return errors.New("checkpoint not supported when using hostinet")
+ }
+
state := control.State{
Kernel: cm.l.k,
Watchdog: cm.l.watchdog,
@@ -340,6 +349,10 @@ func (cm *containerManager) Checkpoint(o *control.SaveOpts, _ *struct{}) error {
// Pause suspends a container.
func (cm *containerManager) Pause(_, _ *struct{}) error {
log.Debugf("containerManager.Pause")
+ // TODO(gvisor.dev/issues/6243): save/restore not supported w/ hostinet
+ if cm.l.root.conf.Network == config.NetworkHost {
+ return errors.New("pause not supported when using hostinet")
+ }
cm.l.k.Pause()
return nil
}
@@ -439,7 +452,7 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error {
// Load the state.
loadOpts := state.LoadOpts{Source: specFile}
- if err := loadOpts.Load(ctx, k, networkStack, time.NewCalibratedClocks(), &vfs.CompleteRestoreOptions{}); err != nil {
+ if err := loadOpts.Load(ctx, k, nil, networkStack, time.NewCalibratedClocks(), &vfs.CompleteRestoreOptions{}); err != nil {
return err
}
diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go
index 49b503f99..33e738efc 100644
--- a/runsc/boot/filter/config.go
+++ b/runsc/boot/filter/config.go
@@ -200,6 +200,12 @@ var allowedSyscalls = seccomp.SyscallRules{
seccomp.MatchAny{},
seccomp.MatchAny{},
seccomp.MatchAny{},
+ seccomp.EqualTo(unix.MAP_SHARED | unix.MAP_FIXED),
+ },
+ {
+ seccomp.MatchAny{},
+ seccomp.MatchAny{},
+ seccomp.MatchAny{},
seccomp.EqualTo(unix.MAP_PRIVATE),
},
{
@@ -265,7 +271,6 @@ var allowedSyscalls = seccomp.SyscallRules{
seccomp.MatchAny{},
seccomp.MatchAny{},
seccomp.EqualTo(unix.MSG_DONTWAIT),
- seccomp.EqualTo(0),
},
},
unix.SYS_RESTART_SYSCALL: {},
@@ -454,6 +459,10 @@ func hostInetFilters() seccomp.SyscallRules {
seccomp.MatchAny{},
seccomp.EqualTo(unix.TIOCINQ),
},
+ {
+ seccomp.MatchAny{},
+ seccomp.EqualTo(unix.SIOCGIFFLAGS),
+ },
},
unix.SYS_LISTEN: {},
unix.SYS_READV: {},
diff --git a/runsc/boot/fs.go b/runsc/boot/fs.go
index bf4a41f77..7fce2b708 100644
--- a/runsc/boot/fs.go
+++ b/runsc/boot/fs.go
@@ -25,6 +25,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -41,7 +42,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/runsc/config"
"gvisor.dev/gvisor/runsc/specutils"
@@ -763,12 +763,10 @@ func (c *containerMounter) createRootMount(ctx context.Context, conf *config.Con
p9FS := mustFindFilesystem("9p")
opts := p9MountData(fd, conf.FileAccess, false /* vfs2 */)
- if conf.OverlayfsStaleRead {
- // We can't check for overlayfs here because sandbox is chroot'ed and gofer
- // can only send mount options for specs.Mounts (specs.Root is missing
- // Options field). So assume root is always on top of overlayfs.
- opts = append(opts, "overlayfs_stale_read")
- }
+ // We can't check for overlayfs here because sandbox is chroot'ed and gofer
+ // can only send mount options for specs.Mounts (specs.Root is missing
+ // Options field). So assume root is always on top of overlayfs.
+ opts = append(opts, "overlayfs_stale_read")
rootInode, err := p9FS.Mount(ctx, rootDevice, mf, strings.Join(opts, ","), nil)
if err != nil {
@@ -1041,8 +1039,8 @@ func (c *containerMounter) mountTmp(ctx context.Context, conf *config.Config, mn
maxTraversals := uint(0)
tmp, err := mns.FindInode(ctx, root, root, "tmp", &maxTraversals)
- switch err {
- case nil:
+ switch {
+ case err == nil:
// Found '/tmp' in filesystem, check if it's empty.
defer tmp.DecRef(ctx)
f, err := tmp.Inode.GetFile(ctx, tmp, fs.FileFlags{Read: true, Directory: true})
@@ -1063,7 +1061,7 @@ func (c *containerMounter) mountTmp(ctx context.Context, conf *config.Config, mn
log.Infof("Mounting internal tmpfs on top of empty %q", "/tmp")
fallthrough
- case syserror.ENOENT:
+ case linuxerr.Equals(linuxerr.ENOENT, err):
// No '/tmp' found (or fallthrough from above). Safe to mount internal
// tmpfs.
tmpMount := specs.Mount{
diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go
index b73ac101f..8d71d7447 100644
--- a/runsc/boot/loader.go
+++ b/runsc/boot/loader.go
@@ -34,11 +34,9 @@ import (
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/memutil"
- "gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/rand"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/refsvfs2"
- "gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/control"
"gvisor.dev/gvisor/pkg/sentry/fdimport"
"gvisor.dev/gvisor/pkg/sentry/fs"
@@ -218,8 +216,6 @@ func New(args Args) (*Loader, error) {
return nil, fmt.Errorf("setting up memory usage: %w", err)
}
- metric.CreateSentryMetrics()
-
// Is this a VFSv2 kernel?
if args.Conf.VFS2 {
kernel.VFS2Enabled = true
@@ -282,19 +278,15 @@ func New(args Args) (*Loader, error) {
}
// Create timekeeper.
- tk, err := kernel.NewTimekeeper(k, vdso.ParamPage.FileRange())
- if err != nil {
- return nil, fmt.Errorf("creating timekeeper: %w", err)
- }
+ tk := kernel.NewTimekeeper(k, vdso.ParamPage.FileRange())
tk.SetClocks(time.NewCalibratedClocks())
- k.SetTimekeeper(tk)
if err := enableStrace(args.Conf); err != nil {
return nil, fmt.Errorf("enabling strace: %w", err)
}
// Create root network namespace/stack.
- netns, err := newRootNetworkNamespace(args.Conf, k, k)
+ netns, err := newRootNetworkNamespace(args.Conf, tk, k)
if err != nil {
return nil, fmt.Errorf("creating network: %w", err)
}
@@ -336,6 +328,7 @@ func New(args Args) (*Loader, error) {
// to createVFS in order to mount (among other things) procfs.
if err = k.Init(kernel.InitKernelArgs{
FeatureSet: cpuid.HostFeatureSet(),
+ Timekeeper: tk,
RootUserNamespace: creds.UserNamespace,
RootNetworkNamespace: netns,
ApplicationCores: uint(args.NumCPU),
@@ -967,10 +960,15 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) {
}
args.Envv = envv
}
+ args.PIDNamespace = tg.PIDNamespace()
+
+ args.Limits, err = createLimitSet(l.root.spec)
+ if err != nil {
+ return 0, fmt.Errorf("creating limits: %w", err)
+ }
// Start the process.
proc := control.Proc{Kernel: l.k}
- args.PIDNamespace = tg.PIDNamespace()
newTG, tgid, ttyFile, ttyFileVFS2, err := control.ExecAsync(&proc, args)
if err != nil {
return 0, err
@@ -1224,7 +1222,7 @@ func (l *Loader) signalProcess(cid string, tgid kernel.ThreadID, signo int32) er
execTG, err := l.threadGroupFromID(execID{cid: cid, pid: tgid})
if err == nil {
// Send signal directly to the identified process.
- return l.k.SendExternalSignalThreadGroup(execTG, &arch.SignalInfo{Signo: signo})
+ return l.k.SendExternalSignalThreadGroup(execTG, &linux.SignalInfo{Signo: signo})
}
// The caller may be signaling a process not started directly via exec.
@@ -1237,7 +1235,7 @@ func (l *Loader) signalProcess(cid string, tgid kernel.ThreadID, signo int32) er
if tg.Leader().ContainerID() != cid {
return fmt.Errorf("process %d belongs to a different container: %q", tgid, tg.Leader().ContainerID())
}
- return l.k.SendExternalSignalThreadGroup(tg, &arch.SignalInfo{Signo: signo})
+ return l.k.SendExternalSignalThreadGroup(tg, &linux.SignalInfo{Signo: signo})
}
// signalForegrondProcessGroup looks up foreground process group from the TTY
@@ -1273,7 +1271,7 @@ func (l *Loader) signalForegrondProcessGroup(cid string, tgid kernel.ThreadID, s
// No foreground process group has been set. Signal the
// original thread group.
log.Warningf("No foreground process group for container %q and PID %d. Sending signal directly to PID %d.", cid, tgid, tgid)
- return l.k.SendExternalSignalThreadGroup(tg, &arch.SignalInfo{Signo: signo})
+ return l.k.SendExternalSignalThreadGroup(tg, &linux.SignalInfo{Signo: signo})
}
// Send the signal to all processes in the process group.
var lastErr error
@@ -1281,7 +1279,7 @@ func (l *Loader) signalForegrondProcessGroup(cid string, tgid kernel.ThreadID, s
if tg.ProcessGroup() != pg {
continue
}
- if err := l.k.SendExternalSignalThreadGroup(tg, &arch.SignalInfo{Signo: signo}); err != nil {
+ if err := l.k.SendExternalSignalThreadGroup(tg, &linux.SignalInfo{Signo: signo}); err != nil {
lastErr = err
}
}
@@ -1296,7 +1294,7 @@ func (l *Loader) signalAllProcesses(cid string, signo int32) error {
// sent to the entire container.
l.k.Pause()
defer l.k.Unpause()
- return l.k.SendContainerSignal(cid, &arch.SignalInfo{Signo: signo})
+ return l.k.SendContainerSignal(cid, &linux.SignalInfo{Signo: signo})
}
// threadGroupFromID is similar to tryThreadGroupFromIDLocked except that it
diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go
index 93c476971..b5e8d08a5 100644
--- a/runsc/boot/loader_test.go
+++ b/runsc/boot/loader_test.go
@@ -214,7 +214,7 @@ func doStartSignal(t *testing.T, vfsEnabled bool) {
// We aren't going to wait on this application, so the control server
// needs to be shut down manually.
- defer l.ctrl.srv.Stop()
+ defer l.ctrl.srv.Stop(time.Hour)
// Start a goroutine that calls WaitForStartSignal and writes to a
// channel when it returns.
diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go
index 7be5176b0..346796d9c 100644
--- a/runsc/boot/vfs.go
+++ b/runsc/boot/vfs.go
@@ -24,6 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cleanup"
"gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/devices/memdev"
@@ -44,7 +45,6 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
- "gvisor.dev/gvisor/pkg/syserror"
"gvisor.dev/gvisor/runsc/config"
"gvisor.dev/gvisor/runsc/specutils"
)
@@ -210,12 +210,10 @@ func (c *containerMounter) createMountNamespaceVFS2(ctx context.Context, conf *c
fd := c.fds.remove()
data := p9MountData(fd, conf.FileAccess, true /* vfs2 */)
- if conf.OverlayfsStaleRead {
- // We can't check for overlayfs here because sandbox is chroot'ed and gofer
- // can only send mount options for specs.Mounts (specs.Root is missing
- // Options field). So assume root is always on top of overlayfs.
- data = append(data, "overlayfs_stale_read")
- }
+ // We can't check for overlayfs here because sandbox is chroot'ed and gofer
+ // can only send mount options for specs.Mounts (specs.Root is missing
+ // Options field). So assume root is always on top of overlayfs.
+ data = append(data, "overlayfs_stale_read")
log.Infof("Mounting root over 9P, ioFD: %d", fd)
opts := &vfs.MountOptions{
@@ -658,20 +656,20 @@ func (c *containerMounter) mountTmpVFS2(ctx context.Context, conf *config.Config
Path: fspath.Parse("/tmp"),
}
fd, err := c.k.VFS().OpenAt(ctx, creds, &pop, &vfs.OpenOptions{Flags: linux.O_RDONLY | linux.O_DIRECTORY})
- switch err {
- case nil:
+ switch {
+ case err == nil:
defer fd.DecRef(ctx)
err := fd.IterDirents(ctx, vfs.IterDirentsCallbackFunc(func(dirent vfs.Dirent) error {
if dirent.Name != "." && dirent.Name != ".." {
- return syserror.ENOTEMPTY
+ return linuxerr.ENOTEMPTY
}
return nil
}))
- switch err {
- case nil:
+ switch {
+ case err == nil:
log.Infof(`Mounting internal tmpfs on top of empty "/tmp"`)
- case syserror.ENOTEMPTY:
+ case linuxerr.Equals(linuxerr.ENOTEMPTY, err):
// If more than "." and ".." is found, skip internal tmpfs to prevent
// hiding existing files.
log.Infof(`Skipping internal tmpfs mount for "/tmp" because it's not empty`)
@@ -681,7 +679,7 @@ func (c *containerMounter) mountTmpVFS2(ctx context.Context, conf *config.Config
}
fallthrough
- case syserror.ENOENT:
+ case linuxerr.Equals(linuxerr.ENOENT, err):
// No '/tmp' found (or fallthrough from above). It's safe to mount internal
// tmpfs.
tmpMount := specs.Mount{
@@ -694,7 +692,7 @@ func (c *containerMounter) mountTmpVFS2(ctx context.Context, conf *config.Config
_, err := c.mountSubmountVFS2(ctx, conf, mns, creds, &mountAndFD{mount: &tmpMount})
return err
- case syserror.ENOTDIR:
+ case linuxerr.Equals(linuxerr.ENOTDIR, err):
// Not a dir?! Let it be.
return nil
diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go
index a14249641..42c66fbcf 100644
--- a/runsc/cmd/boot.go
+++ b/runsc/cmd/boot.go
@@ -157,10 +157,8 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
// we will read it again after the exec call. This works
// because the ReadSpecFromFile function seeks to the beginning
// of the file before reading.
- if err := callSelfAsNobody(args); err != nil {
- Fatalf("%v", err)
- }
- panic("callSelfAsNobody must never return success")
+ Fatalf("callSelfAsNobody(%v): %v", args, callSelfAsNobody(args))
+ panic("unreachable")
}
}
@@ -199,10 +197,8 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
// we will read it again after the exec call. This works
// because the ReadSpecFromFile function seeks to the beginning
// of the file before reading.
- if err := setCapsAndCallSelf(args, caps); err != nil {
- Fatalf("%v", err)
- }
- panic("setCapsAndCallSelf must never return success")
+ Fatalf("setCapsAndCallSelf(%v, %v): %v", args, caps, setCapsAndCallSelf(args, caps))
+ panic("unreachable")
}
// Read resolved mount list and replace the original one from the spec.
diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go
index 6a755ecb6..80da9c9a2 100644
--- a/runsc/cmd/gofer.go
+++ b/runsc/cmd/gofer.go
@@ -116,9 +116,7 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
// Note: minimal argument handling for the default case to keep it simple.
args := os.Args
args = append(args, "--apply-caps=false", "--setup-root=false")
- if err := setCapsAndCallSelf(args, goferCaps); err != nil {
- Fatalf("Unable to apply caps: %v", err)
- }
+ Fatalf("setCapsAndCallSelf(%v, %v): %v", args, goferCaps, setCapsAndCallSelf(args, goferCaps))
panic("unreachable")
}
@@ -473,14 +471,12 @@ func adjustMountOptions(conf *config.Config, path string, opts []string) ([]stri
rv := make([]string, len(opts))
copy(rv, opts)
- if conf.OverlayfsStaleRead {
- statfs := unix.Statfs_t{}
- if err := unix.Statfs(path, &statfs); err != nil {
- return nil, err
- }
- if statfs.Type == unix.OVERLAYFS_SUPER_MAGIC {
- rv = append(rv, "overlayfs_stale_read")
- }
+ statfs := unix.Statfs_t{}
+ if err := unix.Statfs(path, &statfs); err != nil {
+ return nil, err
+ }
+ if statfs.Type == unix.OVERLAYFS_SUPER_MAGIC {
+ rv = append(rv, "overlayfs_stale_read")
}
return rv, nil
}
diff --git a/runsc/config/config.go b/runsc/config/config.go
index fa550ebf7..3d8c7a0ab 100644
--- a/runsc/config/config.go
+++ b/runsc/config/config.go
@@ -151,12 +151,6 @@ type Config struct {
// ReferenceLeakMode sets reference leak check mode
ReferenceLeak refs.LeakMode `flag:"ref-leak-mode"`
- // OverlayfsStaleRead instructs the sandbox to assume that the root mount
- // is on a Linux overlayfs mount, which does not necessarily preserve
- // coherence between read-only and subsequent writable file descriptors
- // representing the "same" file.
- OverlayfsStaleRead bool `flag:"overlayfs-stale-read"`
-
// CPUNumFromQuota sets CPU number count to available CPU quota, using
// least integer value greater than or equal to quota.
//
@@ -245,14 +239,14 @@ func (f *FileAccessType) Get() interface{} {
}
// String implements flag.Value.
-func (f *FileAccessType) String() string {
- switch *f {
+func (f FileAccessType) String() string {
+ switch f {
case FileAccessShared:
return "shared"
case FileAccessExclusive:
return "exclusive"
}
- panic(fmt.Sprintf("Invalid file access type %v", *f))
+ panic(fmt.Sprintf("Invalid file access type %d", f))
}
// NetworkType tells which network stack to use.
@@ -294,8 +288,8 @@ func (n *NetworkType) Get() interface{} {
}
// String implements flag.Value.
-func (n *NetworkType) String() string {
- switch *n {
+func (n NetworkType) String() string {
+ switch n {
case NetworkSandbox:
return "sandbox"
case NetworkHost:
@@ -303,7 +297,7 @@ func (n *NetworkType) String() string {
case NetworkNone:
return "none"
}
- panic(fmt.Sprintf("Invalid network type %v", *n))
+ panic(fmt.Sprintf("Invalid network type %d", n))
}
// QueueingDiscipline is used to specify the kind of Queueing Discipline to
@@ -341,14 +335,14 @@ func (q *QueueingDiscipline) Get() interface{} {
}
// String implements flag.Value.
-func (q *QueueingDiscipline) String() string {
- switch *q {
+func (q QueueingDiscipline) String() string {
+ switch q {
case QDiscNone:
return "none"
case QDiscFIFO:
return "fifo"
}
- panic(fmt.Sprintf("Invalid qdisc %v", *q))
+ panic(fmt.Sprintf("Invalid qdisc %d", q))
}
func leakModePtr(v refs.LeakMode) *refs.LeakMode {
diff --git a/runsc/config/flags.go b/runsc/config/flags.go
index c3dca2352..6f1b5927a 100644
--- a/runsc/config/flags.go
+++ b/runsc/config/flags.go
@@ -72,7 +72,6 @@ func RegisterFlags() {
flag.Var(fileAccessTypePtr(FileAccessShared), "file-access-mounts", "specifies which filesystem validation to use for volumes other than the root mount: shared (default), exclusive.")
flag.Bool("overlay", false, "wrap filesystem mounts with writable overlay. All modifications are stored in memory inside the sandbox.")
flag.Bool("verity", false, "specifies whether a verity file system will be mounted.")
- flag.Bool("overlayfs-stale-read", true, "assume root mount is an overlay filesystem")
flag.Bool("fsgofer-host-uds", false, "allow the gofer to mount Unix Domain Sockets.")
flag.Bool("vfs2", false, "enables VFSv2. This uses the new VFS layer that is faster than the previous one.")
flag.Bool("fuse", false, "TEST ONLY; use while FUSE in VFSv2 is landing. This allows the use of the new experimental FUSE filesystem.")
diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go
index 0e79877b7..249324c5a 100644
--- a/runsc/container/container_test.go
+++ b/runsc/container/container_test.go
@@ -47,6 +47,62 @@ import (
"gvisor.dev/gvisor/runsc/specutils"
)
+func TestMain(m *testing.M) {
+ log.SetLevel(log.Debug)
+ flag.Parse()
+ if err := testutil.ConfigureExePath(); err != nil {
+ panic(err.Error())
+ }
+ specutils.MaybeRunAsRoot()
+ os.Exit(m.Run())
+}
+
+func execute(cont *Container, name string, arg ...string) (unix.WaitStatus, error) {
+ args := &control.ExecArgs{
+ Filename: name,
+ Argv: append([]string{name}, arg...),
+ }
+ return cont.executeSync(args)
+}
+
+func executeCombinedOutput(cont *Container, name string, arg ...string) ([]byte, error) {
+ r, w, err := os.Pipe()
+ if err != nil {
+ return nil, err
+ }
+ defer r.Close()
+
+ args := &control.ExecArgs{
+ Filename: name,
+ Argv: append([]string{name}, arg...),
+ FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, w, w}},
+ }
+ ws, err := cont.executeSync(args)
+ w.Close()
+ if err != nil {
+ return nil, err
+ }
+ if ws != 0 {
+ return nil, fmt.Errorf("exec failed, status: %v", ws)
+ }
+
+ out, err := ioutil.ReadAll(r)
+ return out, err
+}
+
+// executeSync synchronously executes a new process.
+func (c *Container) executeSync(args *control.ExecArgs) (unix.WaitStatus, error) {
+ pid, err := c.Execute(args)
+ if err != nil {
+ return 0, fmt.Errorf("error executing: %v", err)
+ }
+ ws, err := c.WaitPID(pid)
+ if err != nil {
+ return 0, fmt.Errorf("error waiting: %v", err)
+ }
+ return ws, nil
+}
+
// waitForProcessList waits for the given process list to show up in the container.
func waitForProcessList(cont *Container, want []*control.Process) error {
cb := func() error {
@@ -2470,58 +2526,67 @@ func TestBindMountByOption(t *testing.T) {
}
}
-func execute(cont *Container, name string, arg ...string) (unix.WaitStatus, error) {
- args := &control.ExecArgs{
- Filename: name,
- Argv: append([]string{name}, arg...),
+// TestRlimits sets limit to number of open files and checks that the limit
+// is propagated to the container.
+func TestRlimits(t *testing.T) {
+ file, err := ioutil.TempFile(testutil.TmpDir(), "ulimit")
+ if err != nil {
+ t.Fatal(err)
}
- return cont.executeSync(args)
-}
+ cmd := fmt.Sprintf("ulimit -n > %q", file.Name())
-func executeCombinedOutput(cont *Container, name string, arg ...string) ([]byte, error) {
- r, w, err := os.Pipe()
- if err != nil {
- return nil, err
+ spec := testutil.NewSpecWithArgs("sh", "-c", cmd)
+ spec.Process.Rlimits = []specs.POSIXRlimit{
+ {Type: "RLIMIT_NOFILE", Hard: 1000, Soft: 100},
}
- defer r.Close()
- args := &control.ExecArgs{
- Filename: name,
- Argv: append([]string{name}, arg...),
- FilePayload: urpc.FilePayload{Files: []*os.File{os.Stdin, w, w}},
+ conf := testutil.TestConfig(t)
+ if err := run(spec, conf); err != nil {
+ t.Fatalf("Error running container: %v", err)
}
- ws, err := cont.executeSync(args)
- w.Close()
+ got, err := ioutil.ReadFile(file.Name())
if err != nil {
- return nil, err
+ t.Fatal(err)
}
- if ws != 0 {
- return nil, fmt.Errorf("exec failed, status: %v", ws)
+ if want := "100\n"; string(got) != want {
+ t.Errorf("ulimit result, got: %q, want: %q", got, want)
}
-
- out, err := ioutil.ReadAll(r)
- return out, err
}
-// executeSync synchronously executes a new process.
-func (c *Container) executeSync(args *control.ExecArgs) (unix.WaitStatus, error) {
- pid, err := c.Execute(args)
+// TestRlimitsExec sets limit to number of open files and checks that the limit
+// is propagated to exec'd processes.
+func TestRlimitsExec(t *testing.T) {
+ spec := testutil.NewSpecWithArgs("sleep", "100")
+ spec.Process.Rlimits = []specs.POSIXRlimit{
+ {Type: "RLIMIT_NOFILE", Hard: 1000, Soft: 100},
+ }
+
+ conf := testutil.TestConfig(t)
+ _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
if err != nil {
- return 0, fmt.Errorf("error executing: %v", err)
+ t.Fatalf("error setting up container: %v", err)
}
- ws, err := c.WaitPID(pid)
+ defer cleanup()
+
+ args := Args{
+ ID: testutil.RandomContainerID(),
+ Spec: spec,
+ BundleDir: bundleDir,
+ }
+ cont, err := New(conf, args)
if err != nil {
- return 0, fmt.Errorf("error waiting: %v", err)
+ t.Fatalf("error creating container: %v", err)
+ }
+ defer cont.Destroy()
+ if err := cont.Start(conf); err != nil {
+ t.Fatalf("error starting container: %v", err)
}
- return ws, nil
-}
-func TestMain(m *testing.M) {
- log.SetLevel(log.Debug)
- flag.Parse()
- if err := testutil.ConfigureExePath(); err != nil {
- panic(err.Error())
+ got, err := executeCombinedOutput(cont, "/bin/sh", "-c", "ulimit -n")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := "100\n"; string(got) != want {
+ t.Errorf("ulimit result, got: %q, want: %q", got, want)
}
- specutils.MaybeRunAsRoot()
- os.Exit(m.Run())
}
diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go
index 29e202b7d..f14cc7229 100644
--- a/runsc/sandbox/sandbox.go
+++ b/runsc/sandbox/sandbox.go
@@ -797,7 +797,13 @@ func (s *Sandbox) Wait(cid string) (unix.WaitStatus, error) {
// Try the Wait RPC to the sandbox.
var ws unix.WaitStatus
err = conn.Call(boot.ContainerWait, &cid, &ws)
+ conn.Close()
if err == nil {
+ if s.IsRootContainer(cid) {
+ if err := s.waitForStopped(); err != nil {
+ return unix.WaitStatus(0), err
+ }
+ }
// It worked!
return ws, nil
}
diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go
index 11b476690..c228d6299 100644
--- a/runsc/specutils/specutils.go
+++ b/runsc/specutils/specutils.go
@@ -246,7 +246,7 @@ func Capabilities(enableRaw bool, specCaps *specs.LinuxCapabilities) (*auth.Task
if caps.PermittedCaps, err = capsFromNames(specCaps.Permitted, skipSet); err != nil {
return nil, err
}
- // TODO(nlacasse): Support ambient capabilities.
+ // TODO(gvisor.dev/issue/3166): Support ambient capabilities.
}
return &caps, nil
}
diff --git a/test/benchmarks/database/redis_test.go b/test/benchmarks/database/redis_test.go
index f3c4522ac..7497f69b3 100644
--- a/test/benchmarks/database/redis_test.go
+++ b/test/benchmarks/database/redis_test.go
@@ -1,6 +1,6 @@
-// Copyright 2020 The gVisor Authors.
+// Copyright 2021 The gVisor Authors.
//
-// Licensed under the Apache License, Version 2.0 (the "License");
+// Licensed under the Apache License, Version 3.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
@@ -17,6 +17,7 @@ package database
import (
"context"
"os"
+ "strings"
"testing"
"time"
@@ -65,6 +66,34 @@ func BenchmarkRedis(b *testing.B) {
// Redis runs on port 6379 by default.
port := 6379
ctx := context.Background()
+ server := serverMachine.GetContainer(ctx, b)
+ defer server.CleanUp(ctx)
+
+ // The redis docker container takes no arguments to run a redis server.
+ if err := server.Spawn(ctx, dockerutil.RunOpts{
+ Image: "benchmarks/redis",
+ Ports: []int{port},
+ }); err != nil {
+ b.Fatalf("failed to start redis server with: %v", err)
+ }
+
+ if out, err := server.WaitForOutput(ctx, "Ready to accept connections", 3*time.Second); err != nil {
+ b.Fatalf("failed to start redis server: %v %s", err, out)
+ }
+
+ ip, err := serverMachine.IPAddress()
+ if err != nil {
+ b.Fatalf("failed to get IP from server: %v", err)
+ }
+
+ serverPort, err := server.FindPort(ctx, port)
+ if err != nil {
+ b.Fatalf("failed to get IP from server: %v", err)
+ }
+
+ if err = harness.WaitUntilServing(ctx, clientMachine, ip, serverPort); err != nil {
+ b.Fatalf("failed to start redis with: %v", err)
+ }
for _, operation := range operations {
param := tools.Parameter{
Name: "operation",
@@ -74,49 +103,30 @@ func BenchmarkRedis(b *testing.B) {
if err != nil {
b.Fatalf("Failed to parse paramaters: %v", err)
}
- b.Run(name, func(b *testing.B) {
- server := serverMachine.GetContainer(ctx, b)
- defer server.CleanUp(ctx)
-
- // The redis docker container takes no arguments to run a redis server.
- if err := server.Spawn(ctx, dockerutil.RunOpts{
- Image: "benchmarks/redis",
- Ports: []int{port},
- }); err != nil {
- b.Fatalf("failed to start redis server with: %v", err)
- }
- if out, err := server.WaitForOutput(ctx, "Ready to accept connections", 3*time.Second); err != nil {
- b.Fatalf("failed to start redis server: %v %s", err, out)
+ b.Run(name, func(b *testing.B) {
+ redis := tools.Redis{
+ Operation: operation,
}
- ip, err := serverMachine.IPAddress()
- if err != nil {
- b.Fatalf("failed to get IP from server: %v", err)
- }
+ // Sometimes, the connection between the redis client and server can be
+ // flaky such that the client returns infinity as the QPS measurement for
+ // a give operation. If this happens, retry the client up to 3 times.
+ out := "inf"
+ for retries := 0; strings.Contains(out, "inf") && retries < 3; retries++ {
+ b.ResetTimer()
+ client := clientMachine.GetNativeContainer(ctx, b)
+ defer client.CleanUp(ctx)
- serverPort, err := server.FindPort(ctx, port)
- if err != nil {
- b.Fatalf("failed to get IP from server: %v", err)
+ out, err = client.Run(ctx, dockerutil.RunOpts{
+ Image: "benchmarks/redis",
+ }, redis.MakeCmd(ip, serverPort, b.N /*requests*/)...)
}
- if err = harness.WaitUntilServing(ctx, clientMachine, ip, serverPort); err != nil {
- b.Fatalf("failed to start redis with: %v", err)
- }
-
- client := clientMachine.GetNativeContainer(ctx, b)
- defer client.CleanUp(ctx)
-
- redis := tools.Redis{
- Operation: operation,
- }
- b.ResetTimer()
- out, err := client.Run(ctx, dockerutil.RunOpts{
- Image: "benchmarks/redis",
- }, redis.MakeCmd(ip, serverPort, b.N /*requests*/)...)
if err != nil {
b.Fatalf("redis-benchmark failed with: %v", err)
}
+
b.StopTimer()
redis.Report(b, out)
})
diff --git a/test/cmd/test_app/BUILD b/test/cmd/test_app/BUILD
index 98ba5a3d9..7b8b23b4d 100644
--- a/test/cmd/test_app/BUILD
+++ b/test/cmd/test_app/BUILD
@@ -7,7 +7,7 @@ go_binary(
testonly = 1,
srcs = [
"fds.go",
- "test_app.go",
+ "main.go",
],
pure = True,
visibility = ["//runsc/container:__pkg__"],
diff --git a/test/cmd/test_app/test_app.go b/test/cmd/test_app/main.go
index 2d08ce2db..2d08ce2db 100644
--- a/test/cmd/test_app/test_app.go
+++ b/test/cmd/test_app/main.go
diff --git a/test/fuse/BUILD b/test/fuse/BUILD
index 74500ec84..2885ffdb2 100644
--- a/test/fuse/BUILD
+++ b/test/fuse/BUILD
@@ -2,75 +2,77 @@ load("//test/runner:defs.bzl", "syscall_test")
package(licenses = ["notice"])
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:stat_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:open_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:release_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:mknod_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:symlink_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:readlink_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:mkdir_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:read_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:write_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:rmdir_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:readdir_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:create_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:unlink_test",
-)
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:setstat_test",
-)
+# FIXME(b/190750110)
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:stat_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:open_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:release_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:mknod_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:symlink_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:readlink_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:mkdir_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:read_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:write_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:rmdir_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:readdir_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:create_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:unlink_test",
+# )
+#
+# syscall_test(
+# fuse = "True",
+# test = "//test/fuse/linux:setstat_test",
+# )
syscall_test(
fuse = "True",
diff --git a/test/packetimpact/runner/dut.go b/test/packetimpact/runner/dut.go
index 4fb2f5c4b..02678a76a 100644
--- a/test/packetimpact/runner/dut.go
+++ b/test/packetimpact/runner/dut.go
@@ -363,6 +363,9 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
// and receives packets and also sends POSIX socket commands to the
// posix_server to be executed on the DUT.
testArgs := []string{containerTestbenchBinary}
+ if testing.Verbose() {
+ testArgs = append(testArgs, "-test.v")
+ }
testArgs = append(testArgs, extraTestArgs...)
testArgs = append(testArgs,
fmt.Sprintf("--native=%t", native),
@@ -395,6 +398,8 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
} else if expectFailure {
t.Logf(`test failed as expected: %v
%s`, err, testLogs)
+ } else if testing.Verbose() {
+ t.Log(testLogs)
}
}
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
index 269e163bb..7e89ba2b3 100644
--- a/test/packetimpact/testbench/dut.go
+++ b/test/packetimpact/testbench/dut.go
@@ -180,9 +180,7 @@ func (dut *DUT) CreateListener(t *testing.T, typ, proto, backlog int32) (int32,
func (dut *DUT) Accept(t *testing.T, sockfd int32) (int32, unix.Sockaddr) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- fd, sa, err := dut.AcceptWithErrno(ctx, t, sockfd)
+ fd, sa, err := dut.AcceptWithErrno(context.Background(), t, sockfd)
if fd < 0 {
t.Fatalf("failed to accept: %s", err)
}
@@ -209,9 +207,7 @@ func (dut *DUT) AcceptWithErrno(ctx context.Context, t *testing.T, sockfd int32)
func (dut *DUT) Bind(t *testing.T, fd int32, sa unix.Sockaddr) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.BindWithErrno(ctx, t, fd, sa)
+ ret, err := dut.BindWithErrno(context.Background(), t, fd, sa)
if ret != 0 {
t.Fatalf("failed to bind socket: %s", err)
}
@@ -238,9 +234,7 @@ func (dut *DUT) BindWithErrno(ctx context.Context, t *testing.T, fd int32, sa un
func (dut *DUT) Close(t *testing.T, fd int32) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.CloseWithErrno(ctx, t, fd)
+ ret, err := dut.CloseWithErrno(context.Background(), t, fd)
if ret != 0 {
t.Fatalf("failed to close: %s", err)
}
@@ -266,9 +260,7 @@ func (dut *DUT) CloseWithErrno(ctx context.Context, t *testing.T, fd int32) (int
func (dut *DUT) Connect(t *testing.T, fd int32, sa unix.Sockaddr) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.ConnectWithErrno(ctx, t, fd, sa)
+ ret, err := dut.ConnectWithErrno(context.Background(), t, fd, sa)
// Ignore 'operation in progress' error that can be returned when the socket
// is non-blocking.
if err != unix.EINPROGRESS && ret != 0 {
@@ -297,9 +289,7 @@ func (dut *DUT) ConnectWithErrno(ctx context.Context, t *testing.T, fd int32, sa
func (dut *DUT) GetSockName(t *testing.T, sockfd int32) unix.Sockaddr {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, sa, err := dut.GetSockNameWithErrno(ctx, t, sockfd)
+ ret, sa, err := dut.GetSockNameWithErrno(context.Background(), t, sockfd)
if ret != 0 {
t.Fatalf("failed to getsockname: %s", err)
}
@@ -349,9 +339,7 @@ func (dut *DUT) getSockOpt(ctx context.Context, t *testing.T, sockfd, level, opt
func (dut *DUT) GetSockOpt(t *testing.T, sockfd, level, optname, optlen int32) []byte {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, optval, err := dut.GetSockOptWithErrno(ctx, t, sockfd, level, optname, optlen)
+ ret, optval, err := dut.GetSockOptWithErrno(context.Background(), t, sockfd, level, optname, optlen)
if ret != 0 {
t.Fatalf("failed to GetSockOpt: %s", err)
}
@@ -378,9 +366,7 @@ func (dut *DUT) GetSockOptWithErrno(ctx context.Context, t *testing.T, sockfd, l
func (dut *DUT) GetSockOptInt(t *testing.T, sockfd, level, optname int32) int32 {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, intval, err := dut.GetSockOptIntWithErrno(ctx, t, sockfd, level, optname)
+ ret, intval, err := dut.GetSockOptIntWithErrno(context.Background(), t, sockfd, level, optname)
if ret != 0 {
t.Fatalf("failed to GetSockOptInt: %s", err)
}
@@ -405,9 +391,7 @@ func (dut *DUT) GetSockOptIntWithErrno(ctx context.Context, t *testing.T, sockfd
func (dut *DUT) GetSockOptTimeval(t *testing.T, sockfd, level, optname int32) unix.Timeval {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, timeval, err := dut.GetSockOptTimevalWithErrno(ctx, t, sockfd, level, optname)
+ ret, timeval, err := dut.GetSockOptTimevalWithErrno(context.Background(), t, sockfd, level, optname)
if ret != 0 {
t.Fatalf("failed to GetSockOptTimeval: %s", err)
}
@@ -434,9 +418,7 @@ func (dut *DUT) GetSockOptTimevalWithErrno(ctx context.Context, t *testing.T, so
func (dut *DUT) GetSockOptTCPInfo(t *testing.T, sockfd int32) linux.TCPInfo {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, info, err := dut.GetSockOptTCPInfoWithErrno(ctx, t, sockfd)
+ ret, info, err := dut.GetSockOptTCPInfoWithErrno(context.Background(), t, sockfd)
if ret != 0 || err != unix.Errno(0) {
t.Fatalf("failed to GetSockOptTCPInfo: %s", err)
}
@@ -463,9 +445,7 @@ func (dut *DUT) GetSockOptTCPInfoWithErrno(ctx context.Context, t *testing.T, so
func (dut *DUT) Listen(t *testing.T, sockfd, backlog int32) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.ListenWithErrno(ctx, t, sockfd, backlog)
+ ret, err := dut.ListenWithErrno(context.Background(), t, sockfd, backlog)
if ret != 0 {
t.Fatalf("failed to listen: %s", err)
}
@@ -498,8 +478,8 @@ func (dut *DUT) PollOne(t *testing.T, fd int32, events int16, timeout time.Durat
if readyFd := pfds[0].Fd; readyFd != fd {
t.Fatalf("Poll returned an fd %d that was not requested (%d)", readyFd, fd)
}
- if got, want := pfds[0].Revents, int16(events); got&want == 0 {
- t.Fatalf("Poll returned no events in our interest, got: %#b, want: %#b", got, want)
+ if got, want := pfds[0].Revents, int16(events); got&want != want {
+ t.Fatalf("Poll returned events does not include all of the interested events, got: %#b, want: %#b", got, want)
}
}
@@ -510,13 +490,7 @@ func (dut *DUT) PollOne(t *testing.T, fd int32, events int16, timeout time.Durat
func (dut *DUT) Poll(t *testing.T, pfds []unix.PollFd, timeout time.Duration) []unix.PollFd {
t.Helper()
- ctx := context.Background()
- var cancel context.CancelFunc
- if timeout >= 0 {
- ctx, cancel = context.WithTimeout(ctx, timeout+RPCTimeout)
- defer cancel()
- }
- ret, result, err := dut.PollWithErrno(ctx, t, pfds, timeout)
+ ret, result, err := dut.PollWithErrno(context.Background(), t, pfds, timeout)
if ret < 0 {
t.Fatalf("failed to poll: %s", err)
}
@@ -559,9 +533,7 @@ func (dut *DUT) PollWithErrno(ctx context.Context, t *testing.T, pfds []unix.Pol
func (dut *DUT) Send(t *testing.T, sockfd int32, buf []byte, flags int32) int32 {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SendWithErrno(ctx, t, sockfd, buf, flags)
+ ret, err := dut.SendWithErrno(context.Background(), t, sockfd, buf, flags)
if ret == -1 {
t.Fatalf("failed to send: %s", err)
}
@@ -590,9 +562,7 @@ func (dut *DUT) SendWithErrno(ctx context.Context, t *testing.T, sockfd int32, b
func (dut *DUT) SendTo(t *testing.T, sockfd int32, buf []byte, flags int32, destAddr unix.Sockaddr) int32 {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SendToWithErrno(ctx, t, sockfd, buf, flags, destAddr)
+ ret, err := dut.SendToWithErrno(context.Background(), t, sockfd, buf, flags, destAddr)
if ret == -1 {
t.Fatalf("failed to sendto: %s", err)
}
@@ -625,10 +595,8 @@ func (dut *DUT) SetNonBlocking(t *testing.T, fd int32, nonblocking bool) {
Fd: fd,
Nonblocking: nonblocking,
}
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- resp, err := dut.posixServer.SetNonblocking(ctx, req)
+ resp, err := dut.posixServer.SetNonblocking(context.Background(), req)
if err != nil {
t.Fatalf("failed to call SetNonblocking: %s", err)
}
@@ -661,9 +629,7 @@ func (dut *DUT) setSockOpt(ctx context.Context, t *testing.T, sockfd, level, opt
func (dut *DUT) SetSockOpt(t *testing.T, sockfd, level, optname int32, optval []byte) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptWithErrno(ctx, t, sockfd, level, optname, optval)
+ ret, err := dut.SetSockOptWithErrno(context.Background(), t, sockfd, level, optname, optval)
if ret != 0 {
t.Fatalf("failed to SetSockOpt: %s", err)
}
@@ -684,9 +650,7 @@ func (dut *DUT) SetSockOptWithErrno(ctx context.Context, t *testing.T, sockfd, l
func (dut *DUT) SetSockOptInt(t *testing.T, sockfd, level, optname, optval int32) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptIntWithErrno(ctx, t, sockfd, level, optname, optval)
+ ret, err := dut.SetSockOptIntWithErrno(context.Background(), t, sockfd, level, optname, optval)
if ret != 0 {
t.Fatalf("failed to SetSockOptInt: %s", err)
}
@@ -705,9 +669,7 @@ func (dut *DUT) SetSockOptIntWithErrno(ctx context.Context, t *testing.T, sockfd
func (dut *DUT) SetSockOptTimeval(t *testing.T, sockfd, level, optname int32, tv *unix.Timeval) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptTimevalWithErrno(ctx, t, sockfd, level, optname, tv)
+ ret, err := dut.SetSockOptTimevalWithErrno(context.Background(), t, sockfd, level, optname, tv)
if ret != 0 {
t.Fatalf("failed to SetSockOptTimeval: %s", err)
}
@@ -746,8 +708,7 @@ func (dut *DUT) SocketWithErrno(t *testing.T, domain, typ, proto int32) (int32,
Type: typ,
Protocol: proto,
}
- ctx := context.Background()
- resp, err := dut.posixServer.Socket(ctx, req)
+ resp, err := dut.posixServer.Socket(context.Background(), req)
if err != nil {
t.Fatalf("failed to call Socket: %s", err)
}
@@ -760,9 +721,7 @@ func (dut *DUT) SocketWithErrno(t *testing.T, domain, typ, proto int32) (int32,
func (dut *DUT) Recv(t *testing.T, sockfd, len, flags int32) []byte {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, buf, err := dut.RecvWithErrno(ctx, t, sockfd, len, flags)
+ ret, buf, err := dut.RecvWithErrno(context.Background(), t, sockfd, len, flags)
if ret == -1 {
t.Fatalf("failed to recv: %s", err)
}
@@ -805,9 +764,7 @@ func (dut *DUT) SetSockLingerOption(t *testing.T, sockfd int32, timeout time.Dur
func (dut *DUT) Shutdown(t *testing.T, fd, how int32) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.ShutdownWithErrno(ctx, t, fd, how)
+ ret, err := dut.ShutdownWithErrno(context.Background(), t, fd, how)
if ret != 0 {
t.Fatalf("failed to shutdown(%d, %d): %s", fd, how, err)
}
diff --git a/test/packetimpact/testbench/layers.go b/test/packetimpact/testbench/layers.go
index 078f500e4..2644b3248 100644
--- a/test/packetimpact/testbench/layers.go
+++ b/test/packetimpact/testbench/layers.go
@@ -357,7 +357,7 @@ func (l *IPv4) ToBytes() ([]byte, error) {
case *ICMPv4:
fields.Protocol = uint8(header.ICMPv4ProtocolNumber)
default:
- // TODO(b/150301488): Support more protocols as needed.
+ // We can add support for more protocols as needed.
return nil, fmt.Errorf("ipv4 header's next layer is unrecognized: %#v", n)
}
}
diff --git a/test/packetimpact/testbench/rawsockets.go b/test/packetimpact/testbench/rawsockets.go
index feeb0888a..6d95c033d 100644
--- a/test/packetimpact/testbench/rawsockets.go
+++ b/test/packetimpact/testbench/rawsockets.go
@@ -17,7 +17,6 @@ package testbench
import (
"encoding/binary"
"fmt"
- "math"
"net"
"testing"
"time"
@@ -81,19 +80,20 @@ func (s *Sniffer) Recv(t *testing.T, timeout time.Duration) []byte {
deadline := time.Now().Add(timeout)
for {
- timeout = deadline.Sub(time.Now())
+ timeout = time.Until(deadline)
if timeout <= 0 {
return nil
}
- whole, frac := math.Modf(timeout.Seconds())
- tv := unix.Timeval{
- Sec: int64(whole),
- Usec: int64(frac * float64(time.Second/time.Microsecond)),
+ usec := timeout.Microseconds()
+ if usec == 0 {
+ // Timeout is less than a microsecond; set usec to 1 to avoid
+ // blocking indefinitely.
+ usec = 1
}
- // The following should never happen, but having this guard here is better
- // than blocking indefinitely in the future.
- if tv.Sec == 0 && tv.Usec == 0 {
- t.Fatal("setting SO_RCVTIMEO to 0 means blocking indefinitely")
+ const microsInOne = 1e6
+ tv := unix.Timeval{
+ Sec: usec / microsInOne,
+ Usec: usec % microsInOne,
}
if err := unix.SetsockoptTimeval(s.fd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv); err != nil {
t.Fatalf("can't setsockopt SO_RCVTIMEO: %s", err)
diff --git a/test/packetimpact/testbench/testbench.go b/test/packetimpact/testbench/testbench.go
index d0efbcc08..38ae9c1d7 100644
--- a/test/packetimpact/testbench/testbench.go
+++ b/test/packetimpact/testbench/testbench.go
@@ -31,8 +31,6 @@ var (
Native = false
// RPCKeepalive is the gRPC keepalive.
RPCKeepalive = 10 * time.Second
- // RPCTimeout is the gRPC timeout.
- RPCTimeout = 100 * time.Millisecond
// dutInfosJSON is the json string that describes information about all the
// duts available to use.
@@ -124,7 +122,6 @@ func (n *DUTTestNet) SubnetBroadcast() net.IP {
// functions.
func registerFlags(fs *flag.FlagSet) {
fs.BoolVar(&Native, "native", Native, "whether the test is running natively")
- fs.DurationVar(&RPCTimeout, "rpc_timeout", RPCTimeout, "gRPC timeout")
fs.DurationVar(&RPCKeepalive, "rpc_keepalive", RPCKeepalive, "gRPC keepalive")
fs.StringVar(&dutInfosJSON, "dut_infos_json", dutInfosJSON, "json that describes the DUTs")
}
@@ -132,6 +129,7 @@ func registerFlags(fs *flag.FlagSet) {
// Initialize initializes the testbench, it parse the flags and sets up the
// pool of test networks for testbench's later use.
func Initialize(fs *flag.FlagSet) {
+ testing.Init()
registerFlags(fs)
flag.Parse()
if err := loadDUTInfos(); err != nil {
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD
index 37b59b1d9..4cff0cf4c 100644
--- a/test/packetimpact/tests/BUILD
+++ b/test/packetimpact/tests/BUILD
@@ -384,6 +384,7 @@ packetimpact_testbench(
deps = [
"//pkg/tcpip/header",
"//test/packetimpact/testbench",
+ "@com_github_google_go_cmp//cmp:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
diff --git a/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go b/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go
index 00e0f7690..a9ffafc74 100644
--- a/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go
+++ b/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go
@@ -48,7 +48,6 @@ func maxUDPPayloadSize(addr net.IP) int {
func init() {
testbench.Initialize(flag.CommandLine)
- testbench.RPCTimeout = 500 * time.Millisecond
}
func expectedEthLayer(t *testing.T, dut testbench.DUT, socketFD int32, sendTo net.IP) testbench.Layer {
@@ -437,9 +436,7 @@ func (test *icmpV6Test) Send(t *testing.T, dut testbench.DUT, bindTo, sendTo net
copy(destSockaddr.Addr[:], sendTo.To16())
// Tell the DUT to send a packet out the ICMPv6 socket.
- ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout)
- defer cancel()
- gotRet, gotErrno := dut.SendToWithErrno(ctx, t, env.socketFD, bytes, 0, &destSockaddr)
+ gotRet, gotErrno := dut.SendToWithErrno(context.Background(), t, env.socketFD, bytes, 0, &destSockaddr)
if gotErrno != wantErrno {
t.Fatalf("got dut.SendToWithErrno(_, _, %d, _, _, %s) = (_, %s), want = (_, %s)", env.socketFD, sendTo, gotErrno, wantErrno)
@@ -677,9 +674,7 @@ func (test *udpTest) Send(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP
}
// Tell the DUT to send a packet out the UDP socket.
- ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout)
- defer cancel()
- gotRet, gotErrno := dut.SendToWithErrno(ctx, t, env.socketFD, payload, 0, destSockaddr)
+ gotRet, gotErrno := dut.SendToWithErrno(context.Background(), t, env.socketFD, payload, 0, destSockaddr)
if gotErrno != wantErrno {
t.Fatalf("got dut.SendToWithErrno(_, _, %d, _, _, %s) = (_, %s), want = (_, %s)", env.socketFD, sendTo, gotErrno, wantErrno)
diff --git a/test/packetimpact/tests/tcp_connect_icmp_error_test.go b/test/packetimpact/tests/tcp_connect_icmp_error_test.go
index 3b4c4cd63..15d603328 100644
--- a/test/packetimpact/tests/tcp_connect_icmp_error_test.go
+++ b/test/packetimpact/tests/tcp_connect_icmp_error_test.go
@@ -15,9 +15,7 @@
package tcp_connect_icmp_error_test
import (
- "context"
"flag"
- "sync"
"testing"
"time"
@@ -66,35 +64,38 @@ func TestTCPConnectICMPError(t *testing.T) {
t.Fatalf("expected SYN, %s", err)
}
- done := make(chan bool)
- defer close(done)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ // Continuously try to read the ICMP error in an attempt to trigger a race
+ // condition.
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- _, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
for {
select {
case <-done:
return
default:
- if errno := dut.GetSockOptInt(t, clientFD, unix.SOL_SOCKET, unix.SO_ERROR); errno != 0 {
- return
- }
}
+ const want = unix.EHOSTUNREACH
+ switch got := unix.Errno(dut.GetSockOptInt(t, clientFD, unix.SOL_SOCKET, unix.SO_ERROR)); got {
+ case unix.Errno(0):
+ continue
+ case want:
+ return
+ default:
+ t.Fatalf("got SO_ERROR = %s, want %s", got, want)
+ }
+
}
}()
- block.Wait()
+ <-start
sendICMPError(t, &conn, tcp)
dut.PollOne(t, clientFD, unix.POLLHUP, time.Second)
+ <-done
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
// The DUT should reply with RST to our ACK as the state should have
diff --git a/test/packetimpact/tests/tcp_info_test.go b/test/packetimpact/tests/tcp_info_test.go
index b7514e846..5410cc368 100644
--- a/test/packetimpact/tests/tcp_info_test.go
+++ b/test/packetimpact/tests/tcp_info_test.go
@@ -47,7 +47,7 @@ func TestTCPInfo(t *testing.T) {
samplePayload := &testbench.Payload{Bytes: sampleData}
dut.Send(t, acceptFD, sampleData, 0)
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
+ t.Fatalf("expected a packet with payload %s: %s", samplePayload, err)
}
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
@@ -55,20 +55,23 @@ func TestTCPInfo(t *testing.T) {
if got, want := uint32(info.State), linux.TCP_ESTABLISHED; got != want {
t.Fatalf("got %d want %d", got, want)
}
- rtt := time.Duration(info.RTT) * time.Microsecond
- rttvar := time.Duration(info.RTTVar) * time.Microsecond
- rto := time.Duration(info.RTO) * time.Microsecond
- if rtt == 0 || rttvar == 0 || rto == 0 {
- t.Errorf("expected rtt(%v), rttvar(%v) and rto(%v) to be greater than zero", rtt, rttvar, rto)
+ if info.RTT == 0 {
+ t.Errorf("got RTT=0, want nonzero")
+ }
+ if info.RTTVar == 0 {
+ t.Errorf("got RTTVar=0, want nonzero")
+ }
+ if info.RTO == 0 {
+ t.Errorf("got RTO=0, want nonzero")
}
if info.ReordSeen != 0 {
- t.Errorf("expected the connection to not have any reordering, got: %v want: 0", info.ReordSeen)
+ t.Errorf("expected the connection to not have any reordering, got: %d want: 0", info.ReordSeen)
}
if info.SndCwnd == 0 {
t.Errorf("expected send congestion window to be greater than zero")
}
if info.CaState != linux.TCP_CA_Open {
- t.Errorf("expected the connection to be in open state, got: %v want: %v", info.CaState, linux.TCP_CA_Open)
+ t.Errorf("expected the connection to be in open state, got: %d want: %d", info.CaState, linux.TCP_CA_Open)
}
if t.Failed() {
@@ -80,20 +83,20 @@ func TestTCPInfo(t *testing.T) {
seq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))
dut.Send(t, acceptFD, sampleData, 0)
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
+ t.Fatalf("expected a packet with payload %s: %s", samplePayload, err)
}
- // Expect retransmission of the packet within 1.5*RTO.
- timeout := time.Duration(float64(info.RTO)*1.5) * time.Microsecond
+ // Given a generous retransmission timeout.
+ timeout := time.Duration(info.RTO) * 2 * time.Microsecond
if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, timeout); err != nil {
- t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
+ t.Fatalf("expected a packet with payload %s: %s", samplePayload, err)
}
info = dut.GetSockOptTCPInfo(t, acceptFD)
if info.CaState != linux.TCP_CA_Loss {
- t.Errorf("expected the connection to be in loss recovery, got: %v want: %v", info.CaState, linux.TCP_CA_Loss)
+ t.Errorf("expected the connection to be in loss recovery, got: %d want: %d", info.CaState, linux.TCP_CA_Loss)
}
if info.SndCwnd != 1 {
- t.Errorf("expected send congestion window to be 1, got: %v %v", info.SndCwnd)
+ t.Errorf("expected send congestion window to be 1, got: %d", info.SndCwnd)
}
}
diff --git a/test/packetimpact/tests/tcp_linger_test.go b/test/packetimpact/tests/tcp_linger_test.go
index 88942904d..46b5ca5d8 100644
--- a/test/packetimpact/tests/tcp_linger_test.go
+++ b/test/packetimpact/tests/tcp_linger_test.go
@@ -98,20 +98,19 @@ func TestTCPLingerNonZeroTimeout(t *testing.T) {
dut.SetSockLingerOption(t, acceptFD, lingerDuration, tt.lingerOn)
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
+ dut.CloseWithErrno(context.Background(), t, acceptFD)
+ elapsed := time.Since(start)
+
+ expectedMaximum := time.Second
+ if tt.lingerOn {
+ expectedMaximum += lingerDuration
+ if elapsed < lingerDuration {
+ t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
+ }
+ }
+ if elapsed >= expectedMaximum {
+ t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
}
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
@@ -144,20 +143,19 @@ func TestTCPLingerSendNonZeroTimeout(t *testing.T) {
sampleData := []byte("Sample Data")
dut.Send(t, acceptFD, sampleData, 0)
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
+ dut.CloseWithErrno(context.Background(), t, acceptFD)
+ elapsed := time.Since(start)
+
+ expectedMaximum := time.Second
+ if tt.lingerOn {
+ expectedMaximum += lingerDuration
+ if elapsed < lingerDuration {
+ t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
+ }
+ }
+ if elapsed >= expectedMaximum {
+ t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
}
samplePayload := &testbench.Payload{Bytes: sampleData}
@@ -221,20 +219,19 @@ func TestTCPLingerShutdownSendNonZeroTimeout(t *testing.T) {
dut.Shutdown(t, acceptFD, unix.SHUT_RDWR)
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
+ dut.CloseWithErrno(context.Background(), t, acceptFD)
+ elapsed := time.Since(start)
+
+ expectedMaximum := time.Second
+ if tt.lingerOn {
+ expectedMaximum += lingerDuration
+ if elapsed < lingerDuration {
+ t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
+ }
+ }
+ if elapsed >= expectedMaximum {
+ t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
}
samplePayload := &testbench.Payload{Bytes: sampleData}
@@ -259,9 +256,10 @@ func TestTCPLingerNonEstablished(t *testing.T) {
// and return immediately.
start := time.Now()
dut.CloseWithErrno(context.Background(), t, newFD)
- diff := time.Since(start)
+ elapsed := time.Since(start)
- if diff > lingerDuration {
- t.Errorf("expected close to return within %s, but returned after %s", lingerDuration, diff)
+ expectedMaximum := time.Second
+ if elapsed >= time.Second {
+ t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
}
}
diff --git a/test/packetimpact/tests/tcp_listen_backlog_test.go b/test/packetimpact/tests/tcp_listen_backlog_test.go
index 26c812d0a..fea7d5b6f 100644
--- a/test/packetimpact/tests/tcp_listen_backlog_test.go
+++ b/test/packetimpact/tests/tcp_listen_backlog_test.go
@@ -55,15 +55,23 @@ func TestTCPListenBacklog(t *testing.T) {
// Send the ACK to complete handshake.
establishedConn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+
+ // Poll for the established connection ready for accept.
dut.PollOne(t, listenFd, unix.POLLIN, time.Second)
- // Send the ACK to complete handshake, expect this to be ignored by the
- // listener.
+ // Send the ACK to complete handshake, expect this to be dropped by the
+ // listener as the accept queue would be full because of the previous
+ // handshake.
incompleteConn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+ // Let the test wait for sometime so that the ACK is indeed dropped by
+ // the listener. Without such a wait, the DUT accept can race with
+ // ACK handling (dropping) causing the test to be flaky.
+ time.Sleep(100 * time.Millisecond)
// Drain the accept queue to enable poll for subsequent connections on the
// listener.
- dut.Accept(t, listenFd)
+ fd, _ := dut.Accept(t, listenFd)
+ dut.Close(t, fd)
// The ACK for the incomplete connection should be ignored by the
// listening endpoint and the poll on listener should now time out.
diff --git a/test/packetimpact/tests/tcp_network_unreachable_test.go b/test/packetimpact/tests/tcp_network_unreachable_test.go
index 60a2dbf3d..e92e6aa9b 100644
--- a/test/packetimpact/tests/tcp_network_unreachable_test.go
+++ b/test/packetimpact/tests/tcp_network_unreachable_test.go
@@ -41,11 +41,9 @@ func TestTCPSynSentUnreachable(t *testing.T) {
defer conn.Close(t)
// Bring the DUT to SYN-SENT state with a non-blocking connect.
- ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout)
- defer cancel()
sa := unix.SockaddrInet4{Port: int(port)}
copy(sa.Addr[:], dut.Net.LocalIPv4)
- if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != unix.EINPROGRESS {
+ if _, err := dut.ConnectWithErrno(context.Background(), t, clientFD, &sa); err != unix.EINPROGRESS {
t.Errorf("got connect() = %v, want EINPROGRESS", err)
}
@@ -86,14 +84,12 @@ func TestTCPSynSentUnreachable6(t *testing.T) {
defer conn.Close(t)
// Bring the DUT to SYN-SENT state with a non-blocking connect.
- ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout)
- defer cancel()
sa := unix.SockaddrInet6{
Port: int(conn.SrcPort()),
ZoneId: dut.Net.RemoteDevID,
}
copy(sa.Addr[:], dut.Net.LocalIPv6)
- if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != unix.EINPROGRESS {
+ if _, err := dut.ConnectWithErrno(context.Background(), t, clientFD, &sa); err != unix.EINPROGRESS {
t.Errorf("got connect() = %v, want EINPROGRESS", err)
}
diff --git a/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go b/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
index 1c8b72ebe..974c15384 100644
--- a/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
+++ b/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
@@ -20,7 +20,6 @@ import (
"encoding/hex"
"errors"
"flag"
- "sync"
"testing"
"time"
@@ -54,37 +53,39 @@ func TestQueueSendInSynSentHandshake(t *testing.T) {
// Test blocking send.
dut.SetNonBlocking(t, socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
// Issue SEND call in SYN-SENT, this should be queued for
// process until the connection is established.
- n, err := dut.SendWithErrno(ctx, t, socket, sampleData, 0)
- if n == -1 {
+ if _, err := dut.SendWithErrno(context.Background(), t, socket, sampleData, 0); err != unix.Errno(0) {
t.Errorf("failed to send on DUT: %s", err)
- return
}
}()
// Wait for the goroutine to be scheduled and before it
// blocks on endpoint send/receive.
- block.Wait()
+ <-start
// The following sleep is used to prevent the connection
// from being established before we are blocked: there is
// still a small time window between we sending the RPC
// request and the system actually being blocked.
time.Sleep(100 * time.Millisecond)
+ select {
+ case <-done:
+ t.Fatal("expected send to be blocked in SYN-SENT")
+ default:
+ }
+
// Bring the connection to Established.
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)})
+
+ <-done
+
// Expect the data from the DUT's enqueued send request.
//
// On Linux, this can be piggybacked with the ACK completing the
@@ -126,21 +127,16 @@ func TestQueueRecvInSynSentHandshake(t *testing.T) {
// Test blocking read.
dut.SetNonBlocking(t, socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
// Issue RECEIVE call in SYN-SENT, this should be queued for
// process until the connection is established.
- n, buff, err := dut.RecvWithErrno(ctx, t, socket, int32(len(sampleData)), 0)
- if n == -1 {
+ n, buff, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0)
+ if err != unix.Errno(0) {
t.Errorf("failed to recv on DUT: %s", err)
return
}
@@ -151,7 +147,8 @@ func TestQueueRecvInSynSentHandshake(t *testing.T) {
// Wait for the goroutine to be scheduled and before it
// blocks on endpoint send/receive.
- block.Wait()
+ <-start
+
// The following sleep is used to prevent the connection
// from being established before we are blocked: there is
// still a small time window between we sending the RPC
@@ -169,6 +166,8 @@ func TestQueueRecvInSynSentHandshake(t *testing.T) {
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected an ACK from DUT, but got none: %s", err)
}
+
+ <-done
}
// TestQueueSendInSynSentRST tests send behavior when the TCP state
@@ -192,20 +191,15 @@ func TestQueueSendInSynSentRST(t *testing.T) {
// Test blocking send.
dut.SetNonBlocking(t, socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
// Issue SEND call in SYN-SENT, this should be queued for
// process until the connection is established.
- n, err := dut.SendWithErrno(ctx, t, socket, sampleData, 0)
+ n, err := dut.SendWithErrno(context.Background(), t, socket, sampleData, 0)
if err != unix.ECONNREFUSED {
t.Errorf("expected error %s, got %s", unix.ECONNREFUSED, err)
}
@@ -216,14 +210,23 @@ func TestQueueSendInSynSentRST(t *testing.T) {
// Wait for the goroutine to be scheduled and before it
// blocks on endpoint send/receive.
- block.Wait()
+ <-start
+
// The following sleep is used to prevent the connection
// from being established before we are blocked: there is
// still a small time window between we sending the RPC
// request and the system actually being blocked.
time.Sleep(100 * time.Millisecond)
+ select {
+ case <-done:
+ t.Fatal("expected send to be blocked in SYN-SENT")
+ default:
+ }
+
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
+
+ <-done
}
// TestQueueRecvInSynSentRST tests recv behavior when the TCP state
@@ -251,20 +254,15 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
// Test blocking read.
dut.SetNonBlocking(t, socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
// Issue RECEIVE call in SYN-SENT, this should be queued for
// process until the connection is established.
- n, _, err := dut.RecvWithErrno(ctx, t, socket, int32(len(sampleData)), 0)
+ n, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0)
if err != unix.ECONNREFUSED {
t.Errorf("expected error %s, got %s", unix.ECONNREFUSED, err)
}
@@ -275,7 +273,8 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
// Wait for the goroutine to be scheduled and before it
// blocks on endpoint send/receive.
- block.Wait()
+ <-start
+
// The following sleep is used to prevent the connection
// from being established before we are blocked: there is
// still a small time window between we sending the RPC
@@ -283,4 +282,5 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
time.Sleep(100 * time.Millisecond)
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
+ <-done
}
diff --git a/test/packetimpact/tests/tcp_syncookie_test.go b/test/packetimpact/tests/tcp_syncookie_test.go
index 1c21c62ff..6be09996b 100644
--- a/test/packetimpact/tests/tcp_syncookie_test.go
+++ b/test/packetimpact/tests/tcp_syncookie_test.go
@@ -16,9 +16,12 @@ package tcp_syncookie_test
import (
"flag"
+ "fmt"
+ "math"
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/test/packetimpact/testbench"
@@ -28,43 +31,121 @@ func init() {
testbench.Initialize(flag.CommandLine)
}
-// TestSynCookie test if the DUT listener is replying back using syn cookies.
-// The test does not complete the handshake by not sending the ACK to SYNACK.
-// When syncookies are not used, this forces the listener to retransmit SYNACK.
-// And when syncookies are being used, there is no such retransmit.
+// TestTCPSynCookie tests for ACK handling for connections in SYNRCVD state
+// connections with and without syncookies. It verifies if the passive open
+// connection is indeed using syncookies before proceeding.
func TestTCPSynCookie(t *testing.T) {
dut := testbench.NewDUT(t)
+ for _, tt := range []struct {
+ accept bool
+ flags header.TCPFlags
+ }{
+ {accept: true, flags: header.TCPFlagAck},
+ {accept: true, flags: header.TCPFlagAck | header.TCPFlagPsh},
+ {accept: false, flags: header.TCPFlagAck | header.TCPFlagSyn},
+ {accept: true, flags: header.TCPFlagAck | header.TCPFlagFin},
+ {accept: false, flags: header.TCPFlagAck | header.TCPFlagRst},
+ {accept: false, flags: header.TCPFlagRst},
+ } {
+ t.Run(fmt.Sprintf("flags=%s", tt.flags), func(t *testing.T) {
+ // Make a copy before parallelizing the test and refer to that
+ // within the test. Otherwise, the test reference could be pointing
+ // to an incorrect variant based on how it is scheduled.
+ test := tt
- // Listening endpoint accepts one more connection than the listen backlog.
- _, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
+ t.Parallel()
- var withoutSynCookieConn testbench.TCPIPv4
- var withSynCookieConn testbench.TCPIPv4
+ // Listening endpoint accepts one more connection than the listen
+ // backlog. Listener starts using syncookies when it sees a new SYN
+ // and has backlog size of connections in SYNRCVD state. Keep the
+ // listen backlog 1, so that the test can define 2 connections
+ // without and with using syncookies.
+ listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
+ defer dut.Close(t, listenFD)
- // Test if the DUT listener replies to more SYNs than listen backlog+1
- for _, conn := range []*testbench.TCPIPv4{&withoutSynCookieConn, &withSynCookieConn} {
- *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- }
- defer withoutSynCookieConn.Close(t)
- defer withSynCookieConn.Close(t)
+ var withoutSynCookieConn testbench.TCPIPv4
+ var withSynCookieConn testbench.TCPIPv4
- checkSynAck := func(t *testing.T, conn *testbench.TCPIPv4, expectRetransmit bool) {
- // Expect dut connection to have transitioned to SYN-RCVD state.
- conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN-ACK, but got %s", err)
- }
+ for _, conn := range []*testbench.TCPIPv4{&withoutSynCookieConn, &withSynCookieConn} {
+ *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ }
+ defer withoutSynCookieConn.Close(t)
+ defer withSynCookieConn.Close(t)
- // If the DUT listener is using syn cookies, it will not retransmit SYNACK
- got, err := conn.ExpectData(t, &testbench.TCP{SeqNum: testbench.Uint32(uint32(*conn.RemoteSeqNum(t) - 1)), Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, 2*time.Second)
- if expectRetransmit && err != nil {
- t.Fatalf("expected retransmitted SYN-ACK, but got %s", err)
- }
- if !expectRetransmit && err == nil {
- t.Fatalf("expected no retransmitted SYN-ACK, but got %s", got)
- }
- }
+ // Setup the 2 connections in SYNRCVD state and verify if one of the
+ // connection is indeed using syncookies by checking for absence of
+ // SYNACK retransmits.
+ for _, c := range []struct {
+ desc string
+ conn *testbench.TCPIPv4
+ expectRetransmit bool
+ }{
+ {desc: "without syncookies", conn: &withoutSynCookieConn, expectRetransmit: true},
+ {desc: "with syncookies", conn: &withSynCookieConn, expectRetransmit: false},
+ } {
+ t.Run(c.desc, func(t *testing.T) {
+ // Expect dut connection to have transitioned to SYNRCVD state.
+ c.conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
+ if _, err := c.conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
+ t.Fatalf("expected SYNACK, but got %s", err)
+ }
+
+ // If the DUT listener is using syn cookies, it will not retransmit SYNACK.
+ got, err := c.conn.ExpectData(t, &testbench.TCP{SeqNum: testbench.Uint32(uint32(*c.conn.RemoteSeqNum(t) - 1)), Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, 2*time.Second)
+ if c.expectRetransmit && err != nil {
+ t.Fatalf("expected retransmitted SYNACK, but got %s", err)
+ }
+ if !c.expectRetransmit && err == nil {
+ t.Fatalf("expected no retransmitted SYNACK, but got %s", got)
+ }
+ })
+ }
- t.Run("without syncookies", func(t *testing.T) { checkSynAck(t, &withoutSynCookieConn, true /*expectRetransmit*/) })
- t.Run("with syncookies", func(t *testing.T) { checkSynAck(t, &withSynCookieConn, false /*expectRetransmit*/) })
+ // Check whether ACKs with the given flags completes the handshake.
+ for _, c := range []struct {
+ desc string
+ conn *testbench.TCPIPv4
+ }{
+ {desc: "with syncookies", conn: &withSynCookieConn},
+ {desc: "without syncookies", conn: &withoutSynCookieConn},
+ } {
+ t.Run(c.desc, func(t *testing.T) {
+ pfds := dut.Poll(t, []unix.PollFd{{Fd: listenFD, Events: math.MaxInt16}}, 0 /*timeout*/)
+ if got, want := len(pfds), 0; got != want {
+ t.Fatalf("dut.Poll(...) = %d, want = %d", got, want)
+ }
+
+ sampleData := []byte("Sample Data")
+ samplePayload := &testbench.Payload{Bytes: sampleData}
+
+ c.conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(test.flags)}, samplePayload)
+ pfds = dut.Poll(t, []unix.PollFd{{Fd: listenFD, Events: unix.POLLIN}}, time.Second)
+ want := 0
+ if test.accept {
+ want = 1
+ }
+ if got := len(pfds); got != want {
+ t.Fatalf("got dut.Poll(...) = %d, want = %d", got, want)
+ }
+ // Accept the connection to enable poll on any subsequent connection.
+ if test.accept {
+ fd, _ := dut.Accept(t, listenFD)
+ if test.flags.Contains(header.TCPFlagFin) {
+ if dut.Uname.IsLinux() {
+ dut.PollOne(t, fd, unix.POLLIN|unix.POLLRDHUP, time.Second)
+ } else {
+ // TODO(gvisor.dev/issue/6015): Notify POLLIN|POLLRDHUP on incoming FIN.
+ dut.PollOne(t, fd, unix.POLLIN, time.Second)
+ }
+ }
+ got := dut.Recv(t, fd, int32(len(sampleData)), 0)
+ if diff := cmp.Diff(got, sampleData); diff != "" {
+ t.Fatalf("dut.Recv: data mismatch (-want +got):\n%s", diff)
+ }
+ dut.Close(t, fd)
+ }
+ })
+ }
+ })
+ }
}
diff --git a/test/packetimpact/tests/udp_icmp_error_propagation_test.go b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
index 087aeb66e..bb33ca4b3 100644
--- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go
+++ b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
@@ -141,8 +141,6 @@ func testRecv(ctx context.Context, t *testing.T, d testData) {
d.conn.Send(t, testbench.UDP{})
if d.wantErrno != unix.Errno(0) {
- ctx, cancel := context.WithTimeout(ctx, time.Second)
- defer cancel()
ret, _, err := d.dut.RecvWithErrno(ctx, t, d.remoteFD, 100, 0)
if ret != -1 {
t.Fatalf("recv after ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", d.wantErrno)
@@ -167,8 +165,6 @@ func testSendTo(ctx context.Context, t *testing.T, d testData) {
}
if d.wantErrno != unix.Errno(0) {
- ctx, cancel := context.WithTimeout(ctx, time.Second)
- defer cancel()
ret, err := d.dut.SendToWithErrno(ctx, t, d.remoteFD, nil, 0, d.conn.LocalAddr(t))
if ret != -1 {
@@ -315,10 +311,7 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
defer wg.Done()
if wantErrno != unix.Errno(0) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- ret, _, err := dut.RecvWithErrno(ctx, t, remoteFD, 100, 0)
+ ret, _, err := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0)
if ret != -1 {
t.Errorf("recv during ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", wantErrno)
return
@@ -329,10 +322,7 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
}
}
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- if ret, _, err := dut.RecvWithErrno(ctx, t, remoteFD, 100, 0); ret == -1 {
+ if ret, _, err := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0); ret == -1 {
t.Errorf("recv after ICMP error failed with (%[1]d) %[1]", err)
}
}()
@@ -340,10 +330,7 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
go func() {
defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- if ret, _, err := dut.RecvWithErrno(ctx, t, cleanFD, 100, 0); ret == -1 {
+ if ret, _, err := dut.RecvWithErrno(context.Background(), t, cleanFD, 100, 0); ret == -1 {
t.Errorf("recv on clean socket failed with (%[1]d) %[1]", err)
}
}()
diff --git a/test/runner/BUILD b/test/runner/BUILD
index 582d2946d..2d93aa6af 100644
--- a/test/runner/BUILD
+++ b/test/runner/BUILD
@@ -5,9 +5,10 @@ package(licenses = ["notice"])
go_binary(
name = "runner",
testonly = 1,
- srcs = ["runner.go"],
+ srcs = ["main.go"],
data = [
"//runsc",
+ "//test/runner/setup_container",
],
visibility = ["//:sandbox"],
deps = [
diff --git a/test/runner/defs.bzl b/test/runner/defs.bzl
index 416f51935..405e03832 100644
--- a/test/runner/defs.bzl
+++ b/test/runner/defs.bzl
@@ -103,6 +103,8 @@ def _syscall_test(
if platform == "native":
tags.append("nogotsan")
+ container = "container" in tags
+
runner_args = [
# Arguments are passed directly to runner binary.
"--platform=" + platform,
@@ -115,6 +117,7 @@ def _syscall_test(
"--fuse=" + str(fuse),
"--strace=" + str(debug),
"--debug=" + str(debug),
+ "--container=" + str(container),
]
# Call the rule above.
diff --git a/test/runner/runner.go b/test/runner/main.go
index 7e8e88ba2..34e9c6279 100644
--- a/test/runner/runner.go
+++ b/test/runner/main.go
@@ -40,16 +40,18 @@ import (
)
var (
- debug = flag.Bool("debug", false, "enable debug logs")
- strace = flag.Bool("strace", false, "enable strace logs")
- platform = flag.String("platform", "ptrace", "platform to run on")
- network = flag.String("network", "none", "network stack to run on (sandbox, host, none)")
- useTmpfs = flag.Bool("use-tmpfs", false, "mounts tmpfs for /tmp")
- fileAccess = flag.String("file-access", "exclusive", "mounts root in exclusive or shared mode")
- overlay = flag.Bool("overlay", false, "wrap filesystem mounts with writable tmpfs overlay")
- vfs2 = flag.Bool("vfs2", false, "enable VFS2")
- fuse = flag.Bool("fuse", false, "enable FUSE")
- runscPath = flag.String("runsc", "", "path to runsc binary")
+ debug = flag.Bool("debug", false, "enable debug logs")
+ strace = flag.Bool("strace", false, "enable strace logs")
+ platform = flag.String("platform", "ptrace", "platform to run on")
+ network = flag.String("network", "none", "network stack to run on (sandbox, host, none)")
+ useTmpfs = flag.Bool("use-tmpfs", false, "mounts tmpfs for /tmp")
+ fileAccess = flag.String("file-access", "exclusive", "mounts root in exclusive or shared mode")
+ overlay = flag.Bool("overlay", false, "wrap filesystem mounts with writable tmpfs overlay")
+ vfs2 = flag.Bool("vfs2", false, "enable VFS2")
+ fuse = flag.Bool("fuse", false, "enable FUSE")
+ container = flag.Bool("container", false, "run tests in their own namespaces (user ns, network ns, etc), pretending to be root")
+ setupContainerPath = flag.String("setup-container", "", "path to setup_container binary (for use with --container)")
+ runscPath = flag.String("runsc", "", "path to runsc binary")
addUDSTree = flag.Bool("add-uds-tree", false, "expose a tree of UDS utilities for use in tests")
// TODO(gvisor.dev/issue/4572): properly support leak checking for runsc, and
@@ -105,6 +107,27 @@ func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &unix.SysProcAttr{}
+ if *container {
+ // setup_container takes in its target argv as positional arguments.
+ cmd.Path = *setupContainerPath
+ cmd.Args = append([]string{cmd.Path}, cmd.Args...)
+ cmd.SysProcAttr = &unix.SysProcAttr{
+ Cloneflags: unix.CLONE_NEWUSER | unix.CLONE_NEWNET | unix.CLONE_NEWIPC | unix.CLONE_NEWUTS,
+ // Set current user/group as root inside the namespace.
+ UidMappings: []syscall.SysProcIDMap{
+ {ContainerID: 0, HostID: os.Getuid(), Size: 1},
+ },
+ GidMappings: []syscall.SysProcIDMap{
+ {ContainerID: 0, HostID: os.Getgid(), Size: 1},
+ },
+ GidMappingsEnableSetgroups: false,
+ Credential: &syscall.Credential{
+ Uid: 0,
+ Gid: 0,
+ },
+ }
+ }
+
if specutils.HasCapabilities(capability.CAP_SYS_ADMIN) {
cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWUTS
}
@@ -454,6 +477,13 @@ func main() {
}
*runscPath = specutils.ExePath
}
+ if *container && *setupContainerPath == "" {
+ setupContainer, err := testutil.FindFile("test/runner/setup_container/setup_container")
+ if err != nil {
+ fatalf("cannot find setup_container: %v", err)
+ }
+ *setupContainerPath = setupContainer
+ }
// Make sure stdout and stderr are opened with O_APPEND, otherwise logs
// from outside the sandbox can (and will) stomp on logs from inside
diff --git a/test/runner/setup_container/BUILD b/test/runner/setup_container/BUILD
new file mode 100644
index 000000000..5b99d1de9
--- /dev/null
+++ b/test/runner/setup_container/BUILD
@@ -0,0 +1,19 @@
+# setup_container contains a shim binary that runs within the test container
+# for syscall tests with container=True.
+
+load("//tools:defs.bzl", "cc_binary")
+
+package(licenses = ["notice"])
+
+cc_binary(
+ name = "setup_container",
+ testonly = 1,
+ srcs = ["setup_container.cc"],
+ visibility = ["//test/runner:__subpackages__"],
+ deps = [
+ "//test/syscalls/linux:socket_netlink_util",
+ "//test/syscalls/linux:socket_test_util",
+ "//test/util:capability_util",
+ "//test/util:posix_error",
+ ],
+)
diff --git a/test/runner/setup_container/setup_container.cc b/test/runner/setup_container/setup_container.cc
new file mode 100644
index 000000000..9a4e3fb8b
--- /dev/null
+++ b/test/runner/setup_container/setup_container.cc
@@ -0,0 +1,79 @@
+// Copyright 2021 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.
+
+#include <linux/capability.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "test/syscalls/linux/socket_netlink_util.h"
+#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/capability_util.h"
+#include "test/util/posix_error.h"
+
+namespace gvisor {
+namespace testing {
+
+// SetupContainer sets up the networking settings in the current container.
+PosixError SetupContainer() {
+ const PosixErrorOr<bool> have_net_admin = HaveCapability(CAP_NET_ADMIN);
+ if (!have_net_admin.ok()) {
+ std::cerr << "Cannot determine if we have CAP_NET_ADMIN." << std::endl;
+ return have_net_admin.error();
+ }
+ if (have_net_admin.ValueOrDie() && !IsRunningOnGvisor()) {
+ PosixErrorOr<FileDescriptor> sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
+ if (!sockfd.ok()) {
+ std::cerr << "Cannot open socket." << std::endl;
+ return sockfd.error();
+ }
+ int sock = sockfd.ValueOrDie().get();
+ struct ifreq ifr = {};
+ strncpy(ifr.ifr_name, "lo", IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
+ std::cerr << "Cannot get 'lo' flags: " << strerror(errno) << std::endl;
+ return PosixError(errno);
+ }
+ if ((ifr.ifr_flags & IFF_UP) == 0) {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) {
+ std::cerr << "Cannot set 'lo' as UP: " << strerror(errno) << std::endl;
+ return PosixError(errno);
+ }
+ }
+ }
+ return NoError();
+}
+
+} // namespace testing
+} // namespace gvisor
+
+using ::gvisor::testing::SetupContainer;
+
+// Binary setup_container initializes the container environment in which tests
+// with container=True will run, then execs the actual test binary.
+// Usage:
+// ./setup_container test_binary [arguments forwarded to test_binary...]
+int main(int argc, char *argv[], char *envp[]) {
+ if (!SetupContainer().ok()) {
+ return 1;
+ }
+ if (argc < 2) {
+ std::cerr << "Must provide arguments to exec." << std::endl;
+ return 2;
+ }
+ if (execve(argv[1], &argv[1], envp) == -1) {
+ std::cerr << "execv returned errno " << errno << std::endl;
+ return 1;
+ }
+}
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index 99743b14a..de08091af 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -648,6 +648,13 @@ syscall_test(
syscall_test(
size = "large",
shard_count = most_shards,
+ tags = ["container"],
+ test = "//test/syscalls/linux:socket_inet_loopback_isolated_test",
+)
+
+syscall_test(
+ size = "large",
+ shard_count = most_shards,
# Takes too long for TSAN. Creates a lot of TCP sockets.
tags = ["nogotsan"],
test = "//test/syscalls/linux:socket_inet_loopback_nogotsan_test",
@@ -731,6 +738,7 @@ syscall_test(
)
syscall_test(
+ add_hostinet = True,
test = "//test/syscalls/linux:socket_netdevice_test",
)
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index 9a912dba8..2bf685524 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -9,6 +9,8 @@ exports_files(
[
"socket.cc",
"socket_inet_loopback.cc",
+ "socket_inet_loopback_isolated.cc",
+ "socket_inet_loopback_test_params.h",
"socket_ip_loopback_blocking.cc",
"socket_ip_tcp_generic_loopback.cc",
"socket_ip_tcp_loopback.cc",
@@ -1572,10 +1574,13 @@ cc_binary(
srcs = ["ping_socket.cc"],
linkstatic = 1,
deps = [
+ ":ip_socket_test_util",
":socket_test_util",
"//test/util:file_descriptor",
+ "@com_google_absl//absl/algorithm:container",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:optional",
gtest,
- "//test/util:save_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -1880,6 +1885,7 @@ cc_binary(
linkstatic = 1,
deps = [
"@com_google_absl//absl/flags:flag",
+ "@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
gtest,
"//test/util:capability_util",
@@ -3132,6 +3138,16 @@ cc_binary(
],
)
+cc_library(
+ name = "socket_inet_loopback_test_params",
+ testonly = 1,
+ hdrs = ["socket_inet_loopback_test_params.h"],
+ deps = [
+ ":socket_test_util",
+ gtest,
+ ],
+)
+
cc_binary(
name = "socket_inet_loopback_test",
testonly = 1,
@@ -3139,6 +3155,7 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
+ ":socket_inet_loopback_test_params",
":socket_test_util",
"//test/util:file_descriptor",
"@com_google_absl//absl/memory",
@@ -3160,16 +3177,31 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
+ ":socket_inet_loopback_test_params",
":socket_test_util",
"//test/util:file_descriptor",
- "@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
gtest,
"//test/util:posix_error",
"//test/util:save_util",
"//test/util:test_main",
"//test/util:test_util",
- "//test/util:thread_util",
+ ],
+)
+
+cc_binary(
+ name = "socket_inet_loopback_isolated_test",
+ testonly = 1,
+ srcs = ["socket_inet_loopback_isolated.cc"],
+ linkstatic = 1,
+ deps = [
+ ":socket_inet_loopback_test_params",
+ ":socket_netlink_util",
+ ":socket_test_util",
+ gtest,
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "@com_google_absl//absl/time",
],
)
diff --git a/test/syscalls/linux/accept_bind.cc b/test/syscalls/linux/accept_bind.cc
index fe560cfc5..ba3747290 100644
--- a/test/syscalls/linux/accept_bind.cc
+++ b/test/syscalls/linux/accept_bind.cc
@@ -37,8 +37,7 @@ TEST_P(AllSocketPairTest, Listen) {
sockets->first_addr_size()),
SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5),
- SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds());
}
TEST_P(AllSocketPairTest, ListenIncreaseBacklog) {
@@ -48,10 +47,8 @@ TEST_P(AllSocketPairTest, ListenIncreaseBacklog) {
sockets->first_addr_size()),
SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5),
- SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 10),
- SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 10), SyscallSucceeds());
}
TEST_P(AllSocketPairTest, ListenDecreaseBacklog) {
@@ -61,10 +58,8 @@ TEST_P(AllSocketPairTest, ListenDecreaseBacklog) {
sockets->first_addr_size()),
SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5),
- SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 1),
- SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 1), SyscallSucceeds());
}
TEST_P(AllSocketPairTest, ListenBacklogSizes) {
diff --git a/test/syscalls/linux/exec.cc b/test/syscalls/linux/exec.cc
index c5acfc794..a0016146a 100644
--- a/test/syscalls/linux/exec.cc
+++ b/test/syscalls/linux/exec.cc
@@ -283,7 +283,7 @@ TEST(ExecTest, InterpreterScriptTrailingWhitespace) {
TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload)));
TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
- GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " "), 0755));
+ GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " \n"), 0755));
CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(1, 0),
absl::StrCat(link.path(), "\n", script.path(), "\n"));
@@ -304,7 +304,7 @@ TEST(ExecTest, InterpreterScriptArgWhitespace) {
TEST(ExecTest, InterpreterScriptNoPath) {
TempPath script = ASSERT_NO_ERRNO_AND_VALUE(
- TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "#!", 0755));
+ TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "#!\n\n", 0755));
int execve_errno;
ASSERT_NO_ERRNO_AND_VALUE(
diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc
index 95082a0f2..e90a7e411 100644
--- a/test/syscalls/linux/ip_socket_test_util.cc
+++ b/test/syscalls/linux/ip_socket_test_util.cc
@@ -140,6 +140,22 @@ SocketPairKind IPv4UDPUnboundSocketPair(int type) {
/* dual_stack = */ false)};
}
+SocketKind ICMPUnboundSocket(int type) {
+ std::string description =
+ absl::StrCat(DescribeSocketType(type), "ICMP socket");
+ return SocketKind{
+ description, AF_INET, type | SOCK_DGRAM, IPPROTO_ICMP,
+ UnboundSocketCreator(AF_INET, type | SOCK_DGRAM, IPPROTO_ICMP)};
+}
+
+SocketKind ICMPv6UnboundSocket(int type) {
+ std::string description =
+ absl::StrCat(DescribeSocketType(type), "ICMPv6 socket");
+ return SocketKind{
+ description, AF_INET6, type | SOCK_DGRAM, IPPROTO_ICMPV6,
+ UnboundSocketCreator(AF_INET6, type | SOCK_DGRAM, IPPROTO_ICMPV6)};
+}
+
SocketKind IPv4UDPUnboundSocket(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket");
diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h
index 9c3859fcd..bde481f7e 100644
--- a/test/syscalls/linux/ip_socket_test_util.h
+++ b/test/syscalls/linux/ip_socket_test_util.h
@@ -84,20 +84,28 @@ SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type);
// SocketPairs created with AF_INET and the given type.
SocketPairKind IPv4UDPUnboundSocketPair(int type);
+// ICMPUnboundSocket returns a SocketKind that represents a SimpleSocket created
+// with AF_INET, SOCK_DGRAM, IPPROTO_ICMP, and the given type.
+SocketKind ICMPUnboundSocket(int type);
+
+// ICMPv6UnboundSocket returns a SocketKind that represents a SimpleSocket
+// created with AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6, and the given type.
+SocketKind ICMPv6UnboundSocket(int type);
+
// IPv4UDPUnboundSocket returns a SocketKind that represents a SimpleSocket
-// created with AF_INET, SOCK_DGRAM, and the given type.
+// created with AF_INET, SOCK_DGRAM, IPPROTO_UDP, and the given type.
SocketKind IPv4UDPUnboundSocket(int type);
// IPv6UDPUnboundSocket returns a SocketKind that represents a SimpleSocket
-// created with AF_INET6, SOCK_DGRAM, and the given type.
+// created with AF_INET6, SOCK_DGRAM, IPPROTO_UDP, and the given type.
SocketKind IPv6UDPUnboundSocket(int type);
// IPv4TCPUnboundSocket returns a SocketKind that represents a SimpleSocket
-// created with AF_INET, SOCK_STREAM and the given type.
+// created with AF_INET, SOCK_STREAM, IPPROTO_TCP and the given type.
SocketKind IPv4TCPUnboundSocket(int type);
// IPv6TCPUnboundSocket returns a SocketKind that represents a SimpleSocket
-// created with AF_INET6, SOCK_STREAM and the given type.
+// created with AF_INET6, SOCK_STREAM, IPPROTO_TCP and the given type.
SocketKind IPv6TCPUnboundSocket(int type);
// IfAddrHelper is a helper class that determines the local interfaces present
diff --git a/test/syscalls/linux/packet_socket.cc b/test/syscalls/linux/packet_socket.cc
index 861617ff7..1e246c421 100644
--- a/test/syscalls/linux/packet_socket.cc
+++ b/test/syscalls/linux/packet_socket.cc
@@ -55,8 +55,6 @@
//
// These tests require CAP_NET_RAW to run.
-// TODO(gvisor.dev/issue/173): gVisor support.
-
namespace gvisor {
namespace testing {
@@ -188,7 +186,6 @@ void ReceiveMessage(int sock, int ifindex) {
// sizeof(sockaddr_ll).
ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
- // TODO(gvisor.dev/issue/173): Verify protocol once we return it.
// Verify the source address.
EXPECT_EQ(src.sll_family, AF_PACKET);
EXPECT_EQ(src.sll_ifindex, ifindex);
@@ -234,9 +231,6 @@ TEST_P(CookedPacketTest, Receive) {
// Send via a packet socket.
TEST_P(CookedPacketTest, Send) {
- // TODO(gvisor.dev/issue/173): Remove once we support packet socket writing.
- SKIP_IF(IsRunningOnGvisor());
-
// Let's send a UDP packet and receive it using a regular UDP socket.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -290,6 +284,14 @@ TEST_P(CookedPacketTest, Send) {
memcpy(send_buf + sizeof(iphdr), &udphdr, sizeof(udphdr));
memcpy(send_buf + sizeof(iphdr) + sizeof(udphdr), kMessage, sizeof(kMessage));
+ // We don't implement writing to packet sockets on gVisor.
+ if (IsRunningOnGvisor()) {
+ ASSERT_THAT(sendto(socket_, send_buf, sizeof(send_buf), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallFailsWithErrno(EINVAL));
+ GTEST_SKIP();
+ }
+
// Send it.
ASSERT_THAT(sendto(socket_, send_buf, sizeof(send_buf), 0,
reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc
index 72080a272..7e439466e 100644
--- a/test/syscalls/linux/packet_socket_raw.cc
+++ b/test/syscalls/linux/packet_socket_raw.cc
@@ -56,8 +56,6 @@
//
// These tests require CAP_NET_RAW to run.
-// TODO(gvisor.dev/issue/173): gVisor support.
-
namespace gvisor {
namespace testing {
@@ -193,7 +191,6 @@ TEST_P(RawPacketTest, Receive) {
// sizeof(sockaddr_ll).
ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
- // TODO(gvisor.dev/issue/173): Verify protocol once we return it.
// Verify the source address.
EXPECT_EQ(src.sll_family, AF_PACKET);
EXPECT_EQ(src.sll_ifindex, GetLoopbackIndex());
@@ -238,9 +235,6 @@ TEST_P(RawPacketTest, Receive) {
// Send via a packet socket.
TEST_P(RawPacketTest, Send) {
- // TODO(gvisor.dev/issue/173): Remove once we support packet socket writing.
- SKIP_IF(IsRunningOnGvisor());
-
// Let's send a UDP packet and receive it using a regular UDP socket.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -302,6 +296,14 @@ TEST_P(RawPacketTest, Send) {
memcpy(send_buf + sizeof(ethhdr) + sizeof(iphdr) + sizeof(udphdr), kMessage,
sizeof(kMessage));
+ // We don't implement writing to packet sockets on gVisor.
+ if (IsRunningOnGvisor()) {
+ ASSERT_THAT(sendto(s_, send_buf, sizeof(send_buf), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallFailsWithErrno(EINVAL));
+ GTEST_SKIP();
+ }
+
// Send it.
ASSERT_THAT(sendto(s_, send_buf, sizeof(send_buf), 0,
reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
diff --git a/test/syscalls/linux/ping_socket.cc b/test/syscalls/linux/ping_socket.cc
index 8b78e4b16..8268e91da 100644
--- a/test/syscalls/linux/ping_socket.cc
+++ b/test/syscalls/linux/ping_socket.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <errno.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
@@ -19,14 +20,22 @@
#include <sys/types.h>
#include <unistd.h>
+#include <cctype>
+#include <cstring>
#include <vector>
#include "gtest/gtest.h"
+#include "absl/algorithm/container.h"
+#include "absl/strings/str_join.h"
+#include "absl/types/optional.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
-#include "test/util/save_util.h"
#include "test/util/test_util.h"
+// Note: These tests require /proc/sys/net/ipv4/ping_group_range to be
+// configured to allow the tester to create ping sockets (see icmp(7)).
+
namespace gvisor {
namespace testing {
namespace {
@@ -42,7 +51,8 @@ TEST(PingSocket, ICMPPortExhaustion) {
auto s = Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
if (!s.ok()) {
ASSERT_EQ(s.error().errno_value(), EACCES);
- GTEST_SKIP();
+ GTEST_SKIP() << "TODO(gvisor.dev/issue/6126): Buildkite does not allow "
+ "creation of ICMP or ICMPv6 sockets";
}
}
@@ -70,7 +80,212 @@ TEST(PingSocket, ICMPPortExhaustion) {
}
}
-} // namespace
+struct BindTestCase {
+ TestAddress bind_to;
+ int want = 0;
+ absl::optional<int> want_gvisor;
+};
+
+// Test fixture for socket binding.
+class Fixture
+ : public ::testing::TestWithParam<std::tuple<SocketKind, BindTestCase>> {};
+
+TEST_P(Fixture, Bind) {
+ auto [socket_factory, test_case] = GetParam();
+ auto socket = socket_factory.Create();
+ if (!socket.ok()) {
+ ASSERT_EQ(socket.error().errno_value(), EACCES);
+ GTEST_SKIP() << "TODO(gvisor.dev/issue/6126): Buildkite does not allow "
+ "creation of ICMP or ICMPv6 sockets";
+ }
+ auto socket_fd = std::move(socket).ValueOrDie();
+
+ const int want = test_case.want_gvisor.has_value() && IsRunningOnGvisor()
+ ? *test_case.want_gvisor
+ : test_case.want;
+ if (want == 0) {
+ EXPECT_THAT(bind(socket_fd->get(), AsSockAddr(&test_case.bind_to.addr),
+ test_case.bind_to.addr_len),
+ SyscallSucceeds());
+ } else {
+ EXPECT_THAT(bind(socket_fd->get(), AsSockAddr(&test_case.bind_to.addr),
+ test_case.bind_to.addr_len),
+ SyscallFailsWithErrno(want));
+ }
+}
+
+std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() {
+ return ApplyVec<std::tuple<SocketKind, BindTestCase>>(
+ [](const BindTestCase& test_case) {
+ return std::make_tuple(ICMPUnboundSocket(0), test_case);
+ },
+ std::vector<BindTestCase>{
+ {
+ .bind_to = V4Any(),
+ .want = 0,
+ .want_gvisor = 0,
+ },
+ {
+ .bind_to = V4Broadcast(),
+ .want = EADDRNOTAVAIL,
+ // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP
+ // sockets are no longer allowed to bind to broadcast addresses.
+ .want_gvisor = 0,
+ },
+ {
+ .bind_to = V4Loopback(),
+ .want = 0,
+ },
+ {
+ .bind_to = V4LoopbackSubnetBroadcast(),
+ .want = EADDRNOTAVAIL,
+ // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP
+ // sockets are no longer allowed to bind to broadcast addresses.
+ .want_gvisor = 0,
+ },
+ {
+ .bind_to = V4Multicast(),
+ .want = EADDRNOTAVAIL,
+ },
+ {
+ .bind_to = V4MulticastAllHosts(),
+ .want = EADDRNOTAVAIL,
+ },
+ {
+ .bind_to = V4AddrStr("IPv4UnknownUnicast", "192.168.1.1"),
+ .want = EADDRNOTAVAIL,
+ },
+ // TODO(gvisor.dev/issue/6021): Remove want_gvisor from all the test
+ // cases below once ICMP sockets return EAFNOSUPPORT when binding to
+ // IPv6 addresses.
+ {
+ .bind_to = V6Any(),
+ .want = EAFNOSUPPORT,
+ .want_gvisor = EINVAL,
+ },
+ {
+ .bind_to = V6Loopback(),
+ .want = EAFNOSUPPORT,
+ .want_gvisor = EINVAL,
+ },
+ {
+ .bind_to = V6Multicast(),
+ .want = EAFNOSUPPORT,
+ .want_gvisor = EINVAL,
+ },
+ {
+ .bind_to = V6MulticastInterfaceLocalAllNodes(),
+ .want = EAFNOSUPPORT,
+ .want_gvisor = EINVAL,
+ },
+ {
+ .bind_to = V6MulticastLinkLocalAllNodes(),
+ .want = EAFNOSUPPORT,
+ .want_gvisor = EINVAL,
+ },
+ {
+ .bind_to = V6MulticastLinkLocalAllRouters(),
+ .want = EAFNOSUPPORT,
+ .want_gvisor = EINVAL,
+ },
+ {
+ .bind_to = V6AddrStr("IPv6UnknownUnicast", "fc00::1"),
+ .want = EAFNOSUPPORT,
+ .want_gvisor = EINVAL,
+ },
+ });
+}
+std::vector<std::tuple<SocketKind, BindTestCase>> ICMPv6TestCases() {
+ return ApplyVec<std::tuple<SocketKind, BindTestCase>>(
+ [](const BindTestCase& test_case) {
+ return std::make_tuple(ICMPv6UnboundSocket(0), test_case);
+ },
+ std::vector<BindTestCase>{
+ {
+ .bind_to = V4Any(),
+ .want = EINVAL,
+ },
+ {
+ .bind_to = V4Broadcast(),
+ .want = EINVAL,
+ },
+ {
+ .bind_to = V4Loopback(),
+ .want = EINVAL,
+ },
+ {
+ .bind_to = V4LoopbackSubnetBroadcast(),
+ .want = EINVAL,
+ },
+ {
+ .bind_to = V4Multicast(),
+ .want = EINVAL,
+ },
+ {
+ .bind_to = V4MulticastAllHosts(),
+ .want = EINVAL,
+ },
+ {
+ .bind_to = V4AddrStr("IPv4UnknownUnicast", "192.168.1.1"),
+ .want = EINVAL,
+ },
+ {
+ .bind_to = V6Any(),
+ .want = 0,
+ },
+ {
+ .bind_to = V6Loopback(),
+ .want = 0,
+ },
+ // TODO(gvisor.dev/issue/6021): Remove want_gvisor from all the
+ // multicast test cases below once ICMPv6 sockets return EINVAL when
+ // binding to IPv6 multicast addresses.
+ {
+ .bind_to = V6Multicast(),
+ .want = EINVAL,
+ .want_gvisor = EADDRNOTAVAIL,
+ },
+ {
+ .bind_to = V6MulticastInterfaceLocalAllNodes(),
+ .want = EINVAL,
+ .want_gvisor = EADDRNOTAVAIL,
+ },
+ {
+ .bind_to = V6MulticastLinkLocalAllNodes(),
+ .want = EINVAL,
+ .want_gvisor = EADDRNOTAVAIL,
+ },
+ {
+ .bind_to = V6MulticastLinkLocalAllRouters(),
+ .want = EINVAL,
+ .want_gvisor = EADDRNOTAVAIL,
+ },
+ {
+ .bind_to = V6AddrStr("IPv6UnknownUnicast", "fc00::1"),
+ .want = EADDRNOTAVAIL,
+ },
+ });
+}
+
+std::vector<std::tuple<SocketKind, BindTestCase>> AllTestCases() {
+ return VecCat<std::tuple<SocketKind, BindTestCase>>(ICMPTestCases(),
+ ICMPv6TestCases());
+}
+
+std::string TestDescription(
+ const ::testing::TestParamInfo<Fixture::ParamType>& info) {
+ auto [socket_factory, test_case] = info.param;
+ std::string name = absl::StrJoin(
+ {socket_factory.description, test_case.bind_to.description}, "_");
+ absl::c_replace_if(
+ name, [](char c) { return !std::isalnum(c); }, '_');
+ return name;
+}
+
+INSTANTIATE_TEST_SUITE_P(PingSockets, Fixture,
+ ::testing::ValuesIn(AllTestCases()), TestDescription);
+
+} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc
index 294a72468..0bba86846 100644
--- a/test/syscalls/linux/pipe.cc
+++ b/test/syscalls/linux/pipe.cc
@@ -53,6 +53,7 @@ void SigRecordingHandler(int signum, siginfo_t* siginfo,
}
PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) {
+ global_num_signals_received = 0;
struct sigaction handler;
handler.sa_sigaction = SigRecordingHandler;
sigemptyset(&handler.sa_mask);
diff --git a/test/syscalls/linux/poll.cc b/test/syscalls/linux/poll.cc
index 5ce7e8c8d..ccd084244 100644
--- a/test/syscalls/linux/poll.cc
+++ b/test/syscalls/linux/poll.cc
@@ -116,7 +116,7 @@ void BlockingReadableTest(int16_t mask) {
});
notify.WaitForNotification();
- absl::SleepFor(absl::Seconds(1.0));
+ absl::SleepFor(absl::Seconds(1));
// Write some data to the pipe.
char s[] = "foo\n";
@@ -221,7 +221,7 @@ TEST_F(PollTest, BlockingEventPOLLHUP) {
});
notify.WaitForNotification();
- absl::SleepFor(absl::Seconds(1.0));
+ absl::SleepFor(absl::Seconds(1));
// Write some data and close the writer fd.
fd1.reset();
diff --git a/test/syscalls/linux/prctl.cc b/test/syscalls/linux/prctl.cc
index 19a57d353..25b0e63d4 100644
--- a/test/syscalls/linux/prctl.cc
+++ b/test/syscalls/linux/prctl.cc
@@ -101,11 +101,11 @@ TEST(PrctlTest, NoNewPrivsPreservedAcrossCloneForkAndExecve) {
int no_new_privs;
ASSERT_THAT(no_new_privs = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceeds());
- ScopedThread([] {
+ ScopedThread thread = ScopedThread([] {
ASSERT_THAT(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), SyscallSucceeds());
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(1));
- ScopedThread([] {
+ ScopedThread threadInner = ScopedThread([] {
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(1));
// Note that these ASSERT_*s failing will only return from this thread,
@@ -129,9 +129,11 @@ TEST(PrctlTest, NoNewPrivsPreservedAcrossCloneForkAndExecve) {
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(1));
});
+ threadInner.Join();
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(1));
});
+ thread.Join();
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(no_new_privs));
}
@@ -141,7 +143,7 @@ TEST(PrctlTest, PDeathSig) {
// Make the new process' parent a separate thread since the parent death
// signal fires when the parent *thread* exits.
- ScopedThread([&] {
+ ScopedThread thread = ScopedThread([&] {
child_pid = fork();
TEST_CHECK(child_pid >= 0);
if (child_pid == 0) {
@@ -172,6 +174,7 @@ TEST(PrctlTest, PDeathSig) {
// Suppress the SIGSTOP and detach from the child.
ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds());
});
+ thread.Join();
// The child should have been killed by its parent death SIGKILL.
int status;
diff --git a/test/syscalls/linux/priority.cc b/test/syscalls/linux/priority.cc
index 1d9bdfa70..e381248e4 100644
--- a/test/syscalls/linux/priority.cc
+++ b/test/syscalls/linux/priority.cc
@@ -72,7 +72,8 @@ TEST(SetpriorityTest, Implemented) {
// No need to clear errno for setpriority():
// "The setpriority() call returns 0 if there is no error, or -1 if there is"
- EXPECT_THAT(setpriority(PRIO_PROCESS, /*who=*/0, /*nice=*/16),
+ EXPECT_THAT(setpriority(PRIO_PROCESS, /*who=*/0,
+ /*nice=*/16), // NOLINT(bugprone-argument-comment)
SyscallSucceeds());
}
@@ -80,7 +81,8 @@ TEST(SetpriorityTest, Implemented) {
TEST(Setpriority, InvalidWhich) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE)));
- EXPECT_THAT(setpriority(/*which=*/3, /*who=*/0, /*nice=*/16),
+ EXPECT_THAT(setpriority(/*which=*/3, /*who=*/0,
+ /*nice=*/16), // NOLINT(bugprone-argument-comment)
SyscallFailsWithErrno(EINVAL));
}
@@ -88,7 +90,8 @@ TEST(Setpriority, InvalidWhich) {
TEST(SetpriorityTest, ValidWho) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE)));
- EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/16),
+ EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(),
+ /*nice=*/16), // NOLINT(bugprone-argument-comment)
SyscallSucceeds());
}
@@ -142,22 +145,26 @@ TEST(SetpriorityTest, OutsideRange) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE)));
// Set niceval > 19
- EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/100),
+ EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(),
+ /*nice=*/100), // NOLINT(bugprone-argument-comment)
SyscallSucceeds());
errno = 0;
// Test niceval truncated to 19
EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()),
- SyscallSucceedsWithValue(/*maxnice=*/19));
+ SyscallSucceedsWithValue(
+ /*maxnice=*/19)); // NOLINT(bugprone-argument-comment)
// Set niceval < -20
- EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/-100),
+ EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(),
+ /*nice=*/-100), // NOLINT(bugprone-argument-comment)
SyscallSucceeds());
errno = 0;
// Test niceval truncated to -20
EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()),
- SyscallSucceedsWithValue(/*minnice=*/-20));
+ SyscallSucceedsWithValue(
+ /*minnice=*/-20)); // NOLINT(bugprone-argument-comment)
}
// Process is not found when which=PRIO_PROCESS
@@ -167,7 +174,7 @@ TEST(SetpriorityTest, InvalidWho) {
// Flaky, but it's tough to avoid a race condition when finding an unused pid
EXPECT_THAT(setpriority(PRIO_PROCESS,
/*who=*/INT_MAX - 1,
- /*nice=*/16),
+ /*nice=*/16), // NOLINT(bugprone-argument-comment)
SyscallFailsWithErrno(ESRCH));
}
diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc
index 24928d876..78aa73edc 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -2364,6 +2364,15 @@ TEST(ProcSysKernelHostname, MatchesUname) {
EXPECT_EQ(procfs_hostname, hostname);
}
+TEST(ProcSysVmMaxmapCount, HasNumericValue) {
+ const std::string val_str =
+ ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/vm/max_map_count"));
+ int32_t val;
+ EXPECT_TRUE(absl::SimpleAtoi(val_str, &val))
+ << "/proc/sys/vm/max_map_count does not contain a numeric value: "
+ << val_str;
+}
+
TEST(ProcSysVmMmapMinAddr, HasNumericValue) {
const std::string mmap_min_addr_str =
ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/vm/mmap_min_addr"));
diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc
index d519b65e6..f64c23ac0 100644
--- a/test/syscalls/linux/ptrace.cc
+++ b/test/syscalls/linux/ptrace.cc
@@ -30,6 +30,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
+#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/util/capability_util.h"
@@ -51,17 +52,10 @@ ABSL_FLAG(bool, ptrace_test_execve_child, false,
ABSL_FLAG(bool, ptrace_test_trace_descendants_allowed, false,
"If set, run the child workload for "
"PtraceTest_TraceDescendantsAllowed.");
-ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_pid, false,
- "If set, run the child workload for PtraceTest_PrctlSetPtracerPID.");
-ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_any, false,
- "If set, run the child workload for PtraceTest_PrctlSetPtracerAny.");
-ABSL_FLAG(bool, ptrace_test_prctl_clear_ptracer, false,
- "If set, run the child workload for PtraceTest_PrctlClearPtracer.");
-ABSL_FLAG(bool, ptrace_test_prctl_replace_ptracer, false,
- "If set, run the child workload for PtraceTest_PrctlReplacePtracer.");
-ABSL_FLAG(int, ptrace_test_prctl_replace_ptracer_tid, -1,
- "Specifies the replacement tracer tid in the child workload for "
- "PtraceTest_PrctlReplacePtracer.");
+ABSL_FLAG(bool, ptrace_test_ptrace_attacher, false,
+ "If set, run the child workload for PtraceAttacherSubprocess.");
+ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer, false,
+ "If set, run the child workload for PrctlSetPtracerSubprocess.");
ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_and_exit_tracee_thread, false,
"If set, run the child workload for "
"PtraceTest_PrctlSetPtracerPersistsPastTraceeThreadExit.");
@@ -161,6 +155,86 @@ int CheckPtraceAttach(pid_t pid) {
return 0;
}
+class SimpleSubprocess {
+ public:
+ explicit SimpleSubprocess(absl::string_view child_flag) {
+ int sockets[2];
+ TEST_PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
+
+ // Allocate vector before forking (not async-signal-safe).
+ ExecveArray const owned_child_argv = {"/proc/self/exe", child_flag,
+ "--ptrace_test_fd",
+ std::to_string(sockets[0])};
+ char* const* const child_argv = owned_child_argv.get();
+
+ pid_ = fork();
+ if (pid_ == 0) {
+ TEST_PCHECK(close(sockets[1]) == 0);
+ execve(child_argv[0], child_argv, /* envp = */ nullptr);
+ TEST_PCHECK_MSG(false, "Survived execve to test child");
+ }
+ TEST_PCHECK(pid_ > 0);
+ TEST_PCHECK(close(sockets[0]) == 0);
+ sockfd_ = sockets[1];
+ }
+
+ SimpleSubprocess(SimpleSubprocess&& orig)
+ : pid_(orig.pid_), sockfd_(orig.sockfd_) {
+ orig.pid_ = -1;
+ orig.sockfd_ = -1;
+ }
+
+ SimpleSubprocess& operator=(SimpleSubprocess&& orig) {
+ if (this != &orig) {
+ this->~SimpleSubprocess();
+ pid_ = orig.pid_;
+ sockfd_ = orig.sockfd_;
+ orig.pid_ = -1;
+ orig.sockfd_ = -1;
+ }
+ return *this;
+ }
+
+ SimpleSubprocess(SimpleSubprocess const&) = delete;
+ SimpleSubprocess& operator=(SimpleSubprocess const&) = delete;
+
+ ~SimpleSubprocess() {
+ if (pid_ < 0) {
+ return;
+ }
+ EXPECT_THAT(shutdown(sockfd_, SHUT_RDWR), SyscallSucceeds());
+ EXPECT_THAT(close(sockfd_), SyscallSucceeds());
+ int status;
+ EXPECT_THAT(waitpid(pid_, &status, 0), SyscallSucceedsWithValue(pid_));
+ EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ << " status " << status;
+ }
+
+ pid_t pid() const { return pid_; }
+
+ // Sends the child process the given value, receives an errno in response, and
+ // returns a PosixError corresponding to the received errno.
+ template <typename T>
+ PosixError Cmd(T val) {
+ if (WriteFd(sockfd_, &val, sizeof(val)) < 0) {
+ return PosixError(errno, "write failed");
+ }
+ return RecvErrno();
+ }
+
+ private:
+ PosixError RecvErrno() {
+ int resp_errno;
+ if (ReadFd(sockfd_, &resp_errno, sizeof(resp_errno)) < 0) {
+ return PosixError(errno, "read failed");
+ }
+ return PosixError(resp_errno);
+ }
+
+ pid_t pid_ = -1;
+ int sockfd_ = -1;
+};
+
TEST(PtraceTest, AttachSelf) {
EXPECT_THAT(ptrace(PTRACE_ATTACH, gettid(), 0, 0),
SyscallFailsWithErrno(EPERM));
@@ -343,289 +417,128 @@ TEST(PtraceTest, PrctlSetPtracerInvalidPID) {
EXPECT_THAT(prctl(PR_SET_PTRACER, 123456789), SyscallFailsWithErrno(EINVAL));
}
-TEST(PtraceTest, PrctlSetPtracerPID) {
- SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1);
-
- AutoCapability cap(CAP_SYS_PTRACE, false);
-
- // Use sockets to synchronize between tracer and tracee.
- int sockets[2];
- ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds());
-
- // Allocate vector before forking (not async-signal-safe).
- ExecveArray const owned_child_argv = {
- "/proc/self/exe", "--ptrace_test_prctl_set_ptracer_pid",
- "--ptrace_test_fd", std::to_string(sockets[0])};
- char* const* const child_argv = owned_child_argv.get();
-
- pid_t const tracee_pid = fork();
- if (tracee_pid == 0) {
- TEST_PCHECK(close(sockets[1]) == 0);
- // This test will create a new thread in the child process.
- // pthread_create(2) isn't async-signal-safe, so we execve() first.
- execve(child_argv[0], child_argv, /* envp = */ nullptr);
- TEST_PCHECK_MSG(false, "Survived execve to test child");
- }
- ASSERT_THAT(tracee_pid, SyscallSucceeds());
- ASSERT_THAT(close(sockets[0]), SyscallSucceeds());
-
- pid_t const tracer_pid = fork();
- if (tracer_pid == 0) {
- // Wait until tracee has called prctl.
- char done;
- TEST_PCHECK(read(sockets[1], &done, 1) == 1);
- MaybeSave();
-
- TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0);
- _exit(0);
- }
- ASSERT_THAT(tracer_pid, SyscallSucceeds());
-
- // Clean up tracer.
- int status;
- ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds());
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
-
- // Clean up tracee.
- ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(tracee_pid, &status, 0),
- SyscallSucceedsWithValue(tracee_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
+SimpleSubprocess CreatePtraceAttacherSubprocess() {
+ return SimpleSubprocess("--ptrace_test_ptrace_attacher");
}
-[[noreturn]] void RunPrctlSetPtracerPID(int fd) {
- ScopedThread t([fd] {
- // Perform prctl in a separate thread to verify that it is process-wide.
- TEST_PCHECK(prctl(PR_SET_PTRACER, getppid()) == 0);
- MaybeSave();
- // Indicate that the prctl has been set.
- TEST_PCHECK(write(fd, "x", 1) == 1);
- MaybeSave();
+[[noreturn]] static void RunPtraceAttacher(int sockfd) {
+ // execve() may have restored CAP_SYS_PTRACE if we had real UID 0.
+ TEST_CHECK(SetCapability(CAP_SYS_PTRACE, false).ok());
+ // Perform PTRACE_ATTACH in a separate thread to verify that permissions
+ // apply process-wide.
+ ScopedThread t([&] {
+ while (true) {
+ pid_t pid;
+ int rv = read(sockfd, &pid, sizeof(pid));
+ if (rv == 0) {
+ _exit(0);
+ }
+ if (rv < 0) {
+ _exit(1);
+ }
+ int resp_errno = 0;
+ if (CheckPtraceAttach(pid) < 0) {
+ resp_errno = errno;
+ }
+ TEST_PCHECK(write(sockfd, &resp_errno, sizeof(resp_errno)) ==
+ sizeof(resp_errno));
+ }
});
while (true) {
SleepSafe(absl::Seconds(1));
}
}
-TEST(PtraceTest, PrctlSetPtracerAny) {
- SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1);
- AutoCapability cap(CAP_SYS_PTRACE, false);
-
- // Use sockets to synchronize between tracer and tracee.
- int sockets[2];
- ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds());
-
- // Allocate vector before forking (not async-signal-safe).
- ExecveArray const owned_child_argv = {
- "/proc/self/exe", "--ptrace_test_prctl_set_ptracer_any",
- "--ptrace_test_fd", std::to_string(sockets[0])};
- char* const* const child_argv = owned_child_argv.get();
-
- pid_t const tracee_pid = fork();
- if (tracee_pid == 0) {
- // This test will create a new thread in the child process.
- // pthread_create(2) isn't async-signal-safe, so we execve() first.
- TEST_PCHECK(close(sockets[1]) == 0);
- execve(child_argv[0], child_argv, /* envp = */ nullptr);
- TEST_PCHECK_MSG(false, "Survived execve to test child");
- }
- ASSERT_THAT(tracee_pid, SyscallSucceeds());
- ASSERT_THAT(close(sockets[0]), SyscallSucceeds());
-
- pid_t const tracer_pid = fork();
- if (tracer_pid == 0) {
- // Wait until tracee has called prctl.
- char done;
- TEST_PCHECK(read(sockets[1], &done, 1) == 1);
- MaybeSave();
-
- TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0);
- _exit(0);
- }
- ASSERT_THAT(tracer_pid, SyscallSucceeds());
-
- // Clean up tracer.
- int status;
- ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds());
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << " status " << status;
-
- // Clean up tracee.
- ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(tracee_pid, &status, 0),
- SyscallSucceedsWithValue(tracee_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
+SimpleSubprocess CreatePrctlSetPtracerSubprocess() {
+ return SimpleSubprocess("--ptrace_test_prctl_set_ptracer");
}
-[[noreturn]] void RunPrctlSetPtracerAny(int fd) {
- ScopedThread t([fd] {
- // Perform prctl in a separate thread to verify that it is process-wide.
- TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0);
- MaybeSave();
- // Indicate that the prctl has been set.
- TEST_PCHECK(write(fd, "x", 1) == 1);
- MaybeSave();
+[[noreturn]] static void RunPrctlSetPtracer(int sockfd) {
+ // Perform prctl in a separate thread to verify that it applies
+ // process-wide.
+ ScopedThread t([&] {
+ while (true) {
+ pid_t pid;
+ int rv = read(sockfd, &pid, sizeof(pid));
+ if (rv == 0) {
+ _exit(0);
+ }
+ if (rv < 0) {
+ _exit(1);
+ }
+ int resp_errno = 0;
+ if (prctl(PR_SET_PTRACER, pid) < 0) {
+ resp_errno = errno;
+ }
+ TEST_PCHECK(write(sockfd, &resp_errno, sizeof(resp_errno)) ==
+ sizeof(resp_errno));
+ }
});
while (true) {
SleepSafe(absl::Seconds(1));
}
}
-TEST(PtraceTest, PrctlClearPtracer) {
+TEST(PtraceTest, PrctlSetPtracer) {
SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1);
- AutoCapability cap(CAP_SYS_PTRACE, false);
-
- // Use sockets to synchronize between tracer and tracee.
- int sockets[2];
- ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds());
-
- // Allocate vector before forking (not async-signal-safe).
- ExecveArray const owned_child_argv = {
- "/proc/self/exe", "--ptrace_test_prctl_clear_ptracer", "--ptrace_test_fd",
- std::to_string(sockets[0])};
- char* const* const child_argv = owned_child_argv.get();
-
- pid_t const tracee_pid = fork();
- if (tracee_pid == 0) {
- // This test will create a new thread in the child process.
- // pthread_create(2) isn't async-signal-safe, so we execve() first.
- TEST_PCHECK(close(sockets[1]) == 0);
- execve(child_argv[0], child_argv, /* envp = */ nullptr);
- TEST_PCHECK_MSG(false, "Survived execve to test child");
- }
- ASSERT_THAT(tracee_pid, SyscallSucceeds());
- ASSERT_THAT(close(sockets[0]), SyscallSucceeds());
-
- pid_t const tracer_pid = fork();
- if (tracer_pid == 0) {
- // Wait until tracee has called prctl.
- char done;
- TEST_PCHECK(read(sockets[1], &done, 1) == 1);
- MaybeSave();
-
- TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1);
- TEST_PCHECK(errno == EPERM);
- _exit(0);
- }
- ASSERT_THAT(tracer_pid, SyscallSucceeds());
-
- // Clean up tracer.
- int status;
- ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds());
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << " status " << status;
-
- // Clean up tracee.
- ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(tracee_pid, &status, 0),
- SyscallSucceedsWithValue(tracee_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
-}
-
-[[noreturn]] void RunPrctlClearPtracer(int fd) {
- ScopedThread t([fd] {
- // Perform prctl in a separate thread to verify that it is process-wide.
- TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0);
- MaybeSave();
- TEST_PCHECK(prctl(PR_SET_PTRACER, 0) == 0);
- MaybeSave();
- // Indicate that the prctl has been set/cleared.
- TEST_PCHECK(write(fd, "x", 1) == 1);
- MaybeSave();
- });
- while (true) {
- SleepSafe(absl::Seconds(1));
- }
-}
-TEST(PtraceTest, PrctlReplacePtracer) {
- SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1);
AutoCapability cap(CAP_SYS_PTRACE, false);
- pid_t const unused_pid = fork();
- if (unused_pid == 0) {
- while (true) {
- SleepSafe(absl::Seconds(1));
- }
- }
- ASSERT_THAT(unused_pid, SyscallSucceeds());
+ // Ensure that initially, no tracer exception is set.
+ ASSERT_THAT(prctl(PR_SET_PTRACER, 0), SyscallSucceeds());
- // Use sockets to synchronize between tracer and tracee.
- int sockets[2];
- ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds());
+ SimpleSubprocess tracee = CreatePrctlSetPtracerSubprocess();
+ SimpleSubprocess tracer = CreatePtraceAttacherSubprocess();
- // Allocate vector before forking (not async-signal-safe).
- ExecveArray const owned_child_argv = {
- "/proc/self/exe",
- "--ptrace_test_prctl_replace_ptracer",
- "--ptrace_test_prctl_replace_ptracer_tid",
- std::to_string(unused_pid),
- "--ptrace_test_fd",
- std::to_string(sockets[0])};
- char* const* const child_argv = owned_child_argv.get();
+ // By default, Yama should prevent tracer from tracing its parent (this
+ // process) or siblings (tracee).
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(EPERM));
- pid_t const tracee_pid = fork();
- if (tracee_pid == 0) {
- TEST_PCHECK(close(sockets[1]) == 0);
- // This test will create a new thread in the child process.
- // pthread_create(2) isn't async-signal-safe, so we execve() first.
- execve(child_argv[0], child_argv, /* envp = */ nullptr);
- TEST_PCHECK_MSG(false, "Survived execve to test child");
- }
- ASSERT_THAT(tracee_pid, SyscallSucceeds());
- ASSERT_THAT(close(sockets[0]), SyscallSucceeds());
+ // If tracee invokes PR_SET_PTRACER on either tracer's pid, the pid of any of
+ // its ancestors (i.e. us), or PR_SET_PTRACER_ANY, then tracer can trace it
+ // (but not us).
- pid_t const tracer_pid = fork();
- if (tracer_pid == 0) {
- // Wait until tracee has called prctl.
- char done;
- TEST_PCHECK(read(sockets[1], &done, 1) == 1);
- MaybeSave();
+ ASSERT_THAT(tracee.Cmd(tracer.pid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
- TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1);
- TEST_PCHECK(errno == EPERM);
- _exit(0);
- }
- ASSERT_THAT(tracer_pid, SyscallSucceeds());
+ ASSERT_THAT(tracee.Cmd(gettid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
- // Clean up tracer.
- int status;
- ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds());
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << " status " << status;
+ ASSERT_THAT(tracee.Cmd(static_cast<pid_t>(PR_SET_PTRACER_ANY)),
+ PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
- // Clean up tracee.
- ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(tracee_pid, &status, 0),
- SyscallSucceedsWithValue(tracee_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
+ // If tracee invokes PR_SET_PTRACER with pid 0, then tracer can no longer
+ // trace it.
+ ASSERT_THAT(tracee.Cmd(0), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(EPERM));
- // Clean up unused.
- ASSERT_THAT(kill(unused_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(unused_pid, &status, 0),
- SyscallSucceedsWithValue(unused_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
-}
+ // If we invoke PR_SET_PTRACER with tracer's pid, then it can trace us (but
+ // not our descendants).
+ ASSERT_THAT(prctl(PR_SET_PTRACER, tracer.pid()), SyscallSucceeds());
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(EPERM));
-[[noreturn]] void RunPrctlReplacePtracer(int new_tracer_pid, int fd) {
- TEST_PCHECK(prctl(PR_SET_PTRACER, getppid()) == 0);
- MaybeSave();
+ // If we invoke PR_SET_PTRACER with pid 0, then tracer can no longer trace us.
+ ASSERT_THAT(prctl(PR_SET_PTRACER, 0), SyscallSucceeds());
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
- ScopedThread t([new_tracer_pid, fd] {
- TEST_PCHECK(prctl(PR_SET_PTRACER, new_tracer_pid) == 0);
- MaybeSave();
- // Indicate that the prctl has been set.
- TEST_PCHECK(write(fd, "x", 1) == 1);
- MaybeSave();
- });
- while (true) {
- SleepSafe(absl::Seconds(1));
- }
+ // Another thread in our thread group can invoke PR_SET_PTRACER instead; its
+ // effect applies to the whole thread group.
+ pid_t const our_tid = gettid();
+ ScopedThread([&] {
+ ASSERT_THAT(prctl(PR_SET_PTRACER, tracer.pid()), SyscallSucceeds());
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(our_tid), PosixErrorIs(0));
+
+ ASSERT_THAT(prctl(PR_SET_PTRACER, 0), SyscallSucceeds());
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
+ EXPECT_THAT(tracer.Cmd(our_tid), PosixErrorIs(EPERM));
+ }).Join();
}
// Tests that YAMA exceptions store tracees by thread group leader. Exceptions
@@ -2342,21 +2255,12 @@ int main(int argc, char** argv) {
gvisor::testing::RunTraceDescendantsAllowed(fd);
}
- if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer_pid)) {
- gvisor::testing::RunPrctlSetPtracerPID(fd);
- }
-
- if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer_any)) {
- gvisor::testing::RunPrctlSetPtracerAny(fd);
- }
-
- if (absl::GetFlag(FLAGS_ptrace_test_prctl_clear_ptracer)) {
- gvisor::testing::RunPrctlClearPtracer(fd);
+ if (absl::GetFlag(FLAGS_ptrace_test_ptrace_attacher)) {
+ gvisor::testing::RunPtraceAttacher(fd);
}
- if (absl::GetFlag(FLAGS_ptrace_test_prctl_replace_ptracer)) {
- gvisor::testing::RunPrctlReplacePtracer(
- absl::GetFlag(FLAGS_ptrace_test_prctl_replace_ptracer_tid), fd);
+ if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer)) {
+ gvisor::testing::RunPrctlSetPtracer(fd);
}
if (absl::GetFlag(
diff --git a/test/syscalls/linux/raw_socket_hdrincl.cc b/test/syscalls/linux/raw_socket_hdrincl.cc
index 8b3d02d97..4611b6283 100644
--- a/test/syscalls/linux/raw_socket_hdrincl.cc
+++ b/test/syscalls/linux/raw_socket_hdrincl.cc
@@ -177,11 +177,10 @@ TEST_F(RawHDRINCL, ConnectToLoopback) {
SyscallSucceeds());
}
-// FIXME(gvisor.dev/issue/3159): Test currently flaky.
-TEST_F(RawHDRINCL, DISABLED_SendWithoutConnectSucceeds) {
+TEST_F(RawHDRINCL, SendWithoutConnectFails) {
struct iphdr hdr = LoopbackHeader();
ASSERT_THAT(send(socket_, &hdr, sizeof(hdr), 0),
- SyscallSucceedsWithValue(sizeof(hdr)));
+ SyscallFailsWithErrno(EDESTADDRREQ));
}
// HDRINCL implies write-only. Verify that we can't read a packet sent to
@@ -282,9 +281,6 @@ TEST_F(RawHDRINCL, SendAndReceive) {
// Send and receive a packet where the sendto address is not the same as the
// provided destination.
TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
- // FIXME(gvisor.dev/issue/3160): Test currently flaky.
- SKIP_IF(true);
-
int port = 40000;
if (!IsRunningOnGvisor()) {
port = static_cast<short>(ASSERT_NO_ERRNO_AND_VALUE(
@@ -302,18 +298,20 @@ TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
ASSERT_TRUE(
FillPacket(packet, sizeof(packet), port, kPayload, sizeof(kPayload)));
// Overwrite the IP destination address with an IP we can't get to.
+ constexpr int32_t kUnreachable = 42;
struct iphdr iphdr = {};
memcpy(&iphdr, packet, sizeof(iphdr));
- iphdr.daddr = 42;
+ iphdr.daddr = kUnreachable;
memcpy(packet, &iphdr, sizeof(iphdr));
+ // Send to localhost via loopback.
socklen_t addrlen = sizeof(addr_);
ASSERT_NO_FATAL_FAILURE(sendto(socket_, &packet, sizeof(packet), 0,
reinterpret_cast<struct sockaddr*>(&addr_),
addrlen));
- // Receive the payload, since sendto should replace the bad destination with
- // localhost.
+ // Receive the payload. Despite an unreachable destination address, sendto
+ // should have sent the packet through loopback.
char recv_buf[sizeof(packet)];
struct sockaddr_in src;
socklen_t src_size = sizeof(src);
@@ -331,9 +329,8 @@ TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
struct iphdr recv_iphdr = {};
memcpy(&recv_iphdr, recv_buf, sizeof(recv_iphdr));
EXPECT_NE(recv_iphdr.id, 0);
- // The destination address should be localhost, not the bad IP we set
- // initially.
- EXPECT_EQ(absl::gbswap_32(recv_iphdr.daddr), INADDR_LOOPBACK);
+ // The destination address is kUnreachable despite arriving via loopback.
+ EXPECT_EQ(recv_iphdr.daddr, kUnreachable);
}
// Send and receive a packet w/ the IP_HDRINCL option set.
diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc
index bd779da92..275996bd3 100644
--- a/test/syscalls/linux/raw_socket_icmp.cc
+++ b/test/syscalls/linux/raw_socket_icmp.cc
@@ -262,8 +262,8 @@ TEST_F(RawSocketICMPTest, RawAndPingSockets) {
}
// A raw ICMP socket should be able to send a malformed short ICMP Echo Request,
-// while ping socket should not.
-// Neither should be able to receieve a short malformed packet.
+// while a ping socket should not. Neither should be able to receieve a short
+// malformed packet.
TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
diff --git a/test/syscalls/linux/rename.cc b/test/syscalls/linux/rename.cc
index 76a8da65f..561eed99f 100644
--- a/test/syscalls/linux/rename.cc
+++ b/test/syscalls/linux/rename.cc
@@ -26,6 +26,8 @@
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
+using ::testing::AnyOf;
+
namespace gvisor {
namespace testing {
@@ -438,6 +440,60 @@ TEST(RenameTest, SysfsDirectoryToSelf) {
EXPECT_THAT(rename(path.c_str(), path.c_str()), SyscallSucceeds());
}
+#ifndef SYS_renameat2
+#if defined(__x86_64__)
+#define SYS_renameat2 316
+#elif defined(__aarch64__)
+#define SYS_renameat2 276
+#else
+#error "Unknown architecture"
+#endif
+#endif // SYS_renameat2
+
+#ifndef RENAME_NOREPLACE
+#define RENAME_NOREPLACE (1 << 0)
+#endif // RENAME_NOREPLACE
+
+int renameat2(int olddirfd, const char* oldpath, int newdirfd,
+ const char* newpath, unsigned int flags) {
+ return syscall(SYS_renameat2, olddirfd, oldpath, newdirfd, newpath, flags);
+}
+
+TEST(Renameat2Test, NoReplaceSuccess) {
+ auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ std::string const newpath = NewTempAbsPath();
+ // renameat2 may fail with ENOSYS (if the syscall is unsupported) or EINVAL
+ // (if flags are unsupported), or succeed (if RENAME_NOREPLACE is operating
+ // correctly).
+ EXPECT_THAT(
+ renameat2(AT_FDCWD, f.path().c_str(), AT_FDCWD, newpath.c_str(),
+ RENAME_NOREPLACE),
+ AnyOf(SyscallFailsWithErrno(AnyOf(ENOSYS, EINVAL)), SyscallSucceeds()));
+}
+
+TEST(Renameat2Test, NoReplaceExisting) {
+ auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ // renameat2 may fail with ENOSYS (if the syscall is unsupported), EINVAL (if
+ // flags are unsupported), or EEXIST (if RENAME_NOREPLACE is operating
+ // correctly).
+ EXPECT_THAT(renameat2(AT_FDCWD, f1.path().c_str(), AT_FDCWD,
+ f2.path().c_str(), RENAME_NOREPLACE),
+ SyscallFailsWithErrno(AnyOf(ENOSYS, EINVAL, EEXIST)));
+}
+
+TEST(Renameat2Test, NoReplaceDot) {
+ auto d1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ auto d2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ // renameat2 may fail with ENOSYS (if the syscall is unsupported), EINVAL (if
+ // flags are unsupported), or EEXIST (if RENAME_NOREPLACE is operating
+ // correctly).
+ EXPECT_THAT(
+ renameat2(AT_FDCWD, d1.path().c_str(), AT_FDCWD,
+ absl::StrCat(d2.path(), "/.").c_str(), RENAME_NOREPLACE),
+ SyscallFailsWithErrno(AnyOf(ENOSYS, EINVAL, EEXIST)));
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc
index 2ce8f836c..f72957f89 100644
--- a/test/syscalls/linux/semaphore.cc
+++ b/test/syscalls/linux/semaphore.cc
@@ -49,6 +49,9 @@ constexpr int kSemAem = 32767;
class AutoSem {
public:
+ // Creates a new private semaphore.
+ AutoSem() : id_(semget(IPC_PRIVATE, 1, 0)) {}
+
explicit AutoSem(int id) : id_(id) {}
~AutoSem() {
if (id_ >= 0) {
@@ -101,6 +104,20 @@ TEST(SemaphoreTest, SemGet) {
EXPECT_NE(sem3.get(), sem2.get());
}
+// Tests system-wide limits for semget.
+TEST(SemaphoreTest, SemGetSystemLimits) {
+ // Disable save so that we don't trigger save/restore too many times.
+ const DisableSave ds;
+
+ // Exceed number of semaphores per set.
+ EXPECT_THAT(semget(IPC_PRIVATE, kSemMsl + 1, 0),
+ SyscallFailsWithErrno(EINVAL));
+
+ // Exceed system-wide limit for semaphore sets by 1.
+ AutoSem sems[kSemMni];
+ EXPECT_THAT(semget(IPC_PRIVATE, 1, 0), SyscallFailsWithErrno(ENOSPC));
+}
+
// Tests simple operations that shouldn't block in a single-thread.
TEST(SemaphoreTest, SemOpSingleNoBlock) {
AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT));
diff --git a/test/syscalls/linux/socket_bind_to_device_distribution.cc b/test/syscalls/linux/socket_bind_to_device_distribution.cc
index 3b108cbd3..70b0b2742 100644
--- a/test/syscalls/linux/socket_bind_to_device_distribution.cc
+++ b/test/syscalls/linux/socket_bind_to_device_distribution.cc
@@ -77,34 +77,6 @@ class BindToDeviceDistributionTest
}
};
-PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
- switch (family) {
- case AF_INET:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
- case AF_INET6:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
- switch (family) {
- case AF_INET:
- reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
- return NoError();
- case AF_INET6:
- reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
- return NoError();
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
// Binds sockets to different devices and then creates many TCP connections.
// Checks that the distribution of connections received on the sockets matches
// the expectation.
diff --git a/test/syscalls/linux/socket_generic_stress.cc b/test/syscalls/linux/socket_generic_stress.cc
index c35aa2183..778c32a8e 100644
--- a/test/syscalls/linux/socket_generic_stress.cc
+++ b/test/syscalls/linux/socket_generic_stress.cc
@@ -37,49 +37,11 @@
namespace gvisor {
namespace testing {
-constexpr char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range";
-
-PosixErrorOr<int> NumPorts() {
- int min = 0;
- int max = 1 << 16;
-
- // Read the ephemeral range from /proc.
- ASSIGN_OR_RETURN_ERRNO(std::string rangefile, GetContents(kRangeFile));
- const std::string err_msg =
- absl::StrFormat("%s has invalid content: %s", kRangeFile, rangefile);
- if (rangefile.back() != '\n') {
- return PosixError(EINVAL, err_msg);
- }
- rangefile.pop_back();
- std::vector<std::string> range =
- absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
- if (range.size() < 2 || !absl::SimpleAtoi(range.front(), &min) ||
- !absl::SimpleAtoi(range.back(), &max)) {
- return PosixError(EINVAL, err_msg);
- }
-
- // If we can open as writable, limit the range.
- if (!access(kRangeFile, W_OK)) {
- ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd,
- Open(kRangeFile, O_WRONLY | O_TRUNC, 0));
- max = min + 50;
- const std::string small_range = absl::StrFormat("%d %d", min, max);
- int n = write(fd.get(), small_range.c_str(), small_range.size());
- if (n < 0) {
- return PosixError(
- errno,
- absl::StrFormat("write(%d [%s], \"%s\", %d)", fd.get(), kRangeFile,
- small_range.c_str(), small_range.size()));
- }
- }
- return max - min;
-}
-
// Test fixture for tests that apply to pairs of connected sockets.
using ConnectStressTest = SocketPairTest;
TEST_P(ConnectStressTest, Reset) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
for (int i = 0; i < nports * 2; i++) {
const std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
@@ -103,7 +65,7 @@ TEST_P(ConnectStressTest, Reset) {
// Tests that opening too many connections -- without closing them -- does lead
// to port exhaustion.
TEST_P(ConnectStressTest, TooManyOpen) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
int err_num = 0;
std::vector<std::unique_ptr<SocketPair>> sockets =
std::vector<std::unique_ptr<SocketPair>>(nports);
@@ -164,7 +126,7 @@ class PersistentListenerConnectStressTest : public SocketPairTest {
};
TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
for (int i = 0; i < nports * 2; i++) {
std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
@@ -185,7 +147,7 @@ TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) {
}
TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
for (int i = 0; i < nports * 2; i++) {
const std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
@@ -206,7 +168,7 @@ TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) {
}
TEST_P(PersistentListenerConnectStressTest, Close) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
for (int i = 0; i < nports * 2; i++) {
std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index 2fc160cdd..badc42ec5 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -34,6 +34,7 @@
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/syscalls/linux/socket_inet_loopback_test_params.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
@@ -48,45 +49,7 @@ namespace {
using ::testing::Gt;
-PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
- switch (family) {
- case AF_INET:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
- case AF_INET6:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
- switch (family) {
- case AF_INET:
- reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
- return NoError();
- case AF_INET6:
- reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
- return NoError();
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-struct TestParam {
- TestAddress listener;
- TestAddress connector;
-};
-
-std::string DescribeTestParam(::testing::TestParamInfo<TestParam> const& info) {
- return absl::StrCat("Listen", info.param.listener.description, "_Connect",
- info.param.connector.description);
-}
-
-using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>;
+using SocketInetLoopbackTest = ::testing::TestWithParam<SocketInetTestParam>;
TEST(BadSocketPairArgs, ValidateErrForBadCallsToSocketPair) {
int fd[2] = {};
@@ -299,7 +262,7 @@ void tcpSimpleConnectTest(TestAddress const& listener,
}
TEST_P(SocketInetLoopbackTest, TCP) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -307,7 +270,7 @@ TEST_P(SocketInetLoopbackTest, TCP) {
}
TEST_P(SocketInetLoopbackTest, TCPListenUnbound) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -316,7 +279,7 @@ TEST_P(SocketInetLoopbackTest, TCPListenUnbound) {
}
TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) {
- const auto& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
const TestAddress& listener = param.listener;
const TestAddress& connector = param.connector;
@@ -362,13 +325,14 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) {
}
TEST_P(SocketInetLoopbackTest, TCPListenShutdown) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
constexpr int kBacklog = 2;
- constexpr int kFDs = kBacklog + 1;
+ // See the comment in TCPBacklog for why this isn't kBacklog + 1.
+ constexpr int kFDs = kBacklog;
// Create the listening socket.
FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
@@ -429,7 +393,7 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdown) {
}
TEST_P(SocketInetLoopbackTest, TCPListenClose) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -454,6 +418,8 @@ TEST_P(SocketInetLoopbackTest, TCPListenClose) {
uint16_t const port =
ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+ // Connect repeatedly, keeping each connection open. After kBacklog
+ // connections, we'll start getting EINPROGRESS.
sockaddr_storage conn_addr = connector.addr;
ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
std::vector<FileDescriptor> clients;
@@ -474,7 +440,7 @@ TEST_P(SocketInetLoopbackTest, TCPListenClose) {
// Test the protocol state information returned by TCPINFO.
TEST_P(SocketInetLoopbackTest, TCPInfoState) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -543,7 +509,7 @@ TEST_P(SocketInetLoopbackTest, TCPInfoState) {
ASSERT_THAT(close(conn_fd.release()), SyscallSucceeds());
}
-void TestHangupDuringConnect(const TestParam& param,
+void TestHangupDuringConnect(const SocketInetTestParam& param,
void (*hangup)(FileDescriptor&)) {
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -606,8 +572,10 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownDuringConnect) {
});
}
-void TestListenHangupConnectingRead(const TestParam& param,
+void TestListenHangupConnectingRead(const SocketInetTestParam& param,
void (*hangup)(FileDescriptor&)) {
+ constexpr int kTimeout = 10000;
+
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -637,14 +605,33 @@ void TestListenHangupConnectingRead(const TestParam& param,
sockaddr_storage conn_addr = connector.addr;
ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
FileDescriptor established_client = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(connect(established_client.get(), AsSockAddr(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
+ Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
+ int ret = connect(established_client.get(), AsSockAddr(&conn_addr),
+ connector.addr_len);
+ if (ret != 0) {
+ EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS));
+ }
- // Ensure that the accept queue has the completed connection.
- constexpr int kTimeout = 10000;
+ // On some kernels a backlog of 0 means no backlog, while on others it means a
+ // backlog of 1. See commit c609e6aae4efcf383fe86b195d1b060befcb3666 for more
+ // explanation.
+ //
+ // If we timeout connecting to loopback, we're on a kernel with no backlog.
pollfd pfd = {
+ .fd = established_client.get(),
+ .events = POLLIN | POLLOUT,
+ };
+ if (!poll(&pfd, 1, kTimeout)) {
+ // We're on one of those kernels. It should be impossible to establish the
+ // connection, so connect will always return EALREADY.
+ EXPECT_THAT(connect(established_client.get(), AsSockAddr(&conn_addr),
+ connector.addr_len),
+ SyscallFailsWithErrno(EALREADY));
+ return;
+ }
+
+ // Ensure that the accept queue has the completed connection.
+ pfd = {
.fd = listen_fd.get(),
.events = POLLIN,
};
@@ -654,8 +641,8 @@ void TestListenHangupConnectingRead(const TestParam& param,
FileDescriptor connecting_client = ASSERT_NO_ERRNO_AND_VALUE(
Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
// Keep the last client in connecting state.
- int ret = connect(connecting_client.get(), AsSockAddr(&conn_addr),
- connector.addr_len);
+ ret = connect(connecting_client.get(), AsSockAddr(&conn_addr),
+ connector.addr_len);
if (ret != 0) {
EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS));
}
@@ -692,12 +679,84 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownConnectingRead) {
});
}
+// Test close of a non-blocking connecting socket.
+TEST_P(SocketInetLoopbackTest, TCPNonBlockingConnectClose) {
+ SocketInetTestParam const& param = GetParam();
+ TestAddress const& listener = param.listener;
+ TestAddress const& connector = param.connector;
+
+ // Create the listening socket.
+ FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
+ sockaddr_storage listen_addr = listener.addr;
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(&listen_addr), listener.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), 0), SyscallSucceeds());
+
+ // Get the port bound by the listening socket.
+ socklen_t addrlen = listener.addr_len;
+ ASSERT_THAT(getsockname(listen_fd.get(), AsSockAddr(&listen_addr), &addrlen),
+ SyscallSucceeds());
+ ASSERT_EQ(addrlen, listener.addr_len);
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+
+ // Try many iterations to catch a race with socket close and handshake
+ // completion.
+ for (int i = 0; i < 1000; ++i) {
+ FileDescriptor client = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
+ ASSERT_THAT(
+ connect(client.get(), AsSockAddr(&conn_addr), connector.addr_len),
+ SyscallFailsWithErrno(EINPROGRESS));
+ ASSERT_THAT(close(client.release()), SyscallSucceeds());
+
+ // Accept any connections and check if they were closed from the peer. Not
+ // all client connects would result in an acceptable connection as the
+ // client handshake might never complete if the socket close was processed
+ // sooner than the non-blocking connect OR the accept queue is full. We are
+ // only interested in the case where we do have an acceptable completed
+ // connection. The accept is non-blocking here, which means that at the time
+ // of listener close (after the loop ends), we could still have a completed
+ // connection (from connect of any previous iteration) in the accept queue.
+ // The listener close would clean up the accept queue.
+ int accepted_fd;
+ ASSERT_THAT(accepted_fd = accept(listen_fd.get(), nullptr, nullptr),
+ AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(EWOULDBLOCK)));
+ if (accepted_fd < 0) {
+ continue;
+ }
+ FileDescriptor accepted(accepted_fd);
+ struct pollfd pfd = {
+ .fd = accepted.get(),
+ .events = POLLIN | POLLRDHUP,
+ };
+ // Use a large timeout to accomodate for retransmitted FINs.
+ constexpr int kTimeout = 30000;
+ int n = poll(&pfd, 1, kTimeout);
+ ASSERT_GE(n, 0) << strerror(errno);
+ ASSERT_EQ(n, 1);
+
+ if (IsRunningOnGvisor() && GvisorPlatform() != Platform::kFuchsia) {
+ // TODO(gvisor.dev/issue/6015): Notify POLLRDHUP on incoming FIN.
+ ASSERT_EQ(pfd.revents, POLLIN);
+ } else {
+ ASSERT_EQ(pfd.revents, POLLIN | POLLRDHUP);
+ }
+ ASSERT_THAT(close(accepted.release()), SyscallSucceeds());
+ }
+}
+
// TODO(b/157236388): Remove once bug is fixed. Test fails w/
// random save as established connections which can't be delivered to the accept
// queue because the queue is full are not correctly delivered after restore
// causing the last accept to timeout on the restore.
TEST_P(SocketInetLoopbackTest, TCPAcceptBacklogSizes) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -723,7 +782,8 @@ TEST_P(SocketInetLoopbackTest, TCPAcceptBacklogSizes) {
if (backlog < 0) {
expected_accepts = 1024;
} else {
- expected_accepts = backlog + 1;
+ // See the comment in TCPBacklog for why this isn't backlog + 1.
+ expected_accepts = backlog;
}
for (int i = 0; i < expected_accepts; i++) {
SCOPED_TRACE(absl::StrCat("i=", i));
@@ -746,7 +806,7 @@ TEST_P(SocketInetLoopbackTest, TCPAcceptBacklogSizes) {
// queue because the queue is full are not correctly delivered after restore
// causing the last accept to timeout on the restore.
TEST_P(SocketInetLoopbackTest, TCPBacklog) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -824,7 +884,11 @@ TEST_P(SocketInetLoopbackTest, TCPBacklog) {
// enqueuing established connections to the accept queue, newer SYNs could
// still be replied to causing those client connections would be accepted as
// we start dequeuing the queue.
- ASSERT_GE(accepted_conns, kBacklogSize + 1);
+ //
+ // On some kernels this can value can be off by one, so we don't add 1 to
+ // kBacklogSize. See commit c609e6aae4efcf383fe86b195d1b060befcb3666 for more
+ // explanation.
+ ASSERT_GE(accepted_conns, kBacklogSize);
ASSERT_GE(client_conns, accepted_conns);
}
@@ -833,7 +897,7 @@ TEST_P(SocketInetLoopbackTest, TCPBacklog) {
// queue because the queue is full are not correctly delivered after restore
// causing the last accept to timeout on the restore.
TEST_P(SocketInetLoopbackTest, TCPBacklogAcceptAll) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -859,7 +923,9 @@ TEST_P(SocketInetLoopbackTest, TCPBacklogAcceptAll) {
// Fill up the accept queue and trigger more client connections which would be
// waiting to be accepted.
- std::array<FileDescriptor, kBacklog + 1> established_clients;
+ //
+ // See the comment in TCPBacklog for why this isn't backlog + 1.
+ std::array<FileDescriptor, kBacklog> established_clients;
for (auto& fd : established_clients) {
fd = ASSERT_NO_ERRNO_AND_VALUE(
Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
@@ -921,175 +987,12 @@ TEST_P(SocketInetLoopbackTest, TCPBacklogAcceptAll) {
}
}
-// TCPFinWait2Test creates a pair of connected sockets then closes one end to
-// trigger FIN_WAIT2 state for the closed endpoint. Then it binds the same local
-// IP/port on a new socket and tries to connect. The connect should fail w/
-// an EADDRINUSE. Then we wait till the FIN_WAIT2 timeout is over and try the
-// connect again with a new socket and this time it should succeed.
-//
-// TCP timers are not S/R today, this can cause this test to be flaky when run
-// under random S/R due to timer being reset on a restore.
-TEST_P(SocketInetLoopbackTest, TCPFinWait2Test) {
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(
- bind(listen_fd.get(), AsSockAddr(&listen_addr), listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(), AsSockAddr(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- // Lower FIN_WAIT2 state to 5 seconds for test.
- constexpr int kTCPLingerTimeout = 5;
- EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
- &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
- SyscallSucceedsWithValue(0));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- sockaddr_storage conn_bound_addr;
- socklen_t conn_addrlen = connector.addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), AsSockAddr(&conn_bound_addr), &conn_addrlen),
- SyscallSucceeds());
-
- // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
- conn_fd.reset();
-
- // Now bind and connect a new socket.
- const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- // Disable cooperative saves after this point. As a save between the first
- // bind/connect and the second one can cause the linger timeout timer to
- // be restarted causing the final bind/connect to fail.
- DisableSave ds;
-
- ASSERT_THAT(bind(conn_fd2.get(), AsSockAddr(&conn_bound_addr), conn_addrlen),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Sleep for a little over the linger timeout to reduce flakiness in
- // save/restore tests.
- absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 2));
-
- ds.reset();
-
- ASSERT_THAT(
- RetryEINTR(connect)(conn_fd2.get(), AsSockAddr(&conn_addr), conn_addrlen),
- SyscallSucceeds());
-}
-
-// TCPLinger2TimeoutAfterClose creates a pair of connected sockets
-// then closes one end to trigger FIN_WAIT2 state for the closed endpont.
-// It then sleeps for the TCP_LINGER2 timeout and verifies that bind/
-// connecting the same address succeeds.
-//
-// TCP timers are not S/R today, this can cause this test to be flaky when run
-// under random S/R due to timer being reset on a restore.
-TEST_P(SocketInetLoopbackTest, TCPLinger2TimeoutAfterClose) {
- auto const& param = GetParam();
- TestAddress const& listener = param.listener;
- TestAddress const& connector = param.connector;
-
- // Create the listening socket.
- const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
- sockaddr_storage listen_addr = listener.addr;
- ASSERT_THAT(
- bind(listen_fd.get(), AsSockAddr(&listen_addr), listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener.addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(), AsSockAddr(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- sockaddr_storage conn_bound_addr;
- socklen_t conn_addrlen = connector.addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), AsSockAddr(&conn_bound_addr), &conn_addrlen),
- SyscallSucceeds());
-
- // Disable cooperative saves after this point as TCP timers are not restored
- // across a S/R.
- {
- DisableSave ds;
- constexpr int kTCPLingerTimeout = 5;
- EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
- &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
- SyscallSucceedsWithValue(0));
-
- // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
- conn_fd.reset();
-
- absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 1));
-
- // ds going out of scope will Re-enable S/R's since at this point the timer
- // must have fired and cleaned up the endpoint.
- }
-
- // Now bind and connect a new socket and verify that we can immediately
- // rebind the address bound by the conn_fd as it never entered TIME_WAIT.
- const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- ASSERT_THAT(bind(conn_fd2.get(), AsSockAddr(&conn_bound_addr), conn_addrlen),
- SyscallSucceeds());
- ASSERT_THAT(
- RetryEINTR(connect)(conn_fd2.get(), AsSockAddr(&conn_addr), conn_addrlen),
- SyscallSucceeds());
-}
-
// TCPResetAfterClose creates a pair of connected sockets then closes
// one end to trigger FIN_WAIT2 state for the closed endpoint verifies
// that we generate RSTs for any new data after the socket is fully
// closed.
TEST_P(SocketInetLoopbackTest, TCPResetAfterClose) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1149,198 +1052,8 @@ TEST_P(SocketInetLoopbackTest, TCPResetAfterClose) {
SyscallSucceedsWithValue(0));
}
-// setupTimeWaitClose sets up a socket endpoint in TIME_WAIT state.
-// Callers can choose to perform active close on either ends of the connection
-// and also specify if they want to enabled SO_REUSEADDR.
-void setupTimeWaitClose(const TestAddress* listener,
- const TestAddress* connector, bool reuse,
- bool accept_close, sockaddr_storage* listen_addr,
- sockaddr_storage* conn_bound_addr) {
- // Create the listening socket.
- FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener->family(), SOCK_STREAM, IPPROTO_TCP));
- if (reuse) {
- ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- }
- ASSERT_THAT(
- bind(listen_fd.get(), AsSockAddr(listen_addr), listener->addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Get the port bound by the listening socket.
- socklen_t addrlen = listener->addr_len;
- ASSERT_THAT(getsockname(listen_fd.get(), AsSockAddr(listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener->family(), *listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector->family(), SOCK_STREAM, IPPROTO_TCP));
-
- // We disable saves after this point as a S/R causes the netstack seed
- // to be regenerated which changes what ports/ISN is picked for a given
- // tuple (src ip,src port, dst ip, dst port). This can cause the final
- // SYN to use a sequence number that looks like one from the current
- // connection in TIME_WAIT and will not be accepted causing the test
- // to timeout.
- //
- // TODO(gvisor.dev/issue/940): S/R portSeed/portHint
- DisableSave ds;
-
- sockaddr_storage conn_addr = connector->addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector->family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
- connector->addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- socklen_t conn_addrlen = connector->addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), AsSockAddr(conn_bound_addr), &conn_addrlen),
- SyscallSucceeds());
-
- FileDescriptor active_closefd, passive_closefd;
- if (accept_close) {
- active_closefd = std::move(accepted);
- passive_closefd = std::move(conn_fd);
- } else {
- active_closefd = std::move(conn_fd);
- passive_closefd = std::move(accepted);
- }
-
- // shutdown to trigger TIME_WAIT.
- ASSERT_THAT(shutdown(active_closefd.get(), SHUT_WR), SyscallSucceeds());
- {
- constexpr int kTimeout = 10000;
- pollfd pfd = {
- .fd = passive_closefd.get(),
- .events = POLLIN,
- };
- ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
- ASSERT_EQ(pfd.revents, POLLIN);
- }
- ASSERT_THAT(shutdown(passive_closefd.get(), SHUT_WR), SyscallSucceeds());
- {
- constexpr int kTimeout = 10000;
- constexpr int16_t want_events = POLLHUP;
- pollfd pfd = {
- .fd = active_closefd.get(),
- .events = want_events,
- };
- ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
- }
-
- // This sleep is needed to reduce flake to ensure that the passive-close
- // ensures the state transitions to CLOSE from LAST_ACK.
- absl::SleepFor(absl::Seconds(1));
-}
-
-// These tests are disabled under random save as the the restore run
-// results in the stack.Seed() being different which can cause
-// sequence number of final connect to be one that is considered
-// old and can cause the test to be flaky.
-//
-// Test re-binding of client and server bound addresses when the older
-// connection is in TIME_WAIT.
-TEST_P(SocketInetLoopbackTest, TCPPassiveCloseNoTimeWaitTest) {
- auto const& param = GetParam();
- sockaddr_storage listen_addr, conn_bound_addr;
- listen_addr = param.listener.addr;
- setupTimeWaitClose(&param.listener, &param.connector, false /*reuse*/,
- true /*accept_close*/, &listen_addr, &conn_bound_addr);
-
- // Now bind a new socket and verify that we can immediately rebind the address
- // bound by the conn_fd as it never entered TIME_WAIT.
- const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
- param.connector.addr_len),
- SyscallSucceeds());
-
- FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(
- bind(listen_fd.get(), AsSockAddr(&listen_addr), param.listener.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
-TEST_P(SocketInetLoopbackTest, TCPPassiveCloseNoTimeWaitReuseTest) {
- auto const& param = GetParam();
- sockaddr_storage listen_addr, conn_bound_addr;
- listen_addr = param.listener.addr;
- setupTimeWaitClose(&param.listener, &param.connector, true /*reuse*/,
- true /*accept_close*/, &listen_addr, &conn_bound_addr);
-
- FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(
- bind(listen_fd.get(), AsSockAddr(&listen_addr), param.listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Now bind and connect new socket and verify that we can immediately rebind
- // the address bound by the conn_fd as it never entered TIME_WAIT.
- const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
- param.connector.addr_len),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(param.listener.family(), listen_addr));
- sockaddr_storage conn_addr = param.connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(param.connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
- param.connector.addr_len),
- SyscallSucceeds());
-}
-
-TEST_P(SocketInetLoopbackTest, TCPActiveCloseTimeWaitTest) {
- auto const& param = GetParam();
- sockaddr_storage listen_addr, conn_bound_addr;
- listen_addr = param.listener.addr;
- setupTimeWaitClose(&param.listener, &param.connector, false /*reuse*/,
- false /*accept_close*/, &listen_addr, &conn_bound_addr);
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
- param.connector.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
-TEST_P(SocketInetLoopbackTest, TCPActiveCloseTimeWaitReuseTest) {
- auto const& param = GetParam();
- sockaddr_storage listen_addr, conn_bound_addr;
- listen_addr = param.listener.addr;
- setupTimeWaitClose(&param.listener, &param.connector, true /*reuse*/,
- false /*accept_close*/, &listen_addr, &conn_bound_addr);
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
- param.connector.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1392,7 +1105,7 @@ TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) {
}
TEST_P(SocketInetLoopbackTest, TCPAcceptAfterReset) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1503,7 +1216,7 @@ TEST_P(SocketInetLoopbackTest, TCPDeferAccept) {
// saved. Enable S/R issue is fixed.
DisableSave ds;
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1583,7 +1296,7 @@ TEST_P(SocketInetLoopbackTest, TCPDeferAcceptTimeout) {
// saved. Enable S/R once issue is fixed.
DisableSave ds;
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1650,42 +1363,16 @@ TEST_P(SocketInetLoopbackTest, TCPDeferAcceptTimeout) {
ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
}
-INSTANTIATE_TEST_SUITE_P(
- All, SocketInetLoopbackTest,
- ::testing::Values(
- // Listeners bound to IPv4 addresses refuse connections using IPv6
- // addresses.
- TestParam{V4Any(), V4Any()}, TestParam{V4Any(), V4Loopback()},
- TestParam{V4Any(), V4MappedAny()},
- TestParam{V4Any(), V4MappedLoopback()},
- TestParam{V4Loopback(), V4Any()}, TestParam{V4Loopback(), V4Loopback()},
- TestParam{V4Loopback(), V4MappedLoopback()},
- TestParam{V4MappedAny(), V4Any()},
- TestParam{V4MappedAny(), V4Loopback()},
- TestParam{V4MappedAny(), V4MappedAny()},
- TestParam{V4MappedAny(), V4MappedLoopback()},
- TestParam{V4MappedLoopback(), V4Any()},
- TestParam{V4MappedLoopback(), V4Loopback()},
- TestParam{V4MappedLoopback(), V4MappedLoopback()},
+INSTANTIATE_TEST_SUITE_P(All, SocketInetLoopbackTest,
+ SocketInetLoopbackTestValues(),
+ DescribeSocketInetTestParam);
- // Listeners bound to IN6ADDR_ANY accept all connections.
- TestParam{V6Any(), V4Any()}, TestParam{V6Any(), V4Loopback()},
- TestParam{V6Any(), V4MappedAny()},
- TestParam{V6Any(), V4MappedLoopback()}, TestParam{V6Any(), V6Any()},
- TestParam{V6Any(), V6Loopback()},
-
- // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
- // addresses.
- TestParam{V6Loopback(), V6Any()},
- TestParam{V6Loopback(), V6Loopback()}),
- DescribeTestParam);
-
-using SocketInetReusePortTest = ::testing::TestWithParam<TestParam>;
+using SocketInetReusePortTest = ::testing::TestWithParam<SocketInetTestParam>;
// TODO(gvisor.dev/issue/940): Remove when portHint/stack.Seed is
// saved/restored.
TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1795,7 +1482,7 @@ TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread) {
}
TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThread) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1906,7 +1593,7 @@ TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThread) {
}
TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThreadShort) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -2014,32 +1701,23 @@ INSTANTIATE_TEST_SUITE_P(
::testing::Values(
// Listeners bound to IPv4 addresses refuse connections using IPv6
// addresses.
- TestParam{V4Any(), V4Loopback()},
- TestParam{V4Loopback(), V4MappedLoopback()},
+ SocketInetTestParam{V4Any(), V4Loopback()},
+ SocketInetTestParam{V4Loopback(), V4MappedLoopback()},
// Listeners bound to IN6ADDR_ANY accept all connections.
- TestParam{V6Any(), V4Loopback()}, TestParam{V6Any(), V6Loopback()},
+ SocketInetTestParam{V6Any(), V4Loopback()},
+ SocketInetTestParam{V6Any(), V6Loopback()},
// Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
// addresses.
- TestParam{V6Loopback(), V6Loopback()}),
- DescribeTestParam);
-
-struct ProtocolTestParam {
- std::string description;
- int type;
-};
-
-std::string DescribeProtocolTestParam(
- ::testing::TestParamInfo<ProtocolTestParam> const& info) {
- return info.param.description;
-}
+ SocketInetTestParam{V6Loopback(), V6Loopback()}),
+ DescribeSocketInetTestParam);
using SocketMultiProtocolInetLoopbackTest =
::testing::TestWithParam<ProtocolTestParam>;
TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedLoopbackOnlyReservesV4) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v4 loopback on a dual stack socket.
@@ -2088,7 +1766,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedLoopbackOnlyReservesV4) {
}
TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedAnyOnlyReservesV4) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v4 any on a dual stack socket.
@@ -2137,7 +1815,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedAnyOnlyReservesV4) {
}
TEST_P(SocketMultiProtocolInetLoopbackTest, DualStackV6AnyReservesEverything) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// Bind the v6 any on a dual stack socket.
TestAddress const& test_addr_dual = V6Any();
@@ -2200,7 +1878,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, DualStackV6AnyReservesEverything) {
TEST_P(SocketMultiProtocolInetLoopbackTest,
DualStackV6AnyReuseAddrDoesNotReserveV4Any) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// Bind the v6 any on a dual stack socket.
TestAddress const& test_addr_dual = V6Any();
@@ -2237,7 +1915,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
TEST_P(SocketMultiProtocolInetLoopbackTest,
DualStackV6AnyReuseAddrListenReservesV4Any) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// Only TCP sockets are supported.
SKIP_IF((param.type & SOCK_STREAM) == 0);
@@ -2280,7 +1958,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
TEST_P(SocketMultiProtocolInetLoopbackTest,
DualStackV6AnyWithListenReservesEverything) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// Only TCP sockets are supported.
SKIP_IF((param.type & SOCK_STREAM) == 0);
@@ -2347,7 +2025,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
}
TEST_P(SocketMultiProtocolInetLoopbackTest, V6OnlyV6AnyReservesV6) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v6 any on a v6-only socket.
@@ -2400,7 +2078,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V6OnlyV6AnyReservesV6) {
}
TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v6 loopback on a dual stack socket.
@@ -2480,66 +2158,8 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) {
}
}
-TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReservedReuseAddr) {
- auto const& param = GetParam();
-
- // Bind the v6 loopback on a dual stack socket.
- TestAddress const& test_addr = V6Loopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
- SyscallSucceeds());
-}
-
TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v4 loopback on a dual stack socket.
@@ -2651,68 +2271,8 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) {
}
}
-TEST_P(SocketMultiProtocolInetLoopbackTest,
- V4MappedEphemeralPortReservedResueAddr) {
- auto const& param = GetParam();
-
- // Bind the v4 loopback on a dual stack socket.
- TestAddress const& test_addr = V4MappedLoopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
- SyscallSucceeds());
-
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
- SyscallSucceeds());
-}
-
TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v4 loopback on a v4 socket.
@@ -2825,71 +2385,9 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) {
}
}
-TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReservedReuseAddr) {
- auto const& param = GetParam();
-
- // Bind the v4 loopback on a v4 socket.
- TestAddress const& test_addr = V4Loopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
-
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
-
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
- SyscallSucceeds());
-}
-
TEST_P(SocketMultiProtocolInetLoopbackTest,
MultipleBindsAllowedNoListeningReuseAddr) {
- const auto& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// UDP sockets are allowed to bind/listen on the port w/ SO_REUSEADDR, for TCP
// this is only permitted if there is no other listening socket.
SKIP_IF(param.type != SOCK_STREAM);
@@ -2924,7 +2422,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
}
TEST_P(SocketMultiProtocolInetLoopbackTest, PortReuseTwoSockets) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
TestAddress const& test_addr = V4Loopback();
sockaddr_storage addr = test_addr.addr;
@@ -2977,7 +2475,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, PortReuseTwoSockets) {
// closed, we can bind a different socket to the same address without needing
// REUSEPORT.
TEST_P(SocketMultiProtocolInetLoopbackTest, NoReusePortFollowingReusePort) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
TestAddress const& test_addr = V4Loopback();
sockaddr_storage addr = test_addr.addr;
@@ -3004,11 +2502,8 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, NoReusePortFollowingReusePort) {
ASSERT_THAT(bind(fd, AsSockAddr(&addr), addrlen), SyscallSucceeds());
}
-INSTANTIATE_TEST_SUITE_P(
- AllFamilies, SocketMultiProtocolInetLoopbackTest,
- ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM},
- ProtocolTestParam{"UDP", SOCK_DGRAM}),
- DescribeProtocolTestParam);
+INSTANTIATE_TEST_SUITE_P(AllFamilies, SocketMultiProtocolInetLoopbackTest,
+ ProtocolTestValues(), DescribeProtocolTestParam);
} // namespace
diff --git a/test/syscalls/linux/socket_inet_loopback_isolated.cc b/test/syscalls/linux/socket_inet_loopback_isolated.cc
new file mode 100644
index 000000000..ab2259b55
--- /dev/null
+++ b/test/syscalls/linux/socket_inet_loopback_isolated.cc
@@ -0,0 +1,489 @@
+// 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.
+
+#include <netinet/tcp.h>
+
+#include "gtest/gtest.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "test/syscalls/linux/socket_inet_loopback_test_params.h"
+#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/test_util.h"
+
+// Unit tests in this file will run in their own network namespace.
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+using SocketInetLoopbackIsolatedTest =
+ ::testing::TestWithParam<SocketInetTestParam>;
+
+TEST_P(SocketInetLoopbackIsolatedTest, TCPActiveCloseTimeWaitTest) {
+ SocketInetTestParam const& param = GetParam();
+ sockaddr_storage listen_addr, conn_bound_addr;
+ listen_addr = param.listener.addr;
+ SetupTimeWaitClose(&param.listener, &param.connector, false /*reuse*/,
+ false /*accept_close*/, &listen_addr, &conn_bound_addr);
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
+ param.connector.addr_len),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
+TEST_P(SocketInetLoopbackIsolatedTest, TCPActiveCloseTimeWaitReuseTest) {
+ SocketInetTestParam const& param = GetParam();
+ sockaddr_storage listen_addr, conn_bound_addr;
+ listen_addr = param.listener.addr;
+ SetupTimeWaitClose(&param.listener, &param.connector, true /*reuse*/,
+ false /*accept_close*/, &listen_addr, &conn_bound_addr);
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
+ param.connector.addr_len),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
+// These tests are disabled under random save as the restore run
+// results in the stack.Seed() being different which can cause
+// sequence number of final connect to be one that is considered
+// old and can cause the test to be flaky.
+//
+// Test re-binding of client and server bound addresses when the older
+// connection is in TIME_WAIT.
+TEST_P(SocketInetLoopbackIsolatedTest, TCPPassiveCloseNoTimeWaitTest) {
+ SocketInetTestParam const& param = GetParam();
+ sockaddr_storage listen_addr, conn_bound_addr;
+ listen_addr = param.listener.addr;
+ SetupTimeWaitClose(&param.listener, &param.connector, false /*reuse*/,
+ true /*accept_close*/, &listen_addr, &conn_bound_addr);
+
+ // Now bind a new socket and verify that we can immediately rebind the address
+ // bound by the conn_fd as it never entered TIME_WAIT.
+ const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
+ param.connector.addr_len),
+ SyscallSucceeds());
+
+ FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(&listen_addr), param.listener.addr_len),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
+TEST_P(SocketInetLoopbackIsolatedTest, TCPPassiveCloseNoTimeWaitReuseTest) {
+ SocketInetTestParam const& param = GetParam();
+ sockaddr_storage listen_addr, conn_bound_addr;
+ listen_addr = param.listener.addr;
+ SetupTimeWaitClose(&param.listener, &param.connector, true /*reuse*/,
+ true /*accept_close*/, &listen_addr, &conn_bound_addr);
+
+ FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(&listen_addr), param.listener.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
+
+ // Now bind and connect new socket and verify that we can immediately rebind
+ // the address bound by the conn_fd as it never entered TIME_WAIT.
+ const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
+ param.connector.addr_len),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(param.listener.family(), listen_addr));
+ sockaddr_storage conn_addr = param.connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(param.connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
+ param.connector.addr_len),
+ SyscallSucceeds());
+}
+
+// TCPFinWait2Test creates a pair of connected sockets then closes one end to
+// trigger FIN_WAIT2 state for the closed endpoint. Then it binds the same local
+// IP/port on a new socket and tries to connect. The connect should fail w/
+// an EADDRINUSE. Then we wait till the FIN_WAIT2 timeout is over and try the
+// connect again with a new socket and this time it should succeed.
+//
+// TCP timers are not S/R today, this can cause this test to be flaky when run
+// under random S/R due to timer being reset on a restore.
+TEST_P(SocketInetLoopbackIsolatedTest, TCPFinWait2Test) {
+ SocketInetTestParam const& param = GetParam();
+ TestAddress const& listener = param.listener;
+ TestAddress const& connector = param.connector;
+
+ // Create the listening socket.
+ const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ sockaddr_storage listen_addr = listener.addr;
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(&listen_addr), listener.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
+
+ // Get the port bound by the listening socket.
+ socklen_t addrlen = listener.addr_len;
+ ASSERT_THAT(getsockname(listen_fd.get(), AsSockAddr(&listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ // Lower FIN_WAIT2 state to 5 seconds for test.
+ constexpr int kTCPLingerTimeout = 5;
+ EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
+ &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
+ SyscallSucceedsWithValue(0));
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
+ connector.addr_len),
+ SyscallSucceeds());
+
+ // Accept the connection.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+
+ // Get the address/port bound by the connecting socket.
+ sockaddr_storage conn_bound_addr;
+ socklen_t conn_addrlen = connector.addr_len;
+ ASSERT_THAT(
+ getsockname(conn_fd.get(), AsSockAddr(&conn_bound_addr), &conn_addrlen),
+ SyscallSucceeds());
+
+ // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
+ conn_fd.reset();
+
+ // Now bind and connect a new socket.
+ const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ // Disable cooperative saves after this point. As a save between the first
+ // bind/connect and the second one can cause the linger timeout timer to
+ // be restarted causing the final bind/connect to fail.
+ DisableSave ds;
+
+ ASSERT_THAT(bind(conn_fd2.get(), AsSockAddr(&conn_bound_addr), conn_addrlen),
+ SyscallFailsWithErrno(EADDRINUSE));
+
+ // Sleep for a little over the linger timeout to reduce flakiness in
+ // save/restore tests.
+ absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 2));
+
+ ds.reset();
+
+ ASSERT_THAT(
+ RetryEINTR(connect)(conn_fd2.get(), AsSockAddr(&conn_addr), conn_addrlen),
+ SyscallSucceeds());
+}
+
+// TCPLinger2TimeoutAfterClose creates a pair of connected sockets
+// then closes one end to trigger FIN_WAIT2 state for the closed endpoint.
+// It then sleeps for the TCP_LINGER2 timeout and verifies that bind/
+// connecting the same address succeeds.
+//
+// TCP timers are not S/R today, this can cause this test to be flaky when run
+// under random S/R due to timer being reset on a restore.
+TEST_P(SocketInetLoopbackIsolatedTest, TCPLinger2TimeoutAfterClose) {
+ SocketInetTestParam const& param = GetParam();
+ TestAddress const& listener = param.listener;
+ TestAddress const& connector = param.connector;
+
+ // Create the listening socket.
+ const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ sockaddr_storage listen_addr = listener.addr;
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(&listen_addr), listener.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
+
+ // Get the port bound by the listening socket.
+ socklen_t addrlen = listener.addr_len;
+ ASSERT_THAT(getsockname(listen_fd.get(), AsSockAddr(&listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
+ connector.addr_len),
+ SyscallSucceeds());
+
+ // Accept the connection.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+
+ // Get the address/port bound by the connecting socket.
+ sockaddr_storage conn_bound_addr;
+ socklen_t conn_addrlen = connector.addr_len;
+ ASSERT_THAT(
+ getsockname(conn_fd.get(), AsSockAddr(&conn_bound_addr), &conn_addrlen),
+ SyscallSucceeds());
+
+ // Disable cooperative saves after this point as TCP timers are not restored
+ // across a S/R.
+ {
+ DisableSave ds;
+ constexpr int kTCPLingerTimeout = 5;
+ EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
+ &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
+ SyscallSucceedsWithValue(0));
+
+ // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
+ conn_fd.reset();
+
+ absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 1));
+
+ // ds going out of scope will Re-enable S/R's since at this point the timer
+ // must have fired and cleaned up the endpoint.
+ }
+
+ // Now bind and connect a new socket and verify that we can immediately
+ // rebind the address bound by the conn_fd as it never entered TIME_WAIT.
+ const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(conn_fd2.get(), AsSockAddr(&conn_bound_addr), conn_addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(
+ RetryEINTR(connect)(conn_fd2.get(), AsSockAddr(&conn_addr), conn_addrlen),
+ SyscallSucceeds());
+}
+
+INSTANTIATE_TEST_SUITE_P(All, SocketInetLoopbackIsolatedTest,
+ SocketInetLoopbackTestValues(),
+ DescribeSocketInetTestParam);
+
+using SocketMultiProtocolInetLoopbackIsolatedTest =
+ ::testing::TestWithParam<ProtocolTestParam>;
+
+TEST_P(SocketMultiProtocolInetLoopbackIsolatedTest,
+ V4EphemeralPortReservedReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+
+ // Bind the v4 loopback on a v4 socket.
+ TestAddress const& test_addr = V4Loopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ const FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
+ SyscallSucceeds());
+
+ // Listen iff TCP.
+ if (param.type == SOCK_STREAM) {
+ ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
+ }
+
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
+ SyscallSucceeds());
+
+ // Connect to bind an ephemeral port.
+ const FileDescriptor connected_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
+
+ // Get the ephemeral port.
+ sockaddr_storage connected_addr = {};
+ socklen_t connected_addr_len = sizeof(connected_addr);
+ ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
+ &connected_addr_len),
+ SyscallSucceeds());
+ uint16_t const ephemeral_port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
+
+ // Verify that we actually got an ephemeral port.
+ ASSERT_NE(ephemeral_port, 0);
+
+ // Verify that the ephemeral port is not reserved.
+ const FileDescriptor checking_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ EXPECT_THAT(
+ bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
+ SyscallSucceeds());
+}
+
+TEST_P(SocketMultiProtocolInetLoopbackIsolatedTest,
+ V4MappedEphemeralPortReservedReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+
+ // Bind the v4 loopback on a dual stack socket.
+ TestAddress const& test_addr = V4MappedLoopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ const FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
+ SyscallSucceeds());
+
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ // Listen iff TCP.
+ if (param.type == SOCK_STREAM) {
+ ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
+ }
+
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
+ SyscallSucceeds());
+
+ // Connect to bind an ephemeral port.
+ const FileDescriptor connected_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
+
+ // Get the ephemeral port.
+ sockaddr_storage connected_addr = {};
+ socklen_t connected_addr_len = sizeof(connected_addr);
+ ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
+ &connected_addr_len),
+ SyscallSucceeds());
+ uint16_t const ephemeral_port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
+
+ // Verify that we actually got an ephemeral port.
+ ASSERT_NE(ephemeral_port, 0);
+
+ // Verify that the ephemeral port is not reserved.
+ const FileDescriptor checking_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ EXPECT_THAT(
+ bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
+ SyscallSucceeds());
+}
+
+TEST_P(SocketMultiProtocolInetLoopbackIsolatedTest,
+ V6EphemeralPortReservedReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+
+ // Bind the v6 loopback on a dual stack socket.
+ TestAddress const& test_addr = V6Loopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ const FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ // Listen iff TCP.
+ if (param.type == SOCK_STREAM) {
+ ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
+ }
+
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
+ SyscallSucceeds());
+
+ // Connect to bind an ephemeral port.
+ const FileDescriptor connected_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
+
+ // Get the ephemeral port.
+ sockaddr_storage connected_addr = {};
+ socklen_t connected_addr_len = sizeof(connected_addr);
+ ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
+ &connected_addr_len),
+ SyscallSucceeds());
+ uint16_t const ephemeral_port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
+
+ // Verify that we actually got an ephemeral port.
+ ASSERT_NE(ephemeral_port, 0);
+
+ // Verify that the ephemeral port is not reserved.
+ const FileDescriptor checking_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ EXPECT_THAT(
+ bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
+ SyscallSucceeds());
+}
+
+INSTANTIATE_TEST_SUITE_P(AllFamilies,
+ SocketMultiProtocolInetLoopbackIsolatedTest,
+ ProtocolTestValues(), DescribeProtocolTestParam);
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_inet_loopback_nogotsan.cc b/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
index 601ae107b..cc2773af1 100644
--- a/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
+++ b/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
@@ -27,6 +27,7 @@
#include "gtest/gtest.h"
#include "absl/strings/str_cat.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/syscalls/linux/socket_inet_loopback_test_params.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
@@ -38,47 +39,7 @@ namespace testing {
namespace {
-using ::testing::Gt;
-
-PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
- switch (family) {
- case AF_INET:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
- case AF_INET6:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
- switch (family) {
- case AF_INET:
- reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
- return NoError();
- case AF_INET6:
- reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
- return NoError();
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-struct TestParam {
- TestAddress listener;
- TestAddress connector;
-};
-
-std::string DescribeTestParam(::testing::TestParamInfo<TestParam> const& info) {
- return absl::StrCat("Listen", info.param.listener.description, "_Connect",
- info.param.connector.description);
-}
-
-using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>;
+using SocketInetLoopbackTest = ::testing::TestWithParam<SocketInetTestParam>;
// This test verifies that connect returns EADDRNOTAVAIL if all local ephemeral
// ports are already in use for a given destination ip/port.
@@ -87,7 +48,7 @@ using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>;
//
// FIXME(b/162475855): This test is failing reliably.
TEST_P(SocketInetLoopbackTest, DISABLED_TestTCPPortExhaustion) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -136,59 +97,32 @@ TEST_P(SocketInetLoopbackTest, DISABLED_TestTCPPortExhaustion) {
}
}
-INSTANTIATE_TEST_SUITE_P(
- All, SocketInetLoopbackTest,
- ::testing::Values(
- // Listeners bound to IPv4 addresses refuse connections using IPv6
- // addresses.
- TestParam{V4Any(), V4Any()}, TestParam{V4Any(), V4Loopback()},
- TestParam{V4Any(), V4MappedAny()},
- TestParam{V4Any(), V4MappedLoopback()},
- TestParam{V4Loopback(), V4Any()}, TestParam{V4Loopback(), V4Loopback()},
- TestParam{V4Loopback(), V4MappedLoopback()},
- TestParam{V4MappedAny(), V4Any()},
- TestParam{V4MappedAny(), V4Loopback()},
- TestParam{V4MappedAny(), V4MappedAny()},
- TestParam{V4MappedAny(), V4MappedLoopback()},
- TestParam{V4MappedLoopback(), V4Any()},
- TestParam{V4MappedLoopback(), V4Loopback()},
- TestParam{V4MappedLoopback(), V4MappedLoopback()},
-
- // Listeners bound to IN6ADDR_ANY accept all connections.
- TestParam{V6Any(), V4Any()}, TestParam{V6Any(), V4Loopback()},
- TestParam{V6Any(), V4MappedAny()},
- TestParam{V6Any(), V4MappedLoopback()}, TestParam{V6Any(), V6Any()},
- TestParam{V6Any(), V6Loopback()},
-
- // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
- // addresses.
- TestParam{V6Loopback(), V6Any()},
- TestParam{V6Loopback(), V6Loopback()}),
- DescribeTestParam);
-
-struct ProtocolTestParam {
- std::string description;
- int type;
-};
-
-std::string DescribeProtocolTestParam(
- ::testing::TestParamInfo<ProtocolTestParam> const& info) {
- return info.param.description;
-}
+INSTANTIATE_TEST_SUITE_P(All, SocketInetLoopbackTest,
+ SocketInetLoopbackTestValues(),
+ DescribeSocketInetTestParam);
using SocketMultiProtocolInetLoopbackTest =
::testing::TestWithParam<ProtocolTestParam>;
-TEST_P(SocketMultiProtocolInetLoopbackTest, BindAvoidsListeningPortsReuseAddr) {
- const auto& param = GetParam();
- // UDP sockets are allowed to bind/listen on the port w/ SO_REUSEADDR, for TCP
- // this is only permitted if there is no other listening socket.
+TEST_P(SocketMultiProtocolInetLoopbackTest,
+ TCPBindAvoidsOtherBoundPortsReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+ // UDP sockets are allowed to bind/listen on an already bound port w/
+ // SO_REUSEADDR even when requesting a port from the kernel. In case of TCP
+ // rebinding is only permitted when SO_REUSEADDR is set and an explicit port
+ // is specified. When a zero port is specified to the bind() call then an
+ // already bound port will not be picked.
SKIP_IF(param.type != SOCK_STREAM);
DisableSave ds; // Too many syscalls.
// A map of port to file descriptor binding the port.
- std::map<uint16_t, FileDescriptor> listen_sockets;
+ std::map<uint16_t, FileDescriptor> bound_sockets;
+
+ // Reduce number of ephemeral ports if permitted to reduce running time of
+ // the test.
+ [[maybe_unused]] const int nports =
+ ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
// Exhaust all ephemeral ports.
while (true) {
@@ -214,19 +148,63 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, BindAvoidsListeningPortsReuseAddr) {
SyscallSucceeds());
uint16_t port = reinterpret_cast<sockaddr_in*>(&bound_addr)->sin_port;
- // Newly bound port should not already be in use by a listening socket.
- ASSERT_EQ(listen_sockets.find(port), listen_sockets.end());
- auto fd = bound_fd.get();
- listen_sockets.insert(std::make_pair(port, std::move(bound_fd)));
- ASSERT_THAT(listen(fd, SOMAXCONN), SyscallSucceeds());
+ auto [iter, inserted] = bound_sockets.emplace(port, std::move(bound_fd));
+ ASSERT_TRUE(inserted);
+ }
+}
+
+TEST_P(SocketMultiProtocolInetLoopbackTest,
+ UDPBindMayBindOtherBoundPortsReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+ // UDP sockets are allowed to bind/listen on an already bound port w/
+ // SO_REUSEADDR even when requesting a port from the kernel.
+ SKIP_IF(param.type != SOCK_DGRAM);
+
+ DisableSave ds; // Too many syscalls.
+
+ // A map of port to file descriptor binding the port.
+ std::map<uint16_t, FileDescriptor> bound_sockets;
+
+ // Reduce number of ephemeral ports if permitted to reduce running time of
+ // the test.
+ [[maybe_unused]] const int nports =
+ ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
+
+ // Exhaust all ephemeral ports.
+ bool duplicate_binding = false;
+ while (true) {
+ // Bind the v4 loopback on a v4 socket.
+ TestAddress const& test_addr = V4Loopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ ASSERT_THAT(
+ bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
+ SyscallSucceeds());
+
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
+ SyscallSucceeds());
+ uint16_t port = reinterpret_cast<sockaddr_in*>(&bound_addr)->sin_port;
+
+ auto [iter, inserted] = bound_sockets.emplace(port, std::move(bound_fd));
+ if (!inserted) {
+ duplicate_binding = true;
+ break;
+ }
}
+ ASSERT_TRUE(duplicate_binding);
}
-INSTANTIATE_TEST_SUITE_P(
- AllFamilies, SocketMultiProtocolInetLoopbackTest,
- ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM},
- ProtocolTestParam{"UDP", SOCK_DGRAM}),
- DescribeProtocolTestParam);
+INSTANTIATE_TEST_SUITE_P(AllFamilies, SocketMultiProtocolInetLoopbackTest,
+ ProtocolTestValues(), DescribeProtocolTestParam);
} // namespace
diff --git a/test/syscalls/linux/socket_inet_loopback_test_params.h b/test/syscalls/linux/socket_inet_loopback_test_params.h
new file mode 100644
index 000000000..42b48eb8a
--- /dev/null
+++ b/test/syscalls/linux/socket_inet_loopback_test_params.h
@@ -0,0 +1,86 @@
+// 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.
+
+#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_INET_LOOPBACK_TEST_PARAMS_H_
+#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_INET_LOOPBACK_TEST_PARAMS_H_
+
+#include "gtest/gtest.h"
+#include "test/syscalls/linux/socket_test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+struct SocketInetTestParam {
+ TestAddress listener;
+ TestAddress connector;
+};
+
+inline std::string DescribeSocketInetTestParam(
+ ::testing::TestParamInfo<SocketInetTestParam> const& info) {
+ return absl::StrCat("Listen", info.param.listener.description, "_Connect",
+ info.param.connector.description);
+}
+
+inline auto SocketInetLoopbackTestValues() {
+ return ::testing::Values(
+ // Listeners bound to IPv4 addresses refuse connections using IPv6
+ // addresses.
+ SocketInetTestParam{V4Any(), V4Any()},
+ SocketInetTestParam{V4Any(), V4Loopback()},
+ SocketInetTestParam{V4Any(), V4MappedAny()},
+ SocketInetTestParam{V4Any(), V4MappedLoopback()},
+ SocketInetTestParam{V4Loopback(), V4Any()},
+ SocketInetTestParam{V4Loopback(), V4Loopback()},
+ SocketInetTestParam{V4Loopback(), V4MappedLoopback()},
+ SocketInetTestParam{V4MappedAny(), V4Any()},
+ SocketInetTestParam{V4MappedAny(), V4Loopback()},
+ SocketInetTestParam{V4MappedAny(), V4MappedAny()},
+ SocketInetTestParam{V4MappedAny(), V4MappedLoopback()},
+ SocketInetTestParam{V4MappedLoopback(), V4Any()},
+ SocketInetTestParam{V4MappedLoopback(), V4Loopback()},
+ SocketInetTestParam{V4MappedLoopback(), V4MappedLoopback()},
+
+ // Listeners bound to IN6ADDR_ANY accept all connections.
+ SocketInetTestParam{V6Any(), V4Any()},
+ SocketInetTestParam{V6Any(), V4Loopback()},
+ SocketInetTestParam{V6Any(), V4MappedAny()},
+ SocketInetTestParam{V6Any(), V4MappedLoopback()},
+ SocketInetTestParam{V6Any(), V6Any()},
+ SocketInetTestParam{V6Any(), V6Loopback()},
+
+ // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
+ // addresses.
+ SocketInetTestParam{V6Loopback(), V6Any()},
+ SocketInetTestParam{V6Loopback(), V6Loopback()});
+}
+
+struct ProtocolTestParam {
+ std::string description;
+ int type;
+};
+
+inline std::string DescribeProtocolTestParam(
+ ::testing::TestParamInfo<ProtocolTestParam> const& info) {
+ return info.param.description;
+}
+
+inline auto ProtocolTestValues() {
+ return ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM},
+ ProtocolTestParam{"UDP", SOCK_DGRAM});
+}
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_INET_LOOPBACK_TEST_PARAMS_H_
diff --git a/test/syscalls/linux/socket_netdevice.cc b/test/syscalls/linux/socket_netdevice.cc
index 5f8d7f981..8d214a2b7 100644
--- a/test/syscalls/linux/socket_netdevice.cc
+++ b/test/syscalls/linux/socket_netdevice.cc
@@ -37,6 +37,7 @@ using ::testing::AnyOf;
using ::testing::Eq;
TEST(NetdeviceTest, Loopback) {
+ SKIP_IF(IsRunningWithHostinet());
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -60,6 +61,7 @@ TEST(NetdeviceTest, Loopback) {
}
TEST(NetdeviceTest, Netmask) {
+ SKIP_IF(IsRunningWithHostinet());
// We need an interface index to identify the loopback device.
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -135,6 +137,7 @@ TEST(NetdeviceTest, Netmask) {
}
TEST(NetdeviceTest, InterfaceName) {
+ SKIP_IF(IsRunningWithHostinet());
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -168,6 +171,7 @@ TEST(NetdeviceTest, InterfaceFlags) {
}
TEST(NetdeviceTest, InterfaceMTU) {
+ SKIP_IF(IsRunningWithHostinet());
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -181,6 +185,7 @@ TEST(NetdeviceTest, InterfaceMTU) {
}
TEST(NetdeviceTest, EthtoolGetTSInfo) {
+ SKIP_IF(IsRunningWithHostinet());
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc
index 9e3a129cf..1afb1ab50 100644
--- a/test/syscalls/linux/socket_test_util.cc
+++ b/test/syscalls/linux/socket_test_util.cc
@@ -15,6 +15,7 @@
#include "test/syscalls/linux/socket_test_util.h"
#include <arpa/inet.h>
+#include <netinet/in.h>
#include <poll.h>
#include <sys/socket.h>
@@ -23,6 +24,7 @@
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_split.h"
#include "absl/time/clock.h"
#include "absl/types/optional.h"
#include "test/util/file_descriptor.h"
@@ -84,7 +86,8 @@ Creator<SocketPair> AcceptBindSocketPairCreator(bool abstract, int domain,
RETURN_ERROR_IF_SYSCALL_FAIL(
bind(bound, AsSockAddr(&bind_addr), sizeof(bind_addr)));
MaybeSave(); // Successful bind.
- RETURN_ERROR_IF_SYSCALL_FAIL(listen(bound, /* backlog = */ 5));
+ RETURN_ERROR_IF_SYSCALL_FAIL(
+ listen(bound, /* backlog = */ 5)); // NOLINT(bugprone-argument-comment)
MaybeSave(); // Successful listen.
int connected;
@@ -315,7 +318,8 @@ PosixErrorOr<T> BindIP(int fd, bool dual_stack) {
template <typename T>
PosixErrorOr<T> TCPBindAndListen(int fd, bool dual_stack) {
ASSIGN_OR_RETURN_ERRNO(T addr, BindIP<T>(fd, dual_stack));
- RETURN_ERROR_IF_SYSCALL_FAIL(listen(fd, /* backlog = */ 5));
+ RETURN_ERROR_IF_SYSCALL_FAIL(
+ listen(fd, /* backlog = */ 5)); // NOLINT(bugprone-argument-comment)
return addr;
}
@@ -798,84 +802,82 @@ TestAddress TestAddress::WithPort(uint16_t port) const {
return addr;
}
-TestAddress V4Any() {
- TestAddress t("V4Any");
- t.addr.ss_family = AF_INET;
- t.addr_len = sizeof(sockaddr_in);
- reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = htonl(INADDR_ANY);
- return t;
-}
+namespace {
-TestAddress V4Loopback() {
- TestAddress t("V4Loopback");
+TestAddress V4Addr(std::string description, in_addr_t addr) {
+ TestAddress t(std::move(description));
t.addr.ss_family = AF_INET;
t.addr_len = sizeof(sockaddr_in);
- reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr =
- htonl(INADDR_LOOPBACK);
+ reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = addr;
return t;
}
-TestAddress V4MappedAny() {
- TestAddress t("V4MappedAny");
+TestAddress V6Addr(std::string description, const in6_addr& addr) {
+ TestAddress t(std::move(description));
t.addr.ss_family = AF_INET6;
t.addr_len = sizeof(sockaddr_in6);
- inet_pton(AF_INET6, "::ffff:0.0.0.0",
- reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr);
+ reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr = addr;
return t;
}
+} // namespace
+
+TestAddress V4AddrStr(std::string description, const char* addr) {
+ in_addr_t s_addr;
+ inet_pton(AF_INET, addr, &s_addr);
+ return V4Addr(description, s_addr);
+}
+
+TestAddress V6AddrStr(std::string description, const char* addr) {
+ struct in6_addr s_addr;
+ inet_pton(AF_INET6, addr, &s_addr);
+ return V6Addr(description, s_addr);
+}
+
+TestAddress V4Any() { return V4Addr("V4Any", htonl(INADDR_ANY)); }
+
+TestAddress V4Broadcast() {
+ return V4Addr("V4Broadcast", htonl(INADDR_BROADCAST));
+}
+
+TestAddress V4Loopback() {
+ return V4Addr("V4Loopback", htonl(INADDR_LOOPBACK));
+}
+
+TestAddress V4LoopbackSubnetBroadcast() {
+ return V4AddrStr("V4LoopbackSubnetBroadcast", "127.255.255.255");
+}
+
+TestAddress V4MappedAny() { return V6AddrStr("V4MappedAny", "::ffff:0.0.0.0"); }
+
TestAddress V4MappedLoopback() {
- TestAddress t("V4MappedLoopback");
- t.addr.ss_family = AF_INET6;
- t.addr_len = sizeof(sockaddr_in6);
- inet_pton(AF_INET6, "::ffff:127.0.0.1",
- reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr);
- return t;
+ return V6AddrStr("V4MappedLoopback", "::ffff:127.0.0.1");
}
TestAddress V4Multicast() {
- TestAddress t("V4Multicast");
- t.addr.ss_family = AF_INET;
- t.addr_len = sizeof(sockaddr_in);
- reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr =
- inet_addr(kMulticastAddress);
- return t;
+ return V4Addr("V4Multicast", inet_addr(kMulticastAddress));
}
-TestAddress V4Broadcast() {
- TestAddress t("V4Broadcast");
- t.addr.ss_family = AF_INET;
- t.addr_len = sizeof(sockaddr_in);
- reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr =
- htonl(INADDR_BROADCAST);
- return t;
+TestAddress V4MulticastAllHosts() {
+ return V4Addr("V4MulticastAllHosts", htonl(INADDR_ALLHOSTS_GROUP));
}
-TestAddress V6Any() {
- TestAddress t("V6Any");
- t.addr.ss_family = AF_INET6;
- t.addr_len = sizeof(sockaddr_in6);
- reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr = in6addr_any;
- return t;
+TestAddress V6Any() { return V6Addr("V6Any", in6addr_any); }
+
+TestAddress V6Loopback() { return V6Addr("V6Loopback", in6addr_loopback); }
+
+TestAddress V6Multicast() { return V6AddrStr("V6Multicast", "ff05::1234"); }
+
+TestAddress V6MulticastInterfaceLocalAllNodes() {
+ return V6AddrStr("V6MulticastInterfaceLocalAllNodes", "ff01::1");
}
-TestAddress V6Loopback() {
- TestAddress t("V6Loopback");
- t.addr.ss_family = AF_INET6;
- t.addr_len = sizeof(sockaddr_in6);
- reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr = in6addr_loopback;
- return t;
+TestAddress V6MulticastLinkLocalAllNodes() {
+ return V6AddrStr("V6MulticastLinkLocalAllNodes", "ff02::1");
}
-TestAddress V6Multicast() {
- TestAddress t("V6Multicast");
- t.addr.ss_family = AF_INET6;
- t.addr_len = sizeof(sockaddr_in6);
- EXPECT_EQ(
- 1,
- inet_pton(AF_INET6, "ff05::1234",
- reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr));
- return t;
+TestAddress V6MulticastLinkLocalAllRouters() {
+ return V6AddrStr("V6MulticastLinkLocalAllRouters", "ff02::2");
}
// Checksum computes the internet checksum of a buffer.
@@ -947,5 +949,162 @@ uint16_t ICMPChecksum(struct icmphdr icmphdr, const char* payload,
return csum;
}
+PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
+ switch (family) {
+ case AF_INET:
+ return static_cast<uint16_t>(
+ reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
+ case AF_INET6:
+ return static_cast<uint16_t>(
+ reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
+ default:
+ return PosixError(EINVAL,
+ absl::StrCat("unknown socket family: ", family));
+ }
+}
+
+PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
+ switch (family) {
+ case AF_INET:
+ reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
+ return NoError();
+ case AF_INET6:
+ reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
+ return NoError();
+ default:
+ return PosixError(EINVAL,
+ absl::StrCat("unknown socket family: ", family));
+ }
+}
+
+void SetupTimeWaitClose(const TestAddress* listener,
+ const TestAddress* connector, bool reuse,
+ bool accept_close, sockaddr_storage* listen_addr,
+ sockaddr_storage* conn_bound_addr) {
+ // Create the listening socket.
+ FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener->family(), SOCK_STREAM, IPPROTO_TCP));
+ if (reuse) {
+ ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ }
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(listen_addr), listener->addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
+
+ // Get the port bound by the listening socket.
+ socklen_t addrlen = listener->addr_len;
+ ASSERT_THAT(getsockname(listen_fd.get(), AsSockAddr(listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener->family(), *listen_addr));
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector->family(), SOCK_STREAM, IPPROTO_TCP));
+
+ // We disable saves after this point as a S/R causes the netstack seed
+ // to be regenerated which changes what ports/ISN is picked for a given
+ // tuple (src ip,src port, dst ip, dst port). This can cause the final
+ // SYN to use a sequence number that looks like one from the current
+ // connection in TIME_WAIT and will not be accepted causing the test
+ // to timeout.
+ //
+ // TODO(gvisor.dev/issue/940): S/R portSeed/portHint
+ DisableSave ds;
+
+ sockaddr_storage conn_addr = connector->addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector->family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
+ connector->addr_len),
+ SyscallSucceeds());
+
+ // Accept the connection.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+
+ // Get the address/port bound by the connecting socket.
+ socklen_t conn_addrlen = connector->addr_len;
+ ASSERT_THAT(
+ getsockname(conn_fd.get(), AsSockAddr(conn_bound_addr), &conn_addrlen),
+ SyscallSucceeds());
+
+ FileDescriptor active_closefd, passive_closefd;
+ if (accept_close) {
+ active_closefd = std::move(accepted);
+ passive_closefd = std::move(conn_fd);
+ } else {
+ active_closefd = std::move(conn_fd);
+ passive_closefd = std::move(accepted);
+ }
+
+ // shutdown to trigger TIME_WAIT.
+ ASSERT_THAT(shutdown(active_closefd.get(), SHUT_WR), SyscallSucceeds());
+ {
+ constexpr int kTimeout = 10000;
+ pollfd pfd = {
+ .fd = passive_closefd.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ ASSERT_EQ(pfd.revents, POLLIN);
+ }
+ ASSERT_THAT(shutdown(passive_closefd.get(), SHUT_WR), SyscallSucceeds());
+ {
+ constexpr int kTimeout = 10000;
+ constexpr int16_t want_events = POLLHUP;
+ pollfd pfd = {
+ .fd = active_closefd.get(),
+ .events = want_events,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ }
+
+ // This sleep is needed to reduce flake to ensure that the passive-close
+ // ensures the state transitions to CLOSE from LAST_ACK.
+ absl::SleepFor(absl::Seconds(1));
+}
+
+constexpr char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range";
+
+PosixErrorOr<int> MaybeLimitEphemeralPorts() {
+ int min = 0;
+ int max = 1 << 16;
+
+ // Read the ephemeral range from /proc.
+ ASSIGN_OR_RETURN_ERRNO(std::string rangefile, GetContents(kRangeFile));
+ const std::string err_msg =
+ absl::StrFormat("%s has invalid content: %s", kRangeFile, rangefile);
+ if (rangefile.back() != '\n') {
+ return PosixError(EINVAL, err_msg);
+ }
+ rangefile.pop_back();
+ std::vector<std::string> range =
+ absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
+ if (range.size() < 2 || !absl::SimpleAtoi(range.front(), &min) ||
+ !absl::SimpleAtoi(range.back(), &max)) {
+ return PosixError(EINVAL, err_msg);
+ }
+
+ // If we can open as writable, limit the range.
+ if (!access(kRangeFile, W_OK)) {
+ ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd,
+ Open(kRangeFile, O_WRONLY | O_TRUNC, 0));
+ max = min + 50;
+ const std::string small_range = absl::StrFormat("%d %d", min, max);
+ int n = write(fd.get(), small_range.c_str(), small_range.size());
+ if (n < 0) {
+ return PosixError(
+ errno,
+ absl::StrFormat("write(%d [%s], \"%s\", %d)", fd.get(), kRangeFile,
+ small_range.c_str(), small_range.size()));
+ }
+ }
+ return max - min;
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h
index f7ba90130..0e2be63cc 100644
--- a/test/syscalls/linux/socket_test_util.h
+++ b/test/syscalls/linux/socket_test_util.h
@@ -499,15 +499,45 @@ struct TestAddress {
constexpr char kMulticastAddress[] = "224.0.2.1";
constexpr char kBroadcastAddress[] = "255.255.255.255";
+// Returns a TestAddress with `addr` parsed as an IPv4 address described by
+// `description`.
+TestAddress V4AddrStr(std::string description, const char* addr);
+// Returns a TestAddress with `addr` parsed as an IPv6 address described by
+// `description`.
+TestAddress V6AddrStr(std::string description, const char* addr);
+
+// Returns a TestAddress for the IPv4 any address.
TestAddress V4Any();
+// Returns a TestAddress for the IPv4 limited broadcast address.
TestAddress V4Broadcast();
+// Returns a TestAddress for the IPv4 loopback address.
TestAddress V4Loopback();
+// Returns a TestAddress for the subnet broadcast of the IPv4 loopback address.
+TestAddress V4LoopbackSubnetBroadcast();
+// Returns a TestAddress for the IPv4-mapped IPv6 any address.
TestAddress V4MappedAny();
+// Returns a TestAddress for the IPv4-mapped IPv6 loopback address.
TestAddress V4MappedLoopback();
+// Returns a TestAddress for a IPv4 multicast address.
TestAddress V4Multicast();
+// Returns a TestAddress for the IPv4 all-hosts multicast group address.
+TestAddress V4MulticastAllHosts();
+
+// Returns a TestAddress for the IPv6 any address.
TestAddress V6Any();
+// Returns a TestAddress for the IPv6 loopback address.
TestAddress V6Loopback();
+// Returns a TestAddress for a IPv6 multicast address.
TestAddress V6Multicast();
+// Returns a TestAddress for the IPv6 interface-local all-nodes multicast group
+// address.
+TestAddress V6MulticastInterfaceLocalAllNodes();
+// Returns a TestAddress for the IPv6 link-local all-nodes multicast group
+// address.
+TestAddress V6MulticastLinkLocalAllNodes();
+// Returns a TestAddress for the IPv6 link-local all-routers multicast group
+// address.
+TestAddress V6MulticastLinkLocalAllRouters();
// Compute the internet checksum of an IP header.
uint16_t IPChecksum(struct iphdr ip);
@@ -534,6 +564,22 @@ inline sockaddr* AsSockAddr(sockaddr_un* s) {
return reinterpret_cast<sockaddr*>(s);
}
+PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr);
+
+PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port);
+
+// setupTimeWaitClose sets up a socket endpoint in TIME_WAIT state.
+// Callers can choose to perform active close on either ends of the connection
+// and also specify if they want to enabled SO_REUSEADDR.
+void SetupTimeWaitClose(const TestAddress* listener,
+ const TestAddress* connector, bool reuse,
+ bool accept_close, sockaddr_storage* listen_addr,
+ sockaddr_storage* conn_bound_addr);
+
+// MaybeLimitEphemeralPorts attempts to reduce the number of ephemeral ports and
+// returns the number of ephemeral ports.
+PosixErrorOr<int> MaybeLimitEphemeralPorts();
+
namespace internal {
PosixErrorOr<int> TryPortAvailable(int port, AddressFamily family,
SocketType type, bool reuse_addr);
diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc
index 5bfdecc79..183819faf 100644
--- a/test/syscalls/linux/tcp_socket.cc
+++ b/test/syscalls/linux/tcp_socket.cc
@@ -1182,6 +1182,62 @@ TEST_P(SimpleTcpSocketTest, SelfConnectSend) {
EXPECT_THAT(shutdown(s.get(), SHUT_WR), SyscallSucceedsWithValue(0));
}
+TEST_P(SimpleTcpSocketTest, SelfConnectSendShutdownWrite) {
+ // Initialize address to the loopback one.
+ sockaddr_storage addr =
+ ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
+ socklen_t addrlen = sizeof(addr);
+
+ const FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(s.get(), AsSockAddr(&addr), addrlen), SyscallSucceeds());
+ // Get the bound port.
+ ASSERT_THAT(getsockname(s.get(), AsSockAddr(&addr), &addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(s.get(), AsSockAddr(&addr), addrlen),
+ SyscallSucceeds());
+
+ // Write enough data to fill send and receive buffers.
+ size_t write_size = 24 << 20; // 24 MiB.
+ std::vector<char> writebuf(write_size);
+
+ ScopedThread t([&s]() {
+ absl::SleepFor(absl::Milliseconds(250));
+ ASSERT_THAT(shutdown(s.get(), SHUT_WR), SyscallSucceeds());
+ });
+
+ // Try to send the whole thing.
+ int n;
+ ASSERT_THAT(n = SendFd(s.get(), writebuf.data(), writebuf.size(), 0),
+ SyscallFailsWithErrno(EPIPE));
+}
+
+TEST_P(SimpleTcpSocketTest, SelfConnectRecvShutdownRead) {
+ // Initialize address to the loopback one.
+ sockaddr_storage addr =
+ ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
+ socklen_t addrlen = sizeof(addr);
+
+ const FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(s.get(), AsSockAddr(&addr), addrlen), SyscallSucceeds());
+ // Get the bound port.
+ ASSERT_THAT(getsockname(s.get(), AsSockAddr(&addr), &addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(s.get(), AsSockAddr(&addr), addrlen),
+ SyscallSucceeds());
+
+ ScopedThread t([&s]() {
+ absl::SleepFor(absl::Milliseconds(250));
+ ASSERT_THAT(shutdown(s.get(), SHUT_RD), SyscallSucceeds());
+ });
+
+ char buf[1];
+ EXPECT_THAT(recv(s.get(), buf, 0, 0), SyscallSucceedsWithValue(0));
+}
+
void NonBlockingConnect(int family, int16_t pollMask) {
const FileDescriptor listener =
ASSERT_NO_ERRNO_AND_VALUE(Socket(family, SOCK_STREAM, IPPROTO_TCP));
diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc
index 279fe342c..1c74b9724 100644
--- a/test/syscalls/linux/tuntap.cc
+++ b/test/syscalls/linux/tuntap.cc
@@ -24,6 +24,8 @@
#include <sys/socket.h>
#include <sys/types.h>
+#include <cstddef>
+
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/ascii.h"
@@ -44,6 +46,7 @@ constexpr int kIPLen = 4;
constexpr const char kDevNetTun[] = "/dev/net/tun";
constexpr const char kTapName[] = "tap0";
+constexpr const char kTunName[] = "tun0";
#define kTapIPAddr htonl(0x0a000001) /* Inet 10.0.0.1 */
#define kTapPeerIPAddr htonl(0x0a000002) /* Inet 10.0.0.2 */
@@ -413,6 +416,47 @@ TEST_F(TuntapTest, SendUdpTriggersArpResolution) {
}
}
+TEST_F(TuntapTest, TUNNoPacketInfo) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+
+ // Interface creation.
+ FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
+
+ struct ifreq ifr_set = {};
+ ifr_set.ifr_flags = IFF_TUN | IFF_NO_PI;
+ strncpy(ifr_set.ifr_name, kTunName, IFNAMSIZ);
+ EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr_set), SyscallSucceeds());
+
+ // Interface setup.
+ auto link = ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName(kTunName));
+ const struct in_addr dev_ipv4_addr = {.s_addr = kTapIPAddr};
+ EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, 24, &dev_ipv4_addr,
+ sizeof(dev_ipv4_addr)));
+
+ ping_pkt ping_req =
+ CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr);
+ size_t packet_size = sizeof(ping_req) - offsetof(ping_pkt, ip);
+
+ // Send ICMP query
+ EXPECT_THAT(write(fd.get(), &ping_req.ip, packet_size),
+ SyscallSucceedsWithValue(packet_size));
+
+ // Receive loop to process inbound packets.
+ while (1) {
+ ping_pkt ping_resp = {};
+ EXPECT_THAT(read(fd.get(), &ping_resp.ip, packet_size),
+ SyscallSucceedsWithValue(packet_size));
+
+ // Process ping response packet.
+ if (!memcmp(&ping_resp.ip.saddr, &ping_req.ip.daddr, kIPLen) &&
+ !memcmp(&ping_resp.ip.daddr, &ping_req.ip.saddr, kIPLen) &&
+ ping_resp.icmp.type == 0 && ping_resp.icmp.code == 0) {
+ // Ends and passes the test.
+ break;
+ }
+ }
+}
+
// TCPBlockingConnectFailsArpResolution tests for TCP connect to fail on link
// address resolution failure to a routable, but non existent peer.
TEST_F(TuntapTest, TCPBlockingConnectFailsArpResolution) {
diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc
index 29e174f71..b40598767 100644
--- a/test/syscalls/linux/udp_socket.cc
+++ b/test/syscalls/linux/udp_socket.cc
@@ -791,14 +791,14 @@ TEST_P(UdpSocketTest, RecvErrorConnRefused) {
iov.iov_len = kBufLen;
size_t control_buf_len = CMSG_SPACE(sizeof(sock_extended_err) + addrlen_);
- char* control_buf = static_cast<char*>(calloc(1, control_buf_len));
+ std::vector<char> control_buf(control_buf_len);
struct sockaddr_storage remote;
memset(&remote, 0, sizeof(remote));
struct msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
- msg.msg_control = control_buf;
+ msg.msg_control = control_buf.data();
msg.msg_controllen = control_buf_len;
msg.msg_name = reinterpret_cast<void*>(&remote);
msg.msg_namelen = addrlen_;
@@ -1429,12 +1429,8 @@ TEST_P(UdpSocketTest, FIONREADZeroLengthPacket) {
sendto(sock_.get(), buf + i * psize, 0, 0, bind_addr_, addrlen_),
SyscallSucceedsWithValue(0));
- // TODO(gvisor.dev/issue/2726): sending a zero-length message to a hostinet
- // socket does not cause a poll event to be triggered.
- if (!IsRunningWithHostinet()) {
- ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000),
- SyscallSucceedsWithValue(1));
- }
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000),
+ SyscallSucceedsWithValue(1));
// Check that regardless of how many packets are in the queue, the size
// reported is that of a single packet.
diff --git a/test/syscalls/linux/uidgid.cc b/test/syscalls/linux/uidgid.cc
index 4139a18d8..d95a3e010 100644
--- a/test/syscalls/linux/uidgid.cc
+++ b/test/syscalls/linux/uidgid.cc
@@ -170,7 +170,9 @@ TEST(UidGidRootTest, SetgidNotFromThreadGroupLeader) {
const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1);
// NOTE(b/64676707): Do setgid in a separate thread so that we can test if
// info.si_pid is set correctly.
- ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); });
+ ScopedThread thread =
+ ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); });
+ thread.Join();
EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid));
#pragma pop_macro("allow_setgid")
diff --git a/test/syscalls/linux/uname.cc b/test/syscalls/linux/uname.cc
index 759ea4f53..c52abef5c 100644
--- a/test/syscalls/linux/uname.cc
+++ b/test/syscalls/linux/uname.cc
@@ -88,7 +88,7 @@ TEST(UnameTest, UnshareUTS) {
struct utsname init;
ASSERT_THAT(uname(&init), SyscallSucceeds());
- ScopedThread([&]() {
+ ScopedThread thread = ScopedThread([&]() {
EXPECT_THAT(unshare(CLONE_NEWUTS), SyscallSucceeds());
constexpr char kHostname[] = "wubbalubba";
@@ -97,6 +97,7 @@ TEST(UnameTest, UnshareUTS) {
char hostname[65];
EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds());
});
+ thread.Join();
struct utsname after;
EXPECT_THAT(uname(&after), SyscallSucceeds());
diff --git a/test/util/posix_error.h b/test/util/posix_error.h
index 9ca09b77c..40853cb21 100644
--- a/test/util/posix_error.h
+++ b/test/util/posix_error.h
@@ -385,7 +385,7 @@ class PosixErrorIsMatcher {
};
// Returns a gMock matcher that matches a PosixError or PosixErrorOr<> whose
-// whose error code matches code_matcher, and whose error message matches
+// error code matches code_matcher, and whose error message matches
// message_matcher.
template <typename ErrorCodeMatcher>
PosixErrorIsMatcher PosixErrorIs(
@@ -395,6 +395,14 @@ PosixErrorIsMatcher PosixErrorIs(
std::move(message_matcher));
}
+// Returns a gMock matcher that matches a PosixError or PosixErrorOr<> whose
+// error code matches code_matcher.
+template <typename ErrorCodeMatcher>
+PosixErrorIsMatcher PosixErrorIs(ErrorCodeMatcher&& code_matcher) {
+ return PosixErrorIsMatcher(std::forward<ErrorCodeMatcher>(code_matcher),
+ ::testing::_);
+}
+
// Returns a gMock matcher that matches a PosixErrorOr<> which is ok() and
// value matches the inner matcher.
template <typename InnerMatcher>
diff --git a/tools/bazeldefs/BUILD b/tools/bazeldefs/BUILD
index 24e6f8a94..5295f4a85 100644
--- a/tools/bazeldefs/BUILD
+++ b/tools/bazeldefs/BUILD
@@ -46,6 +46,11 @@ genrule(
outs = ["version.txt"],
cmd = "cat bazel-out/stable-status.txt | grep STABLE_VERSION | cut -d' ' -f2- | sed 's/^[^[:digit:]]*//g' >$@",
stamp = True,
+ tags = [
+ "manual",
+ "nobuilder",
+ "notap",
+ ],
visibility = ["//:sandbox"],
)
diff --git a/tools/checkescape/BUILD b/tools/checkescape/BUILD
index 940538b9e..109b5410c 100644
--- a/tools/checkescape/BUILD
+++ b/tools/checkescape/BUILD
@@ -8,6 +8,7 @@ go_library(
nogo = False,
visibility = ["//tools/nogo:__subpackages__"],
deps = [
+ "//tools/nogo/objdump",
"@org_golang_x_tools//go/analysis:go_default_library",
"@org_golang_x_tools//go/analysis/passes/buildssa:go_default_library",
"@org_golang_x_tools//go/ssa:go_default_library",
diff --git a/tools/checkescape/checkescape.go b/tools/checkescape/checkescape.go
index c788654a8..ddd1212d7 100644
--- a/tools/checkescape/checkescape.go
+++ b/tools/checkescape/checkescape.go
@@ -61,21 +61,19 @@ package checkescape
import (
"bufio"
"bytes"
- "flag"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"log"
- "os"
- "os/exec"
"path/filepath"
"strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/buildssa"
"golang.org/x/tools/go/ssa"
+ "gvisor.dev/gvisor/tools/nogo/objdump"
)
const (
@@ -92,21 +90,6 @@ const (
exempt = "// escapes"
)
-var (
- // Binary is the binary under analysis.
- //
- // See Reader, below.
- binary = flag.String("binary", "", "binary under analysis")
-
- // Reader is the input stream.
- //
- // This may be set instead of Binary.
- Reader io.Reader
-
- // objdumpTool is the tool used to dump a binary.
- objdumpTool = flag.String("objdump_tool", "", "tool used to dump a binary")
-)
-
// EscapeReason is an escape reason.
//
// This is a simple enum.
@@ -374,31 +357,6 @@ func MergeAll(others []Escapes) (es Escapes) {
// Note that the map uses <basename.go>:<line> because that is all that is
// provided in the objdump format. Since this is all local, it is sufficient.
func loadObjdump() (map[string][]string, error) {
- var (
- args []string
- stdin io.Reader
- )
- if *binary != "" {
- args = append(args, *binary)
- } else if Reader != nil {
- stdin = Reader
- } else {
- // We have no input stream or binary.
- return nil, fmt.Errorf("no binary or reader provided")
- }
-
- // Construct our command.
- cmd := exec.Command(*objdumpTool, args...)
- cmd.Stdin = stdin
- cmd.Stderr = os.Stderr
- out, err := cmd.StdoutPipe()
- if err != nil {
- return nil, err
- }
- if err := cmd.Start(); err != nil {
- return nil, err
- }
-
// Identify calls by address or name. Note that this is also
// constructed dynamically below, as we encounted the addresses.
// This is because some of the functions (duffzero) may have
@@ -431,78 +389,83 @@ func loadObjdump() (map[string][]string, error) {
// Build the map.
nextFunc := "" // For funcsAllowed.
m := make(map[string][]string)
- r := bufio.NewReader(out)
-NextLine:
- for {
- line, err := r.ReadString('\n')
- if err != nil && err != io.EOF {
- return nil, err
- }
- fields := strings.Fields(line)
-
- // Is this an "allowed" function definition?
- if len(fields) >= 2 && fields[0] == "TEXT" {
- nextFunc = strings.TrimSuffix(fields[1], "(SB)")
- if _, ok := funcsAllowed[nextFunc]; !ok {
- nextFunc = "" // Don't record addresses.
- }
- }
- if nextFunc != "" && len(fields) > 2 {
- // Save the given address (in hex form, as it appears).
- addrsAllowed[fields[1]] = struct{}{}
- }
-
- // We recognize lines corresponding to actual code (not the
- // symbol name or other metadata) and annotate them if they
- // correspond to an explicit CALL instruction. We assume that
- // the lack of a CALL for a given line is evidence that escape
- // analysis has eliminated an allocation.
- //
- // Lines look like this (including the first space):
- // gohacks_unsafe.go:33 0xa39 488b442408 MOVQ 0x8(SP), AX
- if len(fields) >= 5 && line[0] == ' ' {
- if !strings.Contains(fields[3], "CALL") {
- continue
+ if err := objdump.Load(func(origR io.Reader) error {
+ r := bufio.NewReader(origR)
+ NextLine:
+ for {
+ line, err := r.ReadString('\n')
+ if err != nil && err != io.EOF {
+ return err
}
- site := fields[0]
- target := strings.TrimSuffix(fields[4], "(SB)")
+ fields := strings.Fields(line)
- // Ignore strings containing allowed functions.
- if _, ok := funcsAllowed[target]; ok {
- continue
+ // Is this an "allowed" function definition?
+ if len(fields) >= 2 && fields[0] == "TEXT" {
+ nextFunc = strings.TrimSuffix(fields[1], "(SB)")
+ if _, ok := funcsAllowed[nextFunc]; !ok {
+ nextFunc = "" // Don't record addresses.
+ }
}
- if _, ok := addrsAllowed[target]; ok {
- continue
+ if nextFunc != "" && len(fields) > 2 {
+ // Save the given address (in hex form, as it appears).
+ addrsAllowed[fields[1]] = struct{}{}
}
- if len(fields) > 5 {
- // This may be a future relocation. Some
- // objdump versions describe this differently.
- // If it contains any of the functions allowed
- // above as a string, we let it go.
- softTarget := strings.Join(fields[5:], " ")
- for name := range funcsAllowed {
- if strings.Contains(softTarget, name) {
- continue NextLine
+
+ // We recognize lines corresponding to actual code (not the
+ // symbol name or other metadata) and annotate them if they
+ // correspond to an explicit CALL instruction. We assume that
+ // the lack of a CALL for a given line is evidence that escape
+ // analysis has eliminated an allocation.
+ //
+ // Lines look like this (including the first space):
+ // gohacks_unsafe.go:33 0xa39 488b442408 MOVQ 0x8(SP), AX
+ if len(fields) >= 5 && line[0] == ' ' {
+ if !strings.Contains(fields[3], "CALL") {
+ continue
+ }
+ site := fields[0]
+ target := strings.TrimSuffix(fields[4], "(SB)")
+
+ // Ignore strings containing allowed functions.
+ if _, ok := funcsAllowed[target]; ok {
+ continue
+ }
+ if _, ok := addrsAllowed[target]; ok {
+ continue
+ }
+ if len(fields) > 5 {
+ // This may be a future relocation. Some
+ // objdump versions describe this differently.
+ // If it contains any of the functions allowed
+ // above as a string, we let it go.
+ softTarget := strings.Join(fields[5:], " ")
+ for name := range funcsAllowed {
+ if strings.Contains(softTarget, name) {
+ continue NextLine
+ }
}
}
- }
- // Does this exist already?
- existing, ok := m[site]
- if !ok {
- existing = make([]string, 0, 1)
- }
- for _, other := range existing {
- if target == other {
- continue NextLine
+ // Does this exist already?
+ existing, ok := m[site]
+ if !ok {
+ existing = make([]string, 0, 1)
+ }
+ for _, other := range existing {
+ if target == other {
+ continue NextLine
+ }
}
+ existing = append(existing, target)
+ m[site] = existing // Update.
+ }
+ if err == io.EOF {
+ break
}
- existing = append(existing, target)
- m[site] = existing // Update.
- }
- if err == io.EOF {
- break
}
+ return nil
+ }); err != nil {
+ return nil, err
}
// Zap any accidental false positives.
@@ -518,11 +481,6 @@ NextLine:
final[site] = filteredCalls
}
- // Wait for the dump to finish.
- if err := cmd.Wait(); err != nil {
- return nil, err
- }
-
return final, nil
}
diff --git a/tools/checklocks/BUILD b/tools/checklocks/BUILD
index 7d4c63dc7..d23b7cde6 100644
--- a/tools/checklocks/BUILD
+++ b/tools/checklocks/BUILD
@@ -4,11 +4,16 @@ package(licenses = ["notice"])
go_library(
name = "checklocks",
- srcs = ["checklocks.go"],
+ srcs = [
+ "analysis.go",
+ "annotations.go",
+ "checklocks.go",
+ "facts.go",
+ "state.go",
+ ],
nogo = False,
visibility = ["//tools/nogo:__subpackages__"],
deps = [
- "//pkg/log",
"@org_golang_x_tools//go/analysis:go_default_library",
"@org_golang_x_tools//go/analysis/passes/buildssa:go_default_library",
"@org_golang_x_tools//go/ssa:go_default_library",
diff --git a/tools/checklocks/README.md b/tools/checklocks/README.md
index dfb0275ab..bd4beb649 100644
--- a/tools/checklocks/README.md
+++ b/tools/checklocks/README.md
@@ -1,16 +1,29 @@
# CheckLocks Analyzer
-<!--* freshness: { owner: 'gvisor-eng' reviewed: '2020-10-05' } *-->
+<!--* freshness: { owner: 'gvisor-eng' reviewed: '2021-03-21' } *-->
-Checklocks is a nogo analyzer that at compile time uses Go's static analysis
-tools to identify and flag cases where a field that is guarded by a mutex in the
-same struct is accessed outside of a mutex lock.
+Checklocks is an analyzer for lock and atomic constraints. The analyzer relies
+on explicit annotations to identify fields that should be checked for access.
-The analyzer relies on explicit '// +checklocks:<mutex-name>' kind of
-annotations to identify fields that should be checked for access.
+## Atomic annotations
-Individual struct members may be protected by annotations that indicate how they
-must be accessed. These annotations are of the form:
+Individual struct members may be noted as requiring atomic access. These
+annotations are of the form:
+
+```go
+type foo struct {
+ // +checkatomic
+ bar int32
+}
+```
+
+This will ensure that all accesses to bar are atomic, with the exception of
+operations on newly allocated objects.
+
+## Lock annotations
+
+Individual struct members may be protected by annotations that indicate locking
+requirements for accessing members. These annotations are of the form:
```go
type foo struct {
@@ -64,30 +77,6 @@ annotations from becoming stale over time as fields are renamed, etc.
# Currently not supported
-1. The analyzer does not correctly handle deferred functions. e.g The following
- code is not correctly checked by the analyzer. The defer call is never
- evaluated. As a result if the lock was to be say unlocked twice via deferred
- functions it would not be caught by the analyzer.
-
- Similarly deferred anonymous functions are not evaluated either.
-
-```go
-type A struct {
- mu sync.Mutex
-
- // +checklocks:mu
- x int
-}
-
-func abc() {
- var a A
- a.mu.Lock()
- defer a.mu.Unlock()
- defer a.mu.Unlock()
- a.x = 1
-}
-```
-
1. Anonymous functions are not correctly evaluated. The analyzer does not
currently support specifying annotations on anonymous functions as a result
evaluation of a function that accesses protected fields will fail.
@@ -107,10 +96,9 @@ func abc() {
f()
a.mu.Unlock()
}
-
```
-# Explicitly Not Supported
+### Explicitly Not Supported
1. Checking for embedded mutexes as sync.Locker rather than directly as
'sync.Mutex'. In other words, the checker will not track mutex Lock and
@@ -140,3 +128,30 @@ func abc() {
checklocks. Only struct members can be used.
2. The checker will not support checking for lock ordering violations.
+
+## Mixed mode
+
+Some members may allow read-only atomic access, but be protected against writes
+by a mutex. Generally, this imposes the following requirements:
+
+For a read, one of the following must be true:
+
+1. A lock held be held.
+1. The access is atomic.
+
+For a write, both of the following must be true:
+
+1. The lock must be held.
+1. The write must be atomic.
+
+In order to annotate a relevant field, simply apply *both* annotations from
+above. For example:
+
+```go
+type foo struct {
+ mu sync.Mutex
+ // +checklocks:mu
+ // +checkatomic
+ bar int32
+}
+```
diff --git a/tools/checklocks/analysis.go b/tools/checklocks/analysis.go
new file mode 100644
index 000000000..d3fd797d0
--- /dev/null
+++ b/tools/checklocks/analysis.go
@@ -0,0 +1,628 @@
+// Copyright 2020 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 checklocks
+
+import (
+ "go/token"
+ "go/types"
+ "strings"
+
+ "golang.org/x/tools/go/ssa"
+)
+
+func gcd(a, b atomicAlignment) atomicAlignment {
+ for b != 0 {
+ a, b = b, a%b
+ }
+ return a
+}
+
+// typeAlignment returns the type alignment for the given type.
+func (pc *passContext) typeAlignment(pkg *types.Package, obj types.Object) atomicAlignment {
+ requiredOffset := atomicAlignment(1)
+ if pc.pass.ImportObjectFact(obj, &requiredOffset) {
+ return requiredOffset
+ }
+
+ switch x := obj.Type().Underlying().(type) {
+ case *types.Struct:
+ fields := make([]*types.Var, x.NumFields())
+ for i := 0; i < x.NumFields(); i++ {
+ fields[i] = x.Field(i)
+ }
+ offsets := pc.pass.TypesSizes.Offsetsof(fields)
+ for i := 0; i < x.NumFields(); i++ {
+ // Check the offset, and then assuming that this offset
+ // aligns with the offset for the broader type.
+ fieldRequired := pc.typeAlignment(pkg, fields[i])
+ if offsets[i]%int64(fieldRequired) != 0 {
+ // The offset of this field is not compatible.
+ pc.maybeFail(fields[i].Pos(), "have alignment %d, need %d", offsets[i], fieldRequired)
+ }
+ // Ensure the requiredOffset is the LCM of the offset.
+ requiredOffset *= fieldRequired / gcd(requiredOffset, fieldRequired)
+ }
+ case *types.Array:
+ // Export direct alignment requirements.
+ if named, ok := x.Elem().(*types.Named); ok {
+ requiredOffset = pc.typeAlignment(pkg, named.Obj())
+ }
+ default:
+ // Use the compiler's underlying alignment.
+ requiredOffset = atomicAlignment(pc.pass.TypesSizes.Alignof(obj.Type().Underlying()))
+ }
+
+ if pkg == obj.Pkg() {
+ // Cache as an object fact, to subsequent calls. Note that we
+ // can only export object facts for the package that we are
+ // currently analyzing. There may be no exported facts for
+ // array types or alias types, for example.
+ pc.pass.ExportObjectFact(obj, &requiredOffset)
+ }
+
+ return requiredOffset
+}
+
+// checkTypeAlignment checks the alignment of the given type.
+//
+// This calls typeAlignment, which resolves all types recursively. This method
+// should be called for all types individual to ensure full coverage.
+func (pc *passContext) checkTypeAlignment(pkg *types.Package, typ *types.Named) {
+ _ = pc.typeAlignment(pkg, typ.Obj())
+}
+
+// checkAtomicCall checks for an atomic access.
+//
+// inst is the instruction analyzed, obj is used only for maybeFail.
+//
+// If mustBeAtomic is true, then we assert that the instruction *is* an atomic
+// fucnction call. If it is false, then we assert that it is *not* an atomic
+// dispatch.
+//
+// If readOnly is true, then only atomic read access are allowed. Note that
+// readOnly is only meaningful if mustBeAtomic is set.
+func (pc *passContext) checkAtomicCall(inst ssa.Instruction, obj types.Object, mustBeAtomic, readOnly bool) {
+ switch x := inst.(type) {
+ case *ssa.Call:
+ if x.Common().IsInvoke() {
+ if mustBeAtomic {
+ // This is an illegal interface dispatch.
+ pc.maybeFail(inst.Pos(), "dynamic dispatch with atomic-only field")
+ }
+ return
+ }
+ fn, ok := x.Common().Value.(*ssa.Function)
+ if !ok {
+ if mustBeAtomic {
+ // This is an illegal call to a non-static function.
+ pc.maybeFail(inst.Pos(), "dispatch to non-static function with atomic-only field")
+ }
+ return
+ }
+ pkg := fn.Package()
+ if pkg == nil {
+ if mustBeAtomic {
+ // This is a call to some shared wrapper function.
+ pc.maybeFail(inst.Pos(), "dispatch to shared function or wrapper")
+ }
+ return
+ }
+ var lff lockFunctionFacts // Check for exemption.
+ if obj := fn.Object(); obj != nil && pc.pass.ImportObjectFact(obj, &lff) && lff.Ignore {
+ return
+ }
+ if name := pkg.Pkg.Name(); name != "atomic" && name != "atomicbitops" {
+ if mustBeAtomic {
+ // This is an illegal call to a non-atomic package function.
+ pc.maybeFail(inst.Pos(), "dispatch to non-atomic function with atomic-only field")
+ }
+ return
+ }
+ if !mustBeAtomic {
+ // We are *not* expecting an atomic dispatch.
+ if _, ok := pc.forced[pc.positionKey(inst.Pos())]; !ok {
+ pc.maybeFail(inst.Pos(), "unexpected call to atomic function")
+ }
+ }
+ if !strings.HasPrefix(fn.Name(), "Load") && readOnly {
+ // We are not allowing any reads in this context.
+ if _, ok := pc.forced[pc.positionKey(inst.Pos())]; !ok {
+ pc.maybeFail(inst.Pos(), "unexpected call to atomic write function, is a lock missing?")
+ }
+ return
+ }
+ default:
+ if mustBeAtomic {
+ // This is something else entirely.
+ if _, ok := pc.forced[pc.positionKey(inst.Pos())]; !ok {
+ pc.maybeFail(inst.Pos(), "illegal use of atomic-only field by %T instruction", inst)
+ }
+ return
+ }
+ }
+}
+
+func resolveStruct(typ types.Type) (*types.Struct, bool) {
+ structType, ok := typ.Underlying().(*types.Struct)
+ if ok {
+ return structType, true
+ }
+ ptrType, ok := typ.Underlying().(*types.Pointer)
+ if ok {
+ return resolveStruct(ptrType.Elem())
+ }
+ return nil, false
+}
+
+func findField(typ types.Type, field int) (types.Object, bool) {
+ structType, ok := resolveStruct(typ)
+ if !ok {
+ return nil, false
+ }
+ return structType.Field(field), true
+}
+
+// instructionWithReferrers is a generalization over ssa.Field, ssa.FieldAddr.
+type instructionWithReferrers interface {
+ ssa.Instruction
+ Referrers() *[]ssa.Instruction
+}
+
+// checkFieldAccess checks the validity of a field access.
+//
+// This also enforces atomicity constraints for fields that must be accessed
+// atomically. The parameter isWrite indicates whether this field is used
+// downstream for a write operation.
+func (pc *passContext) checkFieldAccess(inst instructionWithReferrers, structObj ssa.Value, field int, ls *lockState, isWrite bool) {
+ var (
+ lff lockFieldFacts
+ lgf lockGuardFacts
+ guardsFound int
+ guardsHeld int
+ )
+
+ fieldObj, _ := findField(structObj.Type(), field)
+ pc.pass.ImportObjectFact(fieldObj, &lff)
+ pc.pass.ImportObjectFact(fieldObj, &lgf)
+
+ for guardName, fl := range lgf.GuardedBy {
+ guardsFound++
+ r := fl.resolve(structObj)
+ if _, ok := ls.isHeld(r); ok {
+ guardsHeld++
+ continue
+ }
+ if _, ok := pc.forced[pc.positionKey(inst.Pos())]; ok {
+ // Mark this as locked, since it has been forced.
+ ls.lockField(r)
+ guardsHeld++
+ continue
+ }
+ // Note that we may allow this if the disposition is atomic,
+ // and we are allowing atomic reads only. This will fall into
+ // the atomic disposition check below, which asserts that the
+ // access is atomic. Further, guardsHeld < guardsFound will be
+ // true for this case, so we require it to be read-only.
+ if lgf.AtomicDisposition != atomicRequired {
+ // There is no force key, no atomic access and no lock held.
+ pc.maybeFail(inst.Pos(), "invalid field access, %s must be locked when accessing %s (locks: %s)", guardName, fieldObj.Name(), ls.String())
+ }
+ }
+
+ // Check the atomic access for this field.
+ switch lgf.AtomicDisposition {
+ case atomicRequired:
+ // Check that this is used safely as an input.
+ readOnly := guardsHeld < guardsFound
+ if refs := inst.Referrers(); refs != nil {
+ for _, otherInst := range *refs {
+ pc.checkAtomicCall(otherInst, fieldObj, true, readOnly)
+ }
+ }
+ // Check that this is not otherwise written non-atomically,
+ // even if we do hold all the locks.
+ if isWrite {
+ pc.maybeFail(inst.Pos(), "non-atomic write of field %s, writes must still be atomic with locks held (locks: %s)", fieldObj.Name(), ls.String())
+ }
+ case atomicDisallow:
+ // Check that this is *not* used atomically.
+ if refs := inst.Referrers(); refs != nil {
+ for _, otherInst := range *refs {
+ pc.checkAtomicCall(otherInst, fieldObj, false, false)
+ }
+ }
+ }
+}
+
+func (pc *passContext) checkCall(call callCommon, ls *lockState) {
+ // See: https://godoc.org/golang.org/x/tools/go/ssa#CallCommon
+ //
+ // 1. "call" mode: when Method is nil (!IsInvoke), a CallCommon represents an ordinary
+ // function call of the value in Value, which may be a *Builtin, a *Function or any
+ // other value of kind 'func'.
+ //
+ // Value may be one of:
+ // (a) a *Function, indicating a statically dispatched call
+ // to a package-level function, an anonymous function, or
+ // a method of a named type.
+ //
+ // (b) a *MakeClosure, indicating an immediately applied
+ // function literal with free variables.
+ //
+ // (c) a *Builtin, indicating a statically dispatched call
+ // to a built-in function.
+ //
+ // (d) any other value, indicating a dynamically dispatched
+ // function call.
+ switch fn := call.Common().Value.(type) {
+ case *ssa.Function:
+ var lff lockFunctionFacts
+ if fn.Object() != nil {
+ pc.pass.ImportObjectFact(fn.Object(), &lff)
+ pc.checkFunctionCall(call, fn, &lff, ls)
+ } else {
+ // Anonymous functions have no facts, and cannot be
+ // annotated. We don't check for violations using the
+ // function facts, since they cannot exist. Instead, we
+ // do a fresh analysis using the current lock state.
+ fnls := ls.fork()
+ for i, arg := range call.Common().Args {
+ fnls.store(fn.Params[i], arg)
+ }
+ pc.checkFunction(call, fn, &lff, fnls, true /* force */)
+ }
+ case *ssa.MakeClosure:
+ // Note that creating and then invoking closures locally is
+ // allowed, but analysis of passing closures is done when
+ // checking individual instructions.
+ pc.checkClosure(call, fn, ls)
+ default:
+ return
+ }
+}
+
+// postFunctionCallUpdate updates all conditions.
+func (pc *passContext) postFunctionCallUpdate(call callCommon, lff *lockFunctionFacts, ls *lockState) {
+ // Release all locks not still held.
+ for fieldName, fg := range lff.HeldOnEntry {
+ if _, ok := lff.HeldOnExit[fieldName]; ok {
+ continue
+ }
+ r := fg.resolveCall(call.Common().Args, call.Value())
+ if s, ok := ls.unlockField(r); !ok {
+ if _, ok := pc.forced[pc.positionKey(call.Pos())]; !ok {
+ pc.maybeFail(call.Pos(), "attempt to release %s (%s), but not held (locks: %s)", fieldName, s, ls.String())
+ }
+ }
+ }
+
+ // Update all held locks if acquired.
+ for fieldName, fg := range lff.HeldOnExit {
+ if _, ok := lff.HeldOnEntry[fieldName]; ok {
+ continue
+ }
+ // Acquire the lock per the annotation.
+ r := fg.resolveCall(call.Common().Args, call.Value())
+ if s, ok := ls.lockField(r); !ok {
+ if _, ok := pc.forced[pc.positionKey(call.Pos())]; !ok {
+ pc.maybeFail(call.Pos(), "attempt to acquire %s (%s), but already held (locks: %s)", fieldName, s, ls.String())
+ }
+ }
+ }
+}
+
+// checkFunctionCall checks preconditions for function calls, and tracks the
+// lock state by recording relevant calls to sync functions. Note that calls to
+// atomic functions are tracked by checkFieldAccess by looking directly at the
+// referrers (because ordering doesn't matter there, so we need not scan in
+// instruction order).
+func (pc *passContext) checkFunctionCall(call callCommon, fn *ssa.Function, lff *lockFunctionFacts, ls *lockState) {
+ // Check all guards required are held.
+ for fieldName, fg := range lff.HeldOnEntry {
+ r := fg.resolveCall(call.Common().Args, call.Value())
+ if s, ok := ls.isHeld(r); !ok {
+ if _, ok := pc.forced[pc.positionKey(call.Pos())]; !ok {
+ pc.maybeFail(call.Pos(), "must hold %s (%s) to call %s, but not held (locks: %s)", fieldName, s, fn.Name(), ls.String())
+ } else {
+ // Force the lock to be acquired.
+ ls.lockField(r)
+ }
+ }
+ }
+
+ // Update all lock state accordingly.
+ pc.postFunctionCallUpdate(call, lff, ls)
+
+ // Check if it's a method dispatch for something in the sync package.
+ // See: https://godoc.org/golang.org/x/tools/go/ssa#Function
+ if fn.Package() != nil && fn.Package().Pkg.Name() == "sync" && fn.Signature.Recv() != nil {
+ switch fn.Name() {
+ case "Lock", "RLock":
+ if s, ok := ls.lockField(resolvedValue{value: call.Common().Args[0], valid: true}); !ok {
+ if _, ok := pc.forced[pc.positionKey(call.Pos())]; !ok {
+ // Double locking a mutex that is already locked.
+ pc.maybeFail(call.Pos(), "%s already locked (locks: %s)", s, ls.String())
+ }
+ }
+ case "Unlock", "RUnlock":
+ if s, ok := ls.unlockField(resolvedValue{value: call.Common().Args[0], valid: true}); !ok {
+ if _, ok := pc.forced[pc.positionKey(call.Pos())]; !ok {
+ // Unlocking something that is already unlocked.
+ pc.maybeFail(call.Pos(), "%s already unlocked (locks: %s)", s, ls.String())
+ }
+ }
+ }
+ }
+}
+
+// checkClosure forks the lock state, and creates a binding for the FreeVars of
+// the closure. This allows the analysis to resolve the closure.
+func (pc *passContext) checkClosure(call callCommon, fn *ssa.MakeClosure, ls *lockState) {
+ clls := ls.fork()
+ clfn := fn.Fn.(*ssa.Function)
+ for i, fv := range clfn.FreeVars {
+ clls.store(fv, fn.Bindings[i])
+ }
+
+ // Note that this is *not* a call to check function call, which checks
+ // against the function preconditions. Instead, this does a fresh
+ // analysis of the function from source code with a different state.
+ var nolff lockFunctionFacts
+ pc.checkFunction(call, clfn, &nolff, clls, true /* force */)
+}
+
+// freshAlloc indicates that v has been allocated within the local scope. There
+// is no lock checking done on objects that are freshly allocated.
+func freshAlloc(v ssa.Value) bool {
+ switch x := v.(type) {
+ case *ssa.Alloc:
+ return true
+ case *ssa.FieldAddr:
+ return freshAlloc(x.X)
+ case *ssa.Field:
+ return freshAlloc(x.X)
+ case *ssa.IndexAddr:
+ return freshAlloc(x.X)
+ case *ssa.Index:
+ return freshAlloc(x.X)
+ case *ssa.Convert:
+ return freshAlloc(x.X)
+ case *ssa.ChangeType:
+ return freshAlloc(x.X)
+ default:
+ return false
+ }
+}
+
+// isWrite indicates that this value is used as the addr field in a store.
+//
+// Note that this may still be used for a write. The return here is optimistic
+// but sufficient for basic analysis.
+func isWrite(v ssa.Value) bool {
+ refs := v.Referrers()
+ if refs == nil {
+ return false
+ }
+ for _, ref := range *refs {
+ if s, ok := ref.(*ssa.Store); ok && s.Addr == v {
+ return true
+ }
+ }
+ return false
+}
+
+// callCommon is an ssa.Value that also implements Common.
+type callCommon interface {
+ Pos() token.Pos
+ Common() *ssa.CallCommon
+ Value() *ssa.Call
+}
+
+// checkInstruction checks the legality the single instruction based on the
+// current lockState.
+func (pc *passContext) checkInstruction(inst ssa.Instruction, ls *lockState) (*ssa.Return, *lockState) {
+ switch x := inst.(type) {
+ case *ssa.Store:
+ // Record that this value is holding this other value. This is
+ // because at the beginning of each ssa execution, there is a
+ // series of assignments of parameter values to alloc objects.
+ // This allows us to trace these back to the original
+ // parameters as aliases above.
+ //
+ // Note that this may overwrite an existing value in the lock
+ // state, but this is intentional.
+ ls.store(x.Addr, x.Val)
+ case *ssa.Field:
+ if !freshAlloc(x.X) {
+ pc.checkFieldAccess(x, x.X, x.Field, ls, false)
+ }
+ case *ssa.FieldAddr:
+ if !freshAlloc(x.X) {
+ pc.checkFieldAccess(x, x.X, x.Field, ls, isWrite(x))
+ }
+ case *ssa.Call:
+ pc.checkCall(x, ls)
+ case *ssa.Defer:
+ ls.pushDefer(x)
+ case *ssa.RunDefers:
+ for d := ls.popDefer(); d != nil; d = ls.popDefer() {
+ pc.checkCall(d, ls)
+ }
+ case *ssa.MakeClosure:
+ refs := x.Referrers()
+ if refs == nil {
+ // This is strange, it's not used? Ignore this case,
+ // since it will probably be optimized away.
+ return nil, nil
+ }
+ hasNonCall := false
+ for _, ref := range *refs {
+ switch ref.(type) {
+ case *ssa.Call, *ssa.Defer:
+ // Analysis will be done on the call itself
+ // subsequently, including the lock state at
+ // the time of the call.
+ default:
+ // We need to analyze separately. Per below,
+ // this means that we'll analyze at closure
+ // construction time no zero assumptions about
+ // when it will be called.
+ hasNonCall = true
+ }
+ }
+ if !hasNonCall {
+ return nil, nil
+ }
+ // Analyze the closure without bindings. This means that we
+ // assume no lock facts or have any existing lock state. Only
+ // trivial closures are acceptable in this case.
+ clfn := x.Fn.(*ssa.Function)
+ var nolff lockFunctionFacts
+ pc.checkFunction(nil, clfn, &nolff, nil, false /* force */)
+ case *ssa.Return:
+ return x, ls // Valid return state.
+ }
+ return nil, nil
+}
+
+// checkBasicBlock traverses the control flow graph starting at a set of given
+// block and checks each instruction for allowed operations.
+func (pc *passContext) checkBasicBlock(fn *ssa.Function, block *ssa.BasicBlock, lff *lockFunctionFacts, parent *lockState, seen map[*ssa.BasicBlock]*lockState) *lockState {
+ if oldLS, ok := seen[block]; ok && oldLS.isCompatible(parent) {
+ return nil
+ }
+
+ // If the lock state is not compatible, then we need to do the
+ // recursive analysis to ensure that it is still sane. For example, the
+ // following is guaranteed to generate incompatible locking states:
+ //
+ // if foo {
+ // mu.Lock()
+ // }
+ // other stuff ...
+ // if foo {
+ // mu.Unlock()
+ // }
+
+ var (
+ rv *ssa.Return
+ rls *lockState
+ )
+
+ // Analyze this block.
+ seen[block] = parent
+ ls := parent.fork()
+ for _, inst := range block.Instrs {
+ rv, rls = pc.checkInstruction(inst, ls)
+ if rls != nil {
+ failed := false
+ // Validate held locks.
+ for fieldName, fg := range lff.HeldOnExit {
+ r := fg.resolveStatic(fn, rv)
+ if s, ok := rls.isHeld(r); !ok {
+ if _, ok := pc.forced[pc.positionKey(rv.Pos())]; !ok {
+ pc.maybeFail(rv.Pos(), "lock %s (%s) not held (locks: %s)", fieldName, s, rls.String())
+ failed = true
+ } else {
+ // Force the lock to be acquired.
+ rls.lockField(r)
+ }
+ }
+ }
+ // Check for other locks, but only if the above didn't trip.
+ if !failed && rls.count() != len(lff.HeldOnExit) {
+ pc.maybeFail(rv.Pos(), "return with unexpected locks held (locks: %s)", rls.String())
+ }
+ }
+ }
+
+ // Analyze all successors.
+ for _, succ := range block.Succs {
+ // Collect possible return values, and make sure that the lock
+ // state aligns with any return value that we may have found
+ // above. Note that checkBasicBlock will recursively analyze
+ // the lock state to ensure that Releases and Acquires are
+ // respected.
+ if pls := pc.checkBasicBlock(fn, succ, lff, ls, seen); pls != nil {
+ if rls != nil && !rls.isCompatible(pls) {
+ if _, ok := pc.forced[pc.positionKey(fn.Pos())]; !ok {
+ pc.maybeFail(fn.Pos(), "incompatible return states (first: %s, second: %v)", rls.String(), pls.String())
+ }
+ }
+ rls = pls
+ }
+ }
+ return rls
+}
+
+// checkFunction checks a function invocation, typically starting with nil lockState.
+func (pc *passContext) checkFunction(call callCommon, fn *ssa.Function, lff *lockFunctionFacts, parent *lockState, force bool) {
+ defer func() {
+ // Mark this function as checked. This is used by the top-level
+ // loop to ensure that all anonymous functions are scanned, if
+ // they are not explicitly invoked here. Note that this can
+ // happen if the anonymous functions are e.g. passed only as
+ // parameters or used to initialize some structure.
+ pc.functions[fn] = struct{}{}
+ }()
+ if _, ok := pc.functions[fn]; !force && ok {
+ // This function has already been analyzed at least once.
+ // That's all we permit for each function, although this may
+ // cause some anonymous functions to be analyzed in only one
+ // context.
+ return
+ }
+
+ // If no return value is provided, then synthesize one. This is used
+ // below only to check against the locks preconditions, which may
+ // include return values.
+ if call == nil {
+ call = &ssa.Call{Call: ssa.CallCommon{Value: fn}}
+ }
+
+ // Initialize ls with any preconditions that require locks to be held
+ // for the method to be invoked. Note that in the overwhleming majority
+ // of cases, parent will be nil. However, in the case of closures and
+ // anonymous functions, we may start with a non-nil lock state.
+ ls := parent.fork()
+ for fieldName, fg := range lff.HeldOnEntry {
+ // The first is the method object itself so we skip that when looking
+ // for receiver/function parameters.
+ r := fg.resolveStatic(fn, call.Value())
+ if s, ok := ls.lockField(r); !ok {
+ // This can only happen if the same value is declared
+ // multiple times, and should be caught by the earlier
+ // fact scanning. Keep it here as a sanity check.
+ pc.maybeFail(fn.Pos(), "lock %s (%s) acquired multiple times (locks: %s)", fieldName, s, ls.String())
+ }
+ }
+
+ // Scan the blocks.
+ seen := make(map[*ssa.BasicBlock]*lockState)
+ if len(fn.Blocks) > 0 {
+ pc.checkBasicBlock(fn, fn.Blocks[0], lff, ls, seen)
+ }
+
+ // Scan the recover block.
+ if fn.Recover != nil {
+ pc.checkBasicBlock(fn, fn.Recover, lff, ls, seen)
+ }
+
+ // Update all lock state accordingly. This will be called only if we
+ // are doing inline analysis for e.g. an anonymous function.
+ if call != nil && parent != nil {
+ pc.postFunctionCallUpdate(call, lff, parent)
+ }
+}
diff --git a/tools/checklocks/annotations.go b/tools/checklocks/annotations.go
new file mode 100644
index 000000000..371260980
--- /dev/null
+++ b/tools/checklocks/annotations.go
@@ -0,0 +1,129 @@
+// Copyright 2020 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 checklocks
+
+import (
+ "fmt"
+
+ "go/token"
+ "strconv"
+ "strings"
+)
+
+const (
+ checkLocksAnnotation = "// +checklocks:"
+ checkLocksAcquires = "// +checklocksacquire:"
+ checkLocksReleases = "// +checklocksrelease:"
+ checkLocksIgnore = "// +checklocksignore"
+ checkLocksForce = "// +checklocksforce"
+ checkLocksFail = "// +checklocksfail"
+ checkAtomicAnnotation = "// +checkatomic"
+)
+
+// failData indicates an expected failure.
+type failData struct {
+ pos token.Pos
+ count int
+ seen int
+}
+
+// positionKey is a simple position string.
+type positionKey string
+
+// positionKey converts from a token.Pos to a key we can use to track failures
+// as the position of the failure annotation is not the same as the position of
+// the actual failure (different column/offsets). Hence we ignore these fields
+// and only use the file/line numbers to track failures.
+func (pc *passContext) positionKey(pos token.Pos) positionKey {
+ position := pc.pass.Fset.Position(pos)
+ return positionKey(fmt.Sprintf("%s:%d", position.Filename, position.Line))
+}
+
+// addFailures adds an expected failure.
+func (pc *passContext) addFailures(pos token.Pos, s string) {
+ count := 1
+ if len(s) > 0 && s[0] == ':' {
+ parsedCount, err := strconv.Atoi(s[1:])
+ if err != nil {
+ pc.pass.Reportf(pos, "unable to parse failure annotation %q: %v", s[1:], err)
+ return
+ }
+ count = parsedCount
+ }
+ pc.failures[pc.positionKey(pos)] = &failData{
+ pos: pos,
+ count: count,
+ }
+}
+
+// addExemption adds an exemption.
+func (pc *passContext) addExemption(pos token.Pos) {
+ pc.exemptions[pc.positionKey(pos)] = struct{}{}
+}
+
+// addForce adds a force annotation.
+func (pc *passContext) addForce(pos token.Pos) {
+ pc.forced[pc.positionKey(pos)] = struct{}{}
+}
+
+// maybeFail checks a potential failure against a specific failure map.
+func (pc *passContext) maybeFail(pos token.Pos, fmtStr string, args ...interface{}) {
+ if fd, ok := pc.failures[pc.positionKey(pos)]; ok {
+ fd.seen++
+ return
+ }
+ if _, ok := pc.exemptions[pc.positionKey(pos)]; ok {
+ return // Ignored, not counted.
+ }
+ pc.pass.Reportf(pos, fmtStr, args...)
+}
+
+// checkFailure checks for the expected failure counts.
+func (pc *passContext) checkFailures() {
+ for _, fd := range pc.failures {
+ if fd.count != fd.seen {
+ // We are missing expect failures, report as much as possible.
+ pc.pass.Reportf(fd.pos, "got %d failures, want %d failures", fd.seen, fd.count)
+ }
+ }
+}
+
+// extractAnnotations extracts annotations from text.
+func (pc *passContext) extractAnnotations(s string, fns map[string]func(p string)) {
+ for prefix, fn := range fns {
+ if strings.HasPrefix(s, prefix) {
+ fn(s[len(prefix):])
+ }
+ }
+}
+
+// extractLineFailures extracts all line-based exceptions.
+//
+// Note that this applies only to individual line exemptions, and does not
+// consider function-wide exemptions, or specific field exemptions, which are
+// extracted separately as part of the saved facts for those objects.
+func (pc *passContext) extractLineFailures() {
+ for _, f := range pc.pass.Files {
+ for _, cg := range f.Comments {
+ for _, c := range cg.List {
+ pc.extractAnnotations(c.Text, map[string]func(string){
+ checkLocksFail: func(p string) { pc.addFailures(c.Pos(), p) },
+ checkLocksIgnore: func(string) { pc.addExemption(c.Pos()) },
+ checkLocksForce: func(string) { pc.addForce(c.Pos()) },
+ })
+ }
+ }
+ }
+}
diff --git a/tools/checklocks/checklocks.go b/tools/checklocks/checklocks.go
index 1e877d394..180f8873f 100644
--- a/tools/checklocks/checklocks.go
+++ b/tools/checklocks/checklocks.go
@@ -13,32 +13,19 @@
// limitations under the License.
// Package checklocks performs lock analysis to identify and flag unprotected
-// access to field annotated with a '// +checklocks:<mutex-name>' annotation.
+// access to annotated fields.
//
-// For detailed ussage refer to README.md in the same directory.
+// For detailed usage refer to README.md in the same directory.
package checklocks
import (
- "bytes"
- "fmt"
"go/ast"
"go/token"
"go/types"
- "reflect"
- "regexp"
- "strconv"
- "strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/buildssa"
"golang.org/x/tools/go/ssa"
- "gvisor.dev/gvisor/pkg/log"
-)
-
-const (
- checkLocksAnnotation = "// +checklocks:"
- checkLocksIgnore = "// +checklocksignore"
- checkLocksFail = "// +checklocksfail"
)
// Analyzer is the main entrypoint.
@@ -47,712 +34,121 @@ var Analyzer = &analysis.Analyzer{
Doc: "checks lock preconditions on functions and fields",
Run: run,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
- FactTypes: []analysis.Fact{(*lockFieldFacts)(nil), (*lockFunctionFacts)(nil)},
-}
-
-// lockFieldFacts apply on every struct field protected by a lock or that is a
-// lock.
-type lockFieldFacts struct {
- // GuardedBy tracks the names and field numbers that guard this field.
- GuardedBy map[string]int
-
- // IsMutex is true if the field is of type sync.Mutex.
- IsMutex bool
-
- // IsRWMutex is true if the field is of type sync.RWMutex.
- IsRWMutex bool
-
- // FieldNumber is the number of this field in the struct.
- FieldNumber int
-}
-
-// AFact implements analysis.Fact.AFact.
-func (*lockFieldFacts) AFact() {}
-
-type functionGuard struct {
- // ParameterNumber is the index of the object that contains the guarding mutex.
- // This is required during SSA analysis as field names and parameters names are
- // not available in SSA. For example, from the example below ParameterNumber would
- // be 1 and FieldNumber would correspond to the field number of 'mu' within b's type.
- //
- // //+checklocks:b.mu
- // func (a *A) method(b *B, c *C) {
- // ...
- // }
- ParameterNumber int
-
- // FieldNumber is the field index of the mutex in the parameter's struct
- // type. Refer to example above for more details.
- FieldNumber int
-}
-
-// lockFunctionFacts apply on every method.
-type lockFunctionFacts struct {
- // GuardedBy tracks the names and number of parameter (including receiver)
- // lockFuncfields that guard calls to this function.
- // The key is the name specified in the checklocks annotation. e.g given
- // the following code.
- // ```
- // type A struct {
- // mu sync.Mutex
- // a int
- // }
- //
- // // +checklocks:a.mu
- // func xyz(a *A) {..}
- // ```
- //
- // '`+checklocks:a.mu' will result in an entry in this map as shown below.
- // GuardedBy: {"a.mu" => {ParameterNumber: 0, FieldNumber: 0}
- GuardedBy map[string]functionGuard
-}
-
-// AFact implements analysis.Fact.AFact.
-func (*lockFunctionFacts) AFact() {}
-
-type positionKey string
-
-// toPositionKey converts from a token.Position to a key we can use to track
-// failures as the position of the failure annotation is not the same as the
-// position of the actual failure (different column/offsets). Hence we ignore
-// these fields and only use the file/line numbers to track failures.
-func toPositionKey(position token.Position) positionKey {
- return positionKey(fmt.Sprintf("%s:%d", position.Filename, position.Line))
-}
-
-type failData struct {
- pos token.Pos
- count int
-}
-
-func (f failData) String() string {
- return fmt.Sprintf("pos: %d, count: %d", f.pos, f.count)
+ FactTypes: []analysis.Fact{(*atomicAlignment)(nil), (*lockFieldFacts)(nil), (*lockGuardFacts)(nil), (*lockFunctionFacts)(nil)},
}
+// passContext is a pass with additional expected failures.
type passContext struct {
- pass *analysis.Pass
-
- // exemptions tracks functions that should be exempted from lock checking due
- // to '// +checklocksignore' annotation.
- exemptions map[types.Object]struct{}
-
- failures map[positionKey]*failData
+ pass *analysis.Pass
+ failures map[positionKey]*failData
+ exemptions map[positionKey]struct{}
+ forced map[positionKey]struct{}
+ functions map[*ssa.Function]struct{}
}
-var (
- mutexRE = regexp.MustCompile("((.*/)|^)sync.(CrossGoroutineMutex|Mutex)")
- rwMutexRE = regexp.MustCompile("((.*/)|^)sync.(CrossGoroutineRWMutex|RWMutex)")
-)
-
-func (pc *passContext) extractFieldAnnotations(field *ast.Field, fieldType *types.Var) *lockFieldFacts {
- s := fieldType.Type().String()
- // We use HasSuffix below because fieldType can be fully qualified with the
- // package name eg for the gvisor sync package mutex fields have the type:
- // "<package path>/sync/sync.Mutex"
- switch {
- case mutexRE.Match([]byte(s)):
- return &lockFieldFacts{IsMutex: true}
- case rwMutexRE.Match([]byte(s)):
- return &lockFieldFacts{IsRWMutex: true}
- default:
- }
- if field.Doc == nil {
- return nil
- }
- fieldFacts := &lockFieldFacts{GuardedBy: make(map[string]int)}
- for _, l := range field.Doc.List {
- if strings.HasPrefix(l.Text, checkLocksAnnotation) {
- guardName := strings.TrimPrefix(l.Text, checkLocksAnnotation)
- if _, ok := fieldFacts.GuardedBy[guardName]; ok {
- pc.pass.Reportf(field.Pos(), "annotation %s specified more than once.", l.Text)
- continue
- }
- fieldFacts.GuardedBy[guardName] = -1
- }
- }
-
- return fieldFacts
-}
-
-func (pc *passContext) findField(v ssa.Value, fieldNumber int) types.Object {
- structType, ok := v.Type().Underlying().(*types.Struct)
- if !ok {
- structType = v.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct)
- }
- return structType.Field(fieldNumber)
-}
-
-// findAndExportStructFacts finds any struct fields that are annotated with the
-// "// +checklocks:" annotation and exports relevant facts about the fields to
-// be used in later analysis.
-func (pc *passContext) findAndExportStructFacts(ss *ast.StructType, structType *types.Struct) {
- type fieldRef struct {
- fieldObj *types.Var
- facts *lockFieldFacts
- }
- mutexes := make(map[string]*fieldRef)
- rwMutexes := make(map[string]*fieldRef)
- guardedFields := make(map[string]*fieldRef)
- for i, field := range ss.Fields.List {
- fieldObj := structType.Field(i)
- fieldFacts := pc.extractFieldAnnotations(field, fieldObj)
- if fieldFacts == nil {
- continue
- }
- fieldFacts.FieldNumber = i
-
- ref := &fieldRef{fieldObj, fieldFacts}
- if fieldFacts.IsMutex {
- mutexes[fieldObj.Name()] = ref
- }
- if fieldFacts.IsRWMutex {
- rwMutexes[fieldObj.Name()] = ref
- }
- if len(fieldFacts.GuardedBy) != 0 {
- guardedFields[fieldObj.Name()] = ref
- }
- }
-
- // Export facts about all mutexes.
- for _, f := range mutexes {
- pc.pass.ExportObjectFact(f.fieldObj, f.facts)
- }
- // Export facts about all rwMutexes.
- for _, f := range rwMutexes {
- pc.pass.ExportObjectFact(f.fieldObj, f.facts)
- }
-
- // Validate that guarded fields annotations refer to actual mutexes or
- // rwMutexes in the struct.
- for _, gf := range guardedFields {
- for g := range gf.facts.GuardedBy {
- if f, ok := mutexes[g]; ok {
- gf.facts.GuardedBy[g] = f.facts.FieldNumber
- } else if f, ok := rwMutexes[g]; ok {
- gf.facts.GuardedBy[g] = f.facts.FieldNumber
- } else {
- pc.maybeFail(gf.fieldObj.Pos(), false /* isExempted */, "invalid mutex guard, no such mutex %s in struct %s", g, structType.String())
- continue
- }
- // Export guarded field fact.
- pc.pass.ExportObjectFact(gf.fieldObj, gf.facts)
- }
- }
-}
-
-func (pc *passContext) findAndExportFuncFacts(d *ast.FuncDecl) {
- log.Debugf("finding and exporting function facts\n")
- // for each function definition, check for +checklocks:mu annotation, which
- // means that the function must be called with that lock held.
- fnObj := pc.pass.TypesInfo.ObjectOf(d.Name)
- funcFacts := lockFunctionFacts{GuardedBy: make(map[string]functionGuard)}
- var (
- ignore bool
- ignorePos token.Pos
- )
-
-outerLoop:
- for _, l := range d.Doc.List {
- if strings.HasPrefix(l.Text, checkLocksIgnore) {
- pc.exemptions[fnObj] = struct{}{}
- ignore = true
- ignorePos = l.Pos()
- continue
- }
- if strings.HasPrefix(l.Text, checkLocksAnnotation) {
- guardName := strings.TrimPrefix(l.Text, checkLocksAnnotation)
- if _, ok := funcFacts.GuardedBy[guardName]; ok {
- pc.pass.Reportf(l.Pos(), "annotation %s specified more than once.", l.Text)
- continue
- }
-
- found := false
- x := strings.Split(guardName, ".")
- if len(x) != 2 {
- pc.pass.Reportf(l.Pos(), "checklocks mutex annotation should be of the form 'a.b'")
+// forAllTypes applies the given function over all types.
+func (pc *passContext) forAllTypes(fn func(ts *ast.TypeSpec)) {
+ for _, f := range pc.pass.Files {
+ for _, decl := range f.Decls {
+ d, ok := decl.(*ast.GenDecl)
+ if !ok || d.Tok != token.TYPE {
continue
}
- paramName, fieldName := x[0], x[1]
- log.Debugf("paramName: %s, fieldName: %s", paramName, fieldName)
- var paramList []*ast.Field
- if d.Recv != nil {
- paramList = append(paramList, d.Recv.List...)
- }
- if d.Type.Params != nil {
- paramList = append(paramList, d.Type.Params.List...)
- }
- for paramNum, field := range paramList {
- log.Debugf("field names: %+v", field.Names)
- if len(field.Names) == 0 {
- log.Debugf("skipping because parameter is unnamed", paramName)
- continue
- }
- nameExists := false
- for _, name := range field.Names {
- if name.Name == paramName {
- nameExists = true
- }
- }
- if !nameExists {
- log.Debugf("skipping because parameter name(s) does not match : %s", paramName)
- continue
- }
- ptrType, ok := pc.pass.TypesInfo.TypeOf(field.Type).Underlying().(*types.Pointer)
- if !ok {
- // Since mutexes cannot be copied we only care about parameters that
- // are pointer types when checking for guards.
- pc.pass.Reportf(l.Pos(), "annotation %s incorrectly specified, parameter name does not refer to a pointer type", l.Text)
- continue outerLoop
- }
-
- structType, ok := ptrType.Elem().Underlying().(*types.Struct)
- if !ok {
- pc.pass.Reportf(l.Pos(), "annotation %s incorrectly specified, parameter name does not refer to a pointer to a struct", l.Text)
- continue outerLoop
- }
-
- for i := 0; i < structType.NumFields(); i++ {
- if structType.Field(i).Name() == fieldName {
- var fieldFacts lockFieldFacts
- pc.pass.ImportObjectFact(structType.Field(i), &fieldFacts)
- if !fieldFacts.IsMutex && !fieldFacts.IsRWMutex {
- pc.pass.Reportf(l.Pos(), "field %s of param %s is not a mutex or an rwmutex", paramName, structType.Field(i))
- continue outerLoop
- }
- funcFacts.GuardedBy[guardName] = functionGuard{ParameterNumber: paramNum, FieldNumber: i}
- found = true
- continue outerLoop
- }
- }
- if !found {
- pc.pass.Reportf(l.Pos(), "annotation refers to a non-existent field %s in %s", guardName, structType)
- continue outerLoop
- }
- }
- if !found {
- pc.pass.Reportf(l.Pos(), "annotation refers to a non-existent parameter %s", paramName)
- }
- }
- }
-
- if len(funcFacts.GuardedBy) == 0 {
- return
- }
- if ignore {
- pc.pass.Reportf(ignorePos, "//+checklocksignore cannot be specified with other annotations on the function")
- }
- funcObj, ok := pc.pass.TypesInfo.Defs[d.Name].(*types.Func)
- if !ok {
- panic(fmt.Sprintf("function type information missing for %+v", d))
- }
- log.Debugf("export fact for d: %+v, funcObj: %+v, funcFacts: %+v\n", d, funcObj, funcFacts)
- pc.pass.ExportObjectFact(funcObj, &funcFacts)
-}
-
-type mutexState struct {
- // lockedMutexes is used to track which mutexes in a given struct are
- // currently locked using the field number of the mutex as the key.
- lockedMutexes map[int]struct{}
-}
-
-// locksHeld tracks all currently held locks.
-type locksHeld struct {
- locks map[ssa.Value]mutexState
-}
-
-// Same returns true if the locks held by other and l are the same.
-func (l *locksHeld) Same(other *locksHeld) bool {
- return reflect.DeepEqual(l.locks, other.locks)
-}
-
-// Copy creates a copy of all the lock state held by l.
-func (l *locksHeld) Copy() *locksHeld {
- out := &locksHeld{locks: make(map[ssa.Value]mutexState)}
- for ssaVal, mState := range l.locks {
- newLM := make(map[int]struct{})
- for k, v := range mState.lockedMutexes {
- newLM[k] = v
- }
- out.locks[ssaVal] = mutexState{lockedMutexes: newLM}
- }
- return out
-}
-
-func isAlias(first, second ssa.Value) bool {
- if first == second {
- return true
- }
- switch x := first.(type) {
- case *ssa.Field:
- if y, ok := second.(*ssa.Field); ok {
- return x.Field == y.Field && isAlias(x.X, y.X)
- }
- case *ssa.FieldAddr:
- if y, ok := second.(*ssa.FieldAddr); ok {
- return x.Field == y.Field && isAlias(x.X, y.X)
- }
- case *ssa.Index:
- if y, ok := second.(*ssa.Index); ok {
- return isAlias(x.Index, y.Index) && isAlias(x.X, y.X)
- }
- case *ssa.IndexAddr:
- if y, ok := second.(*ssa.IndexAddr); ok {
- return isAlias(x.Index, y.Index) && isAlias(x.X, y.X)
- }
- case *ssa.UnOp:
- if y, ok := second.(*ssa.UnOp); ok {
- return isAlias(x.X, y.X)
- }
- }
- return false
-}
-
-// checkBasicBlocks traverses the control flow graph starting at a set of given
-// block and checks each instruction for allowed operations.
-//
-// funcFact are the exported facts for the enclosing function for these basic
-// blocks.
-func (pc *passContext) checkBasicBlocks(blocks []*ssa.BasicBlock, recoverBlock *ssa.BasicBlock, fn *ssa.Function, funcFact lockFunctionFacts) {
- if len(blocks) == 0 {
- return
- }
-
- // mutexes is used to track currently locked sync.Mutexes/sync.RWMutexes for a
- // given *struct identified by ssa.Value.
- seen := make(map[*ssa.BasicBlock]*locksHeld)
- var scan func(block *ssa.BasicBlock, parent *locksHeld)
- scan = func(block *ssa.BasicBlock, parent *locksHeld) {
- _, isExempted := pc.exemptions[block.Parent().Object()]
- if oldLocksHeld, ok := seen[block]; ok {
- if oldLocksHeld.Same(parent) {
- return
- }
- pc.maybeFail(block.Instrs[0].Pos(), isExempted, "failure entering a block %+v with different sets of lock held, oldLocks: %+v, parentLocks: %+v", block, oldLocksHeld, parent)
- return
- }
- seen[block] = parent
- var lh = parent.Copy()
- for _, inst := range block.Instrs {
- pc.checkInstruction(inst, isExempted, lh)
- }
- for _, b := range block.Succs {
- scan(b, lh)
- }
- }
-
- // Initialize lh with any preconditions that require locks to be held for the
- // method to be invoked.
- lh := &locksHeld{locks: make(map[ssa.Value]mutexState)}
- for _, fg := range funcFact.GuardedBy {
- // The first is the method object itself so we skip that when looking
- // for receiver/function parameters.
- log.Debugf("fn: %s, fn.Operands() == %+v", fn, fn.Operands(nil))
- r := fn.Params[fg.ParameterNumber]
- guardObj := findField(r, fg.FieldNumber)
- var fieldFacts lockFieldFacts
- pc.pass.ImportObjectFact(guardObj, &fieldFacts)
- if fieldFacts.IsMutex || fieldFacts.IsRWMutex {
- m, ok := lh.locks[r]
- if !ok {
- m = mutexState{lockedMutexes: make(map[int]struct{})}
- lh.locks[r] = m
+ for _, gs := range d.Specs {
+ fn(gs.(*ast.TypeSpec))
}
- m.lockedMutexes[fieldFacts.FieldNumber] = struct{}{}
- } else {
- panic(fmt.Sprintf("function: %+v has an invalid guard that is not a mutex: %+v", fn, guardObj))
- }
- }
-
- // Start scanning from the first basic block.
- scan(blocks[0], lh)
-
- // Validate that all blocks were touched.
- for _, b := range blocks {
- if _, ok := seen[b]; !ok && b != recoverBlock {
- panic(fmt.Sprintf("block %+v was not visited during checkBasicBlocks", b))
- }
- }
-}
-
-func (pc *passContext) checkInstruction(inst ssa.Instruction, isExempted bool, lh *locksHeld) {
- log.Debugf("checking instruction: %s, isExempted: %t", inst, isExempted)
- switch x := inst.(type) {
- case *ssa.Field:
- pc.checkFieldAccess(inst, x.X, x.Field, isExempted, lh)
- case *ssa.FieldAddr:
- pc.checkFieldAccess(inst, x.X, x.Field, isExempted, lh)
- case *ssa.Call:
- pc.checkFunctionCall(x, isExempted, lh)
- }
-}
-
-func findField(v ssa.Value, field int) types.Object {
- structType, ok := v.Type().Underlying().(*types.Struct)
- if !ok {
- ptrType, ok := v.Type().Underlying().(*types.Pointer)
- if !ok {
- return nil
- }
- structType = ptrType.Elem().Underlying().(*types.Struct)
- }
- return structType.Field(field)
-}
-
-func (pc *passContext) maybeFail(pos token.Pos, isExempted bool, fmtStr string, args ...interface{}) {
- posKey := toPositionKey(pc.pass.Fset.Position(pos))
- log.Debugf("maybeFail: pos: %d, positionKey: %s", pos, posKey)
- if fData, ok := pc.failures[posKey]; ok {
- fData.count--
- if fData.count == 0 {
- delete(pc.failures, posKey)
}
- return
- }
- if !isExempted {
- pc.pass.Reportf(pos, fmt.Sprintf(fmtStr, args...))
}
}
-func (pc *passContext) checkFieldAccess(inst ssa.Instruction, structObj ssa.Value, field int, isExempted bool, lh *locksHeld) {
- var fieldFacts lockFieldFacts
- fieldObj := findField(structObj, field)
- pc.pass.ImportObjectFact(fieldObj, &fieldFacts)
- log.Debugf("fieldObj: %s, fieldFacts: %+v", fieldObj, fieldFacts)
- for _, guardFieldNumber := range fieldFacts.GuardedBy {
- guardObj := findField(structObj, guardFieldNumber)
- var guardfieldFacts lockFieldFacts
- pc.pass.ImportObjectFact(guardObj, &guardfieldFacts)
- log.Debugf("guardObj: %s, guardFieldFacts: %+v", guardObj, guardfieldFacts)
- if guardfieldFacts.IsMutex || guardfieldFacts.IsRWMutex {
- log.Debugf("guard is a mutex")
- m, ok := lh.locks[structObj]
+// forAllFunctions applies the given function over all functions.
+func (pc *passContext) forAllFunctions(fn func(fn *ast.FuncDecl)) {
+ for _, f := range pc.pass.Files {
+ for _, decl := range f.Decls {
+ d, ok := decl.(*ast.FuncDecl)
if !ok {
- pc.maybeFail(inst.Pos(), isExempted, "invalid field access, %s must be locked when accessing %s", guardObj.Name(), fieldObj.Name())
- continue
- }
- if _, ok := m.lockedMutexes[guardfieldFacts.FieldNumber]; !ok {
- pc.maybeFail(inst.Pos(), isExempted, "invalid field access, %s must be locked when accessing %s", guardObj.Name(), fieldObj.Name())
- }
- } else {
- panic("incorrect guard that is not a mutex or an RWMutex")
- }
- }
-}
-
-func (pc *passContext) checkFunctionCall(call *ssa.Call, isExempted bool, lh *locksHeld) {
- // See: https://godoc.org/golang.org/x/tools/go/ssa#CallCommon
- //
- // 1. "call" mode: when Method is nil (!IsInvoke), a CallCommon represents an ordinary
- // function call of the value in Value, which may be a *Builtin, a *Function or any
- // other value of kind 'func'.
- //
- // Value may be one of:
- // (a) a *Function, indicating a statically dispatched call
- // to a package-level function, an anonymous function, or
- // a method of a named type.
- //
- // (b) a *MakeClosure, indicating an immediately applied
- // function literal with free variables.
- //
- // (c) a *Builtin, indicating a statically dispatched call
- // to a built-in function.
- //
- // (d) any other value, indicating a dynamically dispatched
- // function call.
- fn, ok := call.Common().Value.(*ssa.Function)
- if !ok {
- return
- }
- if fn.Object() == nil {
- return
- }
-
- // Check if the function should be called with any locks held.
- var funcFact lockFunctionFacts
- pc.pass.ImportObjectFact(fn.Object(), &funcFact)
- if len(funcFact.GuardedBy) > 0 {
- for _, fg := range funcFact.GuardedBy {
- // The first is the method object itself so we skip that when looking
- // for receiver/function parameters.
- r := (*call.Value().Operands(nil)[fg.ParameterNumber+1])
- guardObj := findField(r, fg.FieldNumber)
- if guardObj == nil {
continue
}
- var fieldFacts lockFieldFacts
- pc.pass.ImportObjectFact(guardObj, &fieldFacts)
- if fieldFacts.IsMutex || fieldFacts.IsRWMutex {
- heldMutexes, ok := lh.locks[r]
- if !ok {
- log.Debugf("fn: %s, funcFact: %+v", fn, funcFact)
- pc.maybeFail(call.Pos(), isExempted, "invalid function call %s must be held", guardObj.Name())
- continue
- }
- if _, ok := heldMutexes.lockedMutexes[fg.FieldNumber]; !ok {
- log.Debugf("fn: %s, funcFact: %+v", fn, funcFact)
- pc.maybeFail(call.Pos(), isExempted, "invalid function call %s must be held", guardObj.Name())
- }
- } else {
- panic(fmt.Sprintf("function: %+v has an invalid guard that is not a mutex: %+v", fn, guardObj))
- }
- }
- }
-
- // Check if it's a method dispatch for something in the sync package.
- // See: https://godoc.org/golang.org/x/tools/go/ssa#Function
- if fn.Package() != nil && fn.Package().Pkg.Name() == "sync" && fn.Signature.Recv() != nil {
- r, ok := call.Common().Args[0].(*ssa.FieldAddr)
- if !ok {
- return
- }
- guardObj := findField(r.X, r.Field)
- var fieldFacts lockFieldFacts
- pc.pass.ImportObjectFact(guardObj, &fieldFacts)
- if fieldFacts.IsMutex || fieldFacts.IsRWMutex {
- switch fn.Name() {
- case "Lock", "RLock":
- obj := r.X
- m := mutexState{lockedMutexes: make(map[int]struct{})}
- for k, v := range lh.locks {
- if isAlias(r.X, k) {
- obj = k
- m = v
- }
- }
- if _, ok := m.lockedMutexes[r.Field]; ok {
- // Double locking a mutex that is already locked.
- pc.maybeFail(call.Pos(), isExempted, "trying to a lock %s when already locked", guardObj.Name())
- return
- }
- m.lockedMutexes[r.Field] = struct{}{}
- lh.locks[obj] = m
- case "Unlock", "RUnlock":
- // Find the associated locker object.
- var (
- obj ssa.Value
- m mutexState
- )
- for k, v := range lh.locks {
- if isAlias(r.X, k) {
- obj = k
- m = v
- break
- }
- }
- if _, ok := m.lockedMutexes[r.Field]; !ok {
- pc.maybeFail(call.Pos(), isExempted, "trying to unlock a mutex %s that is already unlocked", guardObj.Name())
- return
- }
- delete(m.lockedMutexes, r.Field)
- if len(m.lockedMutexes) == 0 {
- delete(lh.locks, obj)
- }
- case "RLocker", "DowngradeLock", "TryLock", "TryRLock":
- // we explicitly ignore this for now.
- default:
- panic(fmt.Sprintf("unexpected mutex/rwmutex method invoked: %s", fn.Name()))
- }
+ fn(d)
}
}
}
+// run is the main entrypoint.
func run(pass *analysis.Pass) (interface{}, error) {
pc := &passContext{
pass: pass,
- exemptions: make(map[types.Object]struct{}),
failures: make(map[positionKey]*failData),
+ exemptions: make(map[positionKey]struct{}),
+ forced: make(map[positionKey]struct{}),
+ functions: make(map[*ssa.Function]struct{}),
}
// Find all line failure annotations.
- for _, f := range pass.Files {
- for _, cg := range f.Comments {
- for _, c := range cg.List {
- if strings.Contains(c.Text, checkLocksFail) {
- cnt := 1
- if strings.Contains(c.Text, checkLocksFail+":") {
- parts := strings.SplitAfter(c.Text, checkLocksFail+":")
- parsedCount, err := strconv.Atoi(parts[1])
- if err != nil {
- pc.pass.Reportf(c.Pos(), "invalid checklocks annotation : %s", err)
- continue
- }
- cnt = parsedCount
- }
- position := toPositionKey(pass.Fset.Position(c.Pos()))
- pc.failures[position] = &failData{pos: c.Pos(), count: cnt}
- }
- }
- }
- }
-
- // Find all struct declarations and export any relevant facts.
- for _, f := range pass.Files {
- for _, decl := range f.Decls {
- d, ok := decl.(*ast.GenDecl)
- // A GenDecl node (generic declaration node) represents an import,
- // constant, type or variable declaration. We only care about struct
- // declarations so skip any declaration that doesn't declare a new type.
- if !ok || d.Tok != token.TYPE {
- continue
- }
+ pc.extractLineFailures()
- for _, gs := range d.Specs {
- ts := gs.(*ast.TypeSpec)
- ss, ok := ts.Type.(*ast.StructType)
- if !ok {
- continue
- }
- structType := pass.TypesInfo.TypeOf(ts.Name).Underlying().(*types.Struct)
- pc.findAndExportStructFacts(ss, structType)
- }
+ // Find all struct declarations and export relevant facts.
+ pc.forAllTypes(func(ts *ast.TypeSpec) {
+ if ss, ok := ts.Type.(*ast.StructType); ok {
+ pc.exportLockFieldFacts(ts, ss)
}
- }
+ })
+ pc.forAllTypes(func(ts *ast.TypeSpec) {
+ if ss, ok := ts.Type.(*ast.StructType); ok {
+ pc.exportLockGuardFacts(ts, ss)
+ }
+ })
- // Find all method calls and export any relevant facts.
- for _, f := range pass.Files {
- for _, decl := range f.Decls {
- d, ok := decl.(*ast.FuncDecl)
- // Ignore any non function declarations and any functions that do not have
- // any comments.
- if !ok || d.Doc == nil {
- continue
- }
- pc.findAndExportFuncFacts(d)
+ // Check all alignments.
+ pc.forAllTypes(func(ts *ast.TypeSpec) {
+ typ, ok := pass.TypesInfo.TypeOf(ts.Name).(*types.Named)
+ if !ok {
+ return
}
- }
+ pc.checkTypeAlignment(pass.Pkg, typ)
+ })
- // log all known facts and all failures if debug logging is enabled.
- allFacts := pass.AllObjectFacts()
- for i := range allFacts {
- log.Debugf("fact.object: %+v, fact.Fact: %+v", allFacts[i].Object, allFacts[i].Fact)
- }
- log.Debugf("all expected failures: %+v", pc.failures)
+ // Find all function declarations and export relevant facts.
+ pc.forAllFunctions(func(fn *ast.FuncDecl) {
+ pc.exportFunctionFacts(fn)
+ })
// Scan all code looking for invalid accesses.
state := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
for _, fn := range state.SrcFuncs {
- var funcFact lockFunctionFacts
- // Anonymous(closures) functions do not have an object() but do show up in
- // the SSA.
- if obj := fn.Object(); obj != nil {
- pc.pass.ImportObjectFact(fn.Object(), &funcFact)
+ // Import function facts generated above.
+ //
+ // Note that anonymous(closures) functions do not have an
+ // object but do show up in the SSA. They can only be invoked
+ // by named functions in the package, and they are analyzing
+ // inline on every call. Thus we skip the analysis here. They
+ // will be hit on calls, or picked up in the pass below.
+ if obj := fn.Object(); obj == nil {
+ continue
}
+ var lff lockFunctionFacts
+ pc.pass.ImportObjectFact(fn.Object(), &lff)
- log.Debugf("checking function: %s", fn)
- var b bytes.Buffer
- ssa.WriteFunction(&b, fn)
- log.Debugf("function SSA: %s", b.String())
- if fn.Recover != nil {
- pc.checkBasicBlocks([]*ssa.BasicBlock{fn.Recover}, nil, fn, funcFact)
+ // Do we ignore this?
+ if lff.Ignore {
+ continue
}
- pc.checkBasicBlocks(fn.Blocks, fn.Recover, fn, funcFact)
- }
- // Scan for remaining failures we expect.
- for _, failure := range pc.failures {
- // We are missing expect failures, report as much as possible.
- pass.Reportf(failure.pos, "expected %d failures", failure.count)
+ // Check the basic blocks in the function.
+ pc.checkFunction(nil, fn, &lff, nil, false /* force */)
}
+ for _, fn := range state.SrcFuncs {
+ // Ensure all anonymous functions are hit. They are not
+ // permitted to have any lock preconditions.
+ if obj := fn.Object(); obj != nil {
+ continue
+ }
+ var nolff lockFunctionFacts
+ pc.checkFunction(nil, fn, &nolff, nil, false /* force */)
+ }
+
+ // Check for expected failures.
+ pc.checkFailures()
return nil, nil
}
diff --git a/tools/checklocks/facts.go b/tools/checklocks/facts.go
new file mode 100644
index 000000000..1a43dbbe6
--- /dev/null
+++ b/tools/checklocks/facts.go
@@ -0,0 +1,614 @@
+// Copyright 2020 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 checklocks
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "regexp"
+ "strings"
+
+ "golang.org/x/tools/go/ssa"
+)
+
+// atomicAlignment is saved per type.
+//
+// This represents the alignment required for the type, which may
+// be implied and imposed by other types within the aggregate type.
+type atomicAlignment int
+
+// AFact implements analysis.Fact.AFact.
+func (*atomicAlignment) AFact() {}
+
+// atomicDisposition is saved per field.
+//
+// This represents how the field must be accessed. It must either
+// be non-atomic (default), atomic or ignored.
+type atomicDisposition int
+
+const (
+ atomicDisallow atomicDisposition = iota
+ atomicIgnore
+ atomicRequired
+)
+
+// fieldList is a simple list of fields, used in two types below.
+//
+// Note that the integers in this list refer to one of two things:
+// - A positive integer refers to a field index in a struct.
+// - A negative integer refers to a field index in a struct, where
+// that field is a pointer and must be subsequently resolved.
+type fieldList []int
+
+// resolvedValue is an ssa.Value with additional fields.
+//
+// This can be resolved to a string as part of a lock state.
+type resolvedValue struct {
+ value ssa.Value
+ valid bool
+ fieldList []int
+}
+
+// findExtract finds a relevant extract. This must exist within the referrers
+// to the call object. If this doesn't then the object which is locked is never
+// consumed, and we should consider this a bug.
+func findExtract(v ssa.Value, index int) (ssa.Value, bool) {
+ if refs := v.Referrers(); refs != nil {
+ for _, inst := range *refs {
+ if x, ok := inst.(*ssa.Extract); ok && x.Tuple == v && x.Index == index {
+ return inst.(ssa.Value), true
+ }
+ }
+ }
+ return nil, false
+}
+
+// resolve resolves the given field list.
+func (fl fieldList) resolve(v ssa.Value) (rv resolvedValue) {
+ return resolvedValue{
+ value: v,
+ fieldList: fl,
+ valid: true,
+ }
+}
+
+// valueAsString returns a string representing this value.
+//
+// This must align with how the string is generated in valueAsString.
+func (rv resolvedValue) valueAsString(ls *lockState) string {
+ typ := rv.value.Type()
+ s := ls.valueAsString(rv.value)
+ for i, fieldNumber := range rv.fieldList {
+ switch {
+ case fieldNumber > 0:
+ field, ok := findField(typ, fieldNumber-1)
+ if !ok {
+ // This can't be resolved, return for debugging.
+ return fmt.Sprintf("{%s+%v}", s, rv.fieldList[i:])
+ }
+ s = fmt.Sprintf("&(%s.%s)", s, field.Name())
+ typ = field.Type()
+ case fieldNumber < 1:
+ field, ok := findField(typ, (-fieldNumber)-1)
+ if !ok {
+ // See above.
+ return fmt.Sprintf("{%s+%v}", s, rv.fieldList[i:])
+ }
+ s = fmt.Sprintf("*(&(%s.%s))", s, field.Name())
+ typ = field.Type()
+ }
+ }
+ return s
+}
+
+// lockFieldFacts apply on every struct field.
+type lockFieldFacts struct {
+ // IsMutex is true if the field is of type sync.Mutex.
+ IsMutex bool
+
+ // IsRWMutex is true if the field is of type sync.RWMutex.
+ IsRWMutex bool
+
+ // IsPointer indicates if the field is a pointer.
+ IsPointer bool
+
+ // FieldNumber is the number of this field in the struct.
+ FieldNumber int
+}
+
+// AFact implements analysis.Fact.AFact.
+func (*lockFieldFacts) AFact() {}
+
+// lockGuardFacts contains guard information.
+type lockGuardFacts struct {
+ // GuardedBy is the set of locks that are guarding this field. The key
+ // is the original annotation value, and the field list is the object
+ // traversal path.
+ GuardedBy map[string]fieldList
+
+ // AtomicDisposition is the disposition for this field. Note that this
+ // can affect the interpretation of the GuardedBy field above, see the
+ // relevant comment.
+ AtomicDisposition atomicDisposition
+}
+
+// AFact implements analysis.Fact.AFact.
+func (*lockGuardFacts) AFact() {}
+
+// functionGuard is used by lockFunctionFacts, below.
+type functionGuard struct {
+ // ParameterNumber is the index of the object that contains the
+ // guarding mutex. From this parameter, a walk is performed
+ // subsequently using the resolve method.
+ //
+ // Note that is ParameterNumber is beyond the size of parameters, then
+ // it may return to a return value. This applies only for the Acquires
+ // relation below.
+ ParameterNumber int
+
+ // NeedsExtract is used in the case of a return value, and indicates
+ // that the field must be extracted from a tuple.
+ NeedsExtract bool
+
+ // FieldList is the traversal path to the object.
+ FieldList fieldList
+}
+
+// resolveReturn resolves a return value.
+//
+// Precondition: rv is either an ssa.Value, or an *ssa.Return.
+func (fg *functionGuard) resolveReturn(rv interface{}, args int) resolvedValue {
+ if rv == nil {
+ // For defers and other objects, this may be nil. This is
+ // handled in state.go in the actual lock checking logic.
+ return resolvedValue{
+ value: nil,
+ valid: false,
+ }
+ }
+ index := fg.ParameterNumber - args
+ // If this is a *ssa.Return object, i.e. we are analyzing the function
+ // and not the call site, then we can just pull the result directly.
+ if r, ok := rv.(*ssa.Return); ok {
+ return fg.FieldList.resolve(r.Results[index])
+ }
+ if fg.NeedsExtract {
+ // Resolve on the extracted field, this is necessary if the
+ // type here is not an explicit return. Note that rv must be an
+ // ssa.Value, since it is not an *ssa.Return.
+ v, ok := findExtract(rv.(ssa.Value), index)
+ if !ok {
+ return resolvedValue{
+ value: v,
+ valid: false,
+ }
+ }
+ return fg.FieldList.resolve(v)
+ }
+ if index != 0 {
+ // This should not happen, NeedsExtract should always be set.
+ panic("NeedsExtract is false, but return value index is non-zero")
+ }
+ // Resolve on the single return.
+ return fg.FieldList.resolve(rv.(ssa.Value))
+}
+
+// resolveStatic returns an ssa.Value representing the given field.
+//
+// Precondition: per resolveReturn.
+func (fg *functionGuard) resolveStatic(fn *ssa.Function, rv interface{}) resolvedValue {
+ if fg.ParameterNumber >= len(fn.Params) {
+ return fg.resolveReturn(rv, len(fn.Params))
+ }
+ return fg.FieldList.resolve(fn.Params[fg.ParameterNumber])
+}
+
+// resolveCall returns an ssa.Value representing the given field.
+func (fg *functionGuard) resolveCall(args []ssa.Value, rv ssa.Value) resolvedValue {
+ if fg.ParameterNumber >= len(args) {
+ return fg.resolveReturn(rv, len(args))
+ }
+ return fg.FieldList.resolve(args[fg.ParameterNumber])
+}
+
+// lockFunctionFacts apply on every method.
+type lockFunctionFacts struct {
+ // HeldOnEntry tracks the names and number of parameter (including receiver)
+ // lockFuncfields that guard calls to this function.
+ //
+ // The key is the name specified in the checklocks annotation. e.g given
+ // the following code:
+ //
+ // ```
+ // type A struct {
+ // mu sync.Mutex
+ // a int
+ // }
+ //
+ // // +checklocks:a.mu
+ // func xyz(a *A) {..}
+ // ```
+ //
+ // '`+checklocks:a.mu' will result in an entry in this map as shown below.
+ // HeldOnEntry: {"a.mu" => {ParameterNumber: 0, FieldNumbers: {0}}
+ //
+ // Unlikely lockFieldFacts, there is no atomic interpretation.
+ HeldOnEntry map[string]functionGuard
+
+ // HeldOnExit tracks the locks that are expected to be held on exit.
+ HeldOnExit map[string]functionGuard
+
+ // Ignore means this function has local analysis ignores.
+ //
+ // This is not used outside the local package.
+ Ignore bool
+}
+
+// AFact implements analysis.Fact.AFact.
+func (*lockFunctionFacts) AFact() {}
+
+// checkGuard validates the guardName.
+func (lff *lockFunctionFacts) checkGuard(pc *passContext, d *ast.FuncDecl, guardName string, allowReturn bool) (functionGuard, bool) {
+ if _, ok := lff.HeldOnEntry[guardName]; ok {
+ pc.maybeFail(d.Pos(), "annotation %s specified more than once, already required", guardName)
+ return functionGuard{}, false
+ }
+ if _, ok := lff.HeldOnExit[guardName]; ok {
+ pc.maybeFail(d.Pos(), "annotation %s specified more than once, already acquired", guardName)
+ return functionGuard{}, false
+ }
+ fg, ok := pc.findFunctionGuard(d, guardName, allowReturn)
+ return fg, ok
+}
+
+// addGuardedBy adds a field to both HeldOnEntry and HeldOnExit.
+func (lff *lockFunctionFacts) addGuardedBy(pc *passContext, d *ast.FuncDecl, guardName string) {
+ if fg, ok := lff.checkGuard(pc, d, guardName, false /* allowReturn */); ok {
+ if lff.HeldOnEntry == nil {
+ lff.HeldOnEntry = make(map[string]functionGuard)
+ }
+ if lff.HeldOnExit == nil {
+ lff.HeldOnExit = make(map[string]functionGuard)
+ }
+ lff.HeldOnEntry[guardName] = fg
+ lff.HeldOnExit[guardName] = fg
+ }
+}
+
+// addAcquires adds a field to HeldOnExit.
+func (lff *lockFunctionFacts) addAcquires(pc *passContext, d *ast.FuncDecl, guardName string) {
+ if fg, ok := lff.checkGuard(pc, d, guardName, true /* allowReturn */); ok {
+ if lff.HeldOnExit == nil {
+ lff.HeldOnExit = make(map[string]functionGuard)
+ }
+ lff.HeldOnExit[guardName] = fg
+ }
+}
+
+// addReleases adds a field to HeldOnEntry.
+func (lff *lockFunctionFacts) addReleases(pc *passContext, d *ast.FuncDecl, guardName string) {
+ if fg, ok := lff.checkGuard(pc, d, guardName, false /* allowReturn */); ok {
+ if lff.HeldOnEntry == nil {
+ lff.HeldOnEntry = make(map[string]functionGuard)
+ }
+ lff.HeldOnEntry[guardName] = fg
+ }
+}
+
+// fieldListFor returns the fieldList for the given object.
+func (pc *passContext) fieldListFor(pos token.Pos, fieldObj types.Object, index int, fieldName string, checkMutex bool) (int, bool) {
+ var lff lockFieldFacts
+ if !pc.pass.ImportObjectFact(fieldObj, &lff) {
+ // This should not happen: we export facts for all fields.
+ panic(fmt.Sprintf("no lockFieldFacts available for field %s", fieldName))
+ }
+ // Check that it is indeed a mutex.
+ if checkMutex && !lff.IsMutex && !lff.IsRWMutex {
+ pc.maybeFail(pos, "field %s is not a mutex or an rwmutex", fieldName)
+ return 0, false
+ }
+ // Return the resolution path.
+ if lff.IsPointer {
+ return -(index + 1), true
+ }
+ return (index + 1), true
+}
+
+// resolveOneField resolves a field in a single struct.
+func (pc *passContext) resolveOneField(pos token.Pos, structType *types.Struct, fieldName string, checkMutex bool) (fl fieldList, fieldObj types.Object, ok bool) {
+ // Scan to match the next field.
+ for i := 0; i < structType.NumFields(); i++ {
+ fieldObj := structType.Field(i)
+ if fieldObj.Name() != fieldName {
+ continue
+ }
+ flOne, ok := pc.fieldListFor(pos, fieldObj, i, fieldName, checkMutex)
+ if !ok {
+ return nil, nil, false
+ }
+ fl = append(fl, flOne)
+ return fl, fieldObj, true
+ }
+ // Is this an embed?
+ for i := 0; i < structType.NumFields(); i++ {
+ fieldObj := structType.Field(i)
+ if !fieldObj.Embedded() {
+ continue
+ }
+ // Is this an embedded struct?
+ structType, ok := resolveStruct(fieldObj.Type())
+ if !ok {
+ continue
+ }
+ // Need to check that there is a resolution path. If there is
+ // no resolution path that's not a failure: we just continue
+ // scanning the next embed to find a match.
+ flEmbed, okEmbed := pc.fieldListFor(pos, fieldObj, i, fieldName, false)
+ flCont, fieldObjCont, okCont := pc.resolveOneField(pos, structType, fieldName, checkMutex)
+ if okEmbed && okCont {
+ fl = append(fl, flEmbed)
+ fl = append(fl, flCont...)
+ return fl, fieldObjCont, true
+ }
+ }
+ pc.maybeFail(pos, "field %s does not exist", fieldName)
+ return nil, nil, false
+}
+
+// resolveField resolves a set of fields given a string, such a 'a.b.c'.
+//
+// Note that this checks that the final element is a mutex of some kind, and
+// will fail appropriately.
+func (pc *passContext) resolveField(pos token.Pos, structType *types.Struct, parts []string) (fl fieldList, ok bool) {
+ for partNumber, fieldName := range parts {
+ flOne, fieldObj, ok := pc.resolveOneField(pos, structType, fieldName, partNumber >= len(parts)-1 /* checkMutex */)
+ if !ok {
+ // Error already reported.
+ return nil, false
+ }
+ fl = append(fl, flOne...)
+ if partNumber < len(parts)-1 {
+ // Traverse to the next type.
+ structType, ok = resolveStruct(fieldObj.Type())
+ if !ok {
+ pc.maybeFail(pos, "invalid intermediate field %s", fieldName)
+ return fl, false
+ }
+ }
+ }
+ return fl, true
+}
+
+var (
+ mutexRE = regexp.MustCompile("((.*/)|^)sync.(CrossGoroutineMutex|Mutex)")
+ rwMutexRE = regexp.MustCompile("((.*/)|^)sync.(CrossGoroutineRWMutex|RWMutex)")
+)
+
+// exportLockFieldFacts finds all struct fields that are mutexes, and ensures
+// that they are annotated approperly.
+//
+// This information is consumed subsequently by exportLockGuardFacts, and this
+// function must be called first on all structures.
+func (pc *passContext) exportLockFieldFacts(ts *ast.TypeSpec, ss *ast.StructType) {
+ structType := pc.pass.TypesInfo.TypeOf(ts.Name).Underlying().(*types.Struct)
+ for i := range ss.Fields.List {
+ lff := &lockFieldFacts{
+ FieldNumber: i,
+ }
+ // We use HasSuffix below because fieldType can be fully
+ // qualified with the package name eg for the gvisor sync
+ // package mutex fields have the type:
+ // "<package path>/sync/sync.Mutex"
+ fieldObj := structType.Field(i)
+ s := fieldObj.Type().String()
+ switch {
+ case mutexRE.MatchString(s):
+ lff.IsMutex = true
+ case rwMutexRE.MatchString(s):
+ lff.IsRWMutex = true
+ }
+ // Save whether this is a pointer.
+ _, lff.IsPointer = fieldObj.Type().Underlying().(*types.Pointer)
+ // We must always export the lockFieldFacts, since traversal
+ // can take place along any object in the struct.
+ pc.pass.ExportObjectFact(fieldObj, lff)
+ }
+}
+
+// exportLockGuardFacts finds all relevant guard information for structures.
+//
+// This function requires exportLockFieldFacts be called first on all
+// structures.
+func (pc *passContext) exportLockGuardFacts(ts *ast.TypeSpec, ss *ast.StructType) {
+ structType := pc.pass.TypesInfo.TypeOf(ts.Name).Underlying().(*types.Struct)
+ for i, field := range ss.Fields.List {
+ if field.Doc == nil {
+ continue
+ }
+ var (
+ lff lockFieldFacts
+ lgf lockGuardFacts
+ )
+ pc.pass.ImportObjectFact(structType.Field(i), &lff)
+ fieldObj := structType.Field(i)
+ for _, l := range field.Doc.List {
+ pc.extractAnnotations(l.Text, map[string]func(string){
+ checkAtomicAnnotation: func(string) {
+ switch lgf.AtomicDisposition {
+ case atomicRequired:
+ pc.maybeFail(fieldObj.Pos(), "annotation is redundant, already atomic required")
+ case atomicIgnore:
+ pc.maybeFail(fieldObj.Pos(), "annotation is contradictory, already atomic ignored")
+ }
+ lgf.AtomicDisposition = atomicRequired
+ },
+ checkLocksIgnore: func(string) {
+ switch lgf.AtomicDisposition {
+ case atomicIgnore:
+ pc.maybeFail(fieldObj.Pos(), "annotation is redundant, already atomic ignored")
+ case atomicRequired:
+ pc.maybeFail(fieldObj.Pos(), "annotation is contradictory, already atomic required")
+ }
+ lgf.AtomicDisposition = atomicIgnore
+ },
+ checkLocksAnnotation: func(guardName string) {
+ // Check for a duplicate annotation.
+ if _, ok := lgf.GuardedBy[guardName]; ok {
+ pc.maybeFail(fieldObj.Pos(), "annotation %s specified more than once", guardName)
+ return
+ }
+ fl, ok := pc.resolveField(fieldObj.Pos(), structType, strings.Split(guardName, "."))
+ if ok {
+ // If we successfully resolved
+ // the field, then save it.
+ if lgf.GuardedBy == nil {
+ lgf.GuardedBy = make(map[string]fieldList)
+ }
+ lgf.GuardedBy[guardName] = fl
+ }
+ },
+ })
+ }
+ // Save only if there is something meaningful.
+ if len(lgf.GuardedBy) > 0 || lgf.AtomicDisposition != atomicDisallow {
+ pc.pass.ExportObjectFact(structType.Field(i), &lgf)
+ }
+ }
+}
+
+// countFields gives an accurate field count, according for unnamed arguments
+// and return values and the compact identifier format.
+func countFields(fl []*ast.Field) (count int) {
+ for _, field := range fl {
+ if len(field.Names) == 0 {
+ count++
+ continue
+ }
+ count += len(field.Names)
+ }
+ return
+}
+
+// matchFieldList attempts to match the given field.
+func (pc *passContext) matchFieldList(pos token.Pos, fl []*ast.Field, guardName string) (functionGuard, bool) {
+ parts := strings.Split(guardName, ".")
+ parameterName := parts[0]
+ parameterNumber := 0
+ for _, field := range fl {
+ // See countFields, above.
+ if len(field.Names) == 0 {
+ parameterNumber++
+ continue
+ }
+ for _, name := range field.Names {
+ if name.Name != parameterName {
+ parameterNumber++
+ continue
+ }
+ ptrType, ok := pc.pass.TypesInfo.TypeOf(field.Type).Underlying().(*types.Pointer)
+ if !ok {
+ // Since mutexes cannot be copied we only care
+ // about parameters that are pointer types when
+ // checking for guards.
+ pc.maybeFail(pos, "parameter name %s does not refer to a pointer type", parameterName)
+ return functionGuard{}, false
+ }
+ structType, ok := ptrType.Elem().Underlying().(*types.Struct)
+ if !ok {
+ // Fields can only be in named structures.
+ pc.maybeFail(pos, "parameter name %s does not refer to a pointer to a struct", parameterName)
+ return functionGuard{}, false
+ }
+ fg := functionGuard{
+ ParameterNumber: parameterNumber,
+ }
+ fl, ok := pc.resolveField(pos, structType, parts[1:])
+ fg.FieldList = fl
+ return fg, ok // If ok is false, already failed.
+ }
+ }
+ return functionGuard{}, false
+}
+
+// findFunctionGuard identifies the parameter number and field number for a
+// particular string of the 'a.b'.
+//
+// This function will report any errors directly.
+func (pc *passContext) findFunctionGuard(d *ast.FuncDecl, guardName string, allowReturn bool) (functionGuard, bool) {
+ var (
+ parameterList []*ast.Field
+ returnList []*ast.Field
+ )
+ if d.Recv != nil {
+ parameterList = append(parameterList, d.Recv.List...)
+ }
+ if d.Type.Params != nil {
+ parameterList = append(parameterList, d.Type.Params.List...)
+ }
+ if fg, ok := pc.matchFieldList(d.Pos(), parameterList, guardName); ok {
+ return fg, ok
+ }
+ if allowReturn {
+ if d.Type.Results != nil {
+ returnList = append(returnList, d.Type.Results.List...)
+ }
+ if fg, ok := pc.matchFieldList(d.Pos(), returnList, guardName); ok {
+ // Fix this up to apply to the return value, as noted
+ // in fg.ParameterNumber. For the ssa analysis, we must
+ // record whether this has multiple results, since
+ // *ssa.Call indicates: "The Call instruction yields
+ // the function result if there is exactly one.
+ // Otherwise it returns a tuple, the components of
+ // which are accessed via Extract."
+ fg.ParameterNumber += countFields(parameterList)
+ fg.NeedsExtract = countFields(returnList) > 1
+ return fg, ok
+ }
+ }
+ // We never saw a matching parameter.
+ pc.maybeFail(d.Pos(), "annotation %s does not have a matching parameter", guardName)
+ return functionGuard{}, false
+}
+
+// exportFunctionFacts exports relevant function findings.
+func (pc *passContext) exportFunctionFacts(d *ast.FuncDecl) {
+ if d.Doc == nil || d.Doc.List == nil {
+ return
+ }
+ var lff lockFunctionFacts
+ for _, l := range d.Doc.List {
+ pc.extractAnnotations(l.Text, map[string]func(string){
+ checkLocksIgnore: func(string) {
+ // Note that this applies to all atomic
+ // analysis as well. There is no provided way
+ // to selectively ignore only lock analysis or
+ // atomic analysis, as we expect this use to be
+ // extremely rare.
+ lff.Ignore = true
+ },
+ checkLocksAnnotation: func(guardName string) { lff.addGuardedBy(pc, d, guardName) },
+ checkLocksAcquires: func(guardName string) { lff.addAcquires(pc, d, guardName) },
+ checkLocksReleases: func(guardName string) { lff.addReleases(pc, d, guardName) },
+ })
+ }
+
+ // Export the function facts if there is anything to save.
+ if lff.Ignore || len(lff.HeldOnEntry) > 0 || len(lff.HeldOnExit) > 0 {
+ funcObj := pc.pass.TypesInfo.Defs[d.Name].(*types.Func)
+ pc.pass.ExportObjectFact(funcObj, &lff)
+ }
+}
diff --git a/tools/checklocks/state.go b/tools/checklocks/state.go
new file mode 100644
index 000000000..57061a32e
--- /dev/null
+++ b/tools/checklocks/state.go
@@ -0,0 +1,315 @@
+// Copyright 2020 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 checklocks
+
+import (
+ "fmt"
+ "go/token"
+ "go/types"
+ "strings"
+ "sync/atomic"
+
+ "golang.org/x/tools/go/ssa"
+)
+
+// lockState tracks the locking state and aliases.
+type lockState struct {
+ // lockedMutexes is used to track which mutexes in a given struct are
+ // currently locked. Note that most of the heavy lifting is done by
+ // valueAsString below, which maps to specific structure fields, etc.
+ lockedMutexes []string
+
+ // stored stores values that have been stored in memory, bound to
+ // FreeVars or passed as Parameterse.
+ stored map[ssa.Value]ssa.Value
+
+ // used is a temporary map, used only for valueAsString. It prevents
+ // multiple use of the same memory location.
+ used map[ssa.Value]struct{}
+
+ // defers are the stack of defers that have been pushed.
+ defers []*ssa.Defer
+
+ // refs indicates the number of references on this structure. If it's
+ // greater than one, we will do copy-on-write.
+ refs *int32
+}
+
+// newLockState makes a new lockState.
+func newLockState() *lockState {
+ refs := int32(1) // Not shared.
+ return &lockState{
+ lockedMutexes: make([]string, 0),
+ used: make(map[ssa.Value]struct{}),
+ stored: make(map[ssa.Value]ssa.Value),
+ defers: make([]*ssa.Defer, 0),
+ refs: &refs,
+ }
+}
+
+// fork forks the locking state. When a lockState is forked, any modifications
+// will cause maps to be copied.
+func (l *lockState) fork() *lockState {
+ if l == nil {
+ return newLockState()
+ }
+ atomic.AddInt32(l.refs, 1)
+ return &lockState{
+ lockedMutexes: l.lockedMutexes,
+ used: make(map[ssa.Value]struct{}),
+ stored: l.stored,
+ defers: l.defers,
+ refs: l.refs,
+ }
+}
+
+// modify indicates that this state will be modified.
+func (l *lockState) modify() {
+ if atomic.LoadInt32(l.refs) > 1 {
+ // Copy the lockedMutexes.
+ lm := make([]string, len(l.lockedMutexes))
+ copy(lm, l.lockedMutexes)
+ l.lockedMutexes = lm
+
+ // Copy the stored values.
+ s := make(map[ssa.Value]ssa.Value)
+ for k, v := range l.stored {
+ s[k] = v
+ }
+ l.stored = s
+
+ // Reset the used values.
+ l.used = make(map[ssa.Value]struct{})
+
+ // Copy the defers.
+ ds := make([]*ssa.Defer, len(l.defers))
+ copy(ds, l.defers)
+ l.defers = ds
+
+ // Drop our reference.
+ atomic.AddInt32(l.refs, -1)
+ newRefs := int32(1) // Not shared.
+ l.refs = &newRefs
+ }
+}
+
+// isHeld indicates whether the field is held is not.
+func (l *lockState) isHeld(rv resolvedValue) (string, bool) {
+ if !rv.valid {
+ return rv.valueAsString(l), false
+ }
+ s := rv.valueAsString(l)
+ for _, k := range l.lockedMutexes {
+ if k == s {
+ return s, true
+ }
+ }
+ return s, false
+}
+
+// lockField locks the given field.
+//
+// If false is returned, the field was already locked.
+func (l *lockState) lockField(rv resolvedValue) (string, bool) {
+ if !rv.valid {
+ return rv.valueAsString(l), false
+ }
+ s := rv.valueAsString(l)
+ for _, k := range l.lockedMutexes {
+ if k == s {
+ return s, false
+ }
+ }
+ l.modify()
+ l.lockedMutexes = append(l.lockedMutexes, s)
+ return s, true
+}
+
+// unlockField unlocks the given field.
+//
+// If false is returned, the field was not locked.
+func (l *lockState) unlockField(rv resolvedValue) (string, bool) {
+ if !rv.valid {
+ return rv.valueAsString(l), false
+ }
+ s := rv.valueAsString(l)
+ for i, k := range l.lockedMutexes {
+ if k == s {
+ // Copy the last lock in and truncate.
+ l.modify()
+ l.lockedMutexes[i] = l.lockedMutexes[len(l.lockedMutexes)-1]
+ l.lockedMutexes = l.lockedMutexes[:len(l.lockedMutexes)-1]
+ return s, true
+ }
+ }
+ return s, false
+}
+
+// store records an alias.
+func (l *lockState) store(addr ssa.Value, v ssa.Value) {
+ l.modify()
+ l.stored[addr] = v
+}
+
+// isSubset indicates other holds all the locks held by l.
+func (l *lockState) isSubset(other *lockState) bool {
+ held := 0 // Number in l, held by other.
+ for _, k := range l.lockedMutexes {
+ for _, ok := range other.lockedMutexes {
+ if k == ok {
+ held++
+ break
+ }
+ }
+ }
+ return held >= len(l.lockedMutexes)
+}
+
+// count indicates the number of locks held.
+func (l *lockState) count() int {
+ return len(l.lockedMutexes)
+}
+
+// isCompatible returns true if the states are compatible.
+func (l *lockState) isCompatible(other *lockState) bool {
+ return l.isSubset(other) && other.isSubset(l)
+}
+
+// elemType is a type that implements the Elem function.
+type elemType interface {
+ Elem() types.Type
+}
+
+// valueAsString returns a string for a given value.
+//
+// This decomposes the value into the simplest possible representation in terms
+// of parameters, free variables and globals. During resolution, stored values
+// may be transferred, as well as bound free variables.
+//
+// Nil may not be passed here.
+func (l *lockState) valueAsString(v ssa.Value) string {
+ switch x := v.(type) {
+ case *ssa.Parameter:
+ // Was this provided as a paramter for a local anonymous
+ // function invocation?
+ v, ok := l.stored[x]
+ if ok {
+ return l.valueAsString(v)
+ }
+ return fmt.Sprintf("{param:%s}", x.Name())
+ case *ssa.Global:
+ return fmt.Sprintf("{global:%s}", x.Name())
+ case *ssa.FreeVar:
+ // Attempt to resolve this, in case we are being invoked in a
+ // scope where all the variables are bound.
+ v, ok := l.stored[x]
+ if ok {
+ // The FreeVar is typically bound to a location, so we
+ // check what's been stored there. Note that the second
+ // may map to the same FreeVar, which we can check.
+ stored, ok := l.stored[v]
+ if ok {
+ return l.valueAsString(stored)
+ }
+ }
+ return fmt.Sprintf("{freevar:%s}", x.Name())
+ case *ssa.Convert:
+ // Just disregard conversion.
+ return l.valueAsString(x.X)
+ case *ssa.ChangeType:
+ // Ditto, disregard.
+ return l.valueAsString(x.X)
+ case *ssa.UnOp:
+ if x.Op != token.MUL {
+ break
+ }
+ // Is this loading a free variable? If yes, then this can be
+ // resolved in the original isAlias function.
+ if fv, ok := x.X.(*ssa.FreeVar); ok {
+ return l.valueAsString(fv)
+ }
+ // Should be try to resolve via a memory address? This needs to
+ // be done since a memory location can hold its own value.
+ if _, ok := l.used[x.X]; !ok {
+ // Check if we know what the accessed location holds.
+ // This is used to disambiguate memory locations.
+ v, ok := l.stored[x.X]
+ if ok {
+ l.used[x.X] = struct{}{}
+ defer func() { delete(l.used, x.X) }()
+ return l.valueAsString(v)
+ }
+ }
+ // x.X.Type is pointer. We must construct this type
+ // dynamically, since the ssa.Value could be synthetic.
+ return fmt.Sprintf("*(%s)", l.valueAsString(x.X))
+ case *ssa.Field:
+ structType, ok := resolveStruct(x.X.Type())
+ if !ok {
+ // This should not happen.
+ panic(fmt.Sprintf("structType not available for struct: %#v", x.X))
+ }
+ fieldObj := structType.Field(x.Field)
+ return fmt.Sprintf("%s.%s", l.valueAsString(x.X), fieldObj.Name())
+ case *ssa.FieldAddr:
+ structType, ok := resolveStruct(x.X.Type())
+ if !ok {
+ // This should not happen.
+ panic(fmt.Sprintf("structType not available for struct: %#v", x.X))
+ }
+ fieldObj := structType.Field(x.Field)
+ return fmt.Sprintf("&(%s.%s)", l.valueAsString(x.X), fieldObj.Name())
+ case *ssa.Index:
+ return fmt.Sprintf("%s[%s]", l.valueAsString(x.X), l.valueAsString(x.Index))
+ case *ssa.IndexAddr:
+ return fmt.Sprintf("&(%s[%s])", l.valueAsString(x.X), l.valueAsString(x.Index))
+ case *ssa.Lookup:
+ return fmt.Sprintf("%s[%s]", l.valueAsString(x.X), l.valueAsString(x.Index))
+ case *ssa.Extract:
+ return fmt.Sprintf("%s[%d]", l.valueAsString(x.Tuple), x.Index)
+ }
+
+ // In the case of any other type (e.g. this may be an alloc, a return
+ // value, etc.), just return the literal pointer value to the Value.
+ // This will be unique within the ssa graph, and so if two values are
+ // equal, they are from the same type.
+ return fmt.Sprintf("{%T:%p}", v, v)
+}
+
+// String returns the full lock state.
+func (l *lockState) String() string {
+ if l.count() == 0 {
+ return "no locks held"
+ }
+ return strings.Join(l.lockedMutexes, ",")
+}
+
+// pushDefer pushes a defer onto the stack.
+func (l *lockState) pushDefer(d *ssa.Defer) {
+ l.modify()
+ l.defers = append(l.defers, d)
+}
+
+// popDefer pops a defer from the stack.
+func (l *lockState) popDefer() *ssa.Defer {
+ // Does not technically modify the underlying slice.
+ count := len(l.defers)
+ if count == 0 {
+ return nil
+ }
+ d := l.defers[count-1]
+ l.defers = l.defers[:count-1]
+ return d
+}
diff --git a/tools/checklocks/test/BUILD b/tools/checklocks/test/BUILD
index b055e71d9..966bbac22 100644
--- a/tools/checklocks/test/BUILD
+++ b/tools/checklocks/test/BUILD
@@ -4,5 +4,17 @@ package(licenses = ["notice"])
go_library(
name = "test",
- srcs = ["test.go"],
+ srcs = [
+ "alignment.go",
+ "atomics.go",
+ "basics.go",
+ "branches.go",
+ "closures.go",
+ "defer.go",
+ "incompat.go",
+ "methods.go",
+ "parameters.go",
+ "return.go",
+ "test.go",
+ ],
)
diff --git a/tools/checklocks/test/alignment.go b/tools/checklocks/test/alignment.go
new file mode 100644
index 000000000..cd857ff73
--- /dev/null
+++ b/tools/checklocks/test/alignment.go
@@ -0,0 +1,51 @@
+// Copyright 2020 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 test
+
+type alignedStruct32 struct {
+ v int32
+}
+
+type alignedStruct64 struct {
+ v int64
+}
+
+type alignedStructGood struct {
+ v0 alignedStruct32
+ v1 alignedStruct32
+ v2 alignedStruct64
+}
+
+type alignedStructGoodArray0 struct {
+ v0 [3]alignedStruct32
+ v1 [3]alignedStruct32
+ v2 alignedStruct64
+}
+
+type alignedStructGoodArray1 [16]alignedStructGood
+
+type alignedStructBad struct {
+ v0 alignedStruct32
+ v1 alignedStruct64
+ v2 alignedStruct32
+}
+
+type alignedStructBadArray0 struct {
+ v0 [3]alignedStruct32
+ v1 [2]alignedStruct64
+ v2 [1]alignedStruct32
+}
+
+type alignedStructBadArray1 [16]alignedStructBad
diff --git a/tools/checklocks/test/atomics.go b/tools/checklocks/test/atomics.go
new file mode 100644
index 000000000..8e060d8a2
--- /dev/null
+++ b/tools/checklocks/test/atomics.go
@@ -0,0 +1,91 @@
+// Copyright 2020 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 test
+
+import (
+ "sync"
+ "sync/atomic"
+)
+
+type atomicStruct struct {
+ accessedNormally int32
+
+ // +checkatomic
+ accessedAtomically int32
+
+ // +checklocksignore
+ ignored int32
+}
+
+func testNormalAccess(tc *atomicStruct, v chan int32, p chan *int32) {
+ v <- tc.accessedNormally
+ p <- &tc.accessedNormally
+}
+
+func testAtomicAccess(tc *atomicStruct, v chan int32) {
+ v <- atomic.LoadInt32(&tc.accessedAtomically)
+}
+
+func testAtomicAccessInvalid(tc *atomicStruct, v chan int32) {
+ v <- atomic.LoadInt32(&tc.accessedNormally) // +checklocksfail
+}
+
+func testNormalAccessInvalid(tc *atomicStruct, v chan int32, p chan *int32) {
+ v <- tc.accessedAtomically // +checklocksfail
+ p <- &tc.accessedAtomically // +checklocksfail
+}
+
+func testIgnored(tc *atomicStruct, v chan int32, p chan *int32) {
+ v <- atomic.LoadInt32(&tc.ignored)
+ v <- tc.ignored
+ p <- &tc.ignored
+}
+
+type atomicMixedStruct struct {
+ mu sync.Mutex
+
+ // +checkatomic
+ // +checklocks:mu
+ accessedMixed int32
+}
+
+func testAtomicMixedValidRead(tc *atomicMixedStruct, v chan int32) {
+ v <- atomic.LoadInt32(&tc.accessedMixed)
+}
+
+func testAtomicMixedInvalidRead(tc *atomicMixedStruct, v chan int32, p chan *int32) {
+ v <- tc.accessedMixed // +checklocksfail
+ p <- &tc.accessedMixed // +checklocksfail
+}
+
+func testAtomicMixedValidLockedWrite(tc *atomicMixedStruct, v chan int32, p chan *int32) {
+ tc.mu.Lock()
+ atomic.StoreInt32(&tc.accessedMixed, 1)
+ tc.mu.Unlock()
+}
+
+func testAtomicMixedInvalidLockedWrite(tc *atomicMixedStruct, v chan int32, p chan *int32) {
+ tc.mu.Lock()
+ tc.accessedMixed = 1 // +checklocksfail:2
+ tc.mu.Unlock()
+}
+
+func testAtomicMixedInvalidAtomicWrite(tc *atomicMixedStruct, v chan int32, p chan *int32) {
+ atomic.StoreInt32(&tc.accessedMixed, 1) // +checklocksfail
+}
+
+func testAtomicMixedInvalidWrite(tc *atomicMixedStruct, v chan int32, p chan *int32) {
+ tc.accessedMixed = 1 // +checklocksfail:2
+}
diff --git a/tools/checklocks/test/basics.go b/tools/checklocks/test/basics.go
new file mode 100644
index 000000000..7a773171f
--- /dev/null
+++ b/tools/checklocks/test/basics.go
@@ -0,0 +1,145 @@
+// Copyright 2020 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 test
+
+import (
+ "sync"
+)
+
+func testLockedAccessValid(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ tc.guardedField = 1
+ tc.mu.Unlock()
+}
+
+func testLockedAccessIgnore(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ tc.unguardedField = 1
+ tc.mu.Unlock()
+}
+
+func testUnlockedAccessInvalidWrite(tc *oneGuardStruct) {
+ tc.guardedField = 2 // +checklocksfail
+}
+
+func testUnlockedAccessInvalidRead(tc *oneGuardStruct) {
+ x := tc.guardedField // +checklocksfail
+ _ = x
+}
+
+func testUnlockedAccessValid(tc *oneGuardStruct) {
+ tc.unguardedField = 2
+}
+
+func testCallValidAccess(tc *oneGuardStruct) {
+ callValidAccess(tc)
+}
+
+func callValidAccess(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ tc.guardedField = 1
+ tc.mu.Unlock()
+}
+
+func testCallValueMixup(tc *oneGuardStruct) {
+ callValueMixup(tc, tc)
+}
+
+func callValueMixup(tc1, tc2 *oneGuardStruct) {
+ tc1.mu.Lock()
+ tc2.guardedField = 2 // +checklocksfail
+ tc1.mu.Unlock()
+}
+
+func testCallPreconditionsInvalid(tc *oneGuardStruct) {
+ callPreconditions(tc) // +checklocksfail
+}
+
+func testCallPreconditionsValid(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ callPreconditions(tc)
+ tc.mu.Unlock()
+}
+
+// +checklocks:tc.mu
+func callPreconditions(tc *oneGuardStruct) {
+ tc.guardedField = 1
+}
+
+type nestedFieldsStruct struct {
+ mu sync.Mutex
+
+ // +checklocks:mu
+ nestedStruct struct {
+ nested1 int
+ nested2 int
+ }
+}
+
+func testNestedGuardValid(tc *nestedFieldsStruct) {
+ tc.mu.Lock()
+ tc.nestedStruct.nested1 = 1
+ tc.nestedStruct.nested2 = 2
+ tc.mu.Unlock()
+}
+
+func testNestedGuardInvalid(tc *nestedFieldsStruct) {
+ tc.nestedStruct.nested1 = 1 // +checklocksfail
+}
+
+type rwGuardStruct struct {
+ rwMu sync.RWMutex
+
+ // +checklocks:rwMu
+ guardedField int
+}
+
+func testRWValidRead(tc *rwGuardStruct) {
+ tc.rwMu.Lock()
+ tc.guardedField = 1
+ tc.rwMu.Unlock()
+}
+
+func testRWValidWrite(tc *rwGuardStruct) {
+ tc.rwMu.RLock()
+ tc.guardedField = 2
+ tc.rwMu.RUnlock()
+}
+
+func testRWInvalidWrite(tc *rwGuardStruct) {
+ tc.guardedField = 3 // +checklocksfail
+}
+
+func testRWInvalidRead(tc *rwGuardStruct) {
+ x := tc.guardedField + 3 // +checklocksfail
+ _ = x
+}
+
+func testTwoLocksDoubleGuardStructValid(tc *twoLocksDoubleGuardStruct) {
+ tc.mu.Lock()
+ tc.secondMu.Lock()
+ tc.doubleGuardedField = 1
+ tc.secondMu.Unlock()
+}
+
+func testTwoLocksDoubleGuardStructOnlyOne(tc *twoLocksDoubleGuardStruct) {
+ tc.mu.Lock()
+ tc.doubleGuardedField = 2 // +checklocksfail
+ tc.mu.Unlock()
+}
+
+func testTwoLocksDoubleGuardStructInvalid(tc *twoLocksDoubleGuardStruct) {
+ tc.doubleGuardedField = 3 // +checklocksfail:2
+}
diff --git a/tools/checklocks/test/branches.go b/tools/checklocks/test/branches.go
new file mode 100644
index 000000000..81fec29e5
--- /dev/null
+++ b/tools/checklocks/test/branches.go
@@ -0,0 +1,56 @@
+// Copyright 2020 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 test
+
+import (
+ "math/rand"
+)
+
+func testInconsistentReturn(tc *oneGuardStruct) { // +checklocksfail
+ if x := rand.Intn(10); x%2 == 1 {
+ tc.mu.Lock()
+ }
+}
+
+func testConsistentBranching(tc *oneGuardStruct) {
+ x := rand.Intn(10)
+ if x%2 == 1 {
+ tc.mu.Lock()
+ } else {
+ tc.mu.Lock()
+ }
+ tc.guardedField = 1
+ if x%2 == 1 {
+ tc.mu.Unlock()
+ } else {
+ tc.mu.Unlock()
+ }
+}
+
+func testInconsistentBranching(tc *oneGuardStruct) { // +checklocksfail:2
+ // We traverse the control flow graph in all consistent ways. We cannot
+ // determine however, that the first if block and second if block will
+ // evaluate to the same condition. Therefore, there are two consistent
+ // paths through this code, and two inconsistent paths. Either way, the
+ // guardedField should be also marked as an invalid access.
+ x := rand.Intn(10)
+ if x%2 == 1 {
+ tc.mu.Lock()
+ }
+ tc.guardedField = 1 // +checklocksfail
+ if x%2 == 1 {
+ tc.mu.Unlock() // +checklocksforce
+ }
+}
diff --git a/tools/checklocks/test/closures.go b/tools/checklocks/test/closures.go
new file mode 100644
index 000000000..7da87540a
--- /dev/null
+++ b/tools/checklocks/test/closures.go
@@ -0,0 +1,100 @@
+// Copyright 2020 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 test
+
+func testClosureInvalid(tc *oneGuardStruct) {
+ // This is expected to fail.
+ callClosure(func() {
+ tc.guardedField = 1 // +checklocksfail
+ })
+}
+
+func testClosureUnsupported(tc *oneGuardStruct) {
+ // Locked outside the closure, so may or may not be valid. This cannot
+ // be handled and we should explicitly fail. This can't be handled
+ // because of the call through callClosure, below, which means the
+ // closure will actually be passed as a value somewhere.
+ tc.mu.Lock()
+ callClosure(func() {
+ tc.guardedField = 1 // +checklocksfail
+ })
+ tc.mu.Unlock()
+}
+
+func testClosureValid(tc *oneGuardStruct) {
+ // All locking happens within the closure. This should not present a
+ // problem for analysis.
+ callClosure(func() {
+ tc.mu.Lock()
+ tc.guardedField = 1
+ tc.mu.Unlock()
+ })
+}
+
+func testClosureInline(tc *oneGuardStruct) {
+ // If the closure is being dispatching inline only, then we should be
+ // able to analyze this call and give it a thumbs up.
+ tc.mu.Lock()
+ func() {
+ tc.guardedField = 1
+ }()
+ tc.mu.Unlock()
+}
+
+func testAnonymousInvalid(tc *oneGuardStruct) {
+ // Invalid, as per testClosureInvalid above.
+ callAnonymous(func(tc *oneGuardStruct) {
+ tc.guardedField = 1 // +checklocksfail
+ }, tc)
+}
+
+func testAnonymousUnsupported(tc *oneGuardStruct) {
+ // Not supportable, as per testClosureUnsupported above.
+ tc.mu.Lock()
+ callAnonymous(func(tc *oneGuardStruct) {
+ tc.guardedField = 1 // +checklocksfail
+ }, tc)
+ tc.mu.Unlock()
+}
+
+func testAnonymousValid(tc *oneGuardStruct) {
+ // Valid, as per testClosureValid above.
+ callAnonymous(func(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ tc.guardedField = 1
+ tc.mu.Unlock()
+ }, tc)
+}
+
+func testAnonymousInline(tc *oneGuardStruct) {
+ // Unlike the closure case, we are able to dynamically infer the set of
+ // preconditions for the function dispatch and assert that this is
+ // a valid call.
+ tc.mu.Lock()
+ func(tc *oneGuardStruct) {
+ tc.guardedField = 1
+ }(tc)
+ tc.mu.Unlock()
+}
+
+//go:noinline
+func callClosure(fn func()) {
+ fn()
+}
+
+//go:noinline
+func callAnonymous(fn func(*oneGuardStruct), tc *oneGuardStruct) {
+ fn(tc)
+}
diff --git a/pkg/flipcall/packet_window_mmap_arm64.go b/tools/checklocks/test/defer.go
index 87ad1a4a1..6e574e5eb 100644
--- a/pkg/flipcall/packet_window_mmap_arm64.go
+++ b/tools/checklocks/test/defer.go
@@ -12,14 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// +build arm64
+package test
-package flipcall
+func testDeferValidUnlock(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ tc.guardedField = 1
+ defer tc.mu.Unlock()
+}
-import "golang.org/x/sys/unix"
+func testDeferValidAccess(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ defer func() {
+ tc.guardedField = 1
+ tc.mu.Unlock()
+ }()
+}
-// Return a memory mapping of the pwd in memory that can be shared outside the sandbox.
-func packetWindowMmap(pwd PacketWindowDescriptor) (uintptr, unix.Errno) {
- m, _, err := unix.RawSyscall6(unix.SYS_MMAP, 0, uintptr(pwd.Length), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED, uintptr(pwd.FD), uintptr(pwd.Offset))
- return m, err
+func testDeferInvalidAccess(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ defer func() {
+ // N.B. Executed after tc.mu.Unlock().
+ tc.guardedField = 1 // +checklocksfail
+ }()
+ tc.mu.Unlock()
}
diff --git a/tools/checklocks/test/incompat.go b/tools/checklocks/test/incompat.go
new file mode 100644
index 000000000..b39bc66c1
--- /dev/null
+++ b/tools/checklocks/test/incompat.go
@@ -0,0 +1,54 @@
+// Copyright 2020 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 test
+
+import (
+ "sync"
+)
+
+// unsupportedLockerStruct verifies that trying to annotate a field that is not a
+// sync.Mutex or sync.RWMutex results in a failure.
+type unsupportedLockerStruct struct {
+ mu sync.Locker
+
+ // +checklocks:mu
+ x int // +checklocksfail
+}
+
+// badFieldsStruct verifies that refering invalid fields fails.
+type badFieldsStruct struct {
+ // +checklocks:mu
+ x int // +checklocksfail
+}
+
+// redundantStruct verifies that redundant annotations fail.
+type redundantStruct struct {
+ mu sync.Mutex
+
+ // +checklocks:mu
+ // +checklocks:mu
+ x int // +checklocksfail
+}
+
+// conflictsStruct verifies that conflicting annotations fail.
+type conflictsStruct struct {
+ // +checkatomicignore
+ // +checkatomic
+ x int // +checklocksfail
+
+ // +checkatomic
+ // +checkatomicignore
+ y int // +checklocksfail
+}
diff --git a/tools/checklocks/test/methods.go b/tools/checklocks/test/methods.go
new file mode 100644
index 000000000..72e26fca6
--- /dev/null
+++ b/tools/checklocks/test/methods.go
@@ -0,0 +1,117 @@
+// Copyright 2020 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 test
+
+import (
+ "sync"
+)
+
+type testMethods struct {
+ mu sync.Mutex
+
+ // +checklocks:mu
+ guardedField int
+}
+
+func (t *testMethods) methodValid() {
+ t.mu.Lock()
+ t.guardedField = 1
+ t.mu.Unlock()
+}
+
+func (t *testMethods) methodInvalid() {
+ t.guardedField = 2 // +checklocksfail
+}
+
+// +checklocks:t.mu
+func (t *testMethods) MethodLocked(a, b, c int) {
+ t.guardedField = 3
+}
+
+// +checklocksignore
+func (t *testMethods) methodIgnore() {
+ t.guardedField = 2
+}
+
+func testMethodCallsValid(tc *testMethods) {
+ tc.methodValid()
+}
+
+func testMethodCallsValidPreconditions(tc *testMethods) {
+ tc.mu.Lock()
+ tc.MethodLocked(1, 2, 3)
+ tc.mu.Unlock()
+}
+
+func testMethodCallsInvalid(tc *testMethods) {
+ tc.MethodLocked(4, 5, 6) // +checklocksfail
+}
+
+func testMultipleParameters(tc1, tc2, tc3 *testMethods) {
+ tc1.mu.Lock()
+ tc1.guardedField = 1
+ tc2.guardedField = 2 // +checklocksfail
+ tc3.guardedField = 3 // +checklocksfail
+ tc1.mu.Unlock()
+}
+
+type testMethodsWithParameters struct {
+ mu sync.Mutex
+
+ // +checklocks:mu
+ guardedField int
+}
+
+type ptrToTestMethodsWithParameters *testMethodsWithParameters
+
+// +checklocks:t.mu
+// +checklocks:a.mu
+func (t *testMethodsWithParameters) methodLockedWithParameters(a *testMethodsWithParameters, b *testMethodsWithParameters) {
+ t.guardedField = a.guardedField
+ b.guardedField = a.guardedField // +checklocksfail
+}
+
+// +checklocks:t.mu
+// +checklocks:a.mu
+// +checklocks:b.mu
+func (t *testMethodsWithParameters) methodLockedWithPtrType(a *testMethodsWithParameters, b ptrToTestMethodsWithParameters) {
+ t.guardedField = a.guardedField
+ b.guardedField = a.guardedField
+}
+
+// +checklocks:a.mu
+func standaloneFunctionWithGuard(a *testMethodsWithParameters) {
+ a.guardedField = 1
+ a.mu.Unlock()
+ a.guardedField = 1 // +checklocksfail
+}
+
+type testMethodsWithEmbedded struct {
+ mu sync.Mutex
+
+ // +checklocks:mu
+ guardedField int
+ p *testMethodsWithParameters
+}
+
+// +checklocks:t.mu
+func (t *testMethodsWithEmbedded) DoLocked(a, b *testMethodsWithParameters) {
+ t.guardedField = 1
+ a.mu.Lock()
+ b.mu.Lock()
+ t.p.methodLockedWithParameters(a, b) // +checklocksfail
+ a.mu.Unlock()
+ b.mu.Unlock()
+}
diff --git a/tools/checklocks/test/parameters.go b/tools/checklocks/test/parameters.go
new file mode 100644
index 000000000..5b9e664b6
--- /dev/null
+++ b/tools/checklocks/test/parameters.go
@@ -0,0 +1,48 @@
+// Copyright 2020 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 test
+
+func testParameterPassingbyAddrValid(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ nestedWithGuardByAddr(&tc.guardedField, &tc.unguardedField)
+ tc.mu.Unlock()
+}
+
+func testParameterPassingByAddrInalid(tc *oneGuardStruct) {
+ nestedWithGuardByAddr(&tc.guardedField, &tc.unguardedField) // +checklocksfail
+}
+
+func testParameterPassingByValueValid(tc *oneGuardStruct) {
+ tc.mu.Lock()
+ nestedWithGuardByValue(tc.guardedField, tc.unguardedField)
+ tc.mu.Unlock()
+}
+
+func testParameterPassingByValueInalid(tc *oneGuardStruct) {
+ nestedWithGuardByValue(tc.guardedField, tc.unguardedField) // +checklocksfail
+}
+
+func nestedWithGuardByAddr(guardedField, unguardedField *int) {
+ *guardedField = 4
+ *unguardedField = 5
+}
+
+func nestedWithGuardByValue(guardedField, unguardedField int) {
+ // read the fields to keep SA4009 static analyzer happy.
+ _ = guardedField
+ _ = unguardedField
+ guardedField = 4
+ unguardedField = 5
+}
diff --git a/tools/checklocks/test/return.go b/tools/checklocks/test/return.go
new file mode 100644
index 000000000..47c7b6773
--- /dev/null
+++ b/tools/checklocks/test/return.go
@@ -0,0 +1,61 @@
+// Copyright 2020 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 test
+
+// +checklocks:tc.mu
+func testReturnInvalidGuard() (tc *oneGuardStruct) { // +checklocksfail
+ return new(oneGuardStruct)
+}
+
+// +checklocksrelease:tc.mu
+func testReturnInvalidRelease() (tc *oneGuardStruct) { // +checklocksfail
+ return new(oneGuardStruct)
+}
+
+// +checklocksacquire:tc.mu
+func testReturnInvalidAcquire() (tc *oneGuardStruct) {
+ return new(oneGuardStruct) // +checklocksfail
+}
+
+// +checklocksacquire:tc.mu
+func testReturnValidAcquire() (tc *oneGuardStruct) {
+ tc = new(oneGuardStruct)
+ tc.mu.Lock()
+ return tc
+}
+
+func testReturnAcquireCall() {
+ tc := testReturnValidAcquire()
+ tc.guardedField = 1
+ tc.mu.Unlock()
+}
+
+// +checklocksacquire:tc.val.mu
+// +checklocksacquire:tc.ptr.mu
+func testReturnValidNestedAcquire() (tc *nestedGuardStruct) {
+ tc = new(nestedGuardStruct)
+ tc.ptr = new(oneGuardStruct)
+ tc.val.mu.Lock()
+ tc.ptr.mu.Lock()
+ return tc
+}
+
+func testReturnNestedAcquireCall() {
+ tc := testReturnValidNestedAcquire()
+ tc.val.guardedField = 1
+ tc.ptr.guardedField = 1
+ tc.val.mu.Unlock()
+ tc.ptr.mu.Unlock()
+}
diff --git a/tools/checklocks/test/test.go b/tools/checklocks/test/test.go
index 05693c183..cbf6b1635 100644
--- a/tools/checklocks/test/test.go
+++ b/tools/checklocks/test/test.go
@@ -13,99 +13,24 @@
// limitations under the License.
// Package test is a test package.
+//
+// Tests are all compilation tests in separate files.
package test
import (
- "math/rand"
"sync"
)
-type oneGuarded struct {
+// oneGuardStruct has one guarded field.
+type oneGuardStruct struct {
mu sync.Mutex
// +checklocks:mu
- guardedField int
-
+ guardedField int
unguardedField int
}
-func testAccessOne() {
- var tc oneGuarded
- // Valid access
- tc.mu.Lock()
- tc.guardedField = 1
- tc.unguardedField = 1
- tc.mu.Unlock()
-
- // Valid access as unguarded field is not protected by mu.
- tc.unguardedField = 2
-
- // Invalid access
- tc.guardedField = 2 // +checklocksfail
-
- // Invalid read of a guarded field.
- x := tc.guardedField // +checklocksfail
- _ = x
-}
-
-func testFunctionCallsNoParameters() {
- // Couple of regular function calls with no parameters.
- funcCallWithValidAccess()
- funcCallWithInvalidAccess()
-}
-
-func funcCallWithValidAccess() {
- var tc2 oneGuarded
- // Valid tc2 access
- tc2.mu.Lock()
- tc2.guardedField = 1
- tc2.mu.Unlock()
-}
-
-func funcCallWithInvalidAccess() {
- var tc oneGuarded
- var tc2 oneGuarded
- // Invalid access, wrong mutex is held.
- tc.mu.Lock()
- tc2.guardedField = 2 // +checklocksfail
- tc.mu.Unlock()
-}
-
-func testParameterPassing() {
- var tc oneGuarded
-
- // Valid call where a guardedField is passed to a function as a parameter.
- tc.mu.Lock()
- nestedWithGuardByAddr(&tc.guardedField, &tc.unguardedField)
- tc.mu.Unlock()
-
- // Invalid call where a guardedField is passed to a function as a parameter
- // without holding locks.
- nestedWithGuardByAddr(&tc.guardedField, &tc.unguardedField) // +checklocksfail
-
- // Valid call where a guardedField is passed to a function as a parameter.
- tc.mu.Lock()
- nestedWithGuardByValue(tc.guardedField, tc.unguardedField)
- tc.mu.Unlock()
-
- // Invalid call where a guardedField is passed to a function as a parameter
- // without holding locks.
- nestedWithGuardByValue(tc.guardedField, tc.unguardedField) // +checklocksfail
-}
-
-func nestedWithGuardByAddr(guardedField, unguardedField *int) {
- *guardedField = 4
- *unguardedField = 5
-}
-
-func nestedWithGuardByValue(guardedField, unguardedField int) {
- // read the fields to keep SA4009 static analyzer happy.
- _ = guardedField
- _ = unguardedField
- guardedField = 4
- unguardedField = 5
-}
-
-type twoGuarded struct {
+// twoGuardStruct has two guarded fields.
+type twoGuardStruct struct {
mu sync.Mutex
// +checklocks:mu
guardedField1 int
@@ -113,250 +38,27 @@ type twoGuarded struct {
guardedField2 int
}
-type twoLocks struct {
+// twoLocksStruct has two locks and two fields.
+type twoLocksStruct struct {
mu sync.Mutex
secondMu sync.Mutex
-
// +checklocks:mu
guardedField1 int
// +checklocks:secondMu
guardedField2 int
}
-type twoLocksDoubleGuard struct {
+// twoLocksDoubleGuardStruct has two locks and a single field with two guards.
+type twoLocksDoubleGuardStruct struct {
mu sync.Mutex
secondMu sync.Mutex
-
// +checklocks:mu
// +checklocks:secondMu
doubleGuardedField int
}
-func testTwoLocksDoubleGuard() {
- var tc twoLocksDoubleGuard
-
- // Double guarded field
- tc.mu.Lock()
- tc.secondMu.Lock()
- tc.doubleGuardedField = 1
- tc.secondMu.Unlock()
-
- // This should fail as we released the secondMu.
- tc.doubleGuardedField = 2 // +checklocksfail
- tc.mu.Unlock()
-
- // This should fail as well as now we are not holding any locks.
- //
- // This line triggers two failures one for each mutex, hence the 2 after
- // fail.
- tc.doubleGuardedField = 3 // +checklocksfail:2
-}
-
-type rwGuarded struct {
- rwMu sync.RWMutex
-
- // +checklocks:rwMu
- rwGuardedField int
-}
-
-func testRWGuarded() {
- var tc rwGuarded
-
- // Assignment w/ exclusive lock should pass.
- tc.rwMu.Lock()
- tc.rwGuardedField = 1
- tc.rwMu.Unlock()
-
- // Assignment w/ RWLock should pass as we don't differentiate between
- // Lock/RLock.
- tc.rwMu.RLock()
- tc.rwGuardedField = 2
- tc.rwMu.RUnlock()
-
- // Assignment w/o hold Lock() should fail.
- tc.rwGuardedField = 3 // +checklocksfail
-
- // Reading w/o holding lock should fail.
- x := tc.rwGuardedField + 3 // +checklocksfail
- _ = x
-}
-
-type nestedFields struct {
- mu sync.Mutex
-
- // +checklocks:mu
- nestedStruct struct {
- nested1 int
- nested2 int
- }
-}
-
-func testNestedStructGuards() {
- var tc nestedFields
- // Valid access with mu held.
- tc.mu.Lock()
- tc.nestedStruct.nested1 = 1
- tc.nestedStruct.nested2 = 2
- tc.mu.Unlock()
-
- // Invalid access to nested1 wihout holding mu.
- tc.nestedStruct.nested1 = 1 // +checklocksfail
-}
-
-type testCaseMethods struct {
- mu sync.Mutex
-
- // +checklocks:mu
- guardedField int
-}
-
-func (t *testCaseMethods) Method() {
- // Valid access
- t.mu.Lock()
- t.guardedField = 1
- t.mu.Unlock()
-
- // invalid access
- t.guardedField = 2 // +checklocksfail
-}
-
-// +checklocks:t.mu
-func (t *testCaseMethods) MethodLocked(a, b, c int) {
- t.guardedField = 3
-}
-
-// +checklocksignore
-func (t *testCaseMethods) IgnoredMethod() {
- // Invalid access but should not fail as the function is annotated
- // with "// +checklocksignore"
- t.guardedField = 2
-}
-
-func testMethodCalls() {
- var tc2 testCaseMethods
-
- // Valid use, tc2.Method acquires lock.
- tc2.Method()
-
- // Valid access tc2.mu is held before calling tc2.MethodLocked.
- tc2.mu.Lock()
- tc2.MethodLocked(1, 2, 3)
- tc2.mu.Unlock()
-
- // Invalid access no locks are being held.
- tc2.MethodLocked(4, 5, 6) // +checklocksfail
-}
-
-type noMutex struct {
- f int
- g int
-}
-
-func (n noMutex) method() {
- n.f = 1
- n.f = n.g
-}
-
-func testNoMutex() {
- var n noMutex
- n.method()
-}
-
-func testMultiple() {
- var tc1, tc2, tc3 testCaseMethods
-
- tc1.mu.Lock()
-
- // Valid access we are holding tc1's lock.
- tc1.guardedField = 1
-
- // Invalid access we are not holding tc2 or tc3's lock.
- tc2.guardedField = 2 // +checklocksfail
- tc3.guardedField = 3 // +checklocksfail
- tc1.mu.Unlock()
-}
-
-func testConditionalBranchingLocks() {
- var tc2 testCaseMethods
- x := rand.Intn(10)
- if x%2 == 1 {
- tc2.mu.Lock()
- }
- // This is invalid access as tc2.mu is not held if we never entered
- // the if block.
- tc2.guardedField = 1 // +checklocksfail
-
- var tc3 testCaseMethods
- if x%2 == 1 {
- tc3.mu.Lock()
- } else {
- tc3.mu.Lock()
- }
- // This is valid as tc3.mu is held in if and else blocks.
- tc3.guardedField = 1
-}
-
-type testMethodWithParams struct {
- mu sync.Mutex
-
- // +checklocks:mu
- guardedField int
-}
-
-type ptrToTestMethodWithParams *testMethodWithParams
-
-// +checklocks:t.mu
-// +checklocks:a.mu
-func (t *testMethodWithParams) methodLockedWithParams(a *testMethodWithParams, b *testMethodWithParams) {
- t.guardedField = a.guardedField
- b.guardedField = a.guardedField // +checklocksfail
-}
-
-// +checklocks:t.mu
-// +checklocks:a.mu
-// +checklocks:b.mu
-func (t *testMethodWithParams) methodLockedWithPtrType(a *testMethodWithParams, b ptrToTestMethodWithParams) {
- t.guardedField = a.guardedField
- b.guardedField = a.guardedField
-}
-
-// +checklocks:a.mu
-func standaloneFunctionWithGuard(a *testMethodWithParams) {
- a.guardedField = 1
- a.mu.Unlock()
- a.guardedField = 1 // +checklocksfail
-}
-
-type testMethodWithEmbedded struct {
- mu sync.Mutex
-
- // +checklocks:mu
- guardedField int
- p *testMethodWithParams
-}
-
-// +checklocks:t.mu
-func (t *testMethodWithEmbedded) DoLocked() {
- var a, b testMethodWithParams
- t.guardedField = 1
- a.mu.Lock()
- b.mu.Lock()
- t.p.methodLockedWithParams(&a, &b) // +checklocksfail
- a.mu.Unlock()
- b.mu.Unlock()
-}
-
-// UnsupportedLockerExample is a test that verifies that trying to annotate a
-// field that is not a sync.Mutex/RWMutex results in a failure.
-type UnsupportedLockerExample struct {
- mu sync.Locker
-
- // +checklocks:mu
- x int // +checklocksfail
-}
-
-func abc() {
- var mu sync.Mutex
- a := UnsupportedLockerExample{mu: &mu}
- a.x = 1
+// nestedGuardStruct nests oneGuardStruct fields.
+type nestedGuardStruct struct {
+ val oneGuardStruct
+ ptr *oneGuardStruct
}
diff --git a/tools/github/BUILD b/tools/github/BUILD
index a345debf6..9c94bbc63 100644
--- a/tools/github/BUILD
+++ b/tools/github/BUILD
@@ -8,7 +8,7 @@ go_binary(
nogo = False,
deps = [
"//tools/github/reviver",
- "@com_github_google_go_github_v32//github:go_default_library",
+ "@com_github_google_go_github_v35//github:go_default_library",
"@org_golang_x_oauth2//:go_default_library",
],
)
diff --git a/tools/github/reviver/BUILD b/tools/github/reviver/BUILD
index fc54782f5..aac1c18bf 100644
--- a/tools/github/reviver/BUILD
+++ b/tools/github/reviver/BUILD
@@ -12,7 +12,7 @@ go_library(
visibility = [
"//tools/github:__subpackages__",
],
- deps = ["@com_github_google_go_github_v32//github:go_default_library"],
+ deps = ["@com_github_google_go_github_v35//github:go_default_library"],
)
go_test(
diff --git a/tools/go_generics/BUILD b/tools/go_generics/BUILD
index 807c08ead..78b636130 100644
--- a/tools/go_generics/BUILD
+++ b/tools/go_generics/BUILD
@@ -5,8 +5,8 @@ package(licenses = ["notice"])
go_binary(
name = "go_generics",
srcs = [
- "generics.go",
"imports.go",
+ "main.go",
"remove.go",
],
visibility = ["//:sandbox"],
diff --git a/tools/go_generics/generics.go b/tools/go_generics/main.go
index 30584006c..30584006c 100644
--- a/tools/go_generics/generics.go
+++ b/tools/go_generics/main.go
diff --git a/tools/nogo/BUILD b/tools/nogo/BUILD
index 6c6f604b5..a7e280b32 100644
--- a/tools/nogo/BUILD
+++ b/tools/nogo/BUILD
@@ -37,6 +37,7 @@ go_library(
"//tools/checkescape",
"//tools/checklocks",
"//tools/checkunsafe",
+ "//tools/nogo/objdump",
"//tools/worker",
"@co_honnef_go_tools//staticcheck:go_default_library",
"@co_honnef_go_tools//stylecheck:go_default_library",
@@ -68,6 +69,7 @@ go_library(
"@org_golang_x_tools//go/analysis/passes/unsafeptr:go_default_library",
"@org_golang_x_tools//go/analysis/passes/unusedresult:go_default_library",
"@org_golang_x_tools//go/gcexportdata:go_default_library",
+ "@org_golang_x_tools//go/types/objectpath:go_default_library",
],
)
diff --git a/tools/nogo/check/main.go b/tools/nogo/check/main.go
index 3a6c3fb08..0e7e92965 100644
--- a/tools/nogo/check/main.go
+++ b/tools/nogo/check/main.go
@@ -62,7 +62,8 @@ func run([]string) int {
// Check & load the configuration.
if *packageFile != "" && *stdlibFile != "" {
- log.Fatalf("unable to perform stdlib and package analysis; provide only one!")
+ fmt.Fprintf(os.Stderr, "unable to perform stdlib and package analysis; provide only one!")
+ return 1
}
// Run the configuration.
@@ -75,18 +76,21 @@ func run([]string) int {
c := loadConfig(*packageFile, new(nogo.PackageConfig)).(*nogo.PackageConfig)
findings, factData, err = nogo.CheckPackage(c, nogo.AllAnalyzers, nil)
} else {
- log.Fatalf("please provide at least one of package or stdlib!")
+ fmt.Fprintf(os.Stderr, "please provide at least one of package or stdlib!")
+ return 1
}
// Check that analysis was successful.
if err != nil {
- log.Fatalf("error performing analysis: %v", err)
+ fmt.Fprintf(os.Stderr, "error performing analysis: %v", err)
+ return 1
}
// Save facts.
if *factsOutput != "" {
if err := ioutil.WriteFile(*factsOutput, factData, 0644); err != nil {
- log.Fatalf("error saving findings to %q: %v", *factsOutput, err)
+ fmt.Fprintf(os.Stderr, "error saving findings to %q: %v", *factsOutput, err)
+ return 1
}
}
@@ -94,10 +98,12 @@ func run([]string) int {
if *findingsOutput != "" {
w, err := os.OpenFile(*findingsOutput, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
- log.Fatalf("error opening output file %q: %v", *findingsOutput, err)
+ fmt.Fprintf(os.Stderr, "error opening output file %q: %v", *findingsOutput, err)
+ return 1
}
if err := nogo.WriteFindingsTo(w, findings, false /* json */); err != nil {
- log.Fatalf("error writing findings to %q: %v", *findingsOutput, err)
+ fmt.Fprintf(os.Stderr, "error writing findings to %q: %v", *findingsOutput, err)
+ return 1
}
} else {
for _, finding := range findings {
diff --git a/tools/nogo/defs.bzl b/tools/nogo/defs.bzl
index ddf5816a6..80182ff6c 100644
--- a/tools/nogo/defs.bzl
+++ b/tools/nogo/defs.bzl
@@ -198,6 +198,22 @@ NogoInfo = provider(
},
)
+def _select_objfile(files):
+ """Returns (.a file, .x file, is_archive).
+
+ If no .a file is available, then the first .x file will be returned
+ instead, and vice versa. If neither are available, then the first provided
+ file will be returned."""
+ a_files = [f for f in files if f.path.endswith(".a")]
+ x_files = [f for f in files if f.path.endswith(".x")]
+ if not len(x_files) and not len(a_files):
+ return (files[0], files[0], False)
+ if not len(x_files):
+ x_files = a_files
+ if not len(a_files):
+ a_files = x_files
+ return a_files[0], x_files[0], True
+
def _nogo_aspect_impl(target, ctx):
# If this is a nogo rule itself (and not the shadow of a go_library or
# go_binary rule created by such a rule), then we simply return nothing.
@@ -232,20 +248,14 @@ def _nogo_aspect_impl(target, ctx):
deps = deps + info.deps
# Start with all target files and srcs as input.
- inputs = target.files.to_list() + srcs
+ binaries = target.files.to_list()
+ inputs = binaries + srcs
# Generate a shell script that dumps the binary. Annoyingly, this seems
# necessary as the context in which a run_shell command runs does not seem
# to cleanly allow us redirect stdout to the actual output file. Perhaps
# I'm missing something here, but the intermediate script does work.
- binaries = target.files.to_list()
- objfiles = [f for f in binaries if f.path.endswith(".a")]
- if len(objfiles) > 0:
- # Prefer the .a files for go_library targets.
- target_objfile = objfiles[0]
- else:
- # Use the raw binary for go_binary and go_test targets.
- target_objfile = binaries[0]
+ target_objfile, target_xfile, has_objfile = _select_objfile(binaries)
inputs.append(target_objfile)
# Extract the importpath for this package.
@@ -274,10 +284,8 @@ def _nogo_aspect_impl(target, ctx):
# Configure where to find the binary & fact files. Note that this will
# use .x and .a regardless of whether this is a go_binary rule, since
# these dependencies must be go_library rules.
- x_files = [f.path for f in info.binaries if f.path.endswith(".x")]
- if not len(x_files):
- x_files = [f.path for f in info.binaries if f.path.endswith(".a")]
- import_map[info.importpath] = x_files[0]
+ _, x_file, _ = _select_objfile(info.binaries)
+ import_map[info.importpath] = x_file.path
fact_map[info.importpath] = info.facts.path
# Collect all findings; duplicates are resolved at the end.
@@ -287,6 +295,11 @@ def _nogo_aspect_impl(target, ctx):
inputs.append(info.facts)
inputs += info.binaries
+ # Add the module itself, for the type sanity check. This applies only to
+ # the libraries, and not binaries or tests.
+ if has_objfile:
+ import_map[importpath] = target_xfile.path
+
# Add the standard library facts.
stdlib_info = ctx.attr._nogo_stdlib[NogoStdlibInfo]
stdlib_facts = stdlib_info.facts
diff --git a/tools/nogo/nogo.go b/tools/nogo/nogo.go
index acee7c8bc..d95d7652f 100644
--- a/tools/nogo/nogo.go
+++ b/tools/nogo/nogo.go
@@ -41,9 +41,10 @@ import (
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/facts"
"golang.org/x/tools/go/gcexportdata"
+ "golang.org/x/tools/go/types/objectpath"
// Special case: flags live here and change overall behavior.
- "gvisor.dev/gvisor/tools/checkescape"
+ "gvisor.dev/gvisor/tools/nogo/objdump"
"gvisor.dev/gvisor/tools/worker"
)
@@ -216,6 +217,11 @@ func (i *importer) Import(path string) (*types.Package, error) {
}
}
+ // Check the cache.
+ if pkg, ok := i.cache[path]; ok && pkg.Complete() {
+ return pkg, nil
+ }
+
// Actually load the data.
realPath, ok := i.ImportMap[path]
var (
@@ -327,6 +333,9 @@ func CheckStdlib(config *StdlibConfig, analyzers []*analysis.Analyzer) (allFindi
// Closure to check a single package.
localStdlibFacts := make(stdlibFacts)
localStdlibErrs := make(map[string]error)
+ stdlibCachedFacts.Lookup([]string{""}, func() worker.Sizer {
+ return localStdlibFacts
+ })
var checkOne func(pkg string) error // Recursive.
checkOne = func(pkg string) error {
// Is this already done?
@@ -355,11 +364,11 @@ func CheckStdlib(config *StdlibConfig, analyzers []*analysis.Analyzer) (allFindi
}
// Provide the input.
- oldReader := checkescape.Reader
- checkescape.Reader = rc // For analysis.
+ oldReader := objdump.Reader
+ objdump.Reader = rc // For analysis.
defer func() {
rc.Close()
- checkescape.Reader = oldReader // Restore.
+ objdump.Reader = oldReader // Restore.
}()
// Run the analysis.
@@ -406,6 +415,56 @@ func CheckStdlib(config *StdlibConfig, analyzers []*analysis.Analyzer) (allFindi
return allFindings, buf.Bytes(), nil
}
+// sanityCheckScope checks that all object in astTypes map to the correct
+// objects in binaryTypes. Note that we don't check whether the sets are the
+// same, we only care about the fidelity of objects in astTypes.
+//
+// When an inconsistency is identified, we record it in the astToBinaryMap.
+// This allows us to dynamically replace facts and correct for the issue. The
+// total number of mismatches is returned.
+func sanityCheckScope(astScope *types.Scope, binaryTypes *types.Package, binaryScope *types.Scope, astToBinary map[types.Object]types.Object) error {
+ for _, x := range astScope.Names() {
+ fe := astScope.Lookup(x)
+ path, err := objectpath.For(fe)
+ if err != nil {
+ continue // Not an encoded object.
+ }
+ se, err := objectpath.Object(binaryTypes, path)
+ if err != nil {
+ continue // May be unused, see below.
+ }
+ if fe.Id() != se.Id() {
+ // These types are incompatible. This means that when
+ // this objectpath is loading from the binaryTypes (for
+ // dependencies) it will resolve to a fact for that
+ // type. We don't actually care about this error since
+ // we do the rewritten, but may as well alert.
+ log.Printf("WARNING: Object %s is a victim of go/issues/44195.", fe.Id())
+ }
+ se = binaryScope.Lookup(x)
+ if se == nil {
+ // The fact may not be exported in the objectdata, if
+ // it is package internal. This is fine, as nothing out
+ // of this package can use these symbols.
+ continue
+ }
+ // Save the translation.
+ astToBinary[fe] = se
+ }
+ for i := 0; i < astScope.NumChildren(); i++ {
+ if err := sanityCheckScope(astScope.Child(i), binaryTypes, binaryScope, astToBinary); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// sanityCheckTypes checks that two types are sane. The total number of
+// mismatches is returned.
+func sanityCheckTypes(astTypes, binaryTypes *types.Package, astToBinary map[types.Object]types.Object) error {
+ return sanityCheckScope(astTypes.Scope(), binaryTypes, binaryTypes.Scope(), astToBinary)
+}
+
// CheckPackage runs all given analyzers.
//
// The implementation was adapted from [1], which was in turn adpated from [2].
@@ -450,17 +509,46 @@ func CheckPackage(config *PackageConfig, analyzers []*analysis.Analyzer, importC
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
- types, err := typeConfig.Check(config.ImportPath, imp.fset, syntax, typesInfo)
+ astTypes, err := typeConfig.Check(config.ImportPath, imp.fset, syntax, typesInfo)
if err != nil && imp.lastErr != ErrSkip {
return nil, nil, fmt.Errorf("error checking types: %w", err)
}
- // Load all package facts.
- facts, err := facts.Decode(types, config.factLoader)
+ // Load all facts using the astTypes, although it may need reconciling
+ // later on. See the fact functions below.
+ astFacts, err := facts.Decode(astTypes, config.factLoader)
if err != nil {
return nil, nil, fmt.Errorf("error decoding facts: %w", err)
}
+ // Sanity check all types and record metadata to prevent
+ // https://github.com/golang/go/issues/44195.
+ //
+ // This block loads the binary types, whose encoding will be well
+ // defined and aligned with any downstream consumers. Below in the fact
+ // functions for the analysis, we serialize types to both the astFacts
+ // and the binaryFacts if available. The binaryFacts are the final
+ // encoded facts in order to ensure compatibility. We keep the
+ // intermediate astTypes in order to allow exporting and importing
+ // within the local package under analysis.
+ var (
+ astToBinary = make(map[types.Object]types.Object)
+ binaryFacts *facts.Set
+ )
+ if _, ok := config.ImportMap[config.ImportPath]; ok {
+ binaryTypes, err := imp.Import(config.ImportPath)
+ if err != nil {
+ return nil, nil, fmt.Errorf("error loading self: %w", err)
+ }
+ if err := sanityCheckTypes(astTypes, binaryTypes, astToBinary); err != nil {
+ return nil, nil, fmt.Errorf("error sanity checking types: %w", err)
+ }
+ binaryFacts, err = facts.Decode(binaryTypes, config.factLoader)
+ if err != nil {
+ return nil, nil, fmt.Errorf("error decoding facts: %w", err)
+ }
+ }
+
// Register fact types and establish dependencies between analyzers.
// The visit closure will execute recursively, and populate results
// will all required analysis results.
@@ -479,15 +567,15 @@ func CheckPackage(config *PackageConfig, analyzers []*analysis.Analyzer, importC
}
// Run the analysis.
- factFilter := make(map[reflect.Type]bool)
+ localFactsFilter := make(map[reflect.Type]bool)
for _, f := range a.FactTypes {
- factFilter[reflect.TypeOf(f)] = true
+ localFactsFilter[reflect.TypeOf(f)] = true
}
p := &analysis.Pass{
Analyzer: a,
Fset: imp.fset,
Files: syntax,
- Pkg: types,
+ Pkg: astTypes,
TypesInfo: typesInfo,
ResultOf: results, // All results.
Report: func(d analysis.Diagnostic) {
@@ -497,13 +585,29 @@ func CheckPackage(config *PackageConfig, analyzers []*analysis.Analyzer, importC
Message: d.Message,
})
},
- ImportPackageFact: facts.ImportPackageFact,
- ExportPackageFact: facts.ExportPackageFact,
- ImportObjectFact: facts.ImportObjectFact,
- ExportObjectFact: facts.ExportObjectFact,
- AllPackageFacts: func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) },
- AllObjectFacts: func() []analysis.ObjectFact { return facts.AllObjectFacts(factFilter) },
- TypesSizes: typesSizes,
+ ImportPackageFact: astFacts.ImportPackageFact,
+ ExportPackageFact: func(fact analysis.Fact) {
+ astFacts.ExportPackageFact(fact)
+ if binaryFacts != nil {
+ binaryFacts.ExportPackageFact(fact)
+ }
+ },
+ ImportObjectFact: astFacts.ImportObjectFact,
+ ExportObjectFact: func(obj types.Object, fact analysis.Fact) {
+ astFacts.ExportObjectFact(obj, fact)
+ // Note that if no object is recorded in
+ // astToBinary and binaryFacts != nil, then the
+ // object doesn't appear in the exported data.
+ // It was likely an internal object to the
+ // package, and there is no meaningful
+ // downstream consumer of the fact.
+ if binaryObj, ok := astToBinary[obj]; ok && binaryFacts != nil {
+ binaryFacts.ExportObjectFact(binaryObj, fact)
+ }
+ },
+ AllPackageFacts: func() []analysis.PackageFact { return astFacts.AllPackageFacts(localFactsFilter) },
+ AllObjectFacts: func() []analysis.ObjectFact { return astFacts.AllObjectFacts(localFactsFilter) },
+ TypesSizes: typesSizes,
}
result, err := a.Run(p)
if err != nil {
@@ -528,8 +632,14 @@ func CheckPackage(config *PackageConfig, analyzers []*analysis.Analyzer, importC
}
}
- // Return all findings.
- return findings, facts.Encode(), nil
+ // Return all findings. Note that we have a preference to returning the
+ // binary facts if available, so that downstream consumers of these
+ // facts will find the export aligns with the internal type details.
+ // See the block above with the call to sanityCheckTypes.
+ if binaryFacts != nil {
+ return findings, binaryFacts.Encode(), nil
+ }
+ return findings, astFacts.Encode(), nil
}
func init() {
diff --git a/tools/nogo/objdump/BUILD b/tools/nogo/objdump/BUILD
new file mode 100644
index 000000000..da56efdf7
--- /dev/null
+++ b/tools/nogo/objdump/BUILD
@@ -0,0 +1,10 @@
+load("//tools:defs.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "objdump",
+ srcs = ["objdump.go"],
+ nogo = False,
+ visibility = ["//tools:__subpackages__"],
+)
diff --git a/tools/nogo/objdump/objdump.go b/tools/nogo/objdump/objdump.go
new file mode 100644
index 000000000..48484abf3
--- /dev/null
+++ b/tools/nogo/objdump/objdump.go
@@ -0,0 +1,96 @@
+// Copyright 2020 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 objdump is a wrapper around relevant objdump flags.
+package objdump
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+)
+
+var (
+ // Binary is the binary under analysis.
+ //
+ // See Reader, below.
+ binary = flag.String("binary", "", "binary under analysis")
+
+ // Reader is the input stream.
+ //
+ // This may be set instead of Binary.
+ Reader io.Reader
+
+ // objdumpTool is the tool used to dump a binary.
+ objdumpTool = flag.String("objdump_tool", "", "tool used to dump a binary")
+)
+
+// LoadRaw reads the raw object output.
+func LoadRaw(fn func(r io.Reader) error) error {
+ var r io.Reader
+ if *binary != "" {
+ f, err := os.Open(*binary)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ r = f
+ } else if Reader != nil {
+ r = Reader
+ } else {
+ // We have no input stream.
+ return fmt.Errorf("no binary or reader provided")
+ }
+ return fn(r)
+}
+
+// Load reads the objdump output.
+func Load(fn func(r io.Reader) error) error {
+ var (
+ args []string
+ stdin io.Reader
+ )
+ if *binary != "" {
+ args = append(args, *binary)
+ } else if Reader != nil {
+ stdin = Reader
+ } else {
+ // We have no input stream or binary.
+ return fmt.Errorf("no binary or reader provided")
+ }
+
+ // Construct our command.
+ cmd := exec.Command(*objdumpTool, args...)
+ cmd.Stdin = stdin
+ cmd.Stderr = os.Stderr
+ out, err := cmd.StdoutPipe()
+ if err != nil {
+ return err
+ }
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+
+ // Call the user hook.
+ userErr := fn(out)
+
+ // Wait for the dump to finish.
+ if err := cmd.Wait(); userErr == nil && err != nil {
+ return err
+ }
+
+ return userErr
+}