Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

squashfs: fix cache race with migration

Migration replaces the page in the mapping before copying the contents and
the flags over from the old page, so check that the page in the page cache
is really up to date before using it. Without this, stressing squashfs
reads with parallel compaction sometimes results in squashfs reporting
data corruption.

Link: https://lkml.kernel.org/r/20230629-squashfs-cache-migration-v1-1-d50ebe55099d@axis.com
Fixes: e994f5b677ee ("squashfs: cache partial compressed blocks")
Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Phillip Lougher <phillip@squashfs.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Vincent Whitchurch and committed by
Andrew Morton
08bab74a 191fcdb6

+23 -4
+23 -4
fs/squashfs/block.c
··· 166 166 return 0; 167 167 } 168 168 169 + static struct page *squashfs_get_cache_page(struct address_space *mapping, 170 + pgoff_t index) 171 + { 172 + struct page *page; 173 + 174 + if (!mapping) 175 + return NULL; 176 + 177 + page = find_get_page(mapping, index); 178 + if (!page) 179 + return NULL; 180 + 181 + if (!PageUptodate(page)) { 182 + put_page(page); 183 + return NULL; 184 + } 185 + 186 + return page; 187 + } 188 + 169 189 static int squashfs_bio_read(struct super_block *sb, u64 index, int length, 170 190 struct bio **biop, int *block_offset) 171 191 { ··· 210 190 for (i = 0; i < page_count; ++i) { 211 191 unsigned int len = 212 192 min_t(unsigned int, PAGE_SIZE - offset, total_len); 213 - struct page *page = NULL; 193 + pgoff_t index = (read_start >> PAGE_SHIFT) + i; 194 + struct page *page; 214 195 215 - if (cache_mapping) 216 - page = find_get_page(cache_mapping, 217 - (read_start >> PAGE_SHIFT) + i); 196 + page = squashfs_get_cache_page(cache_mapping, index); 218 197 if (!page) 219 198 page = alloc_page(GFP_NOIO); 220 199