diff -Nurp linux/fs/squashfs/inode.c linux-mokb/fs/squashfs/inode.c --- linux/fs/squashfs/inode.c 2006-11-14 23:36:59.000000000 +0000 +++ linux-mokb/fs/squashfs/inode.c 2006-11-15 02:15:26.000000000 +0000 @@ -173,14 +173,15 @@ out: SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, long long index, unsigned int length, - long long *next_index) + long long *next_index, int srclength) { struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> msblk->devblksize_log2) + 2]; unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); unsigned int cur_index = index >> msblk->devblksize_log2; - int bytes, avail_bytes, b = 0, k; + int bytes, avail_bytes, b = 0, k = 0; char *c_buffer; unsigned int compressed; unsigned int c_byte = length; @@ -191,8 +192,11 @@ SQSH_EXTERN unsigned int squashfs_read_d c_buffer = compressed ? msblk->read_data : buffer; c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); - TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed - ? "" : "un", (unsigned int) c_byte); + TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed + ? "" : "un", (unsigned int) c_byte, srclength); + + if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used) + goto read_failure; if (!(bh[0] = sb_getblk(s, cur_index))) goto block_release; @@ -204,6 +208,9 @@ SQSH_EXTERN unsigned int squashfs_read_d } ll_rw_block(READ, b, bh); } else { + if (index < 0 || (index + 2) > sblk->bytes_used) + goto read_failure; + if (!(bh[0] = get_block_length(s, &cur_index, &offset, &c_byte))) goto read_failure; @@ -216,6 +223,9 @@ SQSH_EXTERN unsigned int squashfs_read_d TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ? "" : "un", (unsigned int) c_byte); + if (c_byte > srclength || (index + c_byte) > sblk->bytes_used) + goto read_failure; + for (b = 1; bytes < c_byte; b++) { if (!(bh[b] = sb_getblk(s, ++cur_index))) goto block_release; @@ -227,7 +237,7 @@ SQSH_EXTERN unsigned int squashfs_read_d if (compressed) down(&msblk->read_data_mutex); - for (bytes = 0, k = 0; k < b; k++) { + for (bytes = 0; k < b; k++) { avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? msblk->devblksize - offset : c_byte - bytes; @@ -249,7 +259,7 @@ SQSH_EXTERN unsigned int squashfs_read_d msblk->stream.next_in = c_buffer; msblk->stream.avail_in = c_byte; msblk->stream.next_out = buffer; - msblk->stream.avail_out = msblk->read_size; + msblk->stream.avail_out = srclength; if (((zlib_err = zlib_inflateInit(&msblk->stream)) != Z_OK) || ((zlib_err = zlib_inflate(&msblk->stream, Z_FINISH)) @@ -271,8 +281,8 @@ SQSH_EXTERN unsigned int squashfs_read_d return bytes; block_release: - while (--b >= 0) - brelse(bh[b]); + for (; k < b; k++) + brelse(bh[k]); read_failure: ERROR("sb_bread failed reading block 0x%x\n", cur_index); @@ -336,14 +346,20 @@ SQSH_EXTERN int squashfs_get_cached_bloc msblk->block_cache[i].block = SQUASHFS_USED_BLK; up(&msblk->block_cache_mutex); - if (!(msblk->block_cache[i].length = - squashfs_read_data(s, - msblk->block_cache[i].data, - block, 0, &next_index))) { - ERROR("Unable to read cache block [%llx:%x]\n", - block, offset); - goto out; - } + msblk->block_cache[i].length = squashfs_read_data(s, + msblk->block_cache[i].data, block, 0, &next_index, + SQUASHFS_METADATA_SIZE); + + if (msblk->block_cache[i].length == 0) { + ERROR("Unable to read cache block [%llx:%x]\n", + block, offset); + down(&msblk->block_cache_mutex); + msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; + kfree(msblk->block_cache[i].data); + wake_up(&msblk->waitq); + up(&msblk->block_cache_mutex); + goto out; + } down(&msblk->block_cache_mutex); wake_up(&msblk->waitq); @@ -357,7 +373,11 @@ SQSH_EXTERN int squashfs_get_cached_bloc continue; } - if ((bytes = msblk->block_cache[i].length - offset) >= length) { + bytes = msblk->block_cache[i].length - offset; + + if (bytes < 1) + goto out; + else if (bytes >= length) { if (buffer) memcpy(buffer, msblk->block_cache[i].data + offset, length); @@ -442,6 +462,7 @@ SQSH_EXTERN struct squashfs_fragment_cac { int i, n; struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; while ( 1 ) { down(&msblk->fragment_mutex); @@ -487,7 +508,8 @@ SQSH_EXTERN struct squashfs_fragment_cac if (!(msblk->fragment[i].length = squashfs_read_data(s, msblk->fragment[i].data, - start_block, length, NULL))) { + start_block, length, NULL, + sblk->block_size))) { ERROR("Unable to read fragment cache block " "[%llx]\n", start_block); msblk->fragment[i].locked = 0; @@ -876,6 +898,10 @@ static int read_fragment_index_table(str { struct squashfs_sb_info *msblk = s->s_fs_info; struct squashfs_super_block *sblk = &msblk->sblk; + unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments); + + if (length == 0) + return 1; /* Allocate fragment index table */ if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES @@ -884,13 +910,9 @@ static int read_fragment_index_table(str return 0; } - if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) && - !squashfs_read_data(s, (char *) - msblk->fragment_index, - sblk->fragment_table_start, - SQUASHFS_FRAGMENT_INDEX_BYTES - (sblk->fragments) | - SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { + if (!squashfs_read_data(s, (char *) msblk->fragment_index, + sblk->fragment_table_start, length | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) { ERROR("unable to read fragment index table\n"); return 0; } @@ -982,9 +1004,11 @@ static int squashfs_fill_super(struct su init_waitqueue_head(&msblk->waitq); init_waitqueue_head(&msblk->fragment_wait_queue); + sblk->bytes_used = sizeof(struct squashfs_super_block); if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, sizeof(struct squashfs_super_block) | - SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, + sizeof(struct squashfs_super_block))) { SERROR("unable to read superblock\n"); goto failed_mount; } @@ -1012,6 +1036,15 @@ static int squashfs_fill_super(struct su if(!supported_squashfs_filesystem(msblk, silent)) goto failed_mount; + /* Check the filesystem does not extend beyond the end of the + block device */ + if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode)) + goto failed_mount; + + /* Check the root inode for sanity */ + if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE) + goto failed_mount; + TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES @@ -1081,7 +1114,9 @@ static int squashfs_fill_super(struct su if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, ((sblk->no_uids + sblk->no_guids) * sizeof(unsigned int)) | - SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, + (sblk->no_uids + sblk->no_guids) * + sizeof(unsigned int))) { ERROR("unable to read uid/gid table\n"); goto failed_mount; } @@ -1092,7 +1127,9 @@ static int squashfs_fill_super(struct su if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, ((sblk->no_uids + sblk->no_guids) * sizeof(unsigned int)) | - SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, + (sblk->no_uids + sblk->no_guids) * + sizeof(unsigned int))) { ERROR("unable to read uid/gid table\n"); goto failed_mount; } @@ -1518,7 +1555,8 @@ static int squashfs_readpage(struct file down(&msblk->read_page_mutex); if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, - block, bsize, NULL))) { + block, bsize, NULL, + msblk->read_size))) { ERROR("Unable to read page, block %llx, size %x\n", block, bsize); up(&msblk->read_page_mutex); @@ -1618,15 +1656,12 @@ static int squashfs_readpage4K(struct fi if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)) { - pageaddr = kmap_atomic(page, KM_USER0); block_list = NULL; goto skip_read; } if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) { ERROR("Failed to allocate block_list\n"); - pageaddr = kmap_atomic(page, KM_USER0); - block_list = NULL; goto skip_read; } @@ -1638,11 +1673,12 @@ static int squashfs_readpage4K(struct fi down(&msblk->read_page_mutex); bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, - bsize, NULL); - pageaddr = kmap_atomic(page, KM_USER0); - if (bytes) + bsize, NULL, msblk->read_size); + if (bytes) { + pageaddr = kmap_atomic(page, KM_USER0); memcpy(pageaddr, msblk->read_page, bytes); - else + kunmap_atomic(pageaddr, KM_USER0); + } else ERROR("Unable to read page, block %llx, size %x\n", block, bsize); up(&msblk->read_page_mutex); @@ -1652,11 +1688,12 @@ static int squashfs_readpage4K(struct fi SQUASHFS_I(inode)-> u.s1.fragment_start_block, SQUASHFS_I(inode)-> u.s1.fragment_size); - pageaddr = kmap_atomic(page, KM_USER0); if (fragment) { bytes = i_size_read(inode) & (sblk->block_size - 1); + pageaddr = kmap_atomic(page, KM_USER0); memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> u.s1.fragment_offset, bytes); + kunmap_atomic(pageaddr, KM_USER0); release_cached_fragment(msblk, fragment); } else ERROR("Unable to read page, block %llx, size %x\n", @@ -1666,6 +1703,7 @@ static int squashfs_readpage4K(struct fi } skip_read: + pageaddr = kmap_atomic(page, KM_USER0); memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); kunmap_atomic(pageaddr, KM_USER0); flush_dcache_page(page); diff -Nurp linux/fs/squashfs/squashfs2_0.c linux-mokb/fs/squashfs/squashfs2_0.c --- linux/fs/squashfs/squashfs2_0.c 2006-11-14 23:36:59.000000000 +0000 +++ linux-mokb/fs/squashfs/squashfs2_0.c 2006-11-15 02:18:15.000000000 +0000 @@ -73,12 +73,13 @@ static int read_fragment_index_table_2(s } if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) && - !squashfs_read_data(s, (char *) - msblk->fragment_index_2, - sblk->fragment_table_start, - SQUASHFS_FRAGMENT_INDEX_BYTES_2 - (sblk->fragments) | - SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { + !squashfs_read_data(s, (char *) + msblk->fragment_index_2, + sblk->fragment_table_start, + SQUASHFS_FRAGMENT_INDEX_BYTES_2 + (sblk->fragments) | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, + SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) { ERROR("unable to read fragment index table\n"); return 0; } diff -Nurp linux/fs/squashfs/squashfs.h linux-mokb/fs/squashfs/squashfs.h --- linux/fs/squashfs/squashfs.h 2006-11-14 23:36:59.000000000 +0000 +++ linux-mokb/fs/squashfs/squashfs.h 2006-11-15 01:59:01.000000000 +0000 @@ -49,7 +49,7 @@ static inline struct squashfs_inode_info #define SQSH_EXTERN extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, long long index, unsigned int length, - long long *next_index); + long long *next_index, int srclength); extern int squashfs_get_cached_block(struct super_block *s, char *buffer, long long block, unsigned int offset, int length, long long *next_block,