Allow negative seek offsets in /dev/kmem x86-64 has negative kernel memory, but that was not reachable because the VFS rejected negative file offsets. /dev/kmem now sets a new FMODE_NEGATIVE flag that disables these checks. Other file types are not affected for compatibility. I remove redundant checks in sys_p{read,write} because vfs_{read,write} does them anyways. Index: linux/fs/read_write.c =================================================================== --- linux.orig/fs/read_write.c +++ linux/fs/read_write.c @@ -191,7 +191,9 @@ int rw_verify_area(int read_write, struc if (unlikely(count > file->f_maxcount)) goto Einval; pos = *ppos; - if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) + if (unlikely((pos < 0) && !(file->f_mode & FMODE_NEGATIVE))) + goto Einval; + if (((unsigned long long)pos + count) < pos) goto Einval; inode = file->f_dentry->d_inode; @@ -367,9 +369,6 @@ asmlinkage ssize_t sys_pread64(unsigned ssize_t ret = -EBADF; int fput_needed; - if (pos < 0) - return -EINVAL; - file = fget_light(fd, &fput_needed); if (file) { ret = -ESPIPE; @@ -388,9 +387,6 @@ asmlinkage ssize_t sys_pwrite64(unsigned ssize_t ret = -EBADF; int fput_needed; - if (pos < 0) - return -EINVAL; - file = fget_light(fd, &fput_needed); if (file) { ret = -ESPIPE; @@ -683,9 +679,11 @@ static ssize_t do_sendfile(int out_fd, i pos = *ppos; retval = -EINVAL; - if (unlikely(pos < 0)) + if (unlikely(pos < 0 && !(in_file->f_mode & FMODE_NEGATIVE))) + goto fput_out; + if (unlikely((unsigned long long)pos < pos + count)) goto fput_out; - if (unlikely(pos + count > max)) { + if (unlikely((unsigned long long)pos + count > max)) { retval = -EOVERFLOW; if (pos >= max) goto fput_out; Index: linux/drivers/char/mem.c =================================================================== --- linux.orig/drivers/char/mem.c +++ linux/drivers/char/mem.c @@ -749,7 +749,10 @@ static loff_t memory_lseek(struct file * static int open_port(struct inode * inode, struct file * filp) { - return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + filp->f_mode |= FMODE_NEGATIVE; + return 0; } #define zero_lseek null_lseek Index: linux/include/linux/fs.h =================================================================== --- linux.orig/include/linux/fs.h +++ linux/include/linux/fs.h @@ -63,6 +63,7 @@ extern int dir_notify_enable; #define FMODE_LSEEK 4 #define FMODE_PREAD 8 #define FMODE_PWRITE FMODE_PREAD /* These go hand in hand */ +#define FMODE_NEGATIVE 16 /* Allow negative offsets */ #define RW_MASK 1 #define RWA_MASK 2