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>
This commit is contained in:
Steve Magnani 2017-10-12 08:48:40 -05:00 committed by Jan Kara
parent 503c3117d0
commit b490bdd630
10 changed files with 51 additions and 41 deletions

View File

@ -218,16 +218,18 @@ out:
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,

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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 @@ abort:
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 @@ out_free:
} }
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);

View File

@ -28,7 +28,7 @@
#include "udf_i.h" #include "udf_i.h"
#include "udf_sb.h" #include "udf_sb.h"
struct buffer_head *udf_tgetblk(struct super_block *sb, int block) struct buffer_head *udf_tgetblk(struct super_block *sb, udf_pblk_t block)
{ {
if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
return sb_getblk(sb, udf_fixed_to_variable(block)); return sb_getblk(sb, udf_fixed_to_variable(block));
@ -36,7 +36,7 @@ struct buffer_head *udf_tgetblk(struct super_block *sb, int block)
return sb_getblk(sb, block); return sb_getblk(sb, block);
} }
struct buffer_head *udf_tread(struct super_block *sb, int block) struct buffer_head *udf_tread(struct super_block *sb, udf_pblk_t block)
{ {
if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
return sb_bread(sb, udf_fixed_to_variable(block)); return sb_bread(sb, udf_fixed_to_variable(block));

View File

@ -164,7 +164,8 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir,
{ {
struct fileIdentDesc *fi = NULL; struct fileIdentDesc *fi = NULL;
loff_t f_pos; loff_t f_pos;
int block, flen; udf_pblk_t block;
int flen;
unsigned char *fname = NULL, *copy_name = NULL; unsigned char *fname = NULL, *copy_name = NULL;
unsigned char *nameptr; unsigned char *nameptr;
uint8_t lfi; uint8_t lfi;
@ -352,7 +353,7 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir,
int nfidlen; int nfidlen;
uint8_t lfi; uint8_t lfi;
uint16_t liu; uint16_t liu;
int block; udf_pblk_t block;
struct kernel_lb_addr eloc; struct kernel_lb_addr eloc;
uint32_t elen = 0; uint32_t elen = 0;
sector_t offset; sector_t offset;
@ -749,7 +750,7 @@ static int empty_dir(struct inode *dir)
struct udf_fileident_bh fibh; struct udf_fileident_bh fibh;
loff_t f_pos; loff_t f_pos;
loff_t size = udf_ext0_offset(dir) + dir->i_size; loff_t size = udf_ext0_offset(dir) + dir->i_size;
int block; udf_pblk_t block;
struct kernel_lb_addr eloc; struct kernel_lb_addr eloc;
uint32_t elen; uint32_t elen;
sector_t offset; sector_t offset;
@ -913,7 +914,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
int eoffset, elen = 0; int eoffset, elen = 0;
uint8_t *ea; uint8_t *ea;
int err; int err;
int block; udf_pblk_t block;
unsigned char *name = NULL; unsigned char *name = NULL;
int namelen; int namelen;
struct udf_inode_info *iinfo; struct udf_inode_info *iinfo;

View File

@ -2389,7 +2389,7 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb,
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
unsigned int accum = 0; unsigned int accum = 0;
int index; int index;
int block = 0, newblock; udf_pblk_t block = 0, newblock;
struct kernel_lb_addr loc; struct kernel_lb_addr loc;
uint32_t bytes; uint32_t bytes;
uint8_t *ptr; uint8_t *ptr;

View File

@ -48,7 +48,7 @@ static void extent_trunc(struct inode *inode, struct extent_position *epos,
if (elen != nelen) { if (elen != nelen) {
udf_write_aext(inode, epos, &neloc, nelen, 0); udf_write_aext(inode, epos, &neloc, nelen, 0);
if (last_block - first_block > 0) { if (last_block > first_block) {
if (etype == (EXT_RECORDED_ALLOCATED >> 30)) if (etype == (EXT_RECORDED_ALLOCATED >> 30))
mark_inode_dirty(inode); mark_inode_dirty(inode);

View File

@ -73,6 +73,8 @@ static inline size_t udf_ext0_offset(struct inode *inode)
/* computes tag checksum */ /* computes tag checksum */
u8 udf_tag_checksum(const struct tag *t); u8 udf_tag_checksum(const struct tag *t);
typedef uint32_t udf_pblk_t;
struct dentry; struct dentry;
struct inode; struct inode;
struct task_struct; struct task_struct;
@ -144,15 +146,17 @@ static inline struct inode *udf_iget(struct super_block *sb,
return __udf_iget(sb, ino, false); return __udf_iget(sb, ino, false);
} }
extern int udf_expand_file_adinicb(struct inode *); extern int udf_expand_file_adinicb(struct inode *);
extern struct buffer_head *udf_expand_dir_adinicb(struct inode *, int *, int *); extern struct buffer_head *udf_expand_dir_adinicb(struct inode *inode,
extern struct buffer_head *udf_bread(struct inode *, int, int, int *); udf_pblk_t *block, int *err);
extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
int create, int *err);
extern int udf_setsize(struct inode *, loff_t); extern int udf_setsize(struct inode *, loff_t);
extern void udf_evict_inode(struct inode *); extern void udf_evict_inode(struct inode *);
extern int udf_write_inode(struct inode *, struct writeback_control *wbc); extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
extern long udf_block_map(struct inode *, sector_t); extern udf_pblk_t udf_block_map(struct inode *inode, sector_t block);
extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *, extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *,
struct kernel_lb_addr *, uint32_t *, sector_t *); struct kernel_lb_addr *, uint32_t *, sector_t *);
extern int udf_setup_indirect_aext(struct inode *inode, int block, extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
struct extent_position *epos); struct extent_position *epos);
extern int __udf_add_aext(struct inode *inode, struct extent_position *epos, extern int __udf_add_aext(struct inode *inode, struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t elen, int inc); struct kernel_lb_addr *eloc, uint32_t elen, int inc);
@ -168,8 +172,9 @@ extern int8_t udf_current_aext(struct inode *, struct extent_position *,
struct kernel_lb_addr *, uint32_t *, int); struct kernel_lb_addr *, uint32_t *, int);
/* misc.c */ /* misc.c */
extern struct buffer_head *udf_tgetblk(struct super_block *, int); extern struct buffer_head *udf_tgetblk(struct super_block *sb,
extern struct buffer_head *udf_tread(struct super_block *, int); udf_pblk_t block);
extern struct buffer_head *udf_tread(struct super_block *sb, udf_pblk_t block);
extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t,
uint32_t, uint8_t); uint32_t, uint8_t);
extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t,
@ -228,8 +233,8 @@ extern void udf_free_blocks(struct super_block *, struct inode *,
struct kernel_lb_addr *, uint32_t, uint32_t); struct kernel_lb_addr *, uint32_t, uint32_t);
extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t,
uint32_t, uint32_t); uint32_t, uint32_t);
extern int udf_new_block(struct super_block *, struct inode *, uint16_t, extern udf_pblk_t udf_new_block(struct super_block *sb, struct inode *inode,
uint32_t, int *); uint16_t partition, uint32_t goal, int *err);
/* directory.c */ /* directory.c */
extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *, extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *,