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];
DataBlock name = new DataPart(
centralRecordPos + ZipCentralDirectoryFileHeaderRecord.FILE_NAME_OFFSET + nameOffset,
(centralRecord.fileNameLength() & 0xFFFF) - nameOffset);
long localRecordPos = centralRecord.offsetToLocalHeader() & 0xFFFFFFFF;
Short.toUnsignedLong(centralRecord.fileNameLength()) - nameOffset);
long localRecordPos = Integer.toUnsignedLong(centralRecord.offsetToLocalHeader());
ZipLocalFileHeaderRecord localRecord = ZipLocalFileHeaderRecord.load(this.data, localRecordPos);
DataBlock content = new DataPart(localRecordPos + localRecord.size(), centralRecord.compressedSize());
boolean hasDescriptorRecord = ZipDataDescriptorRecord.isPresentBasedOnFlag(centralRecord);
@ -74,8 +74,8 @@ class VirtualZipDataBlock extends VirtualDataBlock implements CloseableDataBlock
long originalRecordPos, DataBlock name, int offsetToLocalHeader) throws IOException {
ZipCentralDirectoryFileHeaderRecord record = originalRecord.withFileNameLength((short) (name.size() & 0xFFFF))
.withOffsetToLocalHeader(offsetToLocalHeader);
int originalExtraFieldLength = originalRecord.extraFieldLength() & 0xFFFF;
int originalFileCommentLength = originalRecord.fileCommentLength() & 0xFFFF;
int originalExtraFieldLength = Short.toUnsignedInt(originalRecord.extraFieldLength());
int originalFileCommentLength = Short.toUnsignedInt(originalRecord.fileCommentLength());
DataBlock extraFieldAndComment = new DataPart(
originalRecordPos + originalRecord.size() - originalExtraFieldLength - originalFileCommentLength,
originalExtraFieldLength + originalFileCommentLength);
@ -89,8 +89,8 @@ class VirtualZipDataBlock extends VirtualDataBlock implements CloseableDataBlock
ZipLocalFileHeaderRecord originalRecord, ZipDataDescriptorRecord dataDescriptorRecord, DataBlock name,
DataBlock content) throws IOException {
ZipLocalFileHeaderRecord record = originalRecord.withFileNameLength((short) (name.size() & 0xFFFF));
long originalRecordPos = centralRecord.offsetToLocalHeader() & 0xFFFFFFFF;
int extraFieldLength = originalRecord.extraFieldLength() & 0xFFFF;
long originalRecordPos = Integer.toUnsignedLong(centralRecord.offsetToLocalHeader());
int extraFieldLength = Short.toUnsignedInt(originalRecord.extraFieldLength());
parts.add(new ByteArrayDataBlock(record.asByteArray()));
parts.add(name);
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
*/
void copyTo(DataBlock dataBlock, long pos, ZipEntry zipEntry) throws IOException {
int fileNameLength = fileNameLength() & 0xFFFF;
int extraLength = extraFieldLength() & 0xFFFF;
int commentLength = fileCommentLength() & 0xFFFF;
zipEntry.setMethod(compressionMethod() & 0xFFFF);
int fileNameLength = Short.toUnsignedInt(fileNameLength());
int extraLength = Short.toUnsignedInt(extraFieldLength());
int commentLength = Short.toUnsignedInt(fileCommentLength());
zipEntry.setMethod(Short.toUnsignedInt(compressionMethod()));
zipEntry.setTime(decodeMsDosFormatDateTime(lastModFileDate(), lastModFileTime()));
zipEntry.setCrc(crc32() & 0xFFFFFFFFL);
zipEntry.setCompressedSize(compressedSize() & 0xFFFFFFFFL);
zipEntry.setSize(uncompressedSize() & 0xFFFFFFFFL);
zipEntry.setCrc(Integer.toUnsignedLong(crc32()));
zipEntry.setCompressedSize(Integer.toUnsignedLong(compressedSize()));
zipEntry.setSize(Integer.toUnsignedLong(uncompressedSize()));
if (extraLength > 0) {
long extraPos = pos + MINIMUM_SIZE + fileNameLength;
ByteBuffer buffer = ByteBuffer.allocate(extraLength);

View File

@ -556,13 +556,16 @@ public final class ZipContent implements Closeable {
Zip64EndOfCentralDirectoryRecord zip64Eocd = Zip64EndOfCentralDirectoryRecord.load(data, zip64Locator);
data = data.slice(getStartOfZipContent(data, eocd, zip64Eocd));
long centralDirectoryPos = (zip64Eocd != null) ? zip64Eocd.offsetToStartOfCentralDirectory()
: eocd.offsetToStartOfCentralDirectory();
: Integer.toUnsignedLong(eocd.offsetToStartOfCentralDirectory());
long numberOfEntries = (zip64Eocd != null) ? zip64Eocd.totalNumberOfCentralDirectoryEntries()
: eocd.totalNumberOfCentralDirectoryEntries();
if (numberOfEntries > 0xFFFFFFFFL) {
: Short.toUnsignedInt(eocd.totalNumberOfCentralDirectoryEntries());
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);
}
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);
boolean hasJarSignatureFile = false;
long pos = centralDirectoryPos;

View File

@ -286,7 +286,7 @@ final class ZipString {
}
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) {
return 1;
}
@ -300,7 +300,7 @@ final class ZipString {
}
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];
for (int j = 1; j < codePointSize; j++) {
codePoint = (codePoint << 6) + (bytes[i + j] & SUBSEQUENT_BYTE_BITMASK);