aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/zip/driver.rs70
-rw-r--r--src/zip/error.rs8
-rw-r--r--tests/zip.rs81
3 files changed, 93 insertions, 66 deletions
diff --git a/src/zip/driver.rs b/src/zip/driver.rs
index 7758479..0502c85 100644
--- a/src/zip/driver.rs
+++ b/src/zip/driver.rs
@@ -79,7 +79,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
79 // Search eocdr 79 // Search eocdr
80 let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64; 80 let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64;
81 let start = io.seek(SeekFrom::End(-limit))?; 81 let start = io.seek(SeekFrom::End(-limit))?;
82 let pos = start 82 let eocdr_pos = start
83 + io.read_vec( 83 + io.read_vec(
84 (limit as usize) 84 (limit as usize)
85 .checked_sub(18) 85 .checked_sub(18)
@@ -90,46 +90,50 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
90 .ok_or(ZipError::StructNotFound("Eocdr"))? as u64; 90 .ok_or(ZipError::StructNotFound("Eocdr"))? as u64;
91 91
92 // Read eocdr 92 // Read eocdr
93 io.seek(SeekFrom::Start(pos + 4))?; 93 io.seek(SeekFrom::Start(eocdr_pos + 4))?;
94 let buf = io.read_arr::<18>()?; 94 let buf = io.read_arr::<18>()?;
95 let eocdr: Eocdr = deserialize(&buf).unwrap(); 95 let eocdr: Eocdr = deserialize(&buf).unwrap();
96 let comment = String::from_cp437(io.read_vec(eocdr.comment_len as usize)?); 96 let comment = String::from_cp437(io.read_vec(eocdr.comment_len as usize)?);
97 97
98 let mut cd_pointer = eocdr.cd_pointer as u64;
99 let mut cd_size = eocdr.cd_size as u64;
100 let mut cd_records = eocdr.cd_records as u64;
101
98 // Try to find eocdr64locator 102 // Try to find eocdr64locator
99 io.seek(SeekFrom::Start(pos.saturating_sub(20)))?; 103 if eocdr_pos >= 20 {
100 let buf = io.read_arr::<20>()?; 104 io.seek(SeekFrom::Start(eocdr_pos - 20))?;
101 let (cd_pointer, cd_size, cd_records) = if buf[..4] == EOCDR64_LOCATOR_SIGNATURE { 105 let buf = io.read_arr::<20>()?;
102 // Locator found
103 let eocdr64locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap();
104
105 io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?;
106 if io.read_arr()? != EOCDR64_SIGNATURE {
107 return Err(ZipError::InvalidSignature("Eocdr64"));
108 }
109 106
110 let eocdr64: Eocdr64 = deserialize(&io.read_arr::<52>()?).unwrap(); 107 if buf[..4] == EOCDR64_LOCATOR_SIGNATURE {
111 if eocdr64.cd_pointer + eocdr64.cd_size > eocdr64locator.eocdr64_pointer { 108 let locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap();
112 return Err(ZipError::Overlapping( 109 io.seek(SeekFrom::Start(locator.eocdr64_pointer))?;
113 "Central directory records",
114 "Zip64 end of central directory record",
115 ));
116 }
117 110
118 (eocdr64.cd_pointer, eocdr64.cd_size, eocdr64.cd_records) 111 if io.read_arr()? != EOCDR64_SIGNATURE {
119 } else { 112 return Err(ZipError::InvalidSignature("Eocdr64"));
120 if (eocdr.cd_pointer + eocdr.cd_size) as u64 > pos { 113 }
121 return Err(ZipError::Overlapping( 114
122 "Central directory records", 115 if locator.eocdr64_pointer + 76 > eocdr_pos {
123 "End of central directory record", 116 return Err(ZipError::Overlapping("Eocdr64", "Eocdr64Locator"));
124 )); 117 }
125 }
126 118
127 ( 119 let eocdr64: Eocdr64 = deserialize(&io.read_arr::<54>()?).unwrap();
128 eocdr.cd_pointer as u64, 120 if locator.eocdr64_pointer + eocdr64.eocdr64_size + 32 > eocdr_pos {
129 eocdr.cd_size as u64, 121 return Err(ZipError::Overlapping("Eocdr64", "Eocdr64Locator"));
130 eocdr.cd_records as u64, 122 }
131 ) 123
132 }; 124 cd_pointer = eocdr64.cd_pointer;
125 cd_size = eocdr64.cd_size;
126 cd_records = eocdr64.cd_records;
127
128 if cd_pointer + cd_size > locator.eocdr64_pointer {
129 return Err(ZipError::Overlapping("Cdr", "Eocdr64"));
130 }
131 } else if cd_pointer + cd_size > eocdr_pos {
132 return Err(ZipError::Overlapping("Cdr", "Eocdr"));
133 }
134 } else if cd_pointer + cd_size > eocdr_pos {
135 return Err(ZipError::Overlapping("Cdr", "Eocdr"));
136 }
133 137
134 // Read cd records 138 // Read cd records
135 let mut indexes = Map::with_capacity(cd_records as usize); 139 let mut indexes = Map::with_capacity(cd_records as usize);
diff --git a/src/zip/error.rs b/src/zip/error.rs
index fbc2ba1..40d45fe 100644
--- a/src/zip/error.rs
+++ b/src/zip/error.rs
@@ -31,10 +31,10 @@ impl From<IoError> for ZipError {
31impl PartialEq for ZipError { 31impl PartialEq for ZipError {
32 fn eq(&self, other: &Self) -> bool { 32 fn eq(&self, other: &Self) -> bool {
33 match (self, other) { 33 match (self, other) {
34 (Self::Io(l0), Self::Io(r0)) => l0.kind() == r0.kind(), 34 (Self::Io(l), Self::Io(r)) => l.kind() == r.kind(),
35 (Self::StructNotFound(l0), Self::StructNotFound(r0)) => l0 == r0, 35 (Self::StructNotFound(l), Self::StructNotFound(r)) => l == r,
36 (Self::InvalidSignature(l0), Self::InvalidSignature(r0)) => l0 == r0, 36 (Self::InvalidSignature(l), Self::InvalidSignature(r)) => l == r,
37 (Self::InvalidField(l0), Self::InvalidField(r0)) => l0 == r0, 37 (Self::InvalidField(l), Self::InvalidField(r)) => l == r,
38 (Self::Overlapping(l0, l1), Self::Overlapping(r0, r1)) => l0 == r0 && l1 == r1, 38 (Self::Overlapping(l0, l1), Self::Overlapping(r0, r1)) => l0 == r0 && l1 == r1,
39 _ => core::mem::discriminant(self) == core::mem::discriminant(other), 39 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
40 } 40 }
diff --git a/tests/zip.rs b/tests/zip.rs
index 1422bb3..e00789d 100644
--- a/tests/zip.rs
+++ b/tests/zip.rs
@@ -90,11 +90,17 @@ fn test_zip_weak() {
90 } 90 }
91} 91}
92 92
93const EMPTY: Cursor<&[u8]> = Cursor::new(b"PK\x05\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
94
95#[test] 93#[test]
96fn test_zip() { 94fn test_zip() {
97 assert_eq!(Archive::<Zip<_>>::read(EMPTY).unwrap().len(), 0); 95 assert_eq!(
96 Archive::<Zip<_>>::read(Cursor::new(&[
97 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, //
98 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr
99 ]))
100 .unwrap()
101 .len(),
102 0
103 );
98 104
99 let mut archive = Archive::<Zip>::read_from_file("tests/files/archive.zip").unwrap(); 105 let mut archive = Archive::<Zip>::read_from_file("tests/files/archive.zip").unwrap();
100 106
@@ -158,32 +164,49 @@ fn test_zip() {
158 } 164 }
159} 165}
160 166
161const NOT_FOUND: Cursor<&[u8]> = Cursor::new(b"");
162const INVALID: Cursor<&[u8]> = Cursor::new(
163 b"PK\x06\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PK\x05\x06\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0",
164);
165const OVERLAP: Cursor<&[u8]> = Cursor::new(b"PK\x05\x06\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0");
166const OVERLAP64: Cursor<&[u8]> = Cursor::new(b"PK\x06\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PK\x06\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PK\x05\x06\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0");
167
168#[test] 167#[test]
169fn test_bad_zip() { 168fn test_bad_zip() {
170 assert!( 169 assert!(Archive::<Zip<_>>::read(Cursor::new(&[]))
171 Archive::<Zip<_>>::read(NOT_FOUND).is_err_and(|e| e == ZipError::StructNotFound("Eocdr")) 170 .is_err_and(|e| e == ZipError::StructNotFound("Eocdr")));
172 ); 171
173 172 assert!(Archive::<Zip<_>>::read(Cursor::new(&[
174 assert!( 173 // No Eocdr64
175 Archive::<Zip<_>>::read(INVALID).is_err_and(|e| e == ZipError::InvalidSignature("Eocdr64")) 174 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0, //
176 ); 175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr64Locator
177 176 //
178 assert!(Archive::<Zip<_>>::read(OVERLAP).is_err_and(|e| e 177 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, //
179 == ZipError::Overlapping( 178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr
180 "Central directory records", 179 ]))
181 "End of central directory record" 180 .is_err_and(|e| e == ZipError::InvalidSignature("Eocdr64")));
182 ))); 181
183 182 assert!(Archive::<Zip<_>>::read(Cursor::new(&[
184 assert!(Archive::<Zip<_>>::read(OVERLAP64).is_err_and(|e| e 183 0x50, 0x4b, 0x06, 0x06, // Eocdr64
185 == ZipError::Overlapping( 184 //
186 "Central directory records", 185 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0, //
187 "Zip64 end of central directory record" 186 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr64Locator
188 ))); 187 //
188 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, //
189 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr
190 ]))
191 .is_err_and(|e| e == ZipError::Overlapping("Eocdr64", "Eocdr64Locator")));
192
193 assert!(Archive::<Zip<_>>::read(Cursor::new(&[
194 // No records
195 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, //
196 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr
197 ]))
198 .is_err_and(|e| e == ZipError::Overlapping("Cdr", "Eocdr")));
199
200 assert!(Archive::<Zip<_>>::read(Cursor::new(&[
201 0x50, 0x4b, 0x06, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
202 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr64
204 //
205 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0, //
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr64Locator
207 //
208 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0, 0, //
209 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Eocdr
210 ]))
211 .is_err_and(|e| e == ZipError::Overlapping("Cdr", "Eocdr64")));
189} 212}