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

udf: Fix 64-bit sign extension issues affecting blocks > 0x7FFFFFFF

Large (> 1 TiB) UDF filesystems appear subject to several problems when
mounted on 64-bit systems:

* readdir() can fail on a directory containing File Identifiers residing
above 0x7FFFFFFF. This manifests as a 'ls' command failing with EIO.

* FIBMAP on a file block located above 0x7FFFFFFF can return a negative
value. The low 32 bits are correct, but applications that don't mask the
high 32 bits of the result can perform incorrectly.

Per suggestion by Jan Kara, introduce a udf_pblk_t type for representation
of UDF block addresses. Ultimately, all driver functions that manipulate
UDF block addresses should use this type; for now, deployment is limited
to functions with actual or potential sign extension issues.

Changes to udf_readdir() and udf_block_map() address the issues noted
above; other changes address potential similar issues uncovered during
audit of the driver code.

Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Steve Magnani and committed by
Jan Kara
b490bdd6 503c3117

+51 -41
+10 -7
fs/udf/balloc.c
··· 218 218 return alloc_count; 219 219 } 220 220 221 - static int udf_bitmap_new_block(struct super_block *sb, 221 + static udf_pblk_t udf_bitmap_new_block(struct super_block *sb, 222 222 struct udf_bitmap *bitmap, uint16_t partition, 223 223 uint32_t goal, int *err) 224 224 { 225 225 struct udf_sb_info *sbi = UDF_SB(sb); 226 - int newbit, bit = 0, block, block_group, group_start; 226 + int newbit, bit = 0; 227 + udf_pblk_t block; 228 + int block_group, group_start; 227 229 int end_goal, nr_groups, bitmap_nr, i; 228 230 struct buffer_head *bh = NULL; 229 231 char *ptr; 230 - int newblock = 0; 232 + udf_pblk_t newblock = 0; 231 233 232 234 *err = -ENOSPC; 233 235 mutex_lock(&sbi->s_alloc_mutex); ··· 547 545 return alloc_count; 548 546 } 549 547 550 - static int udf_table_new_block(struct super_block *sb, 548 + static udf_pblk_t udf_table_new_block(struct super_block *sb, 551 549 struct inode *table, uint16_t partition, 552 550 uint32_t goal, int *err) 553 551 { 554 552 struct udf_sb_info *sbi = UDF_SB(sb); 555 553 uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF; 556 - uint32_t newblock = 0, adsize; 554 + udf_pblk_t newblock = 0; 555 + uint32_t adsize; 557 556 uint32_t elen, goal_elen = 0; 558 557 struct kernel_lb_addr eloc, uninitialized_var(goal_eloc); 559 558 struct extent_position epos, goal_epos; ··· 703 700 return allocated; 704 701 } 705 702 706 - inline int udf_new_block(struct super_block *sb, 703 + inline udf_pblk_t udf_new_block(struct super_block *sb, 707 704 struct inode *inode, 708 705 uint16_t partition, uint32_t goal, int *err) 709 706 { 710 707 struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition]; 711 - int block; 708 + udf_pblk_t block; 712 709 713 710 if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) 714 711 block = udf_bitmap_new_block(sb,
+1 -1
fs/udf/dir.c
··· 43 43 struct udf_fileident_bh fibh = { .sbh = NULL, .ebh = NULL}; 44 44 struct fileIdentDesc *fi = NULL; 45 45 struct fileIdentDesc cfi; 46 - int block, iblock; 46 + udf_pblk_t block, iblock; 47 47 loff_t nf_pos; 48 48 int flen; 49 49 unsigned char *fname = NULL, *copy_name = NULL;
+2 -1
fs/udf/directory.c
··· 26 26 sector_t *offset) 27 27 { 28 28 struct fileIdentDesc *fi; 29 - int i, num, block; 29 + int i, num; 30 + udf_pblk_t block; 30 31 struct buffer_head *tmp, *bha[16]; 31 32 struct udf_inode_info *iinfo = UDF_I(dir); 32 33
+1 -1
fs/udf/ialloc.c
··· 50 50 struct super_block *sb = dir->i_sb; 51 51 struct udf_sb_info *sbi = UDF_SB(sb); 52 52 struct inode *inode; 53 - int block; 53 + udf_pblk_t block; 54 54 uint32_t start = UDF_I(dir)->i_location.logicalBlockNum; 55 55 struct udf_inode_info *iinfo; 56 56 struct udf_inode_info *dinfo = UDF_I(dir);
+15 -15
fs/udf/inode.c
··· 52 52 static sector_t inode_getblk(struct inode *, sector_t, int *, int *); 53 53 static int8_t udf_insert_aext(struct inode *, struct extent_position, 54 54 struct kernel_lb_addr, uint32_t); 55 - static void udf_split_extents(struct inode *, int *, int, int, 55 + static void udf_split_extents(struct inode *, int *, int, udf_pblk_t, 56 56 struct kernel_long_ad *, int *); 57 57 static void udf_prealloc_extents(struct inode *, int, int, 58 58 struct kernel_long_ad *, int *); ··· 316 316 return err; 317 317 } 318 318 319 - struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, int *block, 320 - int *err) 319 + struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, 320 + udf_pblk_t *block, int *err) 321 321 { 322 - int newblock; 322 + udf_pblk_t newblock; 323 323 struct buffer_head *dbh = NULL; 324 324 struct kernel_lb_addr eloc; 325 325 uint8_t alloctype; ··· 446 446 return err; 447 447 } 448 448 449 - static struct buffer_head *udf_getblk(struct inode *inode, long block, 449 + static struct buffer_head *udf_getblk(struct inode *inode, udf_pblk_t block, 450 450 int create, int *err) 451 451 { 452 452 struct buffer_head *bh; ··· 663 663 struct kernel_lb_addr eloc, tmpeloc; 664 664 int c = 1; 665 665 loff_t lbcount = 0, b_off = 0; 666 - uint32_t newblocknum, newblock; 666 + udf_pblk_t newblocknum, newblock; 667 667 sector_t offset = 0; 668 668 int8_t etype; 669 669 struct udf_inode_info *iinfo = UDF_I(inode); 670 - int goal = 0, pgoal = iinfo->i_location.logicalBlockNum; 670 + udf_pblk_t goal = 0, pgoal = iinfo->i_location.logicalBlockNum; 671 671 int lastblock = 0; 672 672 bool isBeyondEOF; 673 673 ··· 879 879 } 880 880 881 881 static void udf_split_extents(struct inode *inode, int *c, int offset, 882 - int newblocknum, struct kernel_long_ad *laarr, 883 - int *endnum) 882 + udf_pblk_t newblocknum, 883 + struct kernel_long_ad *laarr, int *endnum) 884 884 { 885 885 unsigned long blocksize = inode->i_sb->s_blocksize; 886 886 unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; ··· 1166 1166 } 1167 1167 } 1168 1168 1169 - struct buffer_head *udf_bread(struct inode *inode, int block, 1169 + struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block, 1170 1170 int create, int *err) 1171 1171 { 1172 1172 struct buffer_head *bh = NULL; ··· 1852 1852 return inode; 1853 1853 } 1854 1854 1855 - int udf_setup_indirect_aext(struct inode *inode, int block, 1855 + int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block, 1856 1856 struct extent_position *epos) 1857 1857 { 1858 1858 struct super_block *sb = inode->i_sb; ··· 1994 1994 1995 1995 if (epos->offset + (2 * adsize) > sb->s_blocksize) { 1996 1996 int err; 1997 - int new_block; 1997 + udf_pblk_t new_block; 1998 1998 1999 1999 new_block = udf_new_block(sb, NULL, 2000 2000 epos->block.partitionReferenceNum, ··· 2076 2076 2077 2077 while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) == 2078 2078 (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { 2079 - int block; 2079 + udf_pblk_t block; 2080 2080 2081 2081 if (++indirections > UDF_MAX_INDIR_EXTS) { 2082 2082 udf_err(inode->i_sb, ··· 2289 2289 return etype; 2290 2290 } 2291 2291 2292 - long udf_block_map(struct inode *inode, sector_t block) 2292 + udf_pblk_t udf_block_map(struct inode *inode, sector_t block) 2293 2293 { 2294 2294 struct kernel_lb_addr eloc; 2295 2295 uint32_t elen; 2296 2296 sector_t offset; 2297 2297 struct extent_position epos = {}; 2298 - int ret; 2298 + udf_pblk_t ret; 2299 2299 2300 2300 down_read(&UDF_I(inode)->i_data_sem); 2301 2301
+2 -2
fs/udf/misc.c
··· 28 28 #include "udf_i.h" 29 29 #include "udf_sb.h" 30 30 31 - struct buffer_head *udf_tgetblk(struct super_block *sb, int block) 31 + struct buffer_head *udf_tgetblk(struct super_block *sb, udf_pblk_t block) 32 32 { 33 33 if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) 34 34 return sb_getblk(sb, udf_fixed_to_variable(block)); ··· 36 36 return sb_getblk(sb, block); 37 37 } 38 38 39 - struct buffer_head *udf_tread(struct super_block *sb, int block) 39 + struct buffer_head *udf_tread(struct super_block *sb, udf_pblk_t block) 40 40 { 41 41 if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) 42 42 return sb_bread(sb, udf_fixed_to_variable(block));
+5 -4
fs/udf/namei.c
··· 164 164 { 165 165 struct fileIdentDesc *fi = NULL; 166 166 loff_t f_pos; 167 - int block, flen; 167 + udf_pblk_t block; 168 + int flen; 168 169 unsigned char *fname = NULL, *copy_name = NULL; 169 170 unsigned char *nameptr; 170 171 uint8_t lfi; ··· 353 352 int nfidlen; 354 353 uint8_t lfi; 355 354 uint16_t liu; 356 - int block; 355 + udf_pblk_t block; 357 356 struct kernel_lb_addr eloc; 358 357 uint32_t elen = 0; 359 358 sector_t offset; ··· 750 749 struct udf_fileident_bh fibh; 751 750 loff_t f_pos; 752 751 loff_t size = udf_ext0_offset(dir) + dir->i_size; 753 - int block; 752 + udf_pblk_t block; 754 753 struct kernel_lb_addr eloc; 755 754 uint32_t elen; 756 755 sector_t offset; ··· 914 913 int eoffset, elen = 0; 915 914 uint8_t *ea; 916 915 int err; 917 - int block; 916 + udf_pblk_t block; 918 917 unsigned char *name = NULL; 919 918 int namelen; 920 919 struct udf_inode_info *iinfo;
+1 -1
fs/udf/super.c
··· 2389 2389 struct buffer_head *bh = NULL; 2390 2390 unsigned int accum = 0; 2391 2391 int index; 2392 - int block = 0, newblock; 2392 + udf_pblk_t block = 0, newblock; 2393 2393 struct kernel_lb_addr loc; 2394 2394 uint32_t bytes; 2395 2395 uint8_t *ptr;
+1 -1
fs/udf/truncate.c
··· 48 48 49 49 if (elen != nelen) { 50 50 udf_write_aext(inode, epos, &neloc, nelen, 0); 51 - if (last_block - first_block > 0) { 51 + if (last_block > first_block) { 52 52 if (etype == (EXT_RECORDED_ALLOCATED >> 30)) 53 53 mark_inode_dirty(inode); 54 54
+13 -8
fs/udf/udfdecl.h
··· 73 73 /* computes tag checksum */ 74 74 u8 udf_tag_checksum(const struct tag *t); 75 75 76 + typedef uint32_t udf_pblk_t; 77 + 76 78 struct dentry; 77 79 struct inode; 78 80 struct task_struct; ··· 146 144 return __udf_iget(sb, ino, false); 147 145 } 148 146 extern int udf_expand_file_adinicb(struct inode *); 149 - extern struct buffer_head *udf_expand_dir_adinicb(struct inode *, int *, int *); 150 - extern struct buffer_head *udf_bread(struct inode *, int, int, int *); 147 + extern struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, 148 + udf_pblk_t *block, int *err); 149 + extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block, 150 + int create, int *err); 151 151 extern int udf_setsize(struct inode *, loff_t); 152 152 extern void udf_evict_inode(struct inode *); 153 153 extern int udf_write_inode(struct inode *, struct writeback_control *wbc); 154 - extern long udf_block_map(struct inode *, sector_t); 154 + extern udf_pblk_t udf_block_map(struct inode *inode, sector_t block); 155 155 extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *, 156 156 struct kernel_lb_addr *, uint32_t *, sector_t *); 157 - extern int udf_setup_indirect_aext(struct inode *inode, int block, 157 + extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block, 158 158 struct extent_position *epos); 159 159 extern int __udf_add_aext(struct inode *inode, struct extent_position *epos, 160 160 struct kernel_lb_addr *eloc, uint32_t elen, int inc); ··· 172 168 struct kernel_lb_addr *, uint32_t *, int); 173 169 174 170 /* misc.c */ 175 - extern struct buffer_head *udf_tgetblk(struct super_block *, int); 176 - extern struct buffer_head *udf_tread(struct super_block *, int); 171 + extern struct buffer_head *udf_tgetblk(struct super_block *sb, 172 + udf_pblk_t block); 173 + extern struct buffer_head *udf_tread(struct super_block *sb, udf_pblk_t block); 177 174 extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, 178 175 uint32_t, uint8_t); 179 176 extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, ··· 233 228 struct kernel_lb_addr *, uint32_t, uint32_t); 234 229 extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, 235 230 uint32_t, uint32_t); 236 - extern int udf_new_block(struct super_block *, struct inode *, uint16_t, 237 - uint32_t, int *); 231 + extern udf_pblk_t udf_new_block(struct super_block *sb, struct inode *inode, 232 + uint16_t partition, uint32_t goal, int *err); 238 233 239 234 /* directory.c */ 240 235 extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *,