| ... | @@ -215,7 +215,7 @@ const FileExtents = struct { | ... | @@ -215,7 +215,7 @@ const FileExtents = struct { |
| 215 | local_file_header_offset: u64, | 215 | local_file_header_offset: u64, |
| 216 | }; | 216 | }; |
| 217 | | 217 | |
| 218 | fn readZip64FileExtents(header: CentralDirectoryFileHeader, extents: *FileExtents, data: []u8) !void { | 218 | fn readZip64FileExtents(comptime T: type, header: T, extents: *FileExtents, data: []u8) !void { |
| 219 | var data_offset: usize = 0; | 219 | var data_offset: usize = 0; |
| 220 | if (isMaxInt(header.uncompressed_size)) { | 220 | if (isMaxInt(header.uncompressed_size)) { |
| 221 | if (data_offset + 8 > data.len) | 221 | if (data_offset + 8 > data.len) |
| ... | @@ -229,22 +229,28 @@ fn readZip64FileExtents(header: CentralDirectoryFileHeader, extents: *FileExtent | ... | @@ -229,22 +229,28 @@ fn readZip64FileExtents(header: CentralDirectoryFileHeader, extents: *FileExtent |
| 229 | extents.compressed_size = std.mem.readInt(u64, data[data_offset..][0..8], .little); | 229 | extents.compressed_size = std.mem.readInt(u64, data[data_offset..][0..8], .little); |
| 230 | data_offset += 8; | 230 | data_offset += 8; |
| 231 | } | 231 | } |
| 232 | if (isMaxInt(header.local_file_header_offset)) { | 232 | |
| 233 | if (data_offset + 8 > data.len) | 233 | switch (T) { |
| 234 | return error.ZipBadCd64Size; | 234 | CentralDirectoryFileHeader => { |
| 235 | extents.local_file_header_offset = std.mem.readInt(u64, data[data_offset..][0..8], .little); | 235 | if (isMaxInt(header.local_file_header_offset)) { |
| 236 | data_offset += 8; | 236 | if (data_offset + 8 > data.len) |
| 237 | } | 237 | return error.ZipBadCd64Size; |
| 238 | if (isMaxInt(header.disk_number)) { | 238 | extents.local_file_header_offset = std.mem.readInt(u64, data[data_offset..][0..8], .little); |
| 239 | if (data_offset + 4 > data.len) | 239 | data_offset += 8; |
| 240 | return error.ZipInvalid; | 240 | } |
| 241 | const disk_number = std.mem.readInt(u32, data[data_offset..][0..4], .little); | 241 | if (isMaxInt(header.disk_number)) { |
| 242 | if (disk_number != 0) | 242 | if (data_offset + 4 > data.len) |
| 243 | return error.ZipMultiDiskUnsupported; | 243 | return error.ZipInvalid; |
| 244 | data_offset += 4; | 244 | const disk_number = std.mem.readInt(u32, data[data_offset..][0..4], .little); |
| | 245 | if (disk_number != 0) |
| | 246 | return error.ZipMultiDiskUnsupported; |
| | 247 | data_offset += 4; |
| | 248 | } |
| | 249 | if (data_offset > data.len) |
| | 250 | return error.ZipBadCd64Size; |
| | 251 | }, |
| | 252 | else => {}, |
| 245 | } | 253 | } |
| 246 | if (data_offset > data.len) | | |
| 247 | return error.ZipBadCd64Size; | | |
| 248 | } | 254 | } |
| 249 | | 255 | |
| 250 | pub fn Iterator(comptime SeekableStream: type) type { | 256 | pub fn Iterator(comptime SeekableStream: type) type { |
| ... | @@ -394,7 +400,7 @@ pub fn Iterator(comptime SeekableStream: type) type { | ... | @@ -394,7 +400,7 @@ pub fn Iterator(comptime SeekableStream: type) type { |
| 394 | return error.ZipBadExtraFieldSize; | 400 | return error.ZipBadExtraFieldSize; |
| 395 | const data = extra[extra_offset + 4 .. end]; | 401 | const data = extra[extra_offset + 4 .. end]; |
| 396 | switch (@as(ExtraHeader, @enumFromInt(header_id))) { | 402 | switch (@as(ExtraHeader, @enumFromInt(header_id))) { |
| 397 | .zip64_info => try readZip64FileExtents(header, &extents, data), | 403 | .zip64_info => try readZip64FileExtents(CentralDirectoryFileHeader, header, &extents, data), |
| 398 | else => {}, // ignore | 404 | else => {}, // ignore |
| 399 | } | 405 | } |
| 400 | extra_offset = end; | 406 | extra_offset = end; |
| ... | @@ -466,12 +472,45 @@ pub fn Iterator(comptime SeekableStream: type) type { | ... | @@ -466,12 +472,45 @@ pub fn Iterator(comptime SeekableStream: type) type { |
| 466 | return error.ZipMismatchFlags; | 472 | return error.ZipMismatchFlags; |
| 467 | if (local_header.crc32 != 0 and local_header.crc32 != self.crc32) | 473 | if (local_header.crc32 != 0 and local_header.crc32 != self.crc32) |
| 468 | return error.ZipMismatchCrc32; | 474 | return error.ZipMismatchCrc32; |
| 469 | if (local_header.compressed_size != 0 and | 475 | var extents: FileExtents = .{ |
| 470 | local_header.compressed_size != self.compressed_size) | 476 | .uncompressed_size = local_header.uncompressed_size, |
| | 477 | .compressed_size = local_header.compressed_size, |
| | 478 | .local_file_header_offset = 0, |
| | 479 | }; |
| | 480 | if (local_header.extra_len > 0) { |
| | 481 | var extra_buf: [std.math.maxInt(u16)]u8 = undefined; |
| | 482 | const extra = extra_buf[0..local_header.extra_len]; |
| | 483 | |
| | 484 | { |
| | 485 | try stream.seekTo(self.file_offset + @sizeOf(LocalFileHeader) + local_header.filename_len); |
| | 486 | const len = try stream.context.reader().readAll(extra); |
| | 487 | if (len != extra.len) |
| | 488 | return error.ZipTruncated; |
| | 489 | } |
| | 490 | |
| | 491 | var extra_offset: usize = 0; |
| | 492 | while (extra_offset + 4 <= local_header.extra_len) { |
| | 493 | const header_id = std.mem.readInt(u16, extra[extra_offset..][0..2], .little); |
| | 494 | const data_size = std.mem.readInt(u16, extra[extra_offset..][2..4], .little); |
| | 495 | const end = extra_offset + 4 + data_size; |
| | 496 | if (end > local_header.extra_len) |
| | 497 | return error.ZipBadExtraFieldSize; |
| | 498 | const data = extra[extra_offset + 4 .. end]; |
| | 499 | switch (@as(ExtraHeader, @enumFromInt(header_id))) { |
| | 500 | .zip64_info => try readZip64FileExtents(LocalFileHeader, local_header, &extents, data), |
| | 501 | else => {}, // ignore |
| | 502 | } |
| | 503 | extra_offset = end; |
| | 504 | } |
| | 505 | } |
| | 506 | |
| | 507 | if (extents.compressed_size != 0 and |
| | 508 | extents.compressed_size != self.compressed_size) |
| 471 | return error.ZipMismatchCompLen; | 509 | return error.ZipMismatchCompLen; |
| 472 | if (local_header.uncompressed_size != 0 and | 510 | if (extents.uncompressed_size != 0 and |
| 473 | local_header.uncompressed_size != self.uncompressed_size) | 511 | extents.uncompressed_size != self.uncompressed_size) |
| 474 | return error.ZipMismatchUncompLen; | 512 | return error.ZipMismatchUncompLen; |
| | 513 | |
| 475 | if (local_header.filename_len != self.filename_len) | 514 | if (local_header.filename_len != self.filename_len) |
| 476 | return error.ZipMismatchFilenameLen; | 515 | return error.ZipMismatchFilenameLen; |
| 477 | | 516 | |
| ... | @@ -695,6 +734,20 @@ test "zip64" { | ... | @@ -695,6 +734,20 @@ test "zip64" { |
| 695 | .central_directory_offset = std.math.maxInt(u32), // trigger zip64 | 734 | .central_directory_offset = std.math.maxInt(u32), // trigger zip64 |
| 696 | }, | 735 | }, |
| 697 | }); | 736 | }); |
| | 737 | try testZip(.{}, &test_files, .{ |
| | 738 | .end = .{ |
| | 739 | .zip64 = .{}, |
| | 740 | .central_directory_offset = std.math.maxInt(u32), // trigger zip64 |
| | 741 | }, |
| | 742 | .local_header = .{ |
| | 743 | .zip64 = .{ // trigger local header zip64 |
| | 744 | .data_size = 16, |
| | 745 | }, |
| | 746 | .compressed_size = std.math.maxInt(u32), |
| | 747 | .uncompressed_size = std.math.maxInt(u32), |
| | 748 | .extra_len = 20, |
| | 749 | }, |
| | 750 | }); |
| 698 | } | 751 | } |
| 699 | | 752 | |
| 700 | test "bad zip files" { | 753 | test "bad zip files" { |