1515 */
1616public class JvmLocalFileHeader extends LocalFileHeader {
1717 private final NavigableSet <Long > offsets ;
18- private long offset ;
19- private boolean foundPk ;
18+ private long dataOffsetStart ;
19+ private long dataOffsetEnd ;
20+ private boolean foundData ;
2021 private ByteData data ;
2122
2223 /**
@@ -33,29 +34,53 @@ public void read(ByteData data, long offset) {
3334
3435 // JVM file data reading does NOT use the compressed/uncompressed fields.
3536 // Instead, it scans data until the next header.
36- offset += MIN_FIXED_SIZE + getFileNameLength () + getExtraFieldLength ();
37- this .offset = offset ;
38- Long nextOffset = offsets .ceiling (offset );
39- if (nextOffset != null ) {
40- setFileData (data .slice (offset , nextOffset ));
41- foundPk = true ;
37+ long dataOffsetStart = offset + MIN_FIXED_SIZE + getFileNameLength () + getExtraFieldLength ();
38+ Long dataOffsetEnd = offsets .ceiling (dataOffsetStart );
39+ this .dataOffsetStart = dataOffsetStart ;
40+ this .dataOffsetEnd = dataOffsetEnd == null ? -1 : dataOffsetEnd ;
41+ if (dataOffsetEnd != null ) {
42+ // Valid data range found
43+ setFileData (data .slice (dataOffsetStart , dataOffsetEnd ));
44+ foundData = true ;
4245 } else {
46+ // Keep data reference to attempt restoration with later when linking to the CEN.
4347 this .data = data ;
4448 }
4549 }
4650
4751 @ Override
4852 public void link (CentralDirectoryFileHeader directoryFileHeader ) {
4953 super .link (directoryFileHeader );
50- if (!foundPk ) {
54+
55+ // JVM trusts central directory file header contents over local
56+ // - Using fields as this maintains the lazy model
57+ compressionMethod = directoryFileHeader .compressionMethod ;
58+ compressedSize = directoryFileHeader .compressedSize ;
59+ uncompressedSize = directoryFileHeader .uncompressedSize ;
60+ fileName = directoryFileHeader .fileName ;
61+ generalPurposeBitFlag = directoryFileHeader .generalPurposeBitFlag ;
62+ crc32 = directoryFileHeader .crc32 ;
63+ lastModFileDate = directoryFileHeader .lastModFileDate ;
64+ lastModFileTime = directoryFileHeader .lastModFileTime ;
65+
66+ // Update file data with new compressed/uncompressed size if it was not able to be found previously
67+ // with only the local data available.
68+ if (!foundData ) {
69+ // Extract updated length from CEN values
5170 long fileDataLength ;
5271 if (getCompressionMethod () == ZipCompressions .STORED ) {
53- fileDataLength = directoryFileHeader . getUncompressedSize () & 0xFFFFFFFFL ;
72+ fileDataLength = getUncompressedSize () & 0xFFFFFFFFL ;
5473 } else {
55- fileDataLength = directoryFileHeader .getCompressedSize () & 0xFFFFFFFFL ;
74+ fileDataLength = getCompressedSize () & 0xFFFFFFFFL ;
75+ }
76+
77+ // Only allow lengths that are within a sensible range.
78+ // Data should not be overflowing into adjacent header entries.
79+ // - If it is, the data here is likely intentionally tampered with to screw with parsers
80+ if (fileDataLength + offset < dataOffsetEnd ) {
81+ setFileData (data .sliceOf (dataOffsetStart , fileDataLength ));
82+ data = null ;
5683 }
57- setFileData (data .sliceOf (offset , fileDataLength ));
58- data = null ;
5984 }
6085 }
6186}
0 commit comments