Message ID | 1550586751-15363-3-git-send-email-paolo.pisati@canonical.com |
---|---|
State | New |
Headers | show |
Series | squashfs hardening | expand |
On 2019-02-19 15:32:31, Paolo Pisati wrote: > From: Phillip Lougher <phillip@squashfs.org.uk> This line should be added when committing this change: BugLink: https://bugs.launchpad.net/bugs/1816756 With that change, Acked-by: Tyler Hicks <tyhicks@canonical.com> Tyler > > Previously in squashfs_readpage() when copying data into the page > cache, it used the length of the datablock read from the filesystem > (after decompression). However, if the filesystem has been corrupted > this data block may be short, which will leave pages unfilled. > > The fix for this is to compute the expected number of bytes to copy > from the inode size, and use this to detect if the block is short. > > Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk> > Tested-by: Willy Tarreau <w@1wt.eu> > Cc: Анатолий Тросиненко <anatoly.trosinenko@gmail.com> > Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> > (cherry picked from commit a3f94cb99a854fa381fe7fadd97c4f61633717a5) > Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com> > --- > fs/squashfs/file.c | 25 ++++++++++--------------- > fs/squashfs/file_cache.c | 4 ++-- > fs/squashfs/file_direct.c | 16 +++++++++++----- > fs/squashfs/squashfs.h | 2 +- > 4 files changed, 24 insertions(+), 23 deletions(-) > > diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c > index fa3eb0a..68a29c9 100644 > --- a/fs/squashfs/file.c > +++ b/fs/squashfs/file.c > @@ -431,10 +431,9 @@ skip_page: > } > > /* Read datablock stored packed inside a fragment (tail-end packed block) */ > -static int squashfs_readpage_fragment(struct page *page) > +static int squashfs_readpage_fragment(struct page *page, int expected) > { > struct inode *inode = page->mapping->host; > - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; > struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, > squashfs_i(inode)->fragment_block, > squashfs_i(inode)->fragment_size); > @@ -445,23 +444,16 @@ static int squashfs_readpage_fragment(struct page *page) > squashfs_i(inode)->fragment_block, > squashfs_i(inode)->fragment_size); > else > - squashfs_copy_cache(page, buffer, i_size_read(inode) & > - (msblk->block_size - 1), > + squashfs_copy_cache(page, buffer, expected, > squashfs_i(inode)->fragment_offset); > > squashfs_cache_put(buffer); > return res; > } > > -static int squashfs_readpage_sparse(struct page *page, int index, int file_end) > +static int squashfs_readpage_sparse(struct page *page, int expected) > { > - struct inode *inode = page->mapping->host; > - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; > - int bytes = index == file_end ? > - (i_size_read(inode) & (msblk->block_size - 1)) : > - msblk->block_size; > - > - squashfs_copy_cache(page, NULL, bytes, 0); > + squashfs_copy_cache(page, NULL, expected, 0); > return 0; > } > > @@ -471,6 +463,9 @@ static int squashfs_readpage(struct file *file, struct page *page) > struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; > int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); > int file_end = i_size_read(inode) >> msblk->block_log; > + int expected = index == file_end ? > + (i_size_read(inode) & (msblk->block_size - 1)) : > + msblk->block_size; > int res; > void *pageaddr; > > @@ -489,11 +484,11 @@ static int squashfs_readpage(struct file *file, struct page *page) > goto error_out; > > if (bsize == 0) > - res = squashfs_readpage_sparse(page, index, file_end); > + res = squashfs_readpage_sparse(page, expected); > else > - res = squashfs_readpage_block(page, block, bsize); > + res = squashfs_readpage_block(page, block, bsize, expected); > } else > - res = squashfs_readpage_fragment(page); > + res = squashfs_readpage_fragment(page, expected); > > if (!res) > return 0; > diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c > index f2310d2..a9ba8d9 100644 > --- a/fs/squashfs/file_cache.c > +++ b/fs/squashfs/file_cache.c > @@ -20,7 +20,7 @@ > #include "squashfs.h" > > /* Read separately compressed datablock and memcopy into page cache */ > -int squashfs_readpage_block(struct page *page, u64 block, int bsize) > +int squashfs_readpage_block(struct page *page, u64 block, int bsize, int expected) > { > struct inode *i = page->mapping->host; > struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, > @@ -31,7 +31,7 @@ int squashfs_readpage_block(struct page *page, u64 block, int bsize) > ERROR("Unable to read page, block %llx, size %x\n", block, > bsize); > else > - squashfs_copy_cache(page, buffer, buffer->length, 0); > + squashfs_copy_cache(page, buffer, expected, 0); > > squashfs_cache_put(buffer); > return res; > diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c > index f4fcb8c..21ccc4b 100644 > --- a/fs/squashfs/file_direct.c > +++ b/fs/squashfs/file_direct.c > @@ -21,10 +21,11 @@ > #include "page_actor.h" > > static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, > - int pages, struct page **page); > + int pages, struct page **page, int bytes); > > /* Read separately compressed datablock directly into page cache */ > -int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) > +int squashfs_readpage_block(struct page *target_page, u64 block, int bsize, > + int expected) > > { > struct inode *inode = target_page->mapping->host; > @@ -83,7 +84,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) > * using an intermediate buffer. > */ > res = squashfs_read_cache(target_page, block, bsize, pages, > - page); > + page, expected); > if (res < 0) > goto mark_errored; > > @@ -95,6 +96,11 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) > if (res < 0) > goto mark_errored; > > + if (res != expected) { > + res = -EIO; > + goto mark_errored; > + } > + > /* Last page may have trailing bytes not filled */ > bytes = res % PAGE_CACHE_SIZE; > if (bytes) { > @@ -138,12 +144,12 @@ out: > > > static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, > - int pages, struct page **page) > + int pages, struct page **page, int bytes) > { > struct inode *i = target_page->mapping->host; > struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, > block, bsize); > - int bytes = buffer->length, res = buffer->error, n, offset = 0; > + int res = buffer->error, n, offset = 0; > > if (res) { > ERROR("Unable to read page, block %llx, size %x\n", block, > diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h > index d8d4372..f89f8a7 100644 > --- a/fs/squashfs/squashfs.h > +++ b/fs/squashfs/squashfs.h > @@ -72,7 +72,7 @@ void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int, > int); > > /* file_xxx.c */ > -extern int squashfs_readpage_block(struct page *, u64, int); > +extern int squashfs_readpage_block(struct page *, u64, int, int); > > /* id.c */ > extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); > -- > 2.7.4 > > > -- > kernel-team mailing list > kernel-team@lists.ubuntu.com > https://lists.ubuntu.com/mailman/listinfo/kernel-team
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index fa3eb0a..68a29c9 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c @@ -431,10 +431,9 @@ skip_page: } /* Read datablock stored packed inside a fragment (tail-end packed block) */ -static int squashfs_readpage_fragment(struct page *page) +static int squashfs_readpage_fragment(struct page *page, int expected) { struct inode *inode = page->mapping->host; - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, squashfs_i(inode)->fragment_block, squashfs_i(inode)->fragment_size); @@ -445,23 +444,16 @@ static int squashfs_readpage_fragment(struct page *page) squashfs_i(inode)->fragment_block, squashfs_i(inode)->fragment_size); else - squashfs_copy_cache(page, buffer, i_size_read(inode) & - (msblk->block_size - 1), + squashfs_copy_cache(page, buffer, expected, squashfs_i(inode)->fragment_offset); squashfs_cache_put(buffer); return res; } -static int squashfs_readpage_sparse(struct page *page, int index, int file_end) +static int squashfs_readpage_sparse(struct page *page, int expected) { - struct inode *inode = page->mapping->host; - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; - int bytes = index == file_end ? - (i_size_read(inode) & (msblk->block_size - 1)) : - msblk->block_size; - - squashfs_copy_cache(page, NULL, bytes, 0); + squashfs_copy_cache(page, NULL, expected, 0); return 0; } @@ -471,6 +463,9 @@ static int squashfs_readpage(struct file *file, struct page *page) struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); int file_end = i_size_read(inode) >> msblk->block_log; + int expected = index == file_end ? + (i_size_read(inode) & (msblk->block_size - 1)) : + msblk->block_size; int res; void *pageaddr; @@ -489,11 +484,11 @@ static int squashfs_readpage(struct file *file, struct page *page) goto error_out; if (bsize == 0) - res = squashfs_readpage_sparse(page, index, file_end); + res = squashfs_readpage_sparse(page, expected); else - res = squashfs_readpage_block(page, block, bsize); + res = squashfs_readpage_block(page, block, bsize, expected); } else - res = squashfs_readpage_fragment(page); + res = squashfs_readpage_fragment(page, expected); if (!res) return 0; diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c index f2310d2..a9ba8d9 100644 --- a/fs/squashfs/file_cache.c +++ b/fs/squashfs/file_cache.c @@ -20,7 +20,7 @@ #include "squashfs.h" /* Read separately compressed datablock and memcopy into page cache */ -int squashfs_readpage_block(struct page *page, u64 block, int bsize) +int squashfs_readpage_block(struct page *page, u64 block, int bsize, int expected) { struct inode *i = page->mapping->host; struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, @@ -31,7 +31,7 @@ int squashfs_readpage_block(struct page *page, u64 block, int bsize) ERROR("Unable to read page, block %llx, size %x\n", block, bsize); else - squashfs_copy_cache(page, buffer, buffer->length, 0); + squashfs_copy_cache(page, buffer, expected, 0); squashfs_cache_put(buffer); return res; diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c index f4fcb8c..21ccc4b 100644 --- a/fs/squashfs/file_direct.c +++ b/fs/squashfs/file_direct.c @@ -21,10 +21,11 @@ #include "page_actor.h" static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, - int pages, struct page **page); + int pages, struct page **page, int bytes); /* Read separately compressed datablock directly into page cache */ -int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) +int squashfs_readpage_block(struct page *target_page, u64 block, int bsize, + int expected) { struct inode *inode = target_page->mapping->host; @@ -83,7 +84,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) * using an intermediate buffer. */ res = squashfs_read_cache(target_page, block, bsize, pages, - page); + page, expected); if (res < 0) goto mark_errored; @@ -95,6 +96,11 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) if (res < 0) goto mark_errored; + if (res != expected) { + res = -EIO; + goto mark_errored; + } + /* Last page may have trailing bytes not filled */ bytes = res % PAGE_CACHE_SIZE; if (bytes) { @@ -138,12 +144,12 @@ out: static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, - int pages, struct page **page) + int pages, struct page **page, int bytes) { struct inode *i = target_page->mapping->host; struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, block, bsize); - int bytes = buffer->length, res = buffer->error, n, offset = 0; + int res = buffer->error, n, offset = 0; if (res) { ERROR("Unable to read page, block %llx, size %x\n", block, diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index d8d4372..f89f8a7 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -72,7 +72,7 @@ void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int, int); /* file_xxx.c */ -extern int squashfs_readpage_block(struct page *, u64, int); +extern int squashfs_readpage_block(struct page *, u64, int, int); /* id.c */ extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);