security(format): fix unchecked-addition bounds check in symbol_table.rs
SymbolTableNode::parse used raw offset+8 arithmetic that can overflow on a crafted v1-group B-tree leaf with a near-u64::MAX SNOD child pointer (group_v1.rs passes such offsets through unchecked). Switch to checked_add, matching read_offset in the same file. Also harden the entries_start + num_symbols*entry_size computation with checked_add for consistency, even though num_symbols being u16 already bounds that multiply. Add regression tests. INT-02
This commit is contained in:
@@ -80,9 +80,9 @@ impl SymbolTableNode {
|
||||
offset_size: u8,
|
||||
) -> Result<SymbolTableNode, FormatError> {
|
||||
// signature(4) + version(1) + reserved(1) + number_of_symbols(2) = 8
|
||||
if offset + 8 > file_data.len() {
|
||||
if offset.checked_add(8).is_none_or(|end| end > file_data.len()) {
|
||||
return Err(FormatError::UnexpectedEof {
|
||||
expected: offset + 8,
|
||||
expected: offset.saturating_add(8),
|
||||
available: file_data.len(),
|
||||
});
|
||||
}
|
||||
@@ -103,7 +103,12 @@ impl SymbolTableNode {
|
||||
// Each entry: link_name_offset(os) + obj_hdr_addr(os) + cache_type(4) + reserved(4) + scratch(16)
|
||||
let entry_size = os + os + 4 + 4 + 16;
|
||||
let entries_start = offset + 8;
|
||||
let needed = entries_start + num_symbols * entry_size;
|
||||
let needed = entries_start
|
||||
.checked_add(num_symbols * entry_size)
|
||||
.ok_or(FormatError::UnexpectedEof {
|
||||
expected: usize::MAX,
|
||||
available: file_data.len(),
|
||||
})?;
|
||||
if needed > file_data.len() {
|
||||
return Err(FormatError::UnexpectedEof {
|
||||
expected: needed,
|
||||
@@ -228,4 +233,24 @@ mod tests {
|
||||
let err = SymbolTableNode::parse(&data, 0, 8).unwrap_err();
|
||||
assert_eq!(err, FormatError::InvalidSymbolTableNodeVersion(2));
|
||||
}
|
||||
|
||||
/// A near-`usize::MAX` SNOD offset must error cleanly, not overflow/panic.
|
||||
#[test]
|
||||
fn parse_snod_rejects_offset_overflow() {
|
||||
let data = build_snod(&[], 8);
|
||||
let result = SymbolTableNode::parse(&data, usize::MAX - 4, 8);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
/// A huge symbol count combined with a large entries_start must not
|
||||
/// overflow the `needed` size computation.
|
||||
#[test]
|
||||
fn parse_snod_rejects_entries_size_overflow() {
|
||||
let mut data = build_snod(&[], 8);
|
||||
// num_symbols at offset 6..8 — set to max to blow up entries_start + num_symbols*entry_size
|
||||
data[6] = 0xFF;
|
||||
data[7] = 0xFF;
|
||||
let result = SymbolTableNode::parse(&data, usize::MAX / 2, 8);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user