Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

udf: refactor inode_bmap() to handle error

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

authored by

Zhao Mengmeng and committed by
Jan Kara
c226964e b405c1e5

+44 -26
+8 -5
fs/udf/directory.c
··· 246 246 { 247 247 struct udf_inode_info *iinfo = UDF_I(dir); 248 248 int err = 0; 249 + int8_t etype; 249 250 250 251 iter->dir = dir; 251 252 iter->bh[0] = iter->bh[1] = NULL; ··· 266 265 goto out; 267 266 } 268 267 269 - if (inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos, 270 - &iter->eloc, &iter->elen, &iter->loffset) != 271 - (EXT_RECORDED_ALLOCATED >> 30)) { 268 + err = inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos, 269 + &iter->eloc, &iter->elen, &iter->loffset, &etype); 270 + if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) { 272 271 if (pos == dir->i_size) 273 272 return 0; 274 273 udf_err(dir->i_sb, ··· 464 463 sector_t block; 465 464 uint32_t old_elen = iter->elen; 466 465 int err; 466 + int8_t etype; 467 467 468 468 if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)) 469 469 return -EINVAL; ··· 479 477 udf_fiiter_update_elen(iter, old_elen); 480 478 return err; 481 479 } 482 - if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen, 483 - &iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) { 480 + err = inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen, 481 + &iter->loffset, &etype); 482 + if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) { 484 483 udf_err(iter->dir->i_sb, 485 484 "block %llu not allocated in directory (ino %lu)\n", 486 485 (unsigned long long)block, iter->dir->i_ino);
+25 -15
fs/udf/inode.c
··· 404 404 405 405 static int udf_map_block(struct inode *inode, struct udf_map_rq *map) 406 406 { 407 - int err; 407 + int ret; 408 408 struct udf_inode_info *iinfo = UDF_I(inode); 409 409 410 410 if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)) ··· 416 416 uint32_t elen; 417 417 sector_t offset; 418 418 struct extent_position epos = {}; 419 + int8_t etype; 419 420 420 421 down_read(&iinfo->i_data_sem); 421 - if (inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset) 422 - == (EXT_RECORDED_ALLOCATED >> 30)) { 422 + ret = inode_bmap(inode, map->lblk, &epos, &eloc, &elen, &offset, 423 + &etype); 424 + if (ret < 0) 425 + goto out_read; 426 + if (ret > 0 && etype == (EXT_RECORDED_ALLOCATED >> 30)) { 423 427 map->pblk = udf_get_lb_pblock(inode->i_sb, &eloc, 424 428 offset); 425 429 map->oflags |= UDF_BLK_MAPPED; 430 + ret = 0; 426 431 } 432 + out_read: 427 433 up_read(&iinfo->i_data_sem); 428 434 brelse(epos.bh); 429 435 430 - return 0; 436 + return ret; 431 437 } 432 438 433 439 down_write(&iinfo->i_data_sem); ··· 444 438 if (((loff_t)map->lblk) << inode->i_blkbits >= iinfo->i_lenExtents) 445 439 udf_discard_prealloc(inode); 446 440 udf_clear_extent_cache(inode); 447 - err = inode_getblk(inode, map); 441 + ret = inode_getblk(inode, map); 448 442 up_write(&iinfo->i_data_sem); 449 - return err; 443 + return ret; 450 444 } 451 445 452 446 static int __udf_get_block(struct inode *inode, sector_t block, ··· 668 662 */ 669 663 udf_discard_prealloc(inode); 670 664 671 - etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); 672 - within_last_ext = (etype != -1); 665 + err = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset, &etype); 666 + if (err < 0) 667 + goto out; 668 + within_last_ext = (err == 1); 673 669 /* We don't expect extents past EOF... */ 674 670 WARN_ON_ONCE(within_last_ext && 675 671 elen > ((loff_t)offset + 1) << inode->i_blkbits); ··· 2409 2401 return (elen >> 30); 2410 2402 } 2411 2403 2412 - int8_t inode_bmap(struct inode *inode, sector_t block, 2413 - struct extent_position *pos, struct kernel_lb_addr *eloc, 2414 - uint32_t *elen, sector_t *offset) 2404 + /* 2405 + * Returns 1 on success, -errno on error, 0 on hit EOF. 2406 + */ 2407 + int inode_bmap(struct inode *inode, sector_t block, struct extent_position *pos, 2408 + struct kernel_lb_addr *eloc, uint32_t *elen, sector_t *offset, 2409 + int8_t *etype) 2415 2410 { 2416 2411 unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; 2417 2412 loff_t lbcount = 0, bcount = (loff_t) block << blocksize_bits; 2418 - int8_t etype; 2419 2413 struct udf_inode_info *iinfo; 2420 2414 int err = 0; 2421 2415 ··· 2429 2419 } 2430 2420 *elen = 0; 2431 2421 do { 2432 - err = udf_next_aext(inode, pos, eloc, elen, &etype, 1); 2422 + err = udf_next_aext(inode, pos, eloc, elen, etype, 1); 2433 2423 if (err <= 0) { 2434 2424 if (err == 0) { 2435 2425 *offset = (bcount - lbcount) >> blocksize_bits; 2436 2426 iinfo->i_lenExtents = lbcount; 2437 2427 } 2438 - return -1; 2428 + return err; 2439 2429 } 2440 2430 lbcount += *elen; 2441 2431 } while (lbcount <= bcount); ··· 2443 2433 udf_update_extent_cache(inode, lbcount - *elen, pos); 2444 2434 *offset = (bcount + *elen - lbcount) >> blocksize_bits; 2445 2435 2446 - return etype; 2436 + return 1; 2447 2437 }
+4 -2
fs/udf/partition.c
··· 282 282 sector_t ext_offset; 283 283 struct extent_position epos = {}; 284 284 uint32_t phyblock; 285 + int8_t etype; 286 + int err = 0; 285 287 286 - if (inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset) != 287 - (EXT_RECORDED_ALLOCATED >> 30)) 288 + err = inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset, &etype); 289 + if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) 288 290 phyblock = 0xFFFFFFFF; 289 291 else { 290 292 map = &UDF_SB(sb)->s_partmaps[partition];
+4 -2
fs/udf/truncate.c
··· 214 214 else 215 215 BUG(); 216 216 217 - etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); 217 + ret = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset, &etype); 218 + if (ret < 0) 219 + return ret; 218 220 byte_offset = (offset << sb->s_blocksize_bits) + 219 221 (inode->i_size & (sb->s_blocksize - 1)); 220 - if (etype == -1) { 222 + if (ret == 0) { 221 223 /* We should extend the file? */ 222 224 WARN_ON(byte_offset); 223 225 return 0;
+3 -2
fs/udf/udfdecl.h
··· 157 157 extern int udf_setsize(struct inode *, loff_t); 158 158 extern void udf_evict_inode(struct inode *); 159 159 extern int udf_write_inode(struct inode *, struct writeback_control *wbc); 160 - extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *, 161 - struct kernel_lb_addr *, uint32_t *, sector_t *); 160 + extern int inode_bmap(struct inode *inode, sector_t block, 161 + struct extent_position *pos, struct kernel_lb_addr *eloc, 162 + uint32_t *elen, sector_t *offset, int8_t *etype); 162 163 int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); 163 164 extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block, 164 165 struct extent_position *epos);