diff --git a/sys/vfs/udf/udf_vfsops.c b/sys/vfs/udf/udf_vfsops.c --- a/sys/vfs/udf/udf_vfsops.c +++ b/sys/vfs/udf/udf_vfsops.c @@ -630,7 +630,7 @@ struct buf *bp; unsigned char regid_id[UDF_REGID_ID_SIZE + 1]; int ptype, psize, error; - unsigned int i; + unsigned int i, max_entries; for (i = 0; i < lvd->n_pm; i++) { pmap = (union udf_pmap *)&lvd->maps[i * UDF_PMAP_SIZE]; @@ -659,6 +659,13 @@ } pms = &pmap->pms; + + /* Validate st_size: must hold at least the header + 1 entry. */ + if (pms->st_size < sizeof(struct udf_sparing_table)) { + kprintf("udf: sparing table size %u too small\n", + pms->st_size); + return(EINVAL); + } udfmp->s_table = kmalloc(pms->st_size, M_UDFMOUNT, M_WAITOK | M_ZERO); @@ -687,9 +694,15 @@ } /* See how many valid entries there are here. The list is - * supposed to be sorted. 0xfffffff0 and higher are not valid + * supposed to be sorted. 0xfffffff0 and higher are not valid. + * Bound the scan by the number of entries that actually fit + * in the st_size-byte allocation; the on-disk rt_l may be + * forged larger and would drive a heap OOB read otherwise. */ - for (i = 0; i < udfmp->s_table->rt_l; i++) { + max_entries = (pms->st_size - + offsetof(struct udf_sparing_table, entries)) / + sizeof(struct spare_map_entry); + for (i = 0; i < udfmp->s_table->rt_l && i < max_entries; i++) { udfmp->s_table_entries = i; if (udfmp->s_table->entries[i].org >= 0xfffffff0) break;