[PATCH 0/2] fs: honor FOP_UNSIGNED_OFFSET in llseek and positional I/O

Stian Halseth posted 2 patches 1 week ago
fs/read_write.c | 38 +++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 19 deletions(-)
[PATCH 0/2] fs: honor FOP_UNSIGNED_OFFSET in llseek and positional I/O
Posted by Stian Halseth 1 week ago
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
Re: [PATCH 0/2] fs: honor FOP_UNSIGNED_OFFSET in llseek and positional I/O
Posted by Florian Weimer 1 week ago
* 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
Re: [PATCH 0/2] fs: honor FOP_UNSIGNED_OFFSET in llseek and positional I/O
Posted by Stian Halseth 1 week ago
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
> 
Re: [PATCH 0/2] fs: honor FOP_UNSIGNED_OFFSET in llseek and positional I/O
Posted by Stian Halseth 1 week ago
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
> >