diff options
-rw-r--r-- | drivers/char/mem.c | 4 | ||||
-rw-r--r-- | fs/proc/base.c | 2 | ||||
-rw-r--r-- | fs/read_write.c | 28 | ||||
-rw-r--r-- | include/linux/fs.h | 3 |
4 files changed, 33 insertions, 4 deletions
diff --git a/drivers/char/mem.c b/drivers/char/mem.c index e985b1c2730e..1256454b2d43 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c | |||
@@ -876,6 +876,10 @@ static int memory_open(struct inode *inode, struct file *filp) | |||
876 | if (dev->dev_info) | 876 | if (dev->dev_info) |
877 | filp->f_mapping->backing_dev_info = dev->dev_info; | 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 | if (dev->fops->open) | 883 | if (dev->fops->open) |
880 | return dev->fops->open(inode, filp); | 884 | return dev->fops->open(inode, filp); |
881 | 885 | ||
diff --git a/fs/proc/base.c b/fs/proc/base.c index dc5d5f51f3fe..fb2a5abd4e4f 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -771,6 +771,8 @@ static const struct file_operations proc_single_file_operations = { | |||
771 | static int mem_open(struct inode* inode, struct file* file) | 771 | static int mem_open(struct inode* inode, struct file* file) |
772 | { | 772 | { |
773 | file->private_data = (void*)((long)current->self_exec_id); | 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 | return 0; | 776 | return 0; |
775 | } | 777 | } |
776 | 778 | ||
diff --git a/fs/read_write.c b/fs/read_write.c index e757ef26e4ce..9cd9d148105d 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -31,6 +31,20 @@ const struct file_operations generic_ro_fops = { | |||
31 | 31 | ||
32 | EXPORT_SYMBOL(generic_ro_fops); | 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 | * generic_file_llseek_unlocked - lockless generic llseek implementation | 49 | * generic_file_llseek_unlocked - lockless generic llseek implementation |
36 | * @file: file structure to seek on | 50 | * @file: file structure to seek on |
@@ -62,7 +76,9 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) | |||
62 | break; | 76 | break; |
63 | } | 77 | } |
64 | 78 | ||
65 | if (offset < 0 || offset > inode->i_sb->s_maxbytes) | 79 | if (offset < 0 && __negative_fpos_check(file, offset, 0)) |
80 | return -EINVAL; | ||
81 | if (offset > inode->i_sb->s_maxbytes) | ||
66 | return -EINVAL; | 82 | return -EINVAL; |
67 | 83 | ||
68 | /* Special lock needed here? */ | 84 | /* Special lock needed here? */ |
@@ -137,7 +153,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) | |||
137 | offset += file->f_pos; | 153 | offset += file->f_pos; |
138 | } | 154 | } |
139 | retval = -EINVAL; | 155 | retval = -EINVAL; |
140 | if (offset >= 0) { | 156 | if (offset >= 0 || !__negative_fpos_check(file, offset, 0)) { |
141 | if (offset != file->f_pos) { | 157 | if (offset != file->f_pos) { |
142 | file->f_pos = offset; | 158 | file->f_pos = offset; |
143 | file->f_version = 0; | 159 | file->f_version = 0; |
@@ -221,6 +237,7 @@ bad: | |||
221 | } | 237 | } |
222 | #endif | 238 | #endif |
223 | 239 | ||
240 | |||
224 | /* | 241 | /* |
225 | * rw_verify_area doesn't like huge counts. We limit | 242 | * rw_verify_area doesn't like huge counts. We limit |
226 | * them to something that fits in "int" so that others | 243 | * them to something that fits in "int" so that others |
@@ -238,8 +255,11 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count | |||
238 | if (unlikely((ssize_t) count < 0)) | 255 | if (unlikely((ssize_t) count < 0)) |
239 | return retval; | 256 | return retval; |
240 | pos = *ppos; | 257 | pos = *ppos; |
241 | if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) | 258 | if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) { |
242 | return retval; | 259 | retval = __negative_fpos_check(file, pos, count); |
260 | if (retval) | ||
261 | return retval; | ||
262 | } | ||
243 | 263 | ||
244 | if (unlikely(inode->i_flock && mandatory_lock(inode))) { | 264 | if (unlikely(inode->i_flock && mandatory_lock(inode))) { |
245 | retval = locks_mandatory_area( | 265 | retval = locks_mandatory_area( |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 0cd6821013a0..7fc126df1c42 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -92,6 +92,9 @@ struct inodes_stat_t { | |||
92 | /* Expect random access pattern */ | 92 | /* Expect random access pattern */ |
93 | #define FMODE_RANDOM ((__force fmode_t)0x1000) | 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 | /* File was opened by fanotify and shouldn't generate fanotify events */ | 98 | /* File was opened by fanotify and shouldn't generate fanotify events */ |
96 | #define FMODE_NONOTIFY ((__force fmode_t)0x1000000) | 99 | #define FMODE_NONOTIFY ((__force fmode_t)0x1000000) |
97 | 100 | ||