From c931c8e0829914718a729e20d7db0c2bf4e73f0b Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Thu, 18 Apr 2019 15:22:47 -0700 Subject: [PATCH] Format struct pollfd in poll(2)/ppoll(2) I0410 15:40:38.854295 3776 x:0] [ 1] poll_test E poll(0x2b00bfb5c020 [{FD: 0x3 anon_inode:[eventfd], Events: POLLOUT, REvents: ...}], 0x1, 0x1) I0410 15:40:38.854348 3776 x:0] [ 1] poll_test X poll(0x2b00bfb5c020 [{FD: 0x3 anon_inode:[eventfd], Events: POLLOUT|POLLERR|POLLHUP, REvents: POLLOUT}], 0x1, 0x1) = 0x1 (10.765?s) PiperOrigin-RevId: 244269879 Change-Id: If07ba54a486fdeaaedfc0123769b78d1da862307 --- pkg/sentry/strace/BUILD | 1 + pkg/sentry/strace/linux64.go | 4 +- pkg/sentry/strace/poll.go | 72 +++++++++++++++++++++++++++ pkg/sentry/strace/strace.go | 4 ++ pkg/sentry/strace/syscalls.go | 4 ++ pkg/sentry/syscalls/linux/sys_poll.go | 20 ++++++-- 6 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 pkg/sentry/strace/poll.go diff --git a/pkg/sentry/strace/BUILD b/pkg/sentry/strace/BUILD index bcd94b42e..eaaa4d118 100644 --- a/pkg/sentry/strace/BUILD +++ b/pkg/sentry/strace/BUILD @@ -11,6 +11,7 @@ go_library( "futex.go", "linux64.go", "open.go", + "poll.go", "ptrace.go", "signal.go", "socket.go", diff --git a/pkg/sentry/strace/linux64.go b/pkg/sentry/strace/linux64.go index 22b76449c..6043b8cb1 100644 --- a/pkg/sentry/strace/linux64.go +++ b/pkg/sentry/strace/linux64.go @@ -24,7 +24,7 @@ var linuxAMD64 = SyscallMap{ 4: makeSyscallInfo("stat", Path, Stat), 5: makeSyscallInfo("fstat", FD, Stat), 6: makeSyscallInfo("lstat", Path, Stat), - 7: makeSyscallInfo("poll", Hex, Hex, Hex), + 7: makeSyscallInfo("poll", PollFDs, Hex, Hex), 8: makeSyscallInfo("lseek", Hex, Hex, Hex), 9: makeSyscallInfo("mmap", Hex, Hex, Hex, Hex, FD, Hex), 10: makeSyscallInfo("mprotect", Hex, Hex, Hex), @@ -288,7 +288,7 @@ var linuxAMD64 = SyscallMap{ 268: makeSyscallInfo("fchmodat", FD, Path, Mode), 269: makeSyscallInfo("faccessat", FD, Path, Oct, Hex), 270: makeSyscallInfo("pselect6", Hex, Hex, Hex, Hex, Hex, Hex), - 271: makeSyscallInfo("ppoll", Hex, Hex, Timespec, SigSet, Hex), + 271: makeSyscallInfo("ppoll", PollFDs, Hex, Timespec, SigSet, Hex), 272: makeSyscallInfo("unshare", CloneFlags), 273: makeSyscallInfo("set_robust_list", Hex, Hex), 274: makeSyscallInfo("get_robust_list", Hex, Hex, Hex), diff --git a/pkg/sentry/strace/poll.go b/pkg/sentry/strace/poll.go new file mode 100644 index 000000000..b6b05423c --- /dev/null +++ b/pkg/sentry/strace/poll.go @@ -0,0 +1,72 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package strace + +import ( + "fmt" + "strings" + + "gvisor.googlesource.com/gvisor/pkg/abi" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + slinux "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" +) + +// PollEventSet is the set of poll(2) event flags. +var PollEventSet = abi.FlagSet{ + {Flag: linux.POLLIN, Name: "POLLIN"}, + {Flag: linux.POLLPRI, Name: "POLLPRI"}, + {Flag: linux.POLLOUT, Name: "POLLOUT"}, + {Flag: linux.POLLERR, Name: "POLLERR"}, + {Flag: linux.POLLHUP, Name: "POLLHUP"}, + {Flag: linux.POLLNVAL, Name: "POLLNVAL"}, + {Flag: linux.POLLRDNORM, Name: "POLLRDNORM"}, + {Flag: linux.POLLRDBAND, Name: "POLLRDBAND"}, + {Flag: linux.POLLWRNORM, Name: "POLLWRNORM"}, + {Flag: linux.POLLWRBAND, Name: "POLLWRBAND"}, + {Flag: linux.POLLMSG, Name: "POLLMSG"}, + {Flag: linux.POLLREMOVE, Name: "POLLREMOVE"}, + {Flag: linux.POLLRDHUP, Name: "POLLRDHUP"}, + {Flag: linux.POLLFREE, Name: "POLLFREE"}, + {Flag: linux.POLL_BUSY_LOOP, Name: "POLL_BUSY_LOOP"}, +} + +func pollFD(t *kernel.Task, pfd *linux.PollFD, post bool) string { + revents := "..." + if post { + revents = PollEventSet.Parse(uint64(pfd.REvents)) + } + return fmt.Sprintf("{FD: %s, Events: %s, REvents: %s}", fd(t, kdefs.FD(pfd.FD)), PollEventSet.Parse(uint64(pfd.Events)), revents) +} + +func pollFDs(t *kernel.Task, addr usermem.Addr, nfds uint, post bool) string { + if addr == 0 { + return "null" + } + + pfds, err := slinux.CopyInPollFDs(t, addr, nfds) + if err != nil { + return fmt.Sprintf("%#x (error decoding pollfds: %s)", addr, err) + } + + s := make([]string, 0, len(pfds)) + for i := range pfds { + s = append(s, pollFD(t, &pfds[i], post)) + } + + return fmt.Sprintf("%#x [%s]", addr, strings.Join(s, ", ")) +} diff --git a/pkg/sentry/strace/strace.go b/pkg/sentry/strace/strace.go index 398035b65..a6d870b44 100644 --- a/pkg/sentry/strace/strace.go +++ b/pkg/sentry/strace/strace.go @@ -438,6 +438,8 @@ func (i *SyscallInfo) pre(t *kernel.Task, args arch.SyscallArguments, maximumBlo output = append(output, capHeader(t, args[arg].Pointer())) case CapData: output = append(output, capData(t, args[arg-1].Pointer(), args[arg].Pointer())) + case PollFDs: + output = append(output, pollFDs(t, args[arg].Pointer(), uint(args[arg+1].Uint()), false)) case Oct: output = append(output, "0o"+strconv.FormatUint(args[arg].Uint64(), 8)) case Hex: @@ -502,6 +504,8 @@ func (i *SyscallInfo) post(t *kernel.Task, args arch.SyscallArguments, rval uint output[arg] = sigAction(t, args[arg].Pointer()) case PostCapData: output[arg] = capData(t, args[arg-1].Pointer(), args[arg].Pointer()) + case PollFDs: + output[arg] = pollFDs(t, args[arg].Pointer(), uint(args[arg+1].Uint()), true) } } } diff --git a/pkg/sentry/strace/syscalls.go b/pkg/sentry/strace/syscalls.go index 1f255c717..8c897fcbe 100644 --- a/pkg/sentry/strace/syscalls.go +++ b/pkg/sentry/strace/syscalls.go @@ -202,6 +202,10 @@ const ( // PostCapData is the data argument to capget(2)/capset(2), formatted // after syscall execution. The previous argument must be CapHeader. PostCapData + + // PollFDs is an array of struct pollfd. The number of entries in the + // array is in the next argument. + PollFDs ) // defaultFormat is the syscall argument format to use if the actual format is diff --git a/pkg/sentry/syscalls/linux/sys_poll.go b/pkg/sentry/syscalls/linux/sys_poll.go index 23fcb907f..17b6768e5 100644 --- a/pkg/sentry/syscalls/linux/sys_poll.go +++ b/pkg/sentry/syscalls/linux/sys_poll.go @@ -155,18 +155,28 @@ func pollBlock(t *kernel.Task, pfd []linux.PollFD, timeout time.Duration) (time. return timeout, n, nil } -func doPoll(t *kernel.Task, pfdAddr usermem.Addr, nfds uint, timeout time.Duration) (time.Duration, uintptr, error) { +// CopyInPollFDs copies an array of struct pollfd unless nfds exceeds the max. +func CopyInPollFDs(t *kernel.Task, addr usermem.Addr, nfds uint) ([]linux.PollFD, error) { if uint64(nfds) > t.ThreadGroup().Limits().GetCapped(limits.NumberOfFiles, fileCap) { - return timeout, 0, syserror.EINVAL + return nil, syserror.EINVAL } pfd := make([]linux.PollFD, nfds) if nfds > 0 { - if _, err := t.CopyIn(pfdAddr, &pfd); err != nil { - return timeout, 0, err + if _, err := t.CopyIn(addr, &pfd); err != nil { + return nil, err } } + return pfd, nil +} + +func doPoll(t *kernel.Task, addr usermem.Addr, nfds uint, timeout time.Duration) (time.Duration, uintptr, error) { + pfd, err := CopyInPollFDs(t, addr, nfds) + if err != nil { + return timeout, 0, err + } + // Compatibility warning: Linux adds POLLHUP and POLLERR just before // polling, in fs/select.c:do_pollfd(). Since pfd is copied out after // polling, changing event masks here is an application-visible difference. @@ -180,7 +190,7 @@ func doPoll(t *kernel.Task, pfdAddr usermem.Addr, nfds uint, timeout time.Durati // The poll entries are copied out regardless of whether // any are set or not. This aligns with the Linux behavior. if nfds > 0 && err == nil { - if _, err := t.CopyOut(pfdAddr, pfd); err != nil { + if _, err := t.CopyOut(addr, pfd); err != nil { return remainingTimeout, 0, err } }