Merge patch series "fs: honor FOP_UNSIGNED_OFFSET in llseek and positional I/O"

Stian Halseth <stian@itx.no> says:

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;

* patches from https://patch.msgid.link/20260917151614.376867-1-stian@itx.no:
  fs: allow positional I/O on files with unsigned offsets
  fs: fix llseek() result for files with unsigned offsets

Link: https://patch.msgid.link/20260917151614.376867-1-stian@itx.no
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-10-21 12:36:52 +02:00
commit a7e80c8d16
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -441,7 +441,7 @@ SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high,
whence);
retval = (int)offset;
if (offset >= 0) {
if (offset >= 0 || (unsigned_offsets(fd_file(f)) && offset < -MAX_ERRNO)) {
retval = -EFAULT;
if (!copy_to_user(result, &offset, sizeof(offset)))
retval = 0;
@ -753,13 +753,13 @@ SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
ssize_t ksys_pread64(unsigned int fd, char __user *buf, size_t count,
loff_t pos)
{
if (pos < 0)
return -EINVAL;
CLASS(fd, f)(fd);
if (fd_empty(f))
return -EBADF;
if (pos < 0 && !unsigned_offsets(fd_file(f)))
return -EINVAL;
if (fd_file(f)->f_mode & FMODE_PREAD)
return vfs_read(fd_file(f), buf, count, &pos);
@ -783,13 +783,13 @@ COMPAT_SYSCALL_DEFINE5(pread64, unsigned int, fd, char __user *, buf,
ssize_t ksys_pwrite64(unsigned int fd, const char __user *buf,
size_t count, loff_t pos)
{
if (pos < 0)
return -EINVAL;
CLASS(fd, f)(fd);
if (fd_empty(f))
return -EBADF;
if (pos < 0 && !unsigned_offsets(fd_file(f)))
return -EINVAL;
if (fd_file(f)->f_mode & FMODE_PWRITE)
return vfs_write(fd_file(f), buf, count, &pos);
@ -1123,14 +1123,14 @@ static ssize_t do_preadv(unsigned long fd, const struct iovec __user *vec,
{
ssize_t ret = -EBADF;
if (pos < 0)
return -EINVAL;
CLASS(fd, f)(fd);
if (!fd_empty(f)) {
ret = -ESPIPE;
if (fd_file(f)->f_mode & FMODE_PREAD)
ret = vfs_readv(fd_file(f), vec, vlen, &pos, flags);
ret = -EINVAL;
if (pos >= 0 || unsigned_offsets(fd_file(f))) {
ret = -ESPIPE;
if (fd_file(f)->f_mode & FMODE_PREAD)
ret = vfs_readv(fd_file(f), vec, vlen, &pos, flags);
}
}
if (ret > 0)
@ -1144,14 +1144,14 @@ static ssize_t do_pwritev(unsigned long fd, const struct iovec __user *vec,
{
ssize_t ret = -EBADF;
if (pos < 0)
return -EINVAL;
CLASS(fd, f)(fd);
if (!fd_empty(f)) {
ret = -ESPIPE;
if (fd_file(f)->f_mode & FMODE_PWRITE)
ret = vfs_writev(fd_file(f), vec, vlen, &pos, flags);
ret = -EINVAL;
if (pos >= 0 || unsigned_offsets(fd_file(f))) {
ret = -ESPIPE;
if (fd_file(f)->f_mode & FMODE_PWRITE)
ret = vfs_writev(fd_file(f), vec, vlen, &pos, flags);
}
}
if (ret > 0)