diff --git a/fs/proc/base.c b/fs/proc/base.c index ffd54617c35478e92a9f6bef67013e16e6cd3183..00642e7fe99f65b7c1086018b9d4a875376fd850 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -903,18 +903,34 @@ static ssize_t mem_write(struct file *file, const char __user *buf, loff_t mem_lseek(struct file *file, loff_t offset, int orig) { + loff_t ret = 0; + + spin_lock(&file->f_lock); switch (orig) { - case 0: - file->f_pos = offset; - break; - case 1: - file->f_pos += offset; + case SEEK_CUR: + offset += file->f_pos; + __attribute__((__fallthrough__)); + case SEEK_SET: + /* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */ + if ((unsigned long long)offset >= -MAX_ERRNO) + ret = -EOVERFLOW; break; default: - return -EINVAL; + ret = -EINVAL; + } + + if (!ret) { + if (offset < 0 && !(unsigned_offsets(file))) { + ret = -EINVAL; + } else { + file->f_pos = offset; + ret = file->f_pos; + force_successful_syscall_return(); + } } - force_successful_syscall_return(); - return file->f_pos; + + spin_unlock(&file->f_lock); + return ret; } static int mem_release(struct inode *inode, struct file *file) diff --git a/fs/read_write.c b/fs/read_write.c index 4771701c896badcbc7dc2e3f1f528dc9026864ad..2f456d5a1df5be76902154302cf8973cced398e2 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -34,11 +34,6 @@ const struct file_operations generic_ro_fops = { EXPORT_SYMBOL(generic_ro_fops); -static inline bool unsigned_offsets(struct file *file) -{ - return file->f_mode & FMODE_UNSIGNED_OFFSET; -} - /** * vfs_setpos - update the file offset for lseek * @file: file structure in question diff --git a/include/linux/fs.h b/include/linux/fs.h index 4a40823c3c6784f19bc98911cd1557736eafba7a..15c5453596604af8340245b267dc780fdf9f1c96 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2929,6 +2929,10 @@ extern long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, extern void file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping); extern loff_t noop_llseek(struct file *file, loff_t offset, int whence); +static inline bool unsigned_offsets(struct file *file) +{ + return file->f_mode & FMODE_UNSIGNED_OFFSET; +} #define no_llseek NULL extern loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize); extern loff_t generic_file_llseek(struct file *file, loff_t offset, int whence);