diff --git a/src/types.rs b/src/types.rs index 26f94b02..f1921cb4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -803,13 +803,13 @@ impl ZipFileData { }) } - pub(crate) fn block(&self, zip64_extra_field_length: u16) -> ZipCentralEntryBlock { + pub(crate) fn block(&self, zip64_extra_field_length: u16) -> ZipResult { let extra_field_len: u16 = self.extra_field_len().try_into().unwrap(); let central_extra_field_len: u16 = self.central_extra_field_len().try_into().unwrap(); let last_modified_time = self .last_modified_time .unwrap_or_else(DateTime::default_for_write); - ZipCentralEntryBlock { + Ok(ZipCentralEntryBlock { magic: ZipCentralEntryBlock::MAGIC, version_made_by: (self.system as u16) << 8 | (self.version_made_by as u16).max(self.version_needed()), @@ -831,8 +831,10 @@ impl ZipFileData { .unwrap(), file_name_length: self.file_name_raw.len().try_into().unwrap(), extra_field_length: zip64_extra_field_length - + extra_field_len - + central_extra_field_len, + .checked_add(extra_field_len + central_extra_field_len) + .ok_or(ZipError::InvalidArchive( + "Extra field length in central directory exceeds 64KiB", + ))?, file_comment_length: self.file_comment.as_bytes().len().try_into().unwrap(), disk_number: 0, internal_file_attributes: 0, @@ -842,7 +844,7 @@ impl ZipFileData { .min(spec::ZIP64_BYTES_THR) .try_into() .unwrap(), - } + }) } pub(crate) fn zip64_extra_field_block(&self) -> Option { diff --git a/src/write.rs b/src/write.rs index 224a8d54..b5ab15f8 100644 --- a/src/write.rs +++ b/src/write.rs @@ -1875,7 +1875,7 @@ fn write_central_directory_header(writer: &mut T, file: &ZipFileData) let mut zip64_extra_field = [0; 28]; let zip64_extra_field_length = write_central_zip64_extra_field(&mut zip64_extra_field.as_mut(), file)?; - let block = file.block(zip64_extra_field_length); + let block = file.block(zip64_extra_field_length)?; block.write(writer)?; // file name writer.write_all(&file.file_name_raw)?; @@ -2839,4 +2839,38 @@ mod test { let _ = writer.finish_into_readable()?; Ok(()) } + + #[cfg(all(feature = "_deflate-any", feature = "aes-crypto"))] + #[test] + fn test_fuzz_crash_2024_06_14d() -> ZipResult<()> { + use crate::write::EncryptWith::Aes; + use crate::AesMode::Aes256; + use CompressionMethod::Deflated; + let mut writer = ZipWriter::new(Cursor::new(Vec::new())); + writer.set_flush_on_finish_file(false); + let options = FileOptions { + compression_method: Deflated, + compression_level: Some(5), + last_modified_time: DateTime::from_date_and_time(2107, 4, 8, 15, 54, 19)?, + permissions: None, + large_file: true, + encrypt_with: Some(Aes { + mode: Aes256, + password: "", + }), + extended_options: ExtendedFileOptions { + extra_data: vec![2, 0, 1, 0, 0].into(), + central_extra_data: vec![ + 35, 229, 2, 0, 41, 41, 231, 44, 2, 0, 52, 233, 82, 201, 0, 0, 3, 0, 2, 0, 233, + 255, 3, 0, 2, 0, 26, 154, 38, 251, 0, 0, + ] + .into(), + }, + alignment: 65535, + zopfli_buffer_size: None, + }; + writer.add_directory_from_path("", options)?; + let _ = writer.finish_into_readable()?; + Ok(()) + } }