fs/read_write.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-)
Two generic VFS bugs found while fixing pldd(1) on sparc64, where
userspace is mapped above 2^63 so file positions on /proc/PID/mem have
the top bit set.
FOP_UNSIGNED_OFFSET declares a file's offsets unsigned, and vfs_llseek(),
rw_verify_area() and the mmap path honor it. Two sets of syscall
wrappers do not:
1. sys_llseek() takes a negative result from vfs_llseek() for an error
and returns it truncated to int without filling *result.
2. pread64/pwrite64/preadv/pwritev reject a negative position before
looking up the file.
Both patches only change behavior for a file with FOP_UNSIGNED_OFFSET
and a position with the top bit set. Today that is /proc/PID/mem,
/dev/mem, the sparc ADI driver and the DRM/accel device files; for all
of them lseek() followed by read() at that position already works, so
the wrappers now match it.
Tested on an UltraSPARC T4: pread(), preadv(), pwrite(), pwritev() and
lseek() on /proc/PID/mem at positions above 2^63 return the correct data
and offset; a negative position on a regular file or a pipe still fails
with EINVAL and position 0 on a pipe with ESPIPE, as before; and the
stock pldd(1) works again. On other architectures the change is a no-op
for every file without FOP_UNSIGNED_OFFSET, and userspace addresses
never set the top bit, so the new paths are only reachable by passing
a bogus position to /proc/PID/mem or /dev/mem, which then fails in the
driver instead of the wrapper.
The glibc side is https://sourceware.org/bugzilla/show_bug.cgi?id=34641;
Stian Halseth (2):
fs: fix llseek() result for files with unsigned offsets
fs: allow positional I/O on files with unsigned offsets
fs/read_write.c | 38 +++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 19 deletions(-)
--
2.43.0
* Stian Halseth: > Tested on an UltraSPARC T4: pread(), preadv(), pwrite(), pwritev() and > lseek() on /proc/PID/mem at positions above 2^63 return the correct data > and offset; a negative position on a regular file or a pipe still fails > with EINVAL and position 0 on a pipe with ESPIPE, as before; and the > stock pldd(1) works again. On other architectures the change is a no-op > for every file without FOP_UNSIGNED_OFFSET, and userspace addresses > never set the top bit, so the new paths are only reachable by passing > a bogus position to /proc/PID/mem or /dev/mem, which then fails in the > driver instead of the wrapper. For lseek, aren't some file offsets (the top 4095 bytes or so just before 2**64) ambiguous as error indicators? You would have to use _llseek when accessing /proc/PID/mem. Thanks, Florian
Hi, On Thu, 2026-09-17 at 18:01 +0200, Florian Weimer wrote: > * Stian Halseth: > > > Tested on an UltraSPARC T4: pread(), preadv(), pwrite(), pwritev() > > and > > lseek() on /proc/PID/mem at positions above 2^63 return the correct > > data > > and offset; a negative position on a regular file or a pipe still > > fails > > with EINVAL and position 0 on a pipe with ESPIPE, as before; and > > the > > stock pldd(1) works again. On other architectures the change is a > > no-op > > for every file without FOP_UNSIGNED_OFFSET, and userspace addresses > > never set the top bit, so the new paths are only reachable by > > passing > > a bogus position to /proc/PID/mem or /dev/mem, which then fails in > > the > > driver instead of the wrapper. > > For lseek, aren't some file offsets (the top 4095 bytes or so just > before 2**64) ambiguous as error indicators? You would have to use > _llseek when accessing /proc/PID/mem. Yes, for lseek, anything in the top MAX_ERRNO bytes below 2^64 cannot be separated from -errno. For that reason _llseek is the interface that can be exact. Patch 1 ensures that _llseek works for everything except that 4095-byte window. The patch does _not_ change the fact that _llseek can't return an offset in said window. I think fixing the window requires llseek to report errors separately from the offset. A bigger change, I would need some feedback before attempting to implement that. That being said, I _think_ the window is unreachable in practice, and that no architecture maps user memory there. I could add a sentense to patch 1 noting the limitation, or look at the larger change if the VFS maintainers think it's worth it. On the glibc side: 64-bit glibc uses lseek except on sparc64 and ppc64, which use _llseek, so those two get the exact result with patch 1, and the rest carry the lseek ambiguity for that top window regardless. Best regards, Stian > > Thanks, > Florian >
Hi again, a small clarification to my last reply. On Thu, 2026-09-17 at 18:45 +0200, Stian Halseth wrote: > Hi, > > On Thu, 2026-09-17 at 18:01 +0200, Florian Weimer wrote: > > * Stian Halseth: > > > > > Tested on an UltraSPARC T4: pread(), preadv(), pwrite(), > > > pwritev() > > > and > > > lseek() on /proc/PID/mem at positions above 2^63 return the > > > correct > > > data > > > and offset; a negative position on a regular file or a pipe still > > > fails > > > with EINVAL and position 0 on a pipe with ESPIPE, as before; and > > > the > > > stock pldd(1) works again. On other architectures the change is > > > a > > > no-op > > > for every file without FOP_UNSIGNED_OFFSET, and userspace > > > addresses > > > never set the top bit, so the new paths are only reachable by > > > passing > > > a bogus position to /proc/PID/mem or /dev/mem, which then fails > > > in > > > the > > > driver instead of the wrapper. > > > > For lseek, aren't some file offsets (the top 4095 bytes or so just > > before 2**64) ambiguous as error indicators? You would have to use > > _llseek when accessing /proc/PID/mem. > > Yes, for lseek, anything in the top MAX_ERRNO bytes below 2^64 cannot > be separated from -errno. > > For that reason _llseek is the interface that can be exact. > > Patch 1 ensures that _llseek works for everything except that 4095- > byte > window. The patch does _not_ change the fact that _llseek can't > return > an offset in said window. > > I think fixing the window requires llseek to report errors separately > from the offset. A bigger change, I would need some feedback before > attempting to implement that. > > That being said, I _think_ the window is unreachable in practice, and > that no architecture maps user memory there. > > I could add a sentense to patch 1 noting the limitation, or look at > the > larger change if the VFS maintainers think it's worth it. > > On the glibc side: 64-bit glibc uses lseek except on sparc64 and > ppc64, > which use _llseek, so those two get the exact result with patch 1, > and the rest carry the lseek ambiguity for that top window > regardless. Just to clarify: With patch 1, _llseek for ppc64 and sparc64 behaves just like lseek for the other arches, correct for every offset outside of that top window. > > Best regards, > Stian > > > > > Thanks, > > Florian > >
© 2016 - 2026 Red Hat, Inc.