diff options
| author | Jan Kara <jack@suse.cz> | 2012-06-27 21:23:07 +0200 | 
|---|---|---|
| committer | Jan Kara <jack@suse.cz> | 2012-06-28 19:31:09 +0200 | 
| commit | 1df2ae31c724e57be9d7ac00d78db8a5dabdd050 (patch) | |
| tree | 359174ba11f0884c778c72c07578e5a35cf603e3 /fs/udf | |
| parent | adee11b2085bee90bd8f4f52123ffb07882d6256 (diff) | |
| download | olio-linux-3.10-1df2ae31c724e57be9d7ac00d78db8a5dabdd050.tar.xz olio-linux-3.10-1df2ae31c724e57be9d7ac00d78db8a5dabdd050.zip  | |
udf: Fortify loading of sparing table
Add sanity checks when loading sparing table from disk to avoid accessing
unallocated memory or writing to it.
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/udf')
| -rw-r--r-- | fs/udf/super.c | 86 | 
1 files changed, 53 insertions, 33 deletions
diff --git a/fs/udf/super.c b/fs/udf/super.c index ce911f50b39..8d86a8706c0 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,11 +1216,59 @@ 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; @@ -1281,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))) {  |