Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vfs: introduce FMODE_UNSIGNED_OFFSET for allowing negative f_pos

Now, rw_verify_area() checsk f_pos is negative or not. And if negative,
returns -EINVAL.

But, some special files as /dev/(k)mem and /proc/<pid>/mem etc.. has
negative offsets. And we can't do any access via read/write to the
file(device).

So introduce FMODE_UNSIGNED_OFFSET to allow negative file offsets.

Signed-off-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

authored by

KAMEZAWA Hiroyuki and committed by
Al Viro
4a3956c7 ba10f486

+33 -4
+4
drivers/char/mem.c
··· 876 876 if (dev->dev_info) 877 877 filp->f_mapping->backing_dev_info = dev->dev_info; 878 878 879 + /* Is /dev/mem or /dev/kmem ? */ 880 + if (dev->dev_info == &directly_mappable_cdev_bdi) 881 + filp->f_mode |= FMODE_UNSIGNED_OFFSET; 882 + 879 883 if (dev->fops->open) 880 884 return dev->fops->open(inode, filp); 881 885
+2
fs/proc/base.c
··· 771 771 static int mem_open(struct inode* inode, struct file* file) 772 772 { 773 773 file->private_data = (void*)((long)current->self_exec_id); 774 + /* OK to pass negative loff_t, we can catch out-of-range */ 775 + file->f_mode |= FMODE_UNSIGNED_OFFSET; 774 776 return 0; 775 777 } 776 778
+24 -4
fs/read_write.c
··· 31 31 32 32 EXPORT_SYMBOL(generic_ro_fops); 33 33 34 + static int 35 + __negative_fpos_check(struct file *file, loff_t pos, size_t count) 36 + { 37 + /* 38 + * pos or pos+count is negative here, check overflow. 39 + * too big "count" will be caught in rw_verify_area(). 40 + */ 41 + if ((pos < 0) && (pos + count < pos)) 42 + return -EOVERFLOW; 43 + if (file->f_mode & FMODE_UNSIGNED_OFFSET) 44 + return 0; 45 + return -EINVAL; 46 + } 47 + 34 48 /** 35 49 * generic_file_llseek_unlocked - lockless generic llseek implementation 36 50 * @file: file structure to seek on ··· 76 62 break; 77 63 } 78 64 79 - if (offset < 0 || offset > inode->i_sb->s_maxbytes) 65 + if (offset < 0 && __negative_fpos_check(file, offset, 0)) 66 + return -EINVAL; 67 + if (offset > inode->i_sb->s_maxbytes) 80 68 return -EINVAL; 81 69 82 70 /* Special lock needed here? */ ··· 153 137 offset += file->f_pos; 154 138 } 155 139 retval = -EINVAL; 156 - if (offset >= 0) { 140 + if (offset >= 0 || !__negative_fpos_check(file, offset, 0)) { 157 141 if (offset != file->f_pos) { 158 142 file->f_pos = offset; 159 143 file->f_version = 0; ··· 237 221 } 238 222 #endif 239 223 224 + 240 225 /* 241 226 * rw_verify_area doesn't like huge counts. We limit 242 227 * them to something that fits in "int" so that others ··· 255 238 if (unlikely((ssize_t) count < 0)) 256 239 return retval; 257 240 pos = *ppos; 258 - if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) 259 - return retval; 241 + if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) { 242 + retval = __negative_fpos_check(file, pos, count); 243 + if (retval) 244 + return retval; 245 + } 260 246 261 247 if (unlikely(inode->i_flock && mandatory_lock(inode))) { 262 248 retval = locks_mandatory_area(
+3
include/linux/fs.h
··· 92 92 /* Expect random access pattern */ 93 93 #define FMODE_RANDOM ((__force fmode_t)0x1000) 94 94 95 + /* File is huge (eg. /dev/kmem): treat loff_t as unsigned */ 96 + #define FMODE_UNSIGNED_OFFSET ((__force fmode_t)0x2000) 97 + 95 98 /* File was opened by fanotify and shouldn't generate fanotify events */ 96 99 #define FMODE_NONOTIFY ((__force fmode_t)0x1000000) 97 100