This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-compress.git
The following commit(s) were added to refs/heads/master by this push:
new bf49bcfb8 Reject out-of-range cpUTF8Prefix in CpBands.parseCpUtf8
(#800)
bf49bcfb8 is described below
commit bf49bcfb809baedcf23b7eeff59256f4d93830d3
Author: KALI 834X <[email protected]>
AuthorDate: Wed Aug 12 17:51:40 2026 +0530
Reject out-of-range cpUTF8Prefix in CpBands.parseCpUtf8 (#800)
---
.../compress/harmony/unpack200/CpBands.java | 11 +++-
.../compress/harmony/unpack200/CpBandsTest.java | 69 ++++++++++++++++++++++
2 files changed, 78 insertions(+), 2 deletions(-)
diff --git
a/src/main/java/org/apache/commons/compress/harmony/unpack200/CpBands.java
b/src/main/java/org/apache/commons/compress/harmony/unpack200/CpBands.java
index 11b5328b6..19d19c095 100644
--- a/src/main/java/org/apache/commons/compress/harmony/unpack200/CpBands.java
+++ b/src/main/java/org/apache/commons/compress/harmony/unpack200/CpBands.java
@@ -703,12 +703,19 @@ private void parseCpUtf8(final InputStream in) throws
IOException, Pack200Except
bigSuffixCount = 0;
for (int i = 1; i < cpUTF8Count; i++) {
final String lastString = cpUTF8[i - 1];
+ // prefix is decoded with the signed DELTA5 codec, so a corrupt
archive can make it negative or
+ // larger than the previous string. Reject it here instead of
letting String.substring throw an
+ // unchecked StringIndexOutOfBoundsException out of the declared
Pack200Exception contract.
+ final int prefixLength = i > 1 ? prefix[i - 2] : 0;
+ if (prefixLength < 0 || prefixLength > lastString.length()) {
+ throw new Pack200Exception("cpUTF8Prefix value out of range: "
+ prefixLength);
+ }
if (suffix[i - 1] == 0) {
// The big suffix stuff hasn't been tested, and I'll be
// surprised if it works first time w/o errors ...
- cpUTF8[i] = lastString.substring(0, i > 1 ? prefix[i - 2] : 0)
+ new String(bigSuffixData[bigSuffixCount++]);
+ cpUTF8[i] = lastString.substring(0, prefixLength) + new
String(bigSuffixData[bigSuffixCount++]);
} else {
- cpUTF8[i] = lastString.substring(0, i > 1 ? prefix[i - 2] : 0)
+ new String(data, charCount, suffix[i - 1]);
+ cpUTF8[i] = lastString.substring(0, prefixLength) + new
String(data, charCount, suffix[i - 1]);
charCount += suffix[i - 1];
}
mapUTF8.put(cpUTF8[i], Integer.valueOf(i));
diff --git
a/src/test/java/org/apache/commons/compress/harmony/unpack200/CpBandsTest.java
b/src/test/java/org/apache/commons/compress/harmony/unpack200/CpBandsTest.java
new file mode 100644
index 000000000..f8e28afd8
--- /dev/null
+++
b/src/test/java/org/apache/commons/compress/harmony/unpack200/CpBandsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.compress.harmony.unpack200;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import org.apache.commons.compress.harmony.pack200.Codec;
+import org.apache.commons.compress.harmony.pack200.Pack200Exception;
+import org.junit.jupiter.api.Test;
+
+class CpBandsTest extends AbstractBandsTest {
+
+ private final class CpUTF8Header extends MockSegmentHeader {
+
+ CpUTF8Header(final Segment segment) {
+ super(segment);
+ }
+
+ @Override
+ public int getCpUTF8Count() {
+ return 3;
+ }
+ }
+
+ private final class CpUTF8Segment extends MockSegment {
+
+ private final SegmentHeader header = new CpUTF8Header(this);
+
+ @Override
+ public SegmentHeader getSegmentHeader() {
+ return header;
+ }
+ }
+
+ @Test
+ void testParseCpUtf8RejectsOutOfRangePrefix() throws Exception {
+ // The cpUTF8Prefix band is decoded with the signed DELTA5 codec, so a
corrupt archive can make a
+ // prefix larger than the preceding string (or negative). Used
directly as a String.substring end
+ // index that raised a raw StringIndexOutOfBoundsException out of the
declared Pack200Exception
+ // contract; it must now be rejected as corrupt input.
+ final CpBands bands = new CpBands(new CpUTF8Segment());
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(Codec.DELTA5.encode(new int[] { 1000 })); //
cpUTF8Prefix (count cpUTF8Count - 2)
+ baos.write(Codec.UNSIGNED5.encode(new int[] { 1, 1 })); //
cpUTF8Suffix (count cpUTF8Count - 1)
+ baos.write(Codec.CHAR3.encode(new int[] { 'a', 'b' })); //
cp_Utf8_chars
+ final InputStream in = new ByteArrayInputStream(baos.toByteArray());
+ assertThrows(Pack200Exception.class, () -> bands.read(in));
+ }
+}