From 07836a3ff1b0c4612afe80e5914cb637a39f8f0b Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Wed, 10 Nov 2021 17:04:36 -0800 Subject: [PATCH] Implement ioctl(fd, FIONREAD) for host FDs Closes #6796 PiperOrigin-RevId: 409013591 --- .../basic/integrationtest/Dockerfile.x86_64 | 1 + images/basic/integrationtest/host_fd.c | 29 ++++++++++++++++++ pkg/sentry/fsimpl/host/BUILD | 1 + pkg/sentry/fsimpl/host/host.go | 19 ++++++++++++ pkg/sentry/fsimpl/host/host_unsafe.go | 30 +++++++++++++++++++ runsc/boot/filter/config.go | 10 +++++-- test/e2e/integration_test.go | 10 +++++++ 7 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 images/basic/integrationtest/host_fd.c create mode 100644 pkg/sentry/fsimpl/host/host_unsafe.go diff --git a/images/basic/integrationtest/Dockerfile.x86_64 b/images/basic/integrationtest/Dockerfile.x86_64 index b9fed05cb..0a022f3d4 100644 --- a/images/basic/integrationtest/Dockerfile.x86_64 +++ b/images/basic/integrationtest/Dockerfile.x86_64 @@ -11,3 +11,4 @@ RUN gcc -O2 -o test_copy_up test_copy_up.c RUN gcc -O2 -o test_rewinddir test_rewinddir.c RUN gcc -O2 -o link_test link_test.c RUN gcc -O2 -o test_sticky test_sticky.c +RUN gcc -O2 -o host_fd host_fd.c \ No newline at end of file diff --git a/images/basic/integrationtest/host_fd.c b/images/basic/integrationtest/host_fd.c new file mode 100644 index 000000000..32990bb62 --- /dev/null +++ b/images/basic/integrationtest/host_fd.c @@ -0,0 +1,29 @@ +// 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 +#include +#include + +// Tests that FIONREAD is supported with host FD. +int main(int argc, char** argv) { + int size = 0; + if (ioctl(STDOUT_FILENO, FIONREAD, &size) < 0) { + err(1, "ioctl(stdin, FIONREAD)"); + } + if (size != 0) { + err(1, "FIONREAD wrong size, want: 0, got: %d", size); + } + return 0; +} diff --git a/pkg/sentry/fsimpl/host/BUILD b/pkg/sentry/fsimpl/host/BUILD index 180a35583..cd30ebbe2 100644 --- a/pkg/sentry/fsimpl/host/BUILD +++ b/pkg/sentry/fsimpl/host/BUILD @@ -31,6 +31,7 @@ go_library( "connected_endpoint_refs.go", "control.go", "host.go", + "host_unsafe.go", "inode_refs.go", "ioctl_unsafe.go", "save_restore.go", diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go index 984c6e8ee..b970cef14 100644 --- a/pkg/sentry/fsimpl/host/host.go +++ b/pkg/sentry/fsimpl/host/host.go @@ -29,6 +29,7 @@ import ( "gvisor.dev/gvisor/pkg/fspath" "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/log" + "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs" "gvisor.dev/gvisor/pkg/sentry/hostfd" "gvisor.dev/gvisor/pkg/sentry/kernel" @@ -910,3 +911,21 @@ func (f *fileDescription) EventUnregister(e *waiter.Entry) { func (f *fileDescription) Readiness(mask waiter.EventMask) waiter.EventMask { return fdnotifier.NonBlockingPoll(int32(f.inode.hostFD), mask) } + +// Ioctl queries the underlying FD for allowed ioctl commands. +func (f *fileDescription) Ioctl(ctx context.Context, uio usermem.IO, args arch.SyscallArguments) (uintptr, error) { + switch cmd := args[1].Int(); cmd { + case linux.FIONREAD: + v, err := ioctlFionread(f.inode.hostFD) + if err != nil { + return 0, err + } + + var buf [4]byte + hostarch.ByteOrder.PutUint32(buf[:], v) + _, err = uio.CopyOut(ctx, args[2].Pointer(), buf[:], usermem.IOOpts{}) + return 0, err + } + + return f.FileDescriptionDefaultImpl.Ioctl(ctx, uio, args) +} diff --git a/pkg/sentry/fsimpl/host/host_unsafe.go b/pkg/sentry/fsimpl/host/host_unsafe.go new file mode 100644 index 000000000..8a071076b --- /dev/null +++ b/pkg/sentry/fsimpl/host/host_unsafe.go @@ -0,0 +1,30 @@ +// 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 host + +import ( + "unsafe" + + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/abi/linux" +) + +func ioctlFionread(fd int) (uint32, error) { + var v uint32 + if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), linux.FIONREAD, uintptr(unsafe.Pointer(&v))); errno != 0 { + return 0, errno + } + return v, nil +} diff --git a/runsc/boot/filter/config.go b/runsc/boot/filter/config.go index 4a9d30c5f..66f9d5224 100644 --- a/runsc/boot/filter/config.go +++ b/runsc/boot/filter/config.go @@ -134,9 +134,15 @@ var allowedSyscalls = seccomp.SyscallRules{ }, unix.SYS_GETTID: {}, unix.SYS_GETTIMEOFDAY: {}, - // SYS_IOCTL is needed for terminal support, but we only allow - // setting/getting termios and winsize. unix.SYS_IOCTL: []seccomp.Rule{ + // These commands are needed for host FD. + { + seccomp.MatchAny{}, /* fd */ + seccomp.EqualTo(linux.FIONREAD), + seccomp.MatchAny{}, /* int* */ + }, + // These commands are needed for terminal support, but we only allow + // setting/getting termios and winsize. { seccomp.MatchAny{}, /* fd */ seccomp.EqualTo(linux.TCGETS), diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go index c5aa35623..ffb6167f5 100644 --- a/test/e2e/integration_test.go +++ b/test/e2e/integration_test.go @@ -612,6 +612,16 @@ func TestStickyDir(t *testing.T) { runIntegrationTest(t, nil, "./test_sticky") } +func TestHostFD(t *testing.T) { + if vfs2Used, err := dockerutil.UsingVFS2(); err != nil { + t.Fatalf("failed to read config for runtime %s: %v", dockerutil.Runtime(), err) + } else if !vfs2Used { + t.Skip("test fails on VFS1.") + } + + runIntegrationTest(t, nil, "./host_fd") +} + func runIntegrationTest(t *testing.T, capAdd []string, args ...string) { ctx := context.Background() d := dockerutil.MakeContainer(ctx, t)