fuse: check O_LARGEFILE before masking it out of the open flags - #13957
Open
jbbakeng wants to merge 1 commit into
Open
fuse: check O_LARGEFILE before masking it out of the open flags#13957jbbakeng wants to merge 1 commit into
jbbakeng wants to merge 1 commit into
Conversation
inode.Open() masked opts.Flags against a whitelist that does not include O_LARGEFILE on the line immediately before testing for it, so the EOVERFLOW guard was taken on every open of a file larger than MAX_NON_LFS, whatever flags the caller passed. As openat(2) sets O_LARGEFILE for all 64-bit callers, files over 2GiB on a FUSE mount could not be opened at all. Move the mask below the check. Assisted-by: Claude Code
EtiennePerot
requested changes
Aug 3, 2026
EtiennePerot
left a comment
Collaborator
There was a problem hiding this comment.
Thanks for writing a test, but this test looks rather complex for what is otherwise a trivial change. No need to keep it.
jbbakeng
force-pushed
the
jbbakeng/fuse-largefile-open
branch
from
August 3, 2026 18:30
46a54b5 to
3115757
Compare
Author
|
Dropped the test. :) |
EtiennePerot
approved these changes
Aug 3, 2026
copybara-service Bot
pushed a commit
that referenced
this pull request
Aug 3, 2026
Files larger than 2GiB on a FUSE mount cannot be opened inside the sandbox: every
`open()` fails with `EOVERFLOW` ("Value too large for defined data type"), no matter what
flags the caller passes.
In `pkg/sentry/fsimpl/fuse/inode.go`, `Open()` masks the open flags against a whitelist
that does not include `O_LARGEFILE` on the line immediately before testing for it:
```go
opts.Flags &= linux.O_ACCMODE | linux.O_CREAT | linux.O_EXCL | linux.O_TRUNC |
linux.O_DIRECTORY | linux.O_NOFOLLOW | linux.O_NONBLOCK | linux.O_NOCTTY |
linux.O_APPEND | linux.O_DIRECT
i.attrMu.Lock()
defer i.attrMu.Unlock()
if opts.Flags&linux.O_LARGEFILE == 0 && i.size.Load() > linux.MAX_NON_LFS {
return nil, linuxerr.EOVERFLOW
}
```
`O_LARGEFILE` is always clear by the time it is tested, so the guard is unconditional for
any file over `MAX_NON_LFS`. This is not an obscure path: `openat(2)` adds `O_LARGEFILE`
for all 64-bit callers (`pkg/sentry/syscalls/linux/sys_file.go`) and the VFS layer
preserves it (`pkg/sentry/vfs/vfs.go`), so the flag is set on essentially every open and
the check should essentially never fire. No other filesystem implementation has this
check, which is why only FUSE mounts are affected.
Present since 298b5f3 ("Refactor FUSE inode implementation.", 2023-02-01), so in every
release from `release-20230214.0` onwards.
Reproduced with rclone's FUSE mount under `runsc do`: reading a 3GiB file through the
mount fails at `open()`, a 1GiB file on the same mount succeeds, and the same 3GiB file
read outside the mount succeeds.
The fix moves the mask below the check. `TestFUSEOpenLargeFile` covers both directions —
a file over `MAX_NON_LFS` opens with `O_LARGEFILE` and still returns `EOVERFLOW` without
it — and fails on the unpatched `inode.go`.
Assisted-by: Claude Code
FUTURE_COPYBARA_INTEGRATE_REVIEW=#13957 from jbbakeng:jbbakeng/fuse-largefile-open 3115757
PiperOrigin-RevId: 958610773
copybara-service Bot
pushed a commit
that referenced
this pull request
Aug 3, 2026
Files larger than 2GiB on a FUSE mount cannot be opened inside the sandbox: every
`open()` fails with `EOVERFLOW` ("Value too large for defined data type"), no matter what
flags the caller passes.
In `pkg/sentry/fsimpl/fuse/inode.go`, `Open()` masks the open flags against a whitelist
that does not include `O_LARGEFILE` on the line immediately before testing for it:
```go
opts.Flags &= linux.O_ACCMODE | linux.O_CREAT | linux.O_EXCL | linux.O_TRUNC |
linux.O_DIRECTORY | linux.O_NOFOLLOW | linux.O_NONBLOCK | linux.O_NOCTTY |
linux.O_APPEND | linux.O_DIRECT
i.attrMu.Lock()
defer i.attrMu.Unlock()
if opts.Flags&linux.O_LARGEFILE == 0 && i.size.Load() > linux.MAX_NON_LFS {
return nil, linuxerr.EOVERFLOW
}
```
`O_LARGEFILE` is always clear by the time it is tested, so the guard is unconditional for
any file over `MAX_NON_LFS`. This is not an obscure path: `openat(2)` adds `O_LARGEFILE`
for all 64-bit callers (`pkg/sentry/syscalls/linux/sys_file.go`) and the VFS layer
preserves it (`pkg/sentry/vfs/vfs.go`), so the flag is set on essentially every open and
the check should essentially never fire. No other filesystem implementation has this
check, which is why only FUSE mounts are affected.
Present since 298b5f3 ("Refactor FUSE inode implementation.", 2023-02-01), so in every
release from `release-20230214.0` onwards.
Reproduced with rclone's FUSE mount under `runsc do`: reading a 3GiB file through the
mount fails at `open()`, a 1GiB file on the same mount succeeds, and the same 3GiB file
read outside the mount succeeds.
The fix moves the mask below the check. `TestFUSEOpenLargeFile` covers both directions —
a file over `MAX_NON_LFS` opens with `O_LARGEFILE` and still returns `EOVERFLOW` without
it — and fails on the unpatched `inode.go`.
Assisted-by: Claude Code
FUTURE_COPYBARA_INTEGRATE_REVIEW=#13957 from jbbakeng:jbbakeng/fuse-largefile-open 3115757
PiperOrigin-RevId: 958610773
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Files larger than 2GiB on a FUSE mount cannot be opened inside the sandbox: every
open()fails withEOVERFLOW("Value too large for defined data type"), no matter whatflags the caller passes.
In
pkg/sentry/fsimpl/fuse/inode.go,Open()masks the open flags against a whitelistthat does not include
O_LARGEFILEon the line immediately before testing for it:O_LARGEFILEis always clear by the time it is tested, so the guard is unconditional forany file over
MAX_NON_LFS. This is not an obscure path:openat(2)addsO_LARGEFILEfor all 64-bit callers (
pkg/sentry/syscalls/linux/sys_file.go) and the VFS layerpreserves it (
pkg/sentry/vfs/vfs.go), so the flag is set on essentially every open andthe check should essentially never fire. No other filesystem implementation has this
check, which is why only FUSE mounts are affected.
Present since 298b5f3 ("Refactor FUSE inode implementation.", 2023-02-01), so in every
release from
release-20230214.0onwards.Reproduced with rclone's FUSE mount under
runsc do: reading a 3GiB file through themount fails at
open(), a 1GiB file on the same mount succeeds, and the same 3GiB fileread outside the mount succeeds.
The fix moves the mask below the check.
TestFUSEOpenLargeFilecovers both directions —a file over
MAX_NON_LFSopens withO_LARGEFILEand still returnsEOVERFLOWwithoutit — and fails on the unpatched
inode.go.Assisted-by: Claude Code