diff options
author | Jan Kara <jack@suse.cz> | 2012-06-27 15:23:07 -0400 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2012-06-28 13:31:09 -0400 |
commit | 1df2ae31c724e57be9d7ac00d78db8a5dabdd050 (patch) | |
tree | 359174ba11f0884c778c72c07578e5a35cf603e3 /fs | |
parent | adee11b2085bee90bd8f4f52123ffb07882d6256 (diff) |
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')
-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 ce911f50b39d..8d86a8706c0e 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include <linux/seq_file.h> | 56 | #include <linux/seq_file.h> |
57 | #include <linux/bitmap.h> | 57 | #include <linux/bitmap.h> |
58 | #include <linux/crc-itu-t.h> | 58 | #include <linux/crc-itu-t.h> |
59 | #include <linux/log2.h> | ||
59 | #include <asm/byteorder.h> | 60 | #include <asm/byteorder.h> |
60 | 61 | ||
61 | #include "udf_sb.h" | 62 | #include "udf_sb.h" |
@@ -1215,11 +1216,59 @@ out_bh: | |||
1215 | return ret; | 1216 | return ret; |
1216 | } | 1217 | } |
1217 | 1218 | ||
1219 | static int udf_load_sparable_map(struct super_block *sb, | ||
1220 | struct udf_part_map *map, | ||
1221 | struct sparablePartitionMap *spm) | ||
1222 | { | ||
1223 | uint32_t loc; | ||
1224 | uint16_t ident; | ||
1225 | struct sparingTable *st; | ||
1226 | struct udf_sparing_data *sdata = &map->s_type_specific.s_sparing; | ||
1227 | int i; | ||
1228 | struct buffer_head *bh; | ||
1229 | |||
1230 | map->s_partition_type = UDF_SPARABLE_MAP15; | ||
1231 | sdata->s_packet_len = le16_to_cpu(spm->packetLength); | ||
1232 | if (!is_power_of_2(sdata->s_packet_len)) { | ||
1233 | udf_err(sb, "error loading logical volume descriptor: " | ||
1234 | "Invalid packet length %u\n", | ||
1235 | (unsigned)sdata->s_packet_len); | ||
1236 | return -EIO; | ||
1237 | } | ||
1238 | if (spm->numSparingTables > 4) { | ||
1239 | udf_err(sb, "error loading logical volume descriptor: " | ||
1240 | "Too many sparing tables (%d)\n", | ||
1241 | (int)spm->numSparingTables); | ||
1242 | return -EIO; | ||
1243 | } | ||
1244 | |||
1245 | for (i = 0; i < spm->numSparingTables; i++) { | ||
1246 | loc = le32_to_cpu(spm->locSparingTable[i]); | ||
1247 | bh = udf_read_tagged(sb, loc, loc, &ident); | ||
1248 | if (!bh) | ||
1249 | continue; | ||
1250 | |||
1251 | st = (struct sparingTable *)bh->b_data; | ||
1252 | if (ident != 0 || | ||
1253 | strncmp(st->sparingIdent.ident, UDF_ID_SPARING, | ||
1254 | strlen(UDF_ID_SPARING)) || | ||
1255 | sizeof(*st) + le16_to_cpu(st->reallocationTableLen) > | ||
1256 | sb->s_blocksize) { | ||
1257 | brelse(bh); | ||
1258 | continue; | ||
1259 | } | ||
1260 | |||
1261 | sdata->s_spar_map[i] = bh; | ||
1262 | } | ||
1263 | map->s_partition_func = udf_get_pblock_spar15; | ||
1264 | return 0; | ||
1265 | } | ||
1266 | |||
1218 | static int udf_load_logicalvol(struct super_block *sb, sector_t block, | 1267 | static int udf_load_logicalvol(struct super_block *sb, sector_t block, |
1219 | struct kernel_lb_addr *fileset) | 1268 | struct kernel_lb_addr *fileset) |
1220 | { | 1269 | { |
1221 | struct logicalVolDesc *lvd; | 1270 | struct logicalVolDesc *lvd; |
1222 | int i, j, offset; | 1271 | int i, offset; |
1223 | uint8_t type; | 1272 | uint8_t type; |
1224 | struct udf_sb_info *sbi = UDF_SB(sb); | 1273 | struct udf_sb_info *sbi = UDF_SB(sb); |
1225 | struct genericPartitionMap *gpm; | 1274 | struct genericPartitionMap *gpm; |
@@ -1281,38 +1330,9 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block, | |||
1281 | } else if (!strncmp(upm2->partIdent.ident, | 1330 | } else if (!strncmp(upm2->partIdent.ident, |
1282 | UDF_ID_SPARABLE, | 1331 | UDF_ID_SPARABLE, |
1283 | strlen(UDF_ID_SPARABLE))) { | 1332 | strlen(UDF_ID_SPARABLE))) { |
1284 | uint32_t loc; | 1333 | if (udf_load_sparable_map(sb, map, |
1285 | struct sparingTable *st; | 1334 | (struct sparablePartitionMap *)gpm) < 0) |
1286 | struct sparablePartitionMap *spm = | 1335 | goto out_bh; |
1287 | (struct sparablePartitionMap *)gpm; | ||
1288 | |||
1289 | map->s_partition_type = UDF_SPARABLE_MAP15; | ||
1290 | map->s_type_specific.s_sparing.s_packet_len = | ||
1291 | le16_to_cpu(spm->packetLength); | ||
1292 | for (j = 0; j < spm->numSparingTables; j++) { | ||
1293 | struct buffer_head *bh2; | ||
1294 | |||
1295 | loc = le32_to_cpu( | ||
1296 | spm->locSparingTable[j]); | ||
1297 | bh2 = udf_read_tagged(sb, loc, loc, | ||
1298 | &ident); | ||
1299 | map->s_type_specific.s_sparing. | ||
1300 | s_spar_map[j] = bh2; | ||
1301 | |||
1302 | if (bh2 == NULL) | ||
1303 | continue; | ||
1304 | |||
1305 | st = (struct sparingTable *)bh2->b_data; | ||
1306 | if (ident != 0 || strncmp( | ||
1307 | st->sparingIdent.ident, | ||
1308 | UDF_ID_SPARING, | ||
1309 | strlen(UDF_ID_SPARING))) { | ||
1310 | brelse(bh2); | ||
1311 | map->s_type_specific.s_sparing. | ||
1312 | s_spar_map[j] = NULL; | ||
1313 | } | ||
1314 | } | ||
1315 | map->s_partition_func = udf_get_pblock_spar15; | ||
1316 | } else if (!strncmp(upm2->partIdent.ident, | 1336 | } else if (!strncmp(upm2->partIdent.ident, |
1317 | UDF_ID_METADATA, | 1337 | UDF_ID_METADATA, |
1318 | strlen(UDF_ID_METADATA))) { | 1338 | strlen(UDF_ID_METADATA))) { |