From f4743334053d6fc4adeaa207dcddb8e2186f97cb Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Thu, 9 Oct 2025 11:03:51 +0200 Subject: [PATCH] [COMPRESS-711] Fix incorrect CPIO checksum verification This PR fixes an issue in `CpioArchiveInputStream` where the checksum was computed using the wrong slice of the buffer. Previously, it always used bytes `0` through `len`, ignoring the specified `off` parameter. As a result, checksum verification could fail when `read()` was called with a non-zero offset. ### Changes * Corrected checksum calculation to use the actual range `off` through `off + len`. * Added a regression test to ensure checksum verification works correctly with non-zero offsets. --- src/changes/changes.xml | 1 + .../archivers/cpio/CpioArchiveInputStream.java | 4 ++-- .../cpio/CpioArchiveInputStreamTest.java | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 85c50ea0042..63b00a316c3 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -89,6 +89,7 @@ The type attribute can be add,update,fix,remove. CpioArchiveInputStream.getNextEntry() now throws a MemoryLimitException instead of OutOfMemoryError when it can't process input greater than available memory. CpioArchiveInputStream.readOldAsciiEntry(boolean) now throws ArchiveException instead of Arithmetic exception. CpioArchiveInputStream.readOldBinaryEntry(boolean) now throws ArchiveException instead of Arithmetic exception. + Fix checksum calculation in CpioArchiveInputStream when reading with a non-zero offset. GzipParameters.setOperatingSystem(int) now throws CompressorException on illegal input. GZip IOException: Extra subfield length exceeds remaining bytes in extra field; use new option GzipCompressorInputStream.Builder.setIgnoreExtraField(boolean). diff --git a/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java index 2ef00b7b549..2915bb44808 100644 --- a/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java +++ b/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java @@ -404,9 +404,9 @@ public int read(final byte[] b, final int off, final int len) throws IOException final int tmpread = readFully(b, off, tmplength); if (entry.getFormat() == FORMAT_NEW_CRC) { for (int pos = 0; pos < tmpread; pos++) { - crc += b[pos] & 0xFF; - crc &= 0xFFFFFFFFL; + crc += b[off + pos] & 0xFF; } + crc &= 0xFFFFFFFFL; } if (tmpread > 0) { entryBytesRead += tmpread; diff --git a/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java index 75cacb00f2b..ecdab8388c0 100644 --- a/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java @@ -19,6 +19,7 @@ package org.apache.commons.compress.archivers.cpio; import static org.apache.commons.lang3.reflect.FieldUtils.readDeclaredField; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -33,6 +34,7 @@ import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.Issue; class CpioArchiveInputStreamTest extends AbstractTest { @@ -184,4 +186,19 @@ void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception { assertEquals(-1, archive.read()); } } + + @Test + @Issue("https://issues.apache.org/jira/browse/COMPRESS-711") + void testCrcVerification() throws Exception { + try (CpioArchiveInputStream archive = CpioArchiveInputStream.builder().setURI(getURI("bla.cpio")).get()) { + assertNotNull(archive.getNextEntry()); + assertDoesNotThrow(() -> { + final byte[] buffer = new byte[1024]; + // Read with an offset to test that the right bytes are checksummed + while (archive.read(buffer, 1, 1023) != -1) { + // noop + } + }); + } + } }