diff options
author | Akira Fujita <a-fujita@rs.jp.nec.com> | 2009-09-16 14:28:22 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2009-09-16 14:28:22 -0400 |
commit | 70d5d3dcea47c16058d2b093c29e07fdf61b56ad (patch) | |
tree | 82ff7d71973f259d8197bbc7c05461e978324c02 /fs | |
parent | 5f3481e9a80c240f169b36ea886e2325b9aeb745 (diff) |
ext4: Fix wrong comparisons in mext_check_arguments()
The mext_check_arguments() function in move_extents.c has wrong
comparisons. orig_start which is passed from user-space is block
unit, but i_size of inode is byte unit, therefore the checks do not
work fine. This mis-check leads to the overflow of 'len' and then
hits BUG_ON() in ext4_move_extents(). The patch fixes this issue.
Signed-off-by: Akira Fujita <a-fujita@rs.jp.nec.com>
Reviewed-by: Greg Freemyer <greg.freemyer@gmail.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/move_extent.c | 46 |
1 files changed, 27 insertions, 19 deletions
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 5821e0bee917..c593eb2b193a 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c | |||
@@ -898,6 +898,10 @@ mext_check_arguments(struct inode *orig_inode, | |||
898 | struct inode *donor_inode, __u64 orig_start, | 898 | struct inode *donor_inode, __u64 orig_start, |
899 | __u64 donor_start, __u64 *len, __u64 moved_len) | 899 | __u64 donor_start, __u64 *len, __u64 moved_len) |
900 | { | 900 | { |
901 | ext4_lblk_t orig_blocks, donor_blocks; | ||
902 | unsigned int blkbits = orig_inode->i_blkbits; | ||
903 | unsigned int blocksize = 1 << blkbits; | ||
904 | |||
901 | /* Regular file check */ | 905 | /* Regular file check */ |
902 | if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) { | 906 | if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) { |
903 | ext4_debug("ext4 move extent: The argument files should be " | 907 | ext4_debug("ext4 move extent: The argument files should be " |
@@ -972,43 +976,47 @@ mext_check_arguments(struct inode *orig_inode, | |||
972 | } | 976 | } |
973 | 977 | ||
974 | if (orig_inode->i_size > donor_inode->i_size) { | 978 | if (orig_inode->i_size > donor_inode->i_size) { |
975 | if (orig_start >= donor_inode->i_size) { | 979 | donor_blocks = (donor_inode->i_size + blocksize - 1) >> blkbits; |
980 | /* TODO: eliminate this artificial restriction */ | ||
981 | if (orig_start >= donor_blocks) { | ||
976 | ext4_debug("ext4 move extent: orig start offset " | 982 | ext4_debug("ext4 move extent: orig start offset " |
977 | "[%llu] should be less than donor file size " | 983 | "[%llu] should be less than donor file blocks " |
978 | "[%lld] [ino:orig %lu, donor_inode %lu]\n", | 984 | "[%u] [ino:orig %lu, donor %lu]\n", |
979 | orig_start, donor_inode->i_size, | 985 | orig_start, donor_blocks, |
980 | orig_inode->i_ino, donor_inode->i_ino); | 986 | orig_inode->i_ino, donor_inode->i_ino); |
981 | return -EINVAL; | 987 | return -EINVAL; |
982 | } | 988 | } |
983 | 989 | ||
984 | if (orig_start + *len > donor_inode->i_size) { | 990 | /* TODO: eliminate this artificial restriction */ |
991 | if (orig_start + *len > donor_blocks) { | ||
985 | ext4_debug("ext4 move extent: End offset [%llu] should " | 992 | ext4_debug("ext4 move extent: End offset [%llu] should " |
986 | "be less than donor file size [%lld]." | 993 | "be less than donor file blocks [%u]." |
987 | "So adjust length from %llu to %lld " | 994 | "So adjust length from %llu to %llu " |
988 | "[ino:orig %lu, donor %lu]\n", | 995 | "[ino:orig %lu, donor %lu]\n", |
989 | orig_start + *len, donor_inode->i_size, | 996 | orig_start + *len, donor_blocks, |
990 | *len, donor_inode->i_size - orig_start, | 997 | *len, donor_blocks - orig_start, |
991 | orig_inode->i_ino, donor_inode->i_ino); | 998 | orig_inode->i_ino, donor_inode->i_ino); |
992 | *len = donor_inode->i_size - orig_start; | 999 | *len = donor_blocks - orig_start; |
993 | } | 1000 | } |
994 | } else { | 1001 | } else { |
995 | if (orig_start >= orig_inode->i_size) { | 1002 | orig_blocks = (orig_inode->i_size + blocksize - 1) >> blkbits; |
1003 | if (orig_start >= orig_blocks) { | ||
996 | ext4_debug("ext4 move extent: start offset [%llu] " | 1004 | ext4_debug("ext4 move extent: start offset [%llu] " |
997 | "should be less than original file size " | 1005 | "should be less than original file blocks " |
998 | "[%lld] [inode:orig %lu, donor %lu]\n", | 1006 | "[%u] [ino:orig %lu, donor %lu]\n", |
999 | orig_start, orig_inode->i_size, | 1007 | orig_start, orig_blocks, |
1000 | orig_inode->i_ino, donor_inode->i_ino); | 1008 | orig_inode->i_ino, donor_inode->i_ino); |
1001 | return -EINVAL; | 1009 | return -EINVAL; |
1002 | } | 1010 | } |
1003 | 1011 | ||
1004 | if (orig_start + *len > orig_inode->i_size) { | 1012 | if (orig_start + *len > orig_blocks) { |
1005 | ext4_debug("ext4 move extent: Adjust length " | 1013 | ext4_debug("ext4 move extent: Adjust length " |
1006 | "from %llu to %lld. Because it should be " | 1014 | "from %llu to %llu. Because it should be " |
1007 | "less than original file size " | 1015 | "less than original file blocks " |
1008 | "[ino:orig %lu, donor %lu]\n", | 1016 | "[ino:orig %lu, donor %lu]\n", |
1009 | *len, orig_inode->i_size - orig_start, | 1017 | *len, orig_blocks - orig_start, |
1010 | orig_inode->i_ino, donor_inode->i_ino); | 1018 | orig_inode->i_ino, donor_inode->i_ino); |
1011 | *len = orig_inode->i_size - orig_start; | 1019 | *len = orig_blocks - orig_start; |
1012 | } | 1020 | } |
1013 | } | 1021 | } |
1014 | 1022 | ||