Skip to content

Commit 5c48a7d

Browse files
zhangyi089tytso
authored andcommitted
ext4: fix an use-after-free issue about data=journal writeback mode
Our syzkaller report an use-after-free issue that accessing the freed buffer_head on the writeback page in __ext4_journalled_writepage(). The problem is that if there was a truncate racing with the data=journalled writeback procedure, the writeback length could become zero and bget_one() refuse to get buffer_head's refcount, then the truncate procedure release buffer once we drop page lock, finally, the last ext4_walk_page_buffers() trigger the use-after-free problem. sync truncate ext4_sync_file() file_write_and_wait_range() ext4_setattr(0) inode->i_size = 0 ext4_writepage() len = 0 __ext4_journalled_writepage() page_bufs = page_buffers(page) ext4_walk_page_buffers(bget_one) <- does not get refcount do_invalidatepage() free_buffer_head() ext4_walk_page_buffers(page_bufs) <- trigger use-after-free After commit bdf9683 ("ext4: fix race between truncate and __ext4_journalled_writepage()"), we have already handled the racing case, so the bget_one() and bput_one() are not needed. So this patch simply remove these hunk, and recheck the i_size to make it safe. Fixes: bdf9683 ("ext4: fix race between truncate and __ext4_journalled_writepage()") Signed-off-by: Zhang Yi <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 298b5c5 commit 5c48a7d

File tree

1 file changed

+10
-27
lines changed

1 file changed

+10
-27
lines changed

fs/ext4/inode.c

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,30 +1845,16 @@ int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
18451845
return 0;
18461846
}
18471847

1848-
static int bget_one(handle_t *handle, struct inode *inode,
1849-
struct buffer_head *bh)
1850-
{
1851-
get_bh(bh);
1852-
return 0;
1853-
}
1854-
1855-
static int bput_one(handle_t *handle, struct inode *inode,
1856-
struct buffer_head *bh)
1857-
{
1858-
put_bh(bh);
1859-
return 0;
1860-
}
1861-
18621848
static int __ext4_journalled_writepage(struct page *page,
18631849
unsigned int len)
18641850
{
18651851
struct address_space *mapping = page->mapping;
18661852
struct inode *inode = mapping->host;
1867-
struct buffer_head *page_bufs = NULL;
18681853
handle_t *handle = NULL;
18691854
int ret = 0, err = 0;
18701855
int inline_data = ext4_has_inline_data(inode);
18711856
struct buffer_head *inode_bh = NULL;
1857+
loff_t size;
18721858

18731859
ClearPageChecked(page);
18741860

@@ -1878,14 +1864,6 @@ static int __ext4_journalled_writepage(struct page *page,
18781864
inode_bh = ext4_journalled_write_inline_data(inode, len, page);
18791865
if (inode_bh == NULL)
18801866
goto out;
1881-
} else {
1882-
page_bufs = page_buffers(page);
1883-
if (!page_bufs) {
1884-
BUG();
1885-
goto out;
1886-
}
1887-
ext4_walk_page_buffers(handle, inode, page_bufs, 0, len,
1888-
NULL, bget_one);
18891867
}
18901868
/*
18911869
* We need to release the page lock before we start the
@@ -1906,7 +1884,8 @@ static int __ext4_journalled_writepage(struct page *page,
19061884

19071885
lock_page(page);
19081886
put_page(page);
1909-
if (page->mapping != mapping) {
1887+
size = i_size_read(inode);
1888+
if (page->mapping != mapping || page_offset(page) > size) {
19101889
/* The page got truncated from under us */
19111890
ext4_journal_stop(handle);
19121891
ret = 0;
@@ -1916,6 +1895,13 @@ static int __ext4_journalled_writepage(struct page *page,
19161895
if (inline_data) {
19171896
ret = ext4_mark_inode_dirty(handle, inode);
19181897
} else {
1898+
struct buffer_head *page_bufs = page_buffers(page);
1899+
1900+
if (page->index == size >> PAGE_SHIFT)
1901+
len = size & ~PAGE_MASK;
1902+
else
1903+
len = PAGE_SIZE;
1904+
19191905
ret = ext4_walk_page_buffers(handle, inode, page_bufs, 0, len,
19201906
NULL, do_journal_get_write_access);
19211907

@@ -1936,9 +1922,6 @@ static int __ext4_journalled_writepage(struct page *page,
19361922
out:
19371923
unlock_page(page);
19381924
out_no_pagelock:
1939-
if (!inline_data && page_bufs)
1940-
ext4_walk_page_buffers(NULL, inode, page_bufs, 0, len,
1941-
NULL, bput_one);
19421925
brelse(inode_bh);
19431926
return ret;
19441927
}

0 commit comments

Comments
 (0)