Fix NegativeArraySizeException caused by missing unsigned conversion

Update `ZipContent` so that `eocd.totalNumberOfCentralDirectoryEntries`
is converted from a short to an unsigned int to prevent a negative
number from being used.

This commit also updates the code to consistently use `X.toUnsigned...`
helper methods rather than using bitwise operators.

Fixed gh-38572
This commit is contained in:
Phillip Webb 2023-11-27 23:33:15 -08:00
parent 86c2f28cb4
commit 8c7e8778a6
4 changed files with 22 additions and 19 deletions

View File

@ -53,8 +53,8 @@ class VirtualZipDataBlock extends VirtualDataBlock implements CloseableDataBlock
long centralRecordPos = centralRecordPositions[i]; long centralRecordPos = centralRecordPositions[i];
DataBlock name = new DataPart( DataBlock name = new DataPart(
centralRecordPos + ZipCentralDirectoryFileHeaderRecord.FILE_NAME_OFFSET + nameOffset, centralRecordPos + ZipCentralDirectoryFileHeaderRecord.FILE_NAME_OFFSET + nameOffset,
(centralRecord.fileNameLength() & 0xFFFF) - nameOffset); Short.toUnsignedLong(centralRecord.fileNameLength()) - nameOffset);
long localRecordPos = centralRecord.offsetToLocalHeader() & 0xFFFFFFFF; long localRecordPos = Integer.toUnsignedLong(centralRecord.offsetToLocalHeader());
ZipLocalFileHeaderRecord localRecord = ZipLocalFileHeaderRecord.load(this.data, localRecordPos); ZipLocalFileHeaderRecord localRecord = ZipLocalFileHeaderRecord.load(this.data, localRecordPos);
DataBlock content = new DataPart(localRecordPos + localRecord.size(), centralRecord.compressedSize()); DataBlock content = new DataPart(localRecordPos + localRecord.size(), centralRecord.compressedSize());
boolean hasDescriptorRecord = ZipDataDescriptorRecord.isPresentBasedOnFlag(centralRecord); boolean hasDescriptorRecord = ZipDataDescriptorRecord.isPresentBasedOnFlag(centralRecord);
@ -74,8 +74,8 @@ class VirtualZipDataBlock extends VirtualDataBlock implements CloseableDataBlock
long originalRecordPos, DataBlock name, int offsetToLocalHeader) throws IOException { long originalRecordPos, DataBlock name, int offsetToLocalHeader) throws IOException {
ZipCentralDirectoryFileHeaderRecord record = originalRecord.withFileNameLength((short) (name.size() & 0xFFFF)) ZipCentralDirectoryFileHeaderRecord record = originalRecord.withFileNameLength((short) (name.size() & 0xFFFF))
.withOffsetToLocalHeader(offsetToLocalHeader); .withOffsetToLocalHeader(offsetToLocalHeader);
int originalExtraFieldLength = originalRecord.extraFieldLength() & 0xFFFF; int originalExtraFieldLength = Short.toUnsignedInt(originalRecord.extraFieldLength());
int originalFileCommentLength = originalRecord.fileCommentLength() & 0xFFFF; int originalFileCommentLength = Short.toUnsignedInt(originalRecord.fileCommentLength());
DataBlock extraFieldAndComment = new DataPart( DataBlock extraFieldAndComment = new DataPart(
originalRecordPos + originalRecord.size() - originalExtraFieldLength - originalFileCommentLength, originalRecordPos + originalRecord.size() - originalExtraFieldLength - originalFileCommentLength,
originalExtraFieldLength + originalFileCommentLength); originalExtraFieldLength + originalFileCommentLength);
@ -89,8 +89,8 @@ class VirtualZipDataBlock extends VirtualDataBlock implements CloseableDataBlock
ZipLocalFileHeaderRecord originalRecord, ZipDataDescriptorRecord dataDescriptorRecord, DataBlock name, ZipLocalFileHeaderRecord originalRecord, ZipDataDescriptorRecord dataDescriptorRecord, DataBlock name,
DataBlock content) throws IOException { DataBlock content) throws IOException {
ZipLocalFileHeaderRecord record = originalRecord.withFileNameLength((short) (name.size() & 0xFFFF)); ZipLocalFileHeaderRecord record = originalRecord.withFileNameLength((short) (name.size() & 0xFFFF));
long originalRecordPos = centralRecord.offsetToLocalHeader() & 0xFFFFFFFF; long originalRecordPos = Integer.toUnsignedLong(centralRecord.offsetToLocalHeader());
int extraFieldLength = originalRecord.extraFieldLength() & 0xFFFF; int extraFieldLength = Short.toUnsignedInt(originalRecord.extraFieldLength());
parts.add(new ByteArrayDataBlock(record.asByteArray())); parts.add(new ByteArrayDataBlock(record.asByteArray()));
parts.add(name); parts.add(name);
parts.add(new DataPart(originalRecordPos + originalRecord.size() - extraFieldLength, extraFieldLength)); parts.add(new DataPart(originalRecordPos + originalRecord.size() - extraFieldLength, extraFieldLength));

View File

@ -83,14 +83,14 @@ record ZipCentralDirectoryFileHeaderRecord(short versionMadeBy, short versionNee
* @throws IOException on I/O error * @throws IOException on I/O error
*/ */
void copyTo(DataBlock dataBlock, long pos, ZipEntry zipEntry) throws IOException { void copyTo(DataBlock dataBlock, long pos, ZipEntry zipEntry) throws IOException {
int fileNameLength = fileNameLength() & 0xFFFF; int fileNameLength = Short.toUnsignedInt(fileNameLength());
int extraLength = extraFieldLength() & 0xFFFF; int extraLength = Short.toUnsignedInt(extraFieldLength());
int commentLength = fileCommentLength() & 0xFFFF; int commentLength = Short.toUnsignedInt(fileCommentLength());
zipEntry.setMethod(compressionMethod() & 0xFFFF); zipEntry.setMethod(Short.toUnsignedInt(compressionMethod()));
zipEntry.setTime(decodeMsDosFormatDateTime(lastModFileDate(), lastModFileTime())); zipEntry.setTime(decodeMsDosFormatDateTime(lastModFileDate(), lastModFileTime()));
zipEntry.setCrc(crc32() & 0xFFFFFFFFL); zipEntry.setCrc(Integer.toUnsignedLong(crc32()));
zipEntry.setCompressedSize(compressedSize() & 0xFFFFFFFFL); zipEntry.setCompressedSize(Integer.toUnsignedLong(compressedSize()));
zipEntry.setSize(uncompressedSize() & 0xFFFFFFFFL); zipEntry.setSize(Integer.toUnsignedLong(uncompressedSize()));
if (extraLength > 0) { if (extraLength > 0) {
long extraPos = pos + MINIMUM_SIZE + fileNameLength; long extraPos = pos + MINIMUM_SIZE + fileNameLength;
ByteBuffer buffer = ByteBuffer.allocate(extraLength); ByteBuffer buffer = ByteBuffer.allocate(extraLength);

View File

@ -556,13 +556,16 @@ public final class ZipContent implements Closeable {
Zip64EndOfCentralDirectoryRecord zip64Eocd = Zip64EndOfCentralDirectoryRecord.load(data, zip64Locator); Zip64EndOfCentralDirectoryRecord zip64Eocd = Zip64EndOfCentralDirectoryRecord.load(data, zip64Locator);
data = data.slice(getStartOfZipContent(data, eocd, zip64Eocd)); data = data.slice(getStartOfZipContent(data, eocd, zip64Eocd));
long centralDirectoryPos = (zip64Eocd != null) ? zip64Eocd.offsetToStartOfCentralDirectory() long centralDirectoryPos = (zip64Eocd != null) ? zip64Eocd.offsetToStartOfCentralDirectory()
: eocd.offsetToStartOfCentralDirectory(); : Integer.toUnsignedLong(eocd.offsetToStartOfCentralDirectory());
long numberOfEntries = (zip64Eocd != null) ? zip64Eocd.totalNumberOfCentralDirectoryEntries() long numberOfEntries = (zip64Eocd != null) ? zip64Eocd.totalNumberOfCentralDirectoryEntries()
: eocd.totalNumberOfCentralDirectoryEntries(); : Short.toUnsignedInt(eocd.totalNumberOfCentralDirectoryEntries());
if (numberOfEntries > 0xFFFFFFFFL) { if (numberOfEntries < 0) {
throw new IllegalStateException("Invalid number of zip entries in " + source);
}
if (numberOfEntries > Integer.MAX_VALUE) {
throw new IllegalStateException("Too many zip entries in " + source); throw new IllegalStateException("Too many zip entries in " + source);
} }
Loader loader = new Loader(source, null, data, centralDirectoryPos, (int) (numberOfEntries & 0xFFFFFFFFL)); Loader loader = new Loader(source, null, data, centralDirectoryPos, (int) numberOfEntries);
ByteBuffer signatureNameSuffixBuffer = ByteBuffer.allocate(SIGNATURE_SUFFIX.length); ByteBuffer signatureNameSuffixBuffer = ByteBuffer.allocate(SIGNATURE_SUFFIX.length);
boolean hasJarSignatureFile = false; boolean hasJarSignatureFile = false;
long pos = centralDirectoryPos; long pos = centralDirectoryPos;

View File

@ -286,7 +286,7 @@ final class ZipString {
} }
private static int getCodePointSize(byte[] bytes, int i) { private static int getCodePointSize(byte[] bytes, int i) {
int b = bytes[i] & 0xFF; int b = Byte.toUnsignedInt(bytes[i]);
if ((b & 0b1_0000000) == 0b0_0000000) { if ((b & 0b1_0000000) == 0b0_0000000) {
return 1; return 1;
} }
@ -300,7 +300,7 @@ final class ZipString {
} }
private static int getCodePoint(byte[] bytes, int i, int codePointSize) { private static int getCodePoint(byte[] bytes, int i, int codePointSize) {
int codePoint = bytes[i] & 0xFF; int codePoint = Byte.toUnsignedInt(bytes[i]);
codePoint &= INITIAL_BYTE_BITMASK[codePointSize - 1]; codePoint &= INITIAL_BYTE_BITMASK[codePointSize - 1];
for (int j = 1; j < codePointSize; j++) { for (int j = 1; j < codePointSize; j++) {
codePoint = (codePoint << 6) + (bytes[i + j] & SUBSEQUENT_BYTE_BITMASK); codePoint = (codePoint << 6) + (bytes[i + j] & SUBSEQUENT_BYTE_BITMASK);