From 8c7e8778a64d254f591430ad9c2ecc9f29c1f2ab Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 27 Nov 2023 23:33:15 -0800 Subject: [PATCH] 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 --- .../boot/loader/zip/VirtualZipDataBlock.java | 12 ++++++------ .../zip/ZipCentralDirectoryFileHeaderRecord.java | 14 +++++++------- .../boot/loader/zip/ZipContent.java | 11 +++++++---- .../springframework/boot/loader/zip/ZipString.java | 4 ++-- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/VirtualZipDataBlock.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/VirtualZipDataBlock.java index 6ba095c7419..7b2541f4e07 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/VirtualZipDataBlock.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/VirtualZipDataBlock.java @@ -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)); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipCentralDirectoryFileHeaderRecord.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipCentralDirectoryFileHeaderRecord.java index 27f03587ae8..a9ca0a54ad8 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipCentralDirectoryFileHeaderRecord.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipCentralDirectoryFileHeaderRecord.java @@ -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); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java index cc980b915b3..f63a17b9e85 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipContent.java @@ -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; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java index bf246e0c7d6..6ffc4d7d68e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/zip/ZipString.java @@ -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);