diff --git a/images/basic/integrationtest/host_fd.c b/images/basic/integrationtest/host_fd.c index 253cbe041..a6537eecd 100644 --- a/images/basic/integrationtest/host_fd.c +++ b/images/basic/integrationtest/host_fd.c @@ -14,12 +14,17 @@ #include #include +#include #include +#include #include #include #include #include +// Flag to indicate that TTY is being used in the container. +static int tty = 0; + // Tests that FIONREAD is supported with host FD. void testFionread() { int size = 0; @@ -34,6 +39,10 @@ void testFionread() { // Docker maps stdin to /dev/null which doesn't support epoll. Check that error // is correctly propagated. void testEpoll() { + if (tty) { + // stdin is not /dev/null when TTY is used for the container. + return; + } int fd = epoll_create(1); if (fd < 0) { err(1, "epoll_create"); @@ -67,6 +76,18 @@ void testSelect() { } int main(int argc, char** argv) { + int opt; + while ((opt = getopt(argc, argv, "t")) != -1) { + switch (opt) { + case 't': + tty = 1; + break; + default: + fprintf(stderr, "Usage: %s [-t]\n", argv[0]); + return 1; + } + } + testFionread(); testEpoll(); testSelect(); diff --git a/pkg/sentry/fsimpl/host/tty.go b/pkg/sentry/fsimpl/host/tty.go index 63fb30c5d..5f490f467 100644 --- a/pkg/sentry/fsimpl/host/tty.go +++ b/pkg/sentry/fsimpl/host/tty.go @@ -18,6 +18,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/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/kernel" @@ -154,6 +155,17 @@ func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uin fd := t.inode.hostFD ioctl := args[1].Uint64() switch ioctl { + case linux.FIONREAD: + v, err := ioctlFionread(fd) + if err != nil { + return 0, err + } + + var buf [4]byte + hostarch.ByteOrder.PutUint32(buf[:], v) + _, err = io.CopyOut(ctx, args[2].Pointer(), buf[:], usermem.IOOpts{}) + return 0, err + case linux.TCGETS: termios, err := ioctlGetTermios(fd) if err != nil { diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go index 60b94bbf0..504c6ba59 100644 --- a/test/e2e/integration_test.go +++ b/test/e2e/integration_test.go @@ -533,7 +533,32 @@ func TestStickyDir(t *testing.T) { } func TestHostFD(t *testing.T) { - runIntegrationTest(t, nil, "./host_fd") + t.Run("regular", func(t *testing.T) { + runIntegrationTest(t, nil, "./host_fd") + }) + t.Run("tty", func(t *testing.T) { + ctx := context.Background() + d := dockerutil.MakeContainer(ctx, t) + defer d.CleanUp(ctx) + + // Start the container with an attached PTY. + p, err := d.SpawnProcess(ctx, dockerutil.RunOpts{ + Image: "basic/integrationtest", + WorkDir: "/root", + }, "./host_fd", "-t") + if err != nil { + t.Fatalf("docker run failed: %v", err) + } + + if err := d.Wait(ctx); err != nil { + t.Fatalf("Wait failed: %v", err) + } + if out, err := p.Logs(); err != nil { + t.Fatal(err) + } else if len(out) > 0 { + t.Errorf("test failed:\n%s", out) + } + }) } func runIntegrationTest(t *testing.T, capAdd []string, args ...string) {