mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
erofs: handle overlapped pclusters out of crafted images properly
commit9e2f9d34ddupstream. syzbot reported a task hang issue due to a deadlock case where it is waiting for the folio lock of a cached folio that will be used for cache I/Os. After looking into the crafted fuzzed image, I found it's formed with several overlapped big pclusters as below: Ext: logical offset | length : physical offset | length 0: 0.. 16384 | 16384 : 151552.. 167936 | 16384 1: 16384.. 32768 | 16384 : 155648.. 172032 | 16384 2: 32768.. 49152 | 16384 : 537223168.. 537239552 | 16384 ... Here, extent 0/1 are physically overlapped although it's entirely _impossible_ for normal filesystem images generated by mkfs. First, managed folios containing compressed data will be marked as up-to-date and then unlocked immediately (unlike in-place folios) when compressed I/Os are complete. If physical blocks are not submitted in the incremental order, there should be separate BIOs to avoid dependency issues. However, the current code mis-arranges z_erofs_fill_bio_vec() and BIO submission which causes unexpected BIO waits. Second, managed folios will be connected to their own pclusters for efficient inter-queries. However, this is somewhat hard to implement easily if overlapped big pclusters exist. Again, these only appear in fuzzed images so let's simply fall back to temporary short-lived pages for correctness. Additionally, it justifies that referenced managed folios cannot be truncated for now and reverts part of commit2080ca1ed3("erofs: tidy up `struct z_erofs_bvec`") for simplicity although it shouldn't be any difference. Reported-by: syzbot+4fc98ed414ae63d1ada2@syzkaller.appspotmail.com Reported-by: syzbot+de04e06b28cfecf2281c@syzkaller.appspotmail.com Reported-by: syzbot+c8c8238b394be4a1087d@syzkaller.appspotmail.com Tested-by: syzbot+4fc98ed414ae63d1ada2@syzkaller.appspotmail.com Closes: https://lore.kernel.org/r/0000000000002fda01061e334873@google.com Fixes:8e6c8fa9f2("erofs: enable big pcluster feature") Link: https://lore.kernel.org/r/20240910070847.3356592-1-hsiangkao@linux.alibaba.com Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
a1a541fbfa
commit
1bf7e414ca
+31
-28
@@ -1483,14 +1483,13 @@ repeat:
|
||||
goto out;
|
||||
|
||||
lock_page(page);
|
||||
|
||||
/* only true if page reclaim goes wrong, should never happen */
|
||||
DBG_BUGON(justfound && PagePrivate(page));
|
||||
|
||||
/* the page is still in manage cache */
|
||||
if (page->mapping == mc) {
|
||||
if (likely(page->mapping == mc)) {
|
||||
WRITE_ONCE(pcl->compressed_bvecs[nr].page, page);
|
||||
|
||||
/*
|
||||
* The cached folio is still in managed cache but without
|
||||
* a valid `->private` pcluster hint. Let's reconnect them.
|
||||
*/
|
||||
if (!PagePrivate(page)) {
|
||||
/*
|
||||
* impossible to be !PagePrivate(page) for
|
||||
@@ -1504,22 +1503,24 @@ repeat:
|
||||
SetPagePrivate(page);
|
||||
}
|
||||
|
||||
/* no need to submit io if it is already up-to-date */
|
||||
if (PageUptodate(page)) {
|
||||
unlock_page(page);
|
||||
page = NULL;
|
||||
if (likely(page->private == (unsigned long)pcl)) {
|
||||
/* don't submit cache I/Os again if already uptodate */
|
||||
if (PageUptodate(page)) {
|
||||
unlock_page(page);
|
||||
page = NULL;
|
||||
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
/*
|
||||
* Already linked with another pcluster, which only appears in
|
||||
* crafted images by fuzzers for now. But handle this anyway.
|
||||
*/
|
||||
tocache = false; /* use temporary short-lived pages */
|
||||
} else {
|
||||
DBG_BUGON(1); /* referenced managed folios can't be truncated */
|
||||
tocache = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* the managed page has been truncated, it's unsafe to
|
||||
* reuse this one, let's allocate a new cache-managed page.
|
||||
*/
|
||||
DBG_BUGON(page->mapping);
|
||||
DBG_BUGON(!justfound);
|
||||
|
||||
tocache = true;
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
out_allocpage:
|
||||
@@ -1677,16 +1678,11 @@ static void z_erofs_submit_queue(struct z_erofs_decompress_frontend *f,
|
||||
end = cur + pcl->pclusterpages;
|
||||
|
||||
do {
|
||||
struct page *page;
|
||||
|
||||
page = pickup_page_for_submission(pcl, i++,
|
||||
&f->pagepool, mc);
|
||||
if (!page)
|
||||
continue;
|
||||
struct page *page = NULL;
|
||||
|
||||
if (bio && (cur != last_index + 1 ||
|
||||
last_bdev != mdev.m_bdev)) {
|
||||
submit_bio_retry:
|
||||
drain_io:
|
||||
submit_bio(bio);
|
||||
if (memstall) {
|
||||
psi_memstall_leave(&pflags);
|
||||
@@ -1695,6 +1691,13 @@ submit_bio_retry:
|
||||
bio = NULL;
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
page = pickup_page_for_submission(pcl, i++,
|
||||
&f->pagepool, mc);
|
||||
if (!page)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlikely(PageWorkingset(page)) && !memstall) {
|
||||
psi_memstall_enter(&pflags);
|
||||
memstall = 1;
|
||||
@@ -1715,7 +1718,7 @@ submit_bio_retry:
|
||||
}
|
||||
|
||||
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE)
|
||||
goto submit_bio_retry;
|
||||
goto drain_io;
|
||||
|
||||
last_index = cur;
|
||||
bypass = false;
|
||||
|
||||
Reference in New Issue
Block a user