* [PATCH 0/2 v2] ext4: Fix crash when adding entry to corrupted directory @ 2022-05-18 9:33 Jan Kara 2022-05-18 9:33 ` [PATCH 1/2] ext4: Verify dir block before splitting it Jan Kara 2022-05-18 9:33 ` [PATCH 2/2] ext4: Avoid cycles in directory h-tree Jan Kara 0 siblings, 2 replies; 7+ messages in thread From: Jan Kara @ 2022-05-18 9:33 UTC (permalink / raw) To: Ted Tso; +Cc: linux-ext4, Jan Kara Hello, two patches in this series try to fix a crash when adding directory entry to a directory with corrupted h-tree. Since I don't have the filesystem image causing the crash, I'm not sure what was the cause but the stacktrace suggests we have corrupted one h-tree node while modifying another one so likely there was a cycle created in the h-tree. This series checks for it and bails out early. Changes since v1: * Fixed compilation error in the first patch (which got fixed in the second patch) * Fixed stack corruption issue with largedir feature Honza Previous versions: Link: http://lore.kernel.org/r/20220428180355.15209-1-jack@suse.cz # v1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] ext4: Verify dir block before splitting it 2022-05-18 9:33 [PATCH 0/2 v2] ext4: Fix crash when adding entry to corrupted directory Jan Kara @ 2022-05-18 9:33 ` Jan Kara 2022-05-19 2:16 ` Theodore Ts'o 2022-05-18 9:33 ` [PATCH 2/2] ext4: Avoid cycles in directory h-tree Jan Kara 1 sibling, 1 reply; 7+ messages in thread From: Jan Kara @ 2022-05-18 9:33 UTC (permalink / raw) To: Ted Tso; +Cc: linux-ext4, Jan Kara, stable Before splitting a directory block verify its directory entries are sane so that the splitting code does not access memory it should not. CC: stable@vger.kernel.org Signed-off-by: Jan Kara <jack@suse.cz> --- fs/ext4/namei.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 767b4bfe39c3..2a55f23e4524 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -277,9 +277,9 @@ static struct dx_frame *dx_probe(struct ext4_filename *fname, struct dx_hash_info *hinfo, struct dx_frame *frame); static void dx_release(struct dx_frame *frames); -static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, - unsigned blocksize, struct dx_hash_info *hinfo, - struct dx_map_entry map[]); +static int dx_make_map(struct inode *dir, struct buffer_head *bh, + struct dx_hash_info *hinfo, + struct dx_map_entry *map_tail); static void dx_sort_map(struct dx_map_entry *map, unsigned count); static struct ext4_dir_entry_2 *dx_move_dirents(struct inode *dir, char *from, char *to, struct dx_map_entry *offsets, @@ -1249,15 +1249,23 @@ static inline int search_dirblock(struct buffer_head *bh, * Create map of hash values, offsets, and sizes, stored at end of block. * Returns number of entries mapped. */ -static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, - unsigned blocksize, struct dx_hash_info *hinfo, +static int dx_make_map(struct inode *dir, struct buffer_head *bh, + struct dx_hash_info *hinfo, struct dx_map_entry *map_tail) { int count = 0; - char *base = (char *) de; + struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)bh->b_data; + unsigned int buflen = bh->b_size; + char *base = bh->b_data; struct dx_hash_info h = *hinfo; - while ((char *) de < base + blocksize) { + if (ext4_has_metadata_csum(dir->i_sb)) + buflen -= sizeof(struct ext4_dir_entry_tail); + + while ((char *) de < base + buflen) { + if (ext4_check_dir_entry(dir, NULL, de, bh, base, buflen, + ((char *)de) - base)) + return -EFSCORRUPTED; if (de->name_len && de->inode) { if (ext4_hash_in_dirent(dir)) h.hash = EXT4_DIRENT_HASH(de); @@ -1270,8 +1278,7 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, count++; cond_resched(); } - /* XXX: do we need to check rec_len == 0 case? -Chris */ - de = ext4_next_entry(de, blocksize); + de = ext4_next_entry(de, dir->i_sb->s_blocksize); } return count; } @@ -1943,8 +1950,11 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, /* create map in the end of data2 block */ map = (struct dx_map_entry *) (data2 + blocksize); - count = dx_make_map(dir, (struct ext4_dir_entry_2 *) data1, - blocksize, hinfo, map); + count = dx_make_map(dir, *bh, hinfo, map); + if (count < 0) { + err = count; + goto journal_error; + } map -= count; dx_sort_map(map, count); /* Ensure that neither split block is over half full */ -- 2.35.3 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] ext4: Verify dir block before splitting it 2022-05-18 9:33 ` [PATCH 1/2] ext4: Verify dir block before splitting it Jan Kara @ 2022-05-19 2:16 ` Theodore Ts'o 0 siblings, 0 replies; 7+ messages in thread From: Theodore Ts'o @ 2022-05-19 2:16 UTC (permalink / raw) To: Jan Kara; +Cc: Theodore Ts'o, stable, linux-ext4 On Wed, 18 May 2022 11:33:28 +0200, Jan Kara wrote: > Before splitting a directory block verify its directory entries are sane > so that the splitting code does not access memory it should not. > > Applied, thanks! [1/2] ext4: Verify dir block before splitting it commit: dfd094204c1f5bb4b1772bade3769cc92b645f1b [2/2] ext4: Avoid cycles in directory h-tree commit: 01db66b58446cd7372c4f29324d270b4cd15157d Best regards, -- Theodore Ts'o <tytso@mit.edu> ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/2] ext4: Avoid cycles in directory h-tree 2022-05-18 9:33 [PATCH 0/2 v2] ext4: Fix crash when adding entry to corrupted directory Jan Kara 2022-05-18 9:33 ` [PATCH 1/2] ext4: Verify dir block before splitting it Jan Kara @ 2022-05-18 9:33 ` Jan Kara 1 sibling, 0 replies; 7+ messages in thread From: Jan Kara @ 2022-05-18 9:33 UTC (permalink / raw) To: Ted Tso; +Cc: linux-ext4, Jan Kara, stable A maliciously corrupted filesystem can contain cycles in the h-tree stored inside a directory. That can easily lead to the kernel corrupting tree nodes that were already verified under its hands while doing a node split and consequently accessing unallocated memory. Fix the problem by verifying traversed block numbers are unique. CC: stable@vger.kernel.org Signed-off-by: Jan Kara <jack@suse.cz> --- fs/ext4/namei.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 2a55f23e4524..2ca99f1569c0 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -777,12 +777,14 @@ static struct dx_frame * dx_probe(struct ext4_filename *fname, struct inode *dir, struct dx_hash_info *hinfo, struct dx_frame *frame_in) { - unsigned count, indirect; + unsigned count, indirect, level, i; struct dx_entry *at, *entries, *p, *q, *m; struct dx_root *root; struct dx_frame *frame = frame_in; struct dx_frame *ret_err = ERR_PTR(ERR_BAD_DX_DIR); u32 hash; + ext4_lblk_t block; + ext4_lblk_t blocks[EXT4_HTREE_LEVEL]; memset(frame_in, 0, EXT4_HTREE_LEVEL * sizeof(frame_in[0])); frame->bh = ext4_read_dirblock(dir, 0, INDEX); @@ -854,6 +856,8 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, } dxtrace(printk("Look up %x", hash)); + level = 0; + blocks[0] = 0; while (1) { count = dx_get_count(entries); if (!count || count > dx_get_limit(entries)) { @@ -882,15 +886,27 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, dx_get_block(at))); frame->entries = entries; frame->at = at; - if (!indirect--) + + block = dx_get_block(at); + for (i = 0; i <= level; i++) { + if (blocks[i] == block) { + ext4_warning_inode(dir, + "dx entry: tree cycle block %u points back to block %u", + blocks[level], block); + goto fail; + } + } + if (++level > indirect) return frame; + blocks[level] = block; frame++; - frame->bh = ext4_read_dirblock(dir, dx_get_block(at), INDEX); + frame->bh = ext4_read_dirblock(dir, block, INDEX); if (IS_ERR(frame->bh)) { ret_err = (struct dx_frame *) frame->bh; frame->bh = NULL; goto fail; } + entries = ((struct dx_node *) frame->bh->b_data)->entries; if (dx_get_limit(entries) != dx_node_limit(dir)) { -- 2.35.3 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 0/2] ext4: Fix crash when adding entry to corrupted directory @ 2022-04-28 18:31 Jan Kara 2022-04-28 18:31 ` [PATCH 1/2] ext4: Verify dir block before splitting it Jan Kara 0 siblings, 1 reply; 7+ messages in thread From: Jan Kara @ 2022-04-28 18:31 UTC (permalink / raw) To: Ted Tso; +Cc: linux-ext4, Jan Kara Hello, two patches in this series try to fix a crash when adding directory entry to a directory with corrupted h-tree. Since I don't have the filesystem image causing the crash, I'm not sure what was the cause but the stacktrace suggests we have corrupted one h-tree node while modifying another one so likely there was a cycle created in the h-tree. This series checks for it and bails out early. Honza ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] ext4: Verify dir block before splitting it 2022-04-28 18:31 [PATCH 0/2] ext4: Fix crash when adding entry to corrupted directory Jan Kara @ 2022-04-28 18:31 ` Jan Kara 2022-05-17 23:40 ` Theodore Ts'o 0 siblings, 1 reply; 7+ messages in thread From: Jan Kara @ 2022-04-28 18:31 UTC (permalink / raw) To: Ted Tso; +Cc: linux-ext4, Jan Kara, stable Before splitting a directory block verify its directory entries are sane so that the splitting code does not access memory it should not. CC: stable@vger.kernel.org Signed-off-by: Jan Kara <jack@suse.cz> --- fs/ext4/namei.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 767b4bfe39c3..ca6ee9940599 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -277,9 +277,9 @@ static struct dx_frame *dx_probe(struct ext4_filename *fname, struct dx_hash_info *hinfo, struct dx_frame *frame); static void dx_release(struct dx_frame *frames); -static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, - unsigned blocksize, struct dx_hash_info *hinfo, - struct dx_map_entry map[]); +static int dx_make_map(struct inode *dir, struct buffer_head *bh, + struct dx_hash_info *hinfo, + struct dx_map_entry *map_tail); static void dx_sort_map(struct dx_map_entry *map, unsigned count); static struct ext4_dir_entry_2 *dx_move_dirents(struct inode *dir, char *from, char *to, struct dx_map_entry *offsets, @@ -1249,15 +1249,23 @@ static inline int search_dirblock(struct buffer_head *bh, * Create map of hash values, offsets, and sizes, stored at end of block. * Returns number of entries mapped. */ -static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, - unsigned blocksize, struct dx_hash_info *hinfo, +static int dx_make_map(struct inode *dir, struct buffer_head *bh, + struct dx_hash_info *hinfo, struct dx_map_entry *map_tail) { int count = 0; - char *base = (char *) de; + struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)bh->b_data; + unsigned int buflen = bh->b_size; + char *base = bh->b_data; struct dx_hash_info h = *hinfo; - while ((char *) de < base + blocksize) { + if (ext4_has_metadata_csum(dir->i_sb)) + buflen -= sizeof(struct ext4_dir_entry_tail); + + while ((char *) de < base + buflen) { + if (ext4_check_dir_entry(dir, NULL, de, bh, base, buflen, + ((char *)de) - base)) + return -EFSCORRUPTED; if (de->name_len && de->inode) { if (ext4_hash_in_dirent(dir)) h.hash = EXT4_DIRENT_HASH(de); @@ -1270,7 +1278,6 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, count++; cond_resched(); } - /* XXX: do we need to check rec_len == 0 case? -Chris */ de = ext4_next_entry(de, blocksize); } return count; @@ -1943,8 +1950,11 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, /* create map in the end of data2 block */ map = (struct dx_map_entry *) (data2 + blocksize); - count = dx_make_map(dir, (struct ext4_dir_entry_2 *) data1, - blocksize, hinfo, map); + count = dx_make_map(dir, *bh, hinfo, map); + if (count < 0) { + err = count; + goto journal_error; + } map -= count; dx_sort_map(map, count); /* Ensure that neither split block is over half full */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] ext4: Verify dir block before splitting it 2022-04-28 18:31 ` [PATCH 1/2] ext4: Verify dir block before splitting it Jan Kara @ 2022-05-17 23:40 ` Theodore Ts'o 2022-05-18 9:09 ` Jan Kara 0 siblings, 1 reply; 7+ messages in thread From: Theodore Ts'o @ 2022-05-17 23:40 UTC (permalink / raw) To: Jan Kara; +Cc: linux-ext4, stable On Thu, Apr 28, 2022 at 08:31:37PM +0200, Jan Kara wrote: > Before splitting a directory block verify its directory entries are sane > so that the splitting code does not access memory it should not. This commit fails to build due to an undefined variable. It's fixed with this hunk in the next patch, which needs to be brought back into this commit: diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 5951e9bb348e..7286472e9558 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1278,7 +1278,7 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, count++; cond_resched(); } - de = ext4_next_entry(de, blocksize); + de = ext4_next_entry(de, dir->i_sb->s_blocksize); } return count; } I was thinking about folding in this change and apply the patch with that change --- and I may yet do that --- but it looks like there's a bigger problem with this patch series, which is that it's causing a crash when running ext4/052 due to what appears to be a smashed stack. More about that in the reply to patch 2/2 of this series.... - Ted ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] ext4: Verify dir block before splitting it 2022-05-17 23:40 ` Theodore Ts'o @ 2022-05-18 9:09 ` Jan Kara 0 siblings, 0 replies; 7+ messages in thread From: Jan Kara @ 2022-05-18 9:09 UTC (permalink / raw) To: Theodore Ts'o; +Cc: Jan Kara, linux-ext4, stable On Tue 17-05-22 19:40:11, Theodore Ts'o wrote: > On Thu, Apr 28, 2022 at 08:31:37PM +0200, Jan Kara wrote: > > Before splitting a directory block verify its directory entries are sane > > so that the splitting code does not access memory it should not. > > This commit fails to build due to an undefined variable. It's fixed > with this hunk in the next patch, which needs to be brought back into > this commit: > > diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c > index 5951e9bb348e..7286472e9558 100644 > --- a/fs/ext4/namei.c > +++ b/fs/ext4/namei.c > @@ -1278,7 +1278,7 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, > count++; > cond_resched(); > } > - de = ext4_next_entry(de, blocksize); > + de = ext4_next_entry(de, dir->i_sb->s_blocksize); > } > return count; > } > > I was thinking about folding in this change and apply the patch with > that change --- and I may yet do that --- but it looks like there's a > bigger problem with this patch series, which is that it's causing a > crash when running ext4/052 due to what appears to be a smashed stack. > More about that in the reply to patch 2/2 of this series.... Yup, I'll fix that. Thanks for catching this. Honza -- Jan Kara <jack@suse.com> SUSE Labs, CR ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2022-05-19 2:17 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-05-18 9:33 [PATCH 0/2 v2] ext4: Fix crash when adding entry to corrupted directory Jan Kara 2022-05-18 9:33 ` [PATCH 1/2] ext4: Verify dir block before splitting it Jan Kara 2022-05-19 2:16 ` Theodore Ts'o 2022-05-18 9:33 ` [PATCH 2/2] ext4: Avoid cycles in directory h-tree Jan Kara -- strict thread matches above, loose matches on Subject: below -- 2022-04-28 18:31 [PATCH 0/2] ext4: Fix crash when adding entry to corrupted directory Jan Kara 2022-04-28 18:31 ` [PATCH 1/2] ext4: Verify dir block before splitting it Jan Kara 2022-05-17 23:40 ` Theodore Ts'o 2022-05-18 9:09 ` Jan Kara
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).