[lvc-project] [PATCH] ocfs2: consistently validate extent lists in __ocfs2_find_path()
Dmitry Antipov
dmantipov at yandex.ru
Tue Dec 16 14:19:07 MSK 2025
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>
---
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