Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b490bdd6 authored by Steve Magnani's avatar Steve Magnani Committed by Jan Kara
Browse files

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: default avatarSteven J. Magnani <steve@digidescorp.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 503c3117
Loading
Loading
Loading
Loading
+10 −7
Original line number Original line Diff line number Diff line
@@ -218,16 +218,18 @@ static int udf_bitmap_prealloc_blocks(struct super_block *sb,
	return alloc_count;
	return alloc_count;
}
}


static int udf_bitmap_new_block(struct super_block *sb,
static udf_pblk_t udf_bitmap_new_block(struct super_block *sb,
				struct udf_bitmap *bitmap, uint16_t partition,
				struct udf_bitmap *bitmap, uint16_t partition,
				uint32_t goal, int *err)
				uint32_t goal, int *err)
{
{
	struct udf_sb_info *sbi = UDF_SB(sb);
	struct udf_sb_info *sbi = UDF_SB(sb);
	int newbit, bit = 0, block, block_group, group_start;
	int newbit, bit = 0;
	udf_pblk_t block;
	int block_group, group_start;
	int end_goal, nr_groups, bitmap_nr, i;
	int end_goal, nr_groups, bitmap_nr, i;
	struct buffer_head *bh = NULL;
	struct buffer_head *bh = NULL;
	char *ptr;
	char *ptr;
	int newblock = 0;
	udf_pblk_t newblock = 0;


	*err = -ENOSPC;
	*err = -ENOSPC;
	mutex_lock(&sbi->s_alloc_mutex);
	mutex_lock(&sbi->s_alloc_mutex);
@@ -545,13 +547,14 @@ static int udf_table_prealloc_blocks(struct super_block *sb,
	return alloc_count;
	return alloc_count;
}
}


static int udf_table_new_block(struct super_block *sb,
static udf_pblk_t udf_table_new_block(struct super_block *sb,
			       struct inode *table, uint16_t partition,
			       struct inode *table, uint16_t partition,
			       uint32_t goal, int *err)
			       uint32_t goal, int *err)
{
{
	struct udf_sb_info *sbi = UDF_SB(sb);
	struct udf_sb_info *sbi = UDF_SB(sb);
	uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF;
	uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF;
	uint32_t newblock = 0, adsize;
	udf_pblk_t newblock = 0;
	uint32_t adsize;
	uint32_t elen, goal_elen = 0;
	uint32_t elen, goal_elen = 0;
	struct kernel_lb_addr eloc, uninitialized_var(goal_eloc);
	struct kernel_lb_addr eloc, uninitialized_var(goal_eloc);
	struct extent_position epos, goal_epos;
	struct extent_position epos, goal_epos;
@@ -700,12 +703,12 @@ inline int udf_prealloc_blocks(struct super_block *sb,
	return allocated;
	return allocated;
}
}


inline int udf_new_block(struct super_block *sb,
inline udf_pblk_t udf_new_block(struct super_block *sb,
			 struct inode *inode,
			 struct inode *inode,
			 uint16_t partition, uint32_t goal, int *err)
			 uint16_t partition, uint32_t goal, int *err)
{
{
	struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
	struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
	int block;
	udf_pblk_t block;


	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP)
	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP)
		block = udf_bitmap_new_block(sb,
		block = udf_bitmap_new_block(sb,
+1 −1
Original line number Original line Diff line number Diff line
@@ -43,7 +43,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
	struct udf_fileident_bh fibh = { .sbh = NULL, .ebh = NULL};
	struct udf_fileident_bh fibh = { .sbh = NULL, .ebh = NULL};
	struct fileIdentDesc *fi = NULL;
	struct fileIdentDesc *fi = NULL;
	struct fileIdentDesc cfi;
	struct fileIdentDesc cfi;
	int block, iblock;
	udf_pblk_t block, iblock;
	loff_t nf_pos;
	loff_t nf_pos;
	int flen;
	int flen;
	unsigned char *fname = NULL, *copy_name = NULL;
	unsigned char *fname = NULL, *copy_name = NULL;
+2 −1
Original line number Original line Diff line number Diff line
@@ -26,7 +26,8 @@ struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
					 sector_t *offset)
					 sector_t *offset)
{
{
	struct fileIdentDesc *fi;
	struct fileIdentDesc *fi;
	int i, num, block;
	int i, num;
	udf_pblk_t block;
	struct buffer_head *tmp, *bha[16];
	struct buffer_head *tmp, *bha[16];
	struct udf_inode_info *iinfo = UDF_I(dir);
	struct udf_inode_info *iinfo = UDF_I(dir);


+1 −1
Original line number Original line Diff line number Diff line
@@ -50,7 +50,7 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode)
	struct super_block *sb = dir->i_sb;
	struct super_block *sb = dir->i_sb;
	struct udf_sb_info *sbi = UDF_SB(sb);
	struct udf_sb_info *sbi = UDF_SB(sb);
	struct inode *inode;
	struct inode *inode;
	int block;
	udf_pblk_t block;
	uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
	uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
	struct udf_inode_info *iinfo;
	struct udf_inode_info *iinfo;
	struct udf_inode_info *dinfo = UDF_I(dir);
	struct udf_inode_info *dinfo = UDF_I(dir);
+15 −15
Original line number Original line Diff line number Diff line
@@ -52,7 +52,7 @@ static int udf_alloc_i_data(struct inode *inode, size_t size);
static sector_t inode_getblk(struct inode *, sector_t, int *, int *);
static sector_t inode_getblk(struct inode *, sector_t, int *, int *);
static int8_t udf_insert_aext(struct inode *, struct extent_position,
static int8_t udf_insert_aext(struct inode *, struct extent_position,
			      struct kernel_lb_addr, uint32_t);
			      struct kernel_lb_addr, uint32_t);
static void udf_split_extents(struct inode *, int *, int, int,
static void udf_split_extents(struct inode *, int *, int, udf_pblk_t,
			      struct kernel_long_ad *, int *);
			      struct kernel_long_ad *, int *);
static void udf_prealloc_extents(struct inode *, int, int,
static void udf_prealloc_extents(struct inode *, int, int,
				 struct kernel_long_ad *, int *);
				 struct kernel_long_ad *, int *);
@@ -316,10 +316,10 @@ int udf_expand_file_adinicb(struct inode *inode)
	return err;
	return err;
}
}


struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, int *block,
struct buffer_head *udf_expand_dir_adinicb(struct inode *inode,
					   int *err)
					    udf_pblk_t *block, int *err)
{
{
	int newblock;
	udf_pblk_t newblock;
	struct buffer_head *dbh = NULL;
	struct buffer_head *dbh = NULL;
	struct kernel_lb_addr eloc;
	struct kernel_lb_addr eloc;
	uint8_t alloctype;
	uint8_t alloctype;
@@ -446,7 +446,7 @@ static int udf_get_block(struct inode *inode, sector_t block,
	return err;
	return err;
}
}


static struct buffer_head *udf_getblk(struct inode *inode, long block,
static struct buffer_head *udf_getblk(struct inode *inode, udf_pblk_t block,
				      int create, int *err)
				      int create, int *err)
{
{
	struct buffer_head *bh;
	struct buffer_head *bh;
@@ -663,11 +663,11 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
	struct kernel_lb_addr eloc, tmpeloc;
	struct kernel_lb_addr eloc, tmpeloc;
	int c = 1;
	int c = 1;
	loff_t lbcount = 0, b_off = 0;
	loff_t lbcount = 0, b_off = 0;
	uint32_t newblocknum, newblock;
	udf_pblk_t newblocknum, newblock;
	sector_t offset = 0;
	sector_t offset = 0;
	int8_t etype;
	int8_t etype;
	struct udf_inode_info *iinfo = UDF_I(inode);
	struct udf_inode_info *iinfo = UDF_I(inode);
	int goal = 0, pgoal = iinfo->i_location.logicalBlockNum;
	udf_pblk_t goal = 0, pgoal = iinfo->i_location.logicalBlockNum;
	int lastblock = 0;
	int lastblock = 0;
	bool isBeyondEOF;
	bool isBeyondEOF;


@@ -879,8 +879,8 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
}
}


static void udf_split_extents(struct inode *inode, int *c, int offset,
static void udf_split_extents(struct inode *inode, int *c, int offset,
			      int newblocknum, struct kernel_long_ad *laarr,
			       udf_pblk_t newblocknum,
			      int *endnum)
			       struct kernel_long_ad *laarr, int *endnum)
{
{
	unsigned long blocksize = inode->i_sb->s_blocksize;
	unsigned long blocksize = inode->i_sb->s_blocksize;
	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
@@ -1166,7 +1166,7 @@ static void udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr
	}
	}
}
}


struct buffer_head *udf_bread(struct inode *inode, int block,
struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
			      int create, int *err)
			      int create, int *err)
{
{
	struct buffer_head *bh = NULL;
	struct buffer_head *bh = NULL;
@@ -1852,7 +1852,7 @@ struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
	return inode;
	return inode;
}
}


int udf_setup_indirect_aext(struct inode *inode, int block,
int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
			    struct extent_position *epos)
			    struct extent_position *epos)
{
{
	struct super_block *sb = inode->i_sb;
	struct super_block *sb = inode->i_sb;
@@ -1994,7 +1994,7 @@ int udf_add_aext(struct inode *inode, struct extent_position *epos,


	if (epos->offset + (2 * adsize) > sb->s_blocksize) {
	if (epos->offset + (2 * adsize) > sb->s_blocksize) {
		int err;
		int err;
		int new_block;
		udf_pblk_t new_block;


		new_block = udf_new_block(sb, NULL,
		new_block = udf_new_block(sb, NULL,
					  epos->block.partitionReferenceNum,
					  epos->block.partitionReferenceNum,
@@ -2076,7 +2076,7 @@ int8_t udf_next_aext(struct inode *inode, struct extent_position *epos,


	while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) ==
	while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) ==
	       (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) {
	       (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) {
		int block;
		udf_pblk_t block;


		if (++indirections > UDF_MAX_INDIR_EXTS) {
		if (++indirections > UDF_MAX_INDIR_EXTS) {
			udf_err(inode->i_sb,
			udf_err(inode->i_sb,
@@ -2289,13 +2289,13 @@ int8_t inode_bmap(struct inode *inode, sector_t block,
	return etype;
	return etype;
}
}


long udf_block_map(struct inode *inode, sector_t block)
udf_pblk_t udf_block_map(struct inode *inode, sector_t block)
{
{
	struct kernel_lb_addr eloc;
	struct kernel_lb_addr eloc;
	uint32_t elen;
	uint32_t elen;
	sector_t offset;
	sector_t offset;
	struct extent_position epos = {};
	struct extent_position epos = {};
	int ret;
	udf_pblk_t ret;


	down_read(&UDF_I(inode)->i_data_sem);
	down_read(&UDF_I(inode)->i_data_sem);


Loading