diff --git a/pkg/sentry/syscalls/linux/sys_getdents.go b/pkg/sentry/syscalls/linux/sys_getdents.go index 5766327342..99e131dde7 100644 --- a/pkg/sentry/syscalls/linux/sys_getdents.go +++ b/pkg/sentry/syscalls/linux/sys_getdents.go @@ -43,7 +43,8 @@ const DirentStructBytesWithoutName = 8 + 8 + 2 + 1 + 1 func getdents(t *kernel.Task, args arch.SyscallArguments, isGetdents64 bool) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() addr := args[1].Pointer() - size := int(args[2].Uint()) + size := args[2].Int() + if size < DirentStructBytesWithoutName { return 0, nil, linuxerr.EINVAL } @@ -64,7 +65,7 @@ func getdents(t *kernel.Task, args arch.SyscallArguments, isGetdents64 bool) (ui return 0, nil, err } - cb := getGetdentsCallback(t, int(allowedSize), size, isGetdents64) + cb := getGetdentsCallback(t, int(allowedSize), int(size), isGetdents64) err = file.IterDirents(t, cb) n, _ := t.CopyOutBytes(addr, cb.buf[:cb.copied]) diff --git a/test/syscalls/linux/getdents.cc b/test/syscalls/linux/getdents.cc index e276fc136f..64fc2ad013 100644 --- a/test/syscalls/linux/getdents.cc +++ b/test/syscalls/linux/getdents.cc @@ -503,6 +503,22 @@ TYPED_TEST(GetdentsTest, ZeroLengthOutBuffer) { SyscallFailsWithErrno(EINVAL)); } +// Tests that getdents() fails when called with too large size. +TYPED_TEST(GetdentsTest, TooLargeSize) { + auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY)); + + typename TestFixture::DirentBufferType dirents(100); + // Try one over the limit. + EXPECT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), + static_cast(INT32_MAX) + 1), + SyscallFailsWithErrno(EINVAL)); + // Try way over the limit. + EXPECT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), + static_cast(-1)), + SyscallFailsWithErrno(EINVAL)); +} + // Some tests using the glibc readdir interface. TEST(ReaddirTest, OpenDir) { DIR* dev;