diff --git a/pkg/sentry/fsimpl/iouringfs/iouringfs.go b/pkg/sentry/fsimpl/iouringfs/iouringfs.go index 735f78357..1d549212f 100644 --- a/pkg/sentry/fsimpl/iouringfs/iouringfs.go +++ b/pkg/sentry/fsimpl/iouringfs/iouringfs.go @@ -24,6 +24,7 @@ package iouringfs import ( "fmt" + "io" "sync" "gvisor.dev/gvisor/pkg/abi/linux" @@ -382,6 +383,11 @@ func (fd *FileDescription) ProcessSubmission(t *kernel.Task, sqe *linux.IOUringS // For the NOP operation, we don't do anything special. case linux.IORING_OP_READV: retValue, cqeErr = fd.handleReadv(t, sqe, flags) + if cqeErr == io.EOF { + // Don't raise EOF as errno, error translation will fail. Short + // reads aren't failures. + cqeErr = nil + } default: // Unsupported operation retValue = -int32(linuxerr.EINVAL.Errno()) } diff --git a/test/syscalls/linux/io_uring.cc b/test/syscalls/linux/io_uring.cc index 47bc4119e..54585c8f0 100644 --- a/test/syscalls/linux/io_uring.cc +++ b/test/syscalls/linux/io_uring.cc @@ -692,6 +692,58 @@ TEST(IOUringTest, SingleREADVTest) { io_uring->store_cq_head(cq_head + 1); } +// Tests that IORING_OP_READV handles EOF on an empty file correctly. +TEST(IOUringTest, ReadvEmptyFile) { + struct io_uring_params params; + std::unique_ptr io_uring = + ASSERT_NO_ERRNO_AND_VALUE(IOUring::InitIOUring(1, params)); + + uint32_t sq_head = io_uring->load_sq_head(); + + std::string file_name = NewTempAbsPath(); + ASSERT_NO_ERRNO(CreateWithContents(file_name, "", 0666)); + + FileDescriptor filefd = ASSERT_NO_ERRNO_AND_VALUE(Open(file_name, O_RDONLY)); + ASSERT_GE(filefd.get(), 0); + + unsigned *sq_array = io_uring->get_sq_array(); + struct io_uring_sqe *sqe = io_uring->get_sqes(); + + struct iovec iov; + iov.iov_len = 0; + void *buf; + ASSERT_THAT(posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ), SyscallSucceeds()); + iov.iov_base = buf; + + sqe->flags = 0; + sqe->fd = filefd.get(); + sqe->opcode = IORING_OP_READV; + sqe->addr = reinterpret_cast(&iov); + sqe->len = 1; + sqe->off = 0; + sqe->user_data = reinterpret_cast(&iov); + sq_array[0] = 0; + + uint32_t sq_tail = io_uring->load_sq_tail(); + io_uring->store_sq_tail(sq_tail + 1); + + int ret = io_uring->Enter(1, 1, 0, nullptr); + ASSERT_EQ(ret, 1); + + struct io_uring_cqe *cqe = io_uring->get_cqes(); + + sq_head = io_uring->load_sq_head(); + ASSERT_EQ(sq_head, 1); + + uint32_t cq_tail = io_uring->load_cq_tail(); + ASSERT_EQ(cq_tail, 1); + + ASSERT_EQ(cqe->res, 0); // 0 length read, EOF. + + uint32_t cq_head = io_uring->load_cq_head(); + io_uring->store_cq_head(cq_head + 1); +} + // Testing that io_uring_enter(2) successfully handles three READV operations // from three different files submitted through a single invocation. TEST(IOUringTest, ThreeREADVSingleEnterTest) {