OSDN Git Service

libext2fs: don't overflow when punching indirect blocks with large blocks
authorDarrick J. Wong <darrick.wong@oracle.com>
Thu, 12 Dec 2013 17:06:03 +0000 (12:06 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 12 Dec 2013 17:06:07 +0000 (12:06 -0500)
On a FS with a rather large blockize (> 4K), the old block map
structure can construct a fat enough "tree" (or whatever we call that
lopsided thing) that (at least in theory) one could create mappings
for logical blocks higher than 32 bits.  In practice this doesn't
happen, but the 'max' and 'iter' variables that the punch helpers use
will overflow because the BLOCK_SIZE_BITS shifts are too large to fit
a 32-bit variable.  The current variable declarations also cause punch
to fail on TIND-mapped blocks even if the file is < 16T.  So enlarge
the fields to fit.

Yes, this is an obscure corner case, but it seems a little silly if we
can't punch a file's block 300,000,000 on a 64k-block filesystem.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
lib/ext2fs/punch.c

index 4471f46..790a0ad 100644 (file)
@@ -50,15 +50,16 @@ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
                           blk_t start, blk_t count, int max)
 {
        errcode_t       retval;
-       blk_t           b, offset;
-       int             i, incr;
+       blk_t           b;
+       int             i;
+       blk64_t         offset, incr;
        int             freed = 0;
 
 #ifdef PUNCH_DEBUG
        printf("Entering ind_punch, level %d, start %u, count %u, "
               "max %d\n", level, start, count, max);
 #endif
-       incr = 1 << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level);
+       incr = 1ULL << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level);
        for (i=0, offset=0; i < max; i++, p++, offset += incr) {
                if (offset >= start + count)
                        break;
@@ -87,7 +88,7 @@ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
                                continue;
                }
 #ifdef PUNCH_DEBUG
-               printf("Freeing block %u (offset %d)\n", b, offset);
+               printf("Freeing block %u (offset %llu)\n", b, offset);
 #endif
                ext2fs_block_alloc_stats(fs, b, -1);
                *p = 0;
@@ -108,7 +109,7 @@ static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
        int                     num = EXT2_NDIR_BLOCKS;
        blk_t                   *bp = inode->i_block;
        blk_t                   addr_per_block;
-       blk_t                   max = EXT2_NDIR_BLOCKS;
+       blk64_t                 max = EXT2_NDIR_BLOCKS;
 
        if (!block_buf) {
                retval = ext2fs_get_array(3, fs->blocksize, &buf);
@@ -119,10 +120,10 @@ static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
 
        addr_per_block = (blk_t) fs->blocksize >> 2;
 
-       for (level=0; level < 4; level++, max *= addr_per_block) {
+       for (level = 0; level < 4; level++, max *= (blk64_t)addr_per_block) {
 #ifdef PUNCH_DEBUG
                printf("Main loop level %d, start %u count %u "
-                      "max %d num %d\n", level, start, count, max, num);
+                      "max %llu num %d\n", level, start, count, max, num);
 #endif
                if (start < max) {
                        retval = ind_punch(fs, inode, block_buf, bp, level,