Skip to content

Commit

Permalink
Merge pull request #50 from jmacadie/master
Browse files Browse the repository at this point in the history
Allow DIFAT chains to end with FREESECT
  • Loading branch information
mdsteele authored Feb 14, 2024
2 parents eafd9ba + 0e3c04c commit baef54e
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 1 deletion.
13 changes: 12 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,9 @@ impl<F: Read + Seek> CompoundFile<F> {
let mut seen_sector_ids = FnvHashSet::default();
let mut difat_sector_ids = Vec::new();
let mut current_difat_sector = header.first_difat_sector;
while current_difat_sector != consts::END_OF_CHAIN {
while current_difat_sector != consts::END_OF_CHAIN
&& current_difat_sector != consts::FREE_SECTOR
{
if current_difat_sector > consts::MAX_REGULAR_SECTOR {
invalid_data!(
"DIFAT chain includes invalid sector index {}",
Expand Down Expand Up @@ -414,6 +416,15 @@ impl<F: Read + Seek> CompoundFile<F> {
difat.push(next);
}
current_difat_sector = sector.read_u32::<LittleEndian>()?;
if validation.is_strict()
&& current_difat_sector == consts::FREE_SECTOR
{
invalid_data!(
"DIFAT chain must terminate with {}, not {}",
consts::END_OF_CHAIN,
consts::FREE_SECTOR
);
}
}
if validation.is_strict()
&& header.num_difat_sectors as usize != difat_sector_ids.len()
Expand Down
214 changes: 214 additions & 0 deletions tests/malformed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,217 @@ fn check_for_infinite_loops() {
panic_after(Duration::from_secs(1), move || can_read(&path))
}
}

#[rustfmt::skip]
fn difat_terminate_in_freesect() -> Cursor<Vec<u8>> {
let mut data = Vec::with_capacity(7_864_832);

// Header
// =============================
data.extend_from_slice(&[
0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1, // Header signature
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Header CLSID
0x3e, 0x00, // Minor Version
0x03, 0x00, // Major Version
0xfe, 0xff, // Byte Order
0x09, 0x00, // Sector Shift
0x06, 0x00, // Mini Sector Shift
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved
0x00, 0x00, 0x00, 0x00, // Number of Directory Sectors
0x78, 0x00, 0x00, 0x00, // Number of FAT Sectors - 120
0x01, 0x00, 0x00, 0x00, // First Directory Sector Location
0x00, 0x00, 0x00, 0x00, // Transaction Signature Number
0x00, 0x10, 0x00, 0x00, // Mini Stream Cutoff Size
0xfe, 0xff, 0xff, 0xff, // First Mini FAT Sector Location - end of chain
0x00, 0x00, 0x00, 0x00, // Number of Mini FAT Sectors - None
0x02, 0x00, 0x00, 0x00, // First DIFAT Sector Location
0x01, 0x00, 0x00, 0x00, // Number of DIFAT Sectors
]);
// First 109 DIFAT Sectors
data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
for i in 0..108 {
data.extend_from_slice(&[i + 3, 0x00, 0x00, 0x00]);
}

// First FAT Sector
// =============================
data.extend_from_slice(&[
0xfd, 0xff, 0xff, 0xff, // FAT sector - The current sector
0xfe, 0xff, 0xff, 0xff, // Directory - End of chain, just the one
0xfc, 0xff, 0xff, 0xff, // DIFAT #2
]);
// The remaining 119 FAT sectors
for _ in 0..119 {
data.extend_from_slice(&[0xfd, 0xff, 0xff, 0xff]);
}
// The first 6 sectors for content, make it all one stream
for i in 0..6 {
data.extend_from_slice(&[i + 123, 0x00, 0x00, 0x00]);
}

// Directory
// =============================
// Root entry
data.extend_from_slice(&[
b'R', 0x00, b'o', 0x00, b'o', 0x00, b't', 0x00, b' ', 0x00, b'E', 0x00, b'n', 0x00, b't', 0x00,
b'r', 0x00, b'y', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Directory Entry Name
0x16, 0x00, // Directory Entry Name Length
0x05, // Object Type - Root Storage Object
0x00, // Color Flag - red
0xff, 0xff, 0xff, 0xff, // Left Sibling ID - None
0xff, 0xff, 0xff, 0xff, // Right Sibling ID - None
0x01, 0x00, 0x00, 0x00, // Child ID - Stream 1
0x00, 0x67, 0x61, 0x56, 0x54, 0xc1, 0xce, 0x11, 0x85, 0x53, 0x00, 0xaa, 0x00, 0xa1, 0xf9, 0x5b, // CLSID
0x00, 0x00, 0x00, 0x00, // State Bits
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Creation Time
0xb0, 0xfd, 0x97, 0x18, 0xab, 0xe9, 0xc4, 0x01, // Modification Time
0x00, 0x00, 0x00, 0x00, // Starting Sector Location - Mini-stream, None as not using
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Stream Size - Mini-stream, None as not using
]);
// Stream 1
data.extend_from_slice(&[
b'S', 0x00, b't', 0x00, b'r', 0x00, b'e', 0x00, b'a', 0x00, b'm', 0x00, b' ', 0x00, b'1', 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Directory Entry Name
0x12, 0x00, // Directory Entry Name Length
0x02, // Object Type - Stream Object
0x01, // Color Flag - black
0xff, 0xff, 0xff, 0xff, // Left Sibling ID - None
0xff, 0xff, 0xff, 0xff, // Right Sibling ID - None
0xff, 0xff, 0xff, 0xff, // Child ID - No stream
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CLSID
0x00, 0x00, 0x00, 0x00, // State Bits
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Creation Time
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Modification Time
0x7c, 0x00, 0x00, 0x00, // Starting Sector Location - 124
0x00, 0x0c, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, // Stream Size - 7_801_856 bytes
// ((119 FAT pages * 128 sectors per page) + 6 sectors from 1st page) * 512 bytes per sector
]);
// Unused
data.extend_from_slice(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Directory Entry Name
0x00, 0x00, // Directory Entry Name Length
0x00, // Object Type
0x00, // Color Flag
0xff, 0xff, 0xff, 0xff, // Left Sibling ID - None
0xff, 0xff, 0xff, 0xff, // Right Sibling ID - None
0xff, 0xff, 0xff, 0xff, // Child ID - No stream
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CLSID
0x00, 0x00, 0x00, 0x00, // State Bits
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Creation Time
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Modification Time
0x00, 0x00, 0x00, 0x00, // Starting Sector Location
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Stream Size
]);
// Unused
data.extend_from_slice(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Directory Entry Name
0x00, 0x00, // Directory Entry Name Length
0x00, // Object Type
0x00, // Color Flag
0xff, 0xff, 0xff, 0xff, // Left Sibling ID - None
0xff, 0xff, 0xff, 0xff, // Right Sibling ID - None
0xff, 0xff, 0xff, 0xff, // Child ID - No stream
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CLSID
0x00, 0x00, 0x00, 0x00, // State Bits
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Creation Time
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Modification Time
0x00, 0x00, 0x00, 0x00, // Starting Sector Location
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Stream Size
]);

// DIFAT #2
// =============================
// DIFAT Sectors 110 to 120
for i in 0..11 {
data.extend_from_slice(&[i + 111, 0x00, 0x00, 0x00]);
}
// Pad out the rest of the sector with free sector markers
for _ in 0..116 {
data.extend_from_slice(&[0xff, 0xff, 0xff, 0xff]);
}
// Next DIFAT Sector Location
// NOTE: This is wrong and should be 0xfffffffe
data.extend_from_slice(&[0xff, 0xff, 0xff, 0xff]);

// Subsequent FAT Sectors
// =============================
let mut lower = 128;
let mut upper = 0;
let range = (119 * 128) - 1;
for _ in 0..range {
if lower == 0xff {
lower = 0x00;
upper += 1;
} else {
lower += 1;
}
data.extend_from_slice(&[lower, upper, 0x00, 0x00]);
}
// End the chain with an end of chain marker
data.extend_from_slice(&[0xfe, 0xff, 0xff, 0xff]);

// The stream data
// =============================
let sectors = (119 * 128) + 6;
for _ in 0..sectors {
data.extend_from_slice(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
}

Cursor::new(data)
}

#[test]
fn open_difat_terminate_freesect() {
CompoundFile::open(difat_terminate_in_freesect()).unwrap();
}

#[test]
#[should_panic(
expected = "DIFAT chain must terminate with 4294967294, not 4294967295"
)]
fn open_strict_difat_terminate_freesect() {
CompoundFile::open_strict(difat_terminate_in_freesect()).unwrap();
}

0 comments on commit baef54e

Please sign in to comment.