Skip to content

Commit

Permalink
Fix validation of close_range last fd argument.
Browse files Browse the repository at this point in the history
The `last` fd argument can be up to max uint32, and some applications call it
with this maximum:
https://github.com/GNOME/glib/blob/26bc1d08ec574b387ff4bcd919a020a586727bbf/glib/glib-unix.c#L890

PiperOrigin-RevId: 718526878
  • Loading branch information
nlacasse authored and gvisor-bot committed Jan 22, 2025
1 parent ddfbe04 commit c238e15
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 4 deletions.
12 changes: 8 additions & 4 deletions pkg/sentry/syscalls/linux/sys_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
package linux

import (
"math"

"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fspath"
Expand Down Expand Up @@ -455,14 +453,20 @@ func CloseRange(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uint
last := args[1].Uint()
flags := args[2].Uint()

if (first > last) || (last > math.MaxInt32) {
if first > last {
return 0, nil, linuxerr.EINVAL
}

if (flags & ^(linux.CLOSE_RANGE_CLOEXEC | linux.CLOSE_RANGE_UNSHARE)) != 0 {
return 0, nil, linuxerr.EINVAL
}

// close_range allows fd arguments to be up to MaxUint32, but only fds
// up to kernel.MaxFdLimit are valid, so cap it here.
if last > uint32(kernel.MaxFdLimit) {
last = uint32(kernel.MaxFdLimit)
}

cloexec := flags & linux.CLOSE_RANGE_CLOEXEC
unshare := flags & linux.CLOSE_RANGE_UNSHARE

Expand All @@ -474,7 +478,7 @@ func CloseRange(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uint
if cloexec == 0 && int32(last) >= t.FDTable().GetLastFd() {
t.UnshareFdTable(int32(first))
} else {
t.UnshareFdTable(math.MaxInt32)
t.UnshareFdTable(kernel.MaxFdLimit)
}
}

Expand Down
17 changes: 17 additions & 0 deletions test/syscalls/linux/close_range.cc
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,23 @@ TEST_F(CloseRangeTest, RangeFirstGreaterThanLast) {
SyscallFailsWithErrno(EINVAL));
}

// Test that calling with maximum last argument succeeds and closes all fds in
// the range.
TEST_F(CloseRangeTest, RangeMaxLast) {
SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
int num_files_in_range = 10;
unsigned int flags = 0;

CreateFiles(num_files_in_range);
OpenFilesRdwr();

EXPECT_THAT(close_range(fds_[0], -1, flags), SyscallSucceeds());
for (int fd : fds_) {
auto ret = ReadAllFd(fd);
EXPECT_THAT(ret, PosixErrorIs(EBADF));
}
}

// Test that calling with invalid flags does not succeed.
TEST_F(CloseRangeTest, InvalidFlags) {
SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
Expand Down

0 comments on commit c238e15

Please sign in to comment.