lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20190221192712.E5CD895C343F@us180.sjc.aristanetworks.com>
Date:   Thu, 21 Feb 2019 11:27:12 -0800
From:   fruggeri@...sta.com (Francesco Ruggeri)
To:     viro@...iv.linux.org.uk, linux-kernel@...r.kernel.org,
        linux-fsdevel@...r.kernel.org, fruggeri@...sta.com
Subject: [PATCH] block: fix inconsistent page index

__find_get_block_slow() and grow_buffers() use different methods to compute
a page index for a given block: __find_get_block_slow() computes it from
bd_inode->i_blkbits, while grow_buffers() computes it from the block size
argument.
The two can get out of sync, for example if bd_inode->i_blkbits is modified
while a isofs_fill_super() (in mount) is between sb_min_blocksize() and
sb_read/__bread_gfp/__getblk_gfp/__getblk_slow.
The script below can reproduce such a scenario, resulting in an infinite
loop in __getblk_slow(), as grow_buffers() allocates a page but
__find_get_block() keeps looking for a different one.
This patch changes __find_get_block_slow() to also use the block size.
It implicitly relies on size being a power of 2 in the 256..PAGE_SIZE
range.

FILE=/tmp/fsfile
MNT=/tmp/fsmnt
LOOP=`losetup -f`

rm -rf $FILE $MNT

mkdir $MNT
dd if=/dev/zero of=$FILE count=32 bs=1MiB
losetup $LOOP $FILE
mkfs -t ext4 $LOOP
while true; do losetup -D $LOOP; losetup $LOOP $FILE;done 2>&1 >/dev/null &
for ((i=0; i<100; i++)); do echo ================== $i; \
                            mount $LOOP $MNT; umount $MNT; done

Signed-off-by: Francesco Ruggeri <fruggeri@...sta.com>
---
 fs/buffer.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index 48318fb74938..447e8db2ff5f 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -190,7 +190,8 @@ EXPORT_SYMBOL(end_buffer_write_sync);
  * succeeds, there is no need to take private_lock.
  */
 static struct buffer_head *
-__find_get_block_slow(struct block_device *bdev, sector_t block)
+__find_get_block_slow(struct block_device *bdev, sector_t block,
+		      unsigned int size)
 {
 	struct inode *bd_inode = bdev->bd_inode;
 	struct address_space *bd_mapping = bd_inode->i_mapping;
@@ -202,7 +203,7 @@ __find_get_block_slow(struct block_device *bdev, sector_t block)
 	int all_mapped = 1;
 	static DEFINE_RATELIMIT_STATE(last_warned, HZ, 1);
 
-	index = block >> (PAGE_SHIFT - bd_inode->i_blkbits);
+	index = block >> (PAGE_SHIFT - blksize_bits(size));
 	page = find_get_page_flags(bd_mapping, index, FGP_ACCESSED);
 	if (!page)
 		goto out;
@@ -1292,7 +1293,7 @@ __find_get_block(struct block_device *bdev, sector_t block, unsigned size)
 
 	if (bh == NULL) {
 		/* __find_get_block_slow will mark the page accessed */
-		bh = __find_get_block_slow(bdev, block);
+		bh = __find_get_block_slow(bdev, block, size);
 		if (bh)
 			bh_lru_install(bh);
 	} else
-- 
2.19.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ