Skip to content

Reject unknown file type in CPIO entry mode - #790

Open
kali834x wants to merge 2 commits into
apache:masterfrom
kali834x:cpio-mode-file-type
Open

Reject unknown file type in CPIO entry mode#790
kali834x wants to merge 2 commits into
apache:masterfrom
kali834x:cpio-mode-file-type

Conversation

@kali834x

@kali834x kali834x commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Invalid CPIO header fields escape as IllegalArgumentException

CpioArchiveEntry.setMode accepts only the eight file types CPIO defines, so a crafted c_mode naming one of the seven other non-zero S_IFMT values (0170000, 030000, ...) throws a raw IllegalArgumentException out of getNextEntry, which declares IOException. setSize has the same problem in the two ASCII formats: a - in the size field parses to a negative value and the setter throws before the getSize() < 0 checks are reached.

Rather than guarding individual call sites, getNextCPIOEntry now rethrows IllegalArgumentException from header parsing as ArchiveException, keeping the original exception as the cause, the same way ArArchiveInputStream.getNextEntry already does. This covers every entry setter the three header readers call. The UnsupportedOperationException sites in CpioArchiveEntry are format guards and each reader only calls the setters matching the format it constructs, so those are not reachable from input.

  • Read the contribution guidelines for this project.
  • Read the ASF Generative Tooling Guidance if you use Artificial Intelligence (AI).
  • I used AI to create any part of, or all of, this pull request. Which AI tool was used to create this pull request, and to what extent did it contribute?
  • Run a successful build using the default Maven goal with mvn; that's mvn on the command line by itself.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied. This may not always be possible, but it is a best practice.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body. Note that a maintainer may squash commits during the merge process.

@garydgregory
garydgregory requested a review from Copilot July 28, 2026 17:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adjusts CPIO header parsing to reject unsupported file-type bits in c_mode without leaking a raw IllegalArgumentException, and adds regression tests for the three supported header formats.

Changes:

  • Introduces a setMode(...) helper to validate and apply c_mode across new ASCII, old ASCII, and old binary readers.
  • Attempts to convert invalid mode/file-type failures into an ArchiveException.
  • Adds unit tests covering invalid file type bits in c_mode for all three magic formats.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java Centralizes mode parsing/validation and changes exception behavior for invalid file types.
src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java Adds regression tests asserting the new error behavior for invalid c_mode file types.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (CpioUtil.fileType(mode) != 0) { // mode is initialized to 0
newEntry.setMode(mode);
}
setMode(newEntry, mode);
if (CpioUtil.fileType(mode) != 0) {
ret.setMode(mode);
}
setMode(ret, mode);
if (CpioUtil.fileType(mode) != 0) {
oldEntry.setMode(mode);
}
setMode(oldEntry, mode);
Comment on lines +545 to +554
private void setMode(final CpioArchiveEntry entry, final long mode) throws ArchiveException {
if (CpioUtil.fileType(mode) == 0) {
return;
}
try {
entry.setMode(mode);
} catch (final IllegalArgumentException e) {
throw new ArchiveException("Corrupted CPIO archive: Invalid file mode 0%s at byte: %,d", Long.toOctalString(mode), getBytesRead());
}
}
Comment on lines +199 to +203
try (CpioArchiveInputStream cpio = CpioArchiveInputStream.builder()
.setByteArray(header.getBytes(StandardCharsets.US_ASCII))
.get()) {
assertThrows(ArchiveException.class, cpio::getNextEntry);
}
@garydgregory

garydgregory commented Jul 28, 2026

Copy link
Copy Markdown
Member

Hello @kali834x
There plenty of other call sites in the class CpioArchiveEntry that throw IAE and here UOE. Do think the ones that are involved in other input call sites warrant the same treatment? I think we need a comprehensive solution instead of a one-off for a single call site, if this issue should be addressed at all, since IAE is thrown from so many call sites...

@kali834x

Copy link
Copy Markdown
Contributor Author

Went through CpioArchiveEntry to see what is actually reachable from the three header readers. From archive input it's setMode (any of the seven undefined S_IFMT values, in all three formats) and setSize (the two ASCII formats can parse a negative size via a - in the field, and the getSize() < 0 checks sit after the setter so they never fire). The UOE sites are all checkNewFormat/checkOldFormat guards, and each reader only calls the setters matching the format it constructs, so those can't trigger from input.

So agreed, a one-off wasn't the right shape. I dropped the helper and moved the conversion into getNextCPIOEntry instead: a single catch that rethrows IllegalArgumentException from header parsing as ArchiveException with the cause kept, same as ArArchiveInputStream.getNextEntry already does. That covers every setter the readers call, now and in the future, and added a regression test for the negative size case. I do think it's worth addressing since getNextEntry only declares IOException, and byte-mutating the bundled cpio test files reaches the setMode IAE readily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants