diff options
author | KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> | 2010-10-01 17:20:22 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-10-25 21:18:21 -0400 |
commit | 4a3956c790290efeb647bbb0c3a90476bb57800e (patch) | |
tree | 18abb41325273d23ae271e49627f01054c5baeb6 /fs | |
parent | ba10f486658c0ca1bc84c936f6a996e40d071453 (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/proc/base.c | 2 | ||||
-rw-r--r-- | fs/read_write.c | 28 |
2 files changed, 26 insertions, 4 deletions
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( |