Fix validation of close_range last fd argument.

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
This commit is contained in:
Nicolas Lacasse
2025-01-22 14:25:24 -08:00
committed by gVisor bot
parent ddfbe0457b
commit c238e15234
2 changed files with 25 additions and 4 deletions
+8 -4
View File
@@ -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"
@@ -455,7 +453,7 @@ 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
}
@@ -463,6 +461,12 @@ func CloseRange(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uint
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
@@ -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)
}
}
+17
View File
@@ -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);