]> www.infradead.org Git - users/hch/dma-mapping.git/commitdiff
udf: refactor inode_bmap() to handle error
authorZhao Mengmeng <zhaomengmeng@kylinos.cn>
Tue, 1 Oct 2024 11:54:25 +0000 (19:54 +0800)
committerJan Kara <jack@suse.cz>
Wed, 2 Oct 2024 12:32:29 +0000 (14:32 +0200)
Refactor inode_bmap() to handle error since udf_next_aext() can return
error now. On situations like ftruncate, udf_extend_file() can now
detect errors and bail out early without resorting to checking for
particular offsets and assuming internal behavior of these functions.

Reported-by: syzbot+7a4842f0b1801230a989@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=7a4842f0b1801230a989
Tested-by: syzbot+7a4842f0b1801230a989@syzkaller.appspotmail.com
Signed-off-by: Zhao Mengmeng <zhaomengmeng@kylinos.cn>
Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20241001115425.266556-4-zhaomzhao@126.com
fs/udf/directory.c
fs/udf/inode.c
fs/udf/partition.c
fs/udf/truncate.c
fs/udf/udfdecl.h

index c6950050e7aeb939748aaa148c59389f61033c97..632453aa38934a0b58b420717ec0674c85627ce5 100644 (file)
@@ -246,6 +246,7 @@ int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
 {
        struct udf_inode_info *iinfo = UDF_I(dir);
        int err = 0;
+       int8_t etype;
 
        iter->dir = dir;
        iter->bh[0] = iter->bh[1] = NULL;
@@ -265,9 +266,9 @@ int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
                goto out;
        }
 
-       if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
-                      &iter->eloc, &iter->elen, &iter->loffset) !=
-           (EXT_RECORDED_ALLOCATED >> 30)) {
+       err = inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
+                        &iter->eloc, &iter->elen, &iter->loffset, &etype);
+       if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
                if (pos == dir->i_size)
                        return 0;
                udf_err(dir->i_sb,
@@ -463,6 +464,7 @@ int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
        sector_t block;
        uint32_t old_elen = iter->elen;
        int err;
+       int8_t etype;
 
        if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
                return -EINVAL;
@@ -477,8 +479,9 @@ int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
                udf_fiiter_update_elen(iter, old_elen);
                return err;
        }
-       if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
-                      &iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) {
+       err = inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
+                  &iter->loffset, &etype);
+       if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
                udf_err(iter->dir->i_sb,
                        "block %llu not allocated in directory (ino %lu)\n",
                        (unsigned long long)block, iter->dir->i_ino);
index 30f925f891b6fd2c4d61fde828291ca5609104c5..75c8cccfebd40b9dcc6f2ac0f1f9cf89cb4c0b5d 100644 (file)
@@ -404,7 +404,7 @@ struct udf_map_rq {
 
 static int udf_map_block(struct inode *inode, struct udf_map_rq *map)
 {
-       int err;
+       int ret;
        struct udf_inode_info *iinfo = UDF_I(inode);
 
        if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
@@ -416,18 +416,24 @@ static int udf_map_block(struct inode *inode, struct udf_map_rq *map)
                uint32_t elen;
                sector_t offset;
                struct extent_position epos = {};
+               int8_t etype;
 
                down_read(&iinfo->i_data_sem);
-               if (inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset)
-                               == (EXT_RECORDED_ALLOCATED >> 30)) {
+               ret = inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset,
+                                &etype);
+               if (ret < 0)
+                       goto out_read;
+               if (ret > 0 && etype == (EXT_RECORDED_ALLOCATED >> 30)) {
                        map->pblk = udf_get_lb_pblock(inode->i_sb, &eloc,
                                                        offset);
                        map->oflags |= UDF_BLK_MAPPED;
+                       ret = 0;
                }
+out_read:
                up_read(&iinfo->i_data_sem);
                brelse(epos.bh);
 
-               return 0;
+               return ret;
        }
 
        down_write(&iinfo->i_data_sem);
@@ -438,9 +444,9 @@ static int udf_map_block(struct inode *inode, struct udf_map_rq *map)
        if (((loff_t)map->lblk) << inode->i_blkbits >= iinfo->i_lenExtents)
                udf_discard_prealloc(inode);
        udf_clear_extent_cache(inode);
-       err = inode_getblk(inode, map);
+       ret = inode_getblk(inode, map);
        up_write(&iinfo->i_data_sem);
-       return err;
+       return ret;
 }
 
 static int __udf_get_block(struct inode *inode, sector_t block,
@@ -662,8 +668,10 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
         */
        udf_discard_prealloc(inode);
 
-       etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
-       within_last_ext = (etype != -1);
+       err = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset, &etype);
+       if (err < 0)
+               goto out;
+       within_last_ext = (err == 1);
        /* We don't expect extents past EOF... */
        WARN_ON_ONCE(within_last_ext &&
                     elen > ((loff_t)offset + 1) << inode->i_blkbits);
@@ -2401,13 +2409,15 @@ int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
        return (elen >> 30);
 }
 
-int8_t inode_bmap(struct inode *inode, sector_t block,
-                 struct extent_position *pos, struct kernel_lb_addr *eloc,
-                 uint32_t *elen, sector_t *offset)
+/*
+ * Returns 1 on success, -errno on error, 0 on hit EOF.
+ */
+int inode_bmap(struct inode *inode, sector_t block, struct extent_position *pos,
+              struct kernel_lb_addr *eloc, uint32_t *elen, sector_t *offset,
+              int8_t *etype)
 {
        unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
        loff_t lbcount = 0, bcount = (loff_t) block << blocksize_bits;
-       int8_t etype;
        struct udf_inode_info *iinfo;
        int err = 0;
 
@@ -2419,13 +2429,13 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
        }
        *elen = 0;
        do {
-               err = udf_next_aext(inode, pos, eloc, elen, &etype, 1);
+               err = udf_next_aext(inode, pos, eloc, elen, etype, 1);
                if (err <= 0) {
                        if (err == 0) {
                                *offset = (bcount - lbcount) >> blocksize_bits;
                                iinfo->i_lenExtents = lbcount;
                        }
-                       return -1;
+                       return err;
                }
                lbcount += *elen;
        } while (lbcount <= bcount);
@@ -2433,5 +2443,5 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
        udf_update_extent_cache(inode, lbcount - *elen, pos);
        *offset = (bcount + *elen - lbcount) >> blocksize_bits;
 
-       return etype;
+       return 1;
 }
index af877991edc13acb1c91b47c7bc4eb1f63d7e8f9..2b85c9501bed89a3e4e07d4ee26f8949078c4223 100644 (file)
@@ -282,9 +282,11 @@ static uint32_t udf_try_read_meta(struct inode *inode, uint32_t block,
        sector_t ext_offset;
        struct extent_position epos = {};
        uint32_t phyblock;
+       int8_t etype;
+       int err = 0;
 
-       if (inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset) !=
-                                               (EXT_RECORDED_ALLOCATED >> 30))
+       err = inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset, &etype);
+       if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30))
                phyblock = 0xFFFFFFFF;
        else {
                map = &UDF_SB(sb)->s_partmaps[partition];
index 399958f891d145c044b40f3360a69a64640e1eab..4f33a4a4888613b5f7a9d00d7e01565af9cc2e07 100644 (file)
@@ -214,10 +214,12 @@ int udf_truncate_extents(struct inode *inode)
        else
                BUG();
 
-       etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
+       ret = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset, &etype);
+       if (ret < 0)
+               return ret;
        byte_offset = (offset << sb->s_blocksize_bits) +
                (inode->i_size & (sb->s_blocksize - 1));
-       if (etype == -1) {
+       if (ret == 0) {
                /* We should extend the file? */
                WARN_ON(byte_offset);
                return 0;
index 5067ed68a8b4545c0de5e9eba594fd13e2f072b3..d159f20d61e89a293623ea182a7d507aa615781f 100644 (file)
@@ -157,8 +157,9 @@ extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
 extern int udf_setsize(struct inode *, loff_t);
 extern void udf_evict_inode(struct inode *);
 extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
-extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *,
-                        struct kernel_lb_addr *, uint32_t *, sector_t *);
+extern int inode_bmap(struct inode *inode, sector_t block,
+                     struct extent_position *pos, struct kernel_lb_addr *eloc,
+                     uint32_t *elen, sector_t *offset, int8_t *etype);
 int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
 extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
                                   struct extent_position *epos);