[lvc-project] [PATCH] ocfs2: consistently validate extent lists in __ocfs2_find_path()
Heming Zhao
heming.zhao at suse.com
Fri Dec 19 07:28:28 MSK 2025
On Tue, Dec 16, 2025 at 02:19:07PM +0300, Dmitry Antipov wrote:
> Introduce 'ocfs2_validate_extent_list()' to validate all of the extent
> lists processed by '__ocfs2_find_path()' and reject ones of an invalid
> length, whatever it comes from a regular inode or from an extent block.
>
> Reported-by: syzbot+151afab124dfbc5f15e6 at syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=151afab124dfbc5f15e6
> Signed-off-by: Dmitry Antipov <dmantipov at yandex.ru>
Hi,
As I mentioned in another thread [1], all __ocfs2_find_path() operations occur
in memory, Please don't introduce I/O into this function.
btw, I have also provided a fix for this syzbot issue in the same thread [1].
[1]:
https://lore.kernel.org/ocfs2-devel/693fd627.a70a0220.33cd7b.00f4.GAE@google.com/T/#t
Thanks,
Heming
> ---
> fs/ocfs2/alloc.c | 86 +++++++++++++++++++++++++++++++++---------------
> 1 file changed, 59 insertions(+), 27 deletions(-)
>
> diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
> index 58bf58b68955..22e2de4fb3b0 100644
> --- a/fs/ocfs2/alloc.c
> +++ b/fs/ocfs2/alloc.c
> @@ -1784,6 +1784,53 @@ int ocfs2_find_subtree_root(struct ocfs2_extent_tree *et,
>
> typedef void (path_insert_t)(void *, struct buffer_head *);
>
> +/*
> + * Validate an extent list read from the regular inode or from an extent block.
> + */
> +static int ocfs2_validate_extent_list(struct ocfs2_caching_info *ci,
> + struct super_block *sb,
> + struct ocfs2_extent_list *el,
> + struct buffer_head *bh)
> +{
> + unsigned long long owner = ocfs2_metadata_cache_owner(ci);
> + u8 signature[8];
> + int count;
> +
> + if (le16_to_cpu(el->l_tree_depth) >= OCFS2_MAX_PATH_DEPTH) {
> + ocfs2_error(sb, "Owner %llu has an extent list with invalid tree depth %u\n",
> + owner, le16_to_cpu(el->l_tree_depth));
> + return 1;
> + }
> +
> + if (OCFS2_IS_VALID_DINODE((struct ocfs2_dinode *)bh->b_data))
> + count = ocfs2_extent_recs_per_inode_with_xattr
> + (sb, (struct ocfs2_dinode *)bh->b_data);
> + else if (OCFS2_IS_VALID_EXTENT_BLOCK((struct ocfs2_extent_block *)bh->b_data))
> + count = ocfs2_extent_recs_per_eb(sb);
> + else {
> + /* Should be NUL terminated to print it safely. */
> + strscpy(signature, bh->b_data);
> + ocfs2_error(sb, "Owner %llu has unexpected signature '%s'\n",
> + owner, signature);
> + return 1;
> + }
> +
> + if (le16_to_cpu(el->l_count) != count) {
> + ocfs2_error(sb, "Owner %llu has an extent list with invalid length %u\n",
> + owner, le16_to_cpu(el->l_count));
> + return 1;
> + }
> +
> + if (le16_to_cpu(el->l_next_free_rec) == 0 ||
> + (le16_to_cpu(el->l_next_free_rec) > count)) {
> + ocfs2_error(sb, "Owner %llu has an extent list with invalid unused slot %u\n",
> + owner, le16_to_cpu(el->l_next_free_rec));
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> /*
> * Traverse a btree path in search of cpos, starting at root_el.
> *
> @@ -1797,31 +1844,23 @@ static int __ocfs2_find_path(struct ocfs2_caching_info *ci,
> int i, ret = 0;
> u32 range;
> u64 blkno;
> - struct buffer_head *bh = NULL;
> struct ocfs2_extent_block *eb;
> struct ocfs2_extent_list *el;
> struct ocfs2_extent_rec *rec;
> + struct super_block *sb = ocfs2_metadata_cache_get_super(ci);
> + struct buffer_head *bh = sb_getblk(sb, ocfs2_metadata_cache_owner(ci));
>
> + if (!bh) {
> + ret = -ENOMEM;
> + goto out;
> + }
> el = root_el;
> - while (el->l_tree_depth) {
> - if (unlikely(le16_to_cpu(el->l_tree_depth) >= OCFS2_MAX_PATH_DEPTH)) {
> - ocfs2_error(ocfs2_metadata_cache_get_super(ci),
> - "Owner %llu has invalid tree depth %u in extent list\n",
> - (unsigned long long)ocfs2_metadata_cache_owner(ci),
> - le16_to_cpu(el->l_tree_depth));
> - ret = -EROFS;
> - goto out;
> - }
> - if (le16_to_cpu(el->l_next_free_rec) == 0) {
> - ocfs2_error(ocfs2_metadata_cache_get_super(ci),
> - "Owner %llu has empty extent list at depth %u\n",
> - (unsigned long long)ocfs2_metadata_cache_owner(ci),
> - le16_to_cpu(el->l_tree_depth));
> - ret = -EROFS;
> - goto out;
> -
> - }
> + if (ocfs2_validate_extent_list(ci, sb, el, bh)) {
> + ret = -EROFS;
> + goto out;
> + }
>
> + while (el->l_tree_depth) {
> for(i = 0; i < le16_to_cpu(el->l_next_free_rec) - 1; i++) {
> rec = &el->l_recs[i];
>
> @@ -1857,14 +1896,7 @@ static int __ocfs2_find_path(struct ocfs2_caching_info *ci,
> eb = (struct ocfs2_extent_block *) bh->b_data;
> el = &eb->h_list;
>
> - if (le16_to_cpu(el->l_next_free_rec) >
> - le16_to_cpu(el->l_count)) {
> - ocfs2_error(ocfs2_metadata_cache_get_super(ci),
> - "Owner %llu has bad count in extent list at block %llu (next free=%u, count=%u)\n",
> - (unsigned long long)ocfs2_metadata_cache_owner(ci),
> - (unsigned long long)bh->b_blocknr,
> - le16_to_cpu(el->l_next_free_rec),
> - le16_to_cpu(el->l_count));
> + if (ocfs2_validate_extent_list(ci, sb, el, bh)) {
> ret = -EROFS;
> goto out;
> }
> --
> 2.52.0
>
More information about the lvc-project
mailing list