DF-3103 / fix.diff
# DF-3103 fix: validate l_ea/l_ad at vget and honor offset/length in # the embedded-ICB read path. --- a/sys/vfs/udf/udf_vnops.c @@ -403,7 +404,7 @@ error = uiomove(data, size, uio); if (bp != NULL) brelse(bp); - if (error) + if (error || size == 0) break; } @@ -1037,6 +1038,12 @@ fentry = node->fentry; *data = &fentry->data[fentry->l_ea]; *size = fentry->l_ad; + if (offset >= *size) + *size = 0; + else { + *data += offset; + *size -= offset; + } return(0); } else if (error != 0) { return(error); --- a/sys/vfs/udf/udf_vfsops.c @@ -524,6 +524,22 @@ brelse(bp); return(ENOMEM); } + /* + * A file entry is exactly one logical block; the EA area and the + * allocation-descriptor area must both fit in it. Otherwise the + * 32-bit UDF_FENTRY_SIZE + l_ea + l_ad sum can wrap and undersize + * the fentry allocation while udf_readatoffset()'s embedded path + * still trusts the raw l_ea/l_ad. + */ + if (udfmp->bsize < UDF_FENTRY_SIZE || + fe->l_ea > (uint32_t)(udfmp->bsize - UDF_FENTRY_SIZE) || + fe->l_ad > (uint32_t)(udfmp->bsize - UDF_FENTRY_SIZE) - fe->l_ea) { + kprintf("udf_vget: invalid l_ea %u / l_ad %u\n", + fe->l_ea, fe->l_ad); + kfree(unode, M_UDFNODE); + brelse(bp); + return(EINVAL); + } size = UDF_FENTRY_SIZE + fe->l_ea + fe->l_ad; unode->fentry = kmalloc(size, M_UDFFENTRY, M_WAITOK | M_ZERO); |