diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-06-28 11:43:45 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-06-28 11:43:45 -0700 | 
| commit | 221d3ebf3ad2c69704e21495e9a936c78d797732 (patch) | |
| tree | 49df5243c7fbf2705b5118ae3aae0ed755eee8da /fs/udf | |
| parent | 9a7c6b73c40e916861eb852dc5855ffb3d58a3cd (diff) | |
| parent | 1df2ae31c724e57be9d7ac00d78db8a5dabdd050 (diff) | |
| download | linux-221d3ebf3ad2c69704e21495e9a936c78d797732.tar.bz2 | |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
Pull UDF fixes from Jan Kara:
 "Make UDF more robust in presence of corrupted filesystem"
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  udf: Fortify loading of sparing table
  udf: Avoid run away loop when partition table length is corrupted
  udf: Use 'ret' instead of abusing 'i' in udf_load_logicalvol()
Diffstat (limited to 'fs/udf')
| -rw-r--r-- | fs/udf/super.c | 102 | 
1 files changed, 64 insertions, 38 deletions
| diff --git a/fs/udf/super.c b/fs/udf/super.c index ac8a348dcb69..8d86a8706c0e 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -56,6 +56,7 @@  #include <linux/seq_file.h>  #include <linux/bitmap.h>  #include <linux/crc-itu-t.h> +#include <linux/log2.h>  #include <asm/byteorder.h>  #include "udf_sb.h" @@ -1215,16 +1216,65 @@ out_bh:  	return ret;  } +static int udf_load_sparable_map(struct super_block *sb, +				 struct udf_part_map *map, +				 struct sparablePartitionMap *spm) +{ +	uint32_t loc; +	uint16_t ident; +	struct sparingTable *st; +	struct udf_sparing_data *sdata = &map->s_type_specific.s_sparing; +	int i; +	struct buffer_head *bh; + +	map->s_partition_type = UDF_SPARABLE_MAP15; +	sdata->s_packet_len = le16_to_cpu(spm->packetLength); +	if (!is_power_of_2(sdata->s_packet_len)) { +		udf_err(sb, "error loading logical volume descriptor: " +			"Invalid packet length %u\n", +			(unsigned)sdata->s_packet_len); +		return -EIO; +	} +	if (spm->numSparingTables > 4) { +		udf_err(sb, "error loading logical volume descriptor: " +			"Too many sparing tables (%d)\n", +			(int)spm->numSparingTables); +		return -EIO; +	} + +	for (i = 0; i < spm->numSparingTables; i++) { +		loc = le32_to_cpu(spm->locSparingTable[i]); +		bh = udf_read_tagged(sb, loc, loc, &ident); +		if (!bh) +			continue; + +		st = (struct sparingTable *)bh->b_data; +		if (ident != 0 || +		    strncmp(st->sparingIdent.ident, UDF_ID_SPARING, +			    strlen(UDF_ID_SPARING)) || +		    sizeof(*st) + le16_to_cpu(st->reallocationTableLen) > +							sb->s_blocksize) { +			brelse(bh); +			continue; +		} + +		sdata->s_spar_map[i] = bh; +	} +	map->s_partition_func = udf_get_pblock_spar15; +	return 0; +} +  static int udf_load_logicalvol(struct super_block *sb, sector_t block,  			       struct kernel_lb_addr *fileset)  {  	struct logicalVolDesc *lvd; -	int i, j, offset; +	int i, offset;  	uint8_t type;  	struct udf_sb_info *sbi = UDF_SB(sb);  	struct genericPartitionMap *gpm;  	uint16_t ident;  	struct buffer_head *bh; +	unsigned int table_len;  	int ret = 0;  	bh = udf_read_tagged(sb, block, block, &ident); @@ -1232,15 +1282,20 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,  		return 1;  	BUG_ON(ident != TAG_IDENT_LVD);  	lvd = (struct logicalVolDesc *)bh->b_data; - -	i = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps)); -	if (i != 0) { -		ret = i; +	table_len = le32_to_cpu(lvd->mapTableLength); +	if (sizeof(*lvd) + table_len > sb->s_blocksize) { +		udf_err(sb, "error loading logical volume descriptor: " +			"Partition table too long (%u > %lu)\n", table_len, +			sb->s_blocksize - sizeof(*lvd));  		goto out_bh;  	} +	ret = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps)); +	if (ret) +		goto out_bh; +  	for (i = 0, offset = 0; -	     i < sbi->s_partitions && offset < le32_to_cpu(lvd->mapTableLength); +	     i < sbi->s_partitions && offset < table_len;  	     i++, offset += gpm->partitionMapLength) {  		struct udf_part_map *map = &sbi->s_partmaps[i];  		gpm = (struct genericPartitionMap *) @@ -1275,38 +1330,9 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,  			} else if (!strncmp(upm2->partIdent.ident,  						UDF_ID_SPARABLE,  						strlen(UDF_ID_SPARABLE))) { -				uint32_t loc; -				struct sparingTable *st; -				struct sparablePartitionMap *spm = -					(struct sparablePartitionMap *)gpm; - -				map->s_partition_type = UDF_SPARABLE_MAP15; -				map->s_type_specific.s_sparing.s_packet_len = -						le16_to_cpu(spm->packetLength); -				for (j = 0; j < spm->numSparingTables; j++) { -					struct buffer_head *bh2; - -					loc = le32_to_cpu( -						spm->locSparingTable[j]); -					bh2 = udf_read_tagged(sb, loc, loc, -							     &ident); -					map->s_type_specific.s_sparing. -							s_spar_map[j] = bh2; - -					if (bh2 == NULL) -						continue; - -					st = (struct sparingTable *)bh2->b_data; -					if (ident != 0 || strncmp( -						st->sparingIdent.ident, -						UDF_ID_SPARING, -						strlen(UDF_ID_SPARING))) { -						brelse(bh2); -						map->s_type_specific.s_sparing. -							s_spar_map[j] = NULL; -					} -				} -				map->s_partition_func = udf_get_pblock_spar15; +				if (udf_load_sparable_map(sb, map, +				    (struct sparablePartitionMap *)gpm) < 0) +					goto out_bh;  			} else if (!strncmp(upm2->partIdent.ident,  						UDF_ID_METADATA,  						strlen(UDF_ID_METADATA))) { |