mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
fix(zip): validate CRC32 on small-entry extract fast path (#4510)
This commit is contained in:
@@ -604,6 +604,14 @@ fn write_small_zip_entry<R: Read>(reader: &mut R, output_path: &Path, size: u64)
|
||||
let size = usize::try_from(size).map_err(|_| io::Error::other("small zip entry size overflow"))?;
|
||||
let mut buffer = [0_u8; SMALL_ZIP_EXTRACT_FAST_PATH_LIMIT as usize];
|
||||
reader.read_exact(&mut buffer[..size])?;
|
||||
// `read_exact` stops as soon as the declared bytes are read and never performs the
|
||||
// terminal zero-length read that the zip crate's `Crc32Reader` uses to validate the
|
||||
// entry checksum. Force one extra read to EOF so a corrupted small entry is rejected
|
||||
// here, matching the large-entry `io::copy` path which already reads through EOF.
|
||||
let mut trailing = [0_u8; 1];
|
||||
if reader.read(&mut trailing)? != 0 {
|
||||
return Err(io::Error::other("small zip entry produced more data than its declared size").into());
|
||||
}
|
||||
std::fs::write(output_path, &buffer[..size])?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1557,6 +1565,39 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_zip_rejects_small_entry_with_corrupted_crc() {
|
||||
let temp = tempdir().expect("tempdir should be created");
|
||||
let zip_path = temp.path().join("corrupt-crc.zip");
|
||||
let extract_path = temp.path().join("extract");
|
||||
|
||||
// A stored entry well under the small-entry fast-path limit so extraction takes
|
||||
// the in-memory buffer path rather than the streaming `io::copy` path.
|
||||
let content = b"small-entry-crc-payload";
|
||||
assert!((content.len() as u64) <= SMALL_ZIP_EXTRACT_FAST_PATH_LIMIT);
|
||||
build_zip_file_with_entries(&zip_path, &[("small.txt", content)])
|
||||
.await
|
||||
.expect("zip fixture should be created");
|
||||
|
||||
// Corrupt the stored entry data so its bytes no longer match the recorded CRC32.
|
||||
let mut raw = fs::read(&zip_path).await.expect("zip fixture should be readable");
|
||||
let offset = raw
|
||||
.windows(content.len())
|
||||
.position(|window| window == content)
|
||||
.expect("stored entry data should be present in the zip");
|
||||
raw[offset] ^= 0xFF;
|
||||
fs::write(&zip_path, &raw).await.expect("corrupted zip should be writable");
|
||||
|
||||
let err = extract_zip_simple(&zip_path, &extract_path)
|
||||
.await
|
||||
.expect_err("small entry with a corrupted CRC should be rejected");
|
||||
|
||||
assert!(
|
||||
matches!(err, ZipError::Io(ref e) if e.kind() == std::io::ErrorKind::InvalidData),
|
||||
"expected an InvalidData checksum error, got {err:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_zip_allows_entry_count_exactly_at_limit() {
|
||||
let temp = tempdir().expect("tempdir should be created");
|
||||
|
||||
Reference in New Issue
Block a user