diff mbox series

[2/2] Squashfs: Compute expected length from inode size rather than block length

Message ID 1550586751-15363-3-git-send-email-paolo.pisati@canonical.com
State New
Headers show
Series squashfs hardening | expand

Commit Message

Paolo Pisati Feb. 19, 2019, 2:32 p.m. UTC
From: Phillip Lougher <phillip@squashfs.org.uk>

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(-)

Comments

Tyler Hicks Feb. 20, 2019, 1:35 p.m. UTC | #1
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 mbox series

Patch

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 *);