This is an automated email from the ASF dual-hosted git repository. ggregory 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 63c4c5e64 COMPRESS-632: Fixes and tests for ArInputStream (#440) 63c4c5e64 is described below commit 63c4c5e6468c0faa0d9af05d563ba3fdccf32c21 Author: Yakov Shafranovich <yako...@users.noreply.github.com> AuthorDate: Tue Nov 14 16:38:35 2023 -0500 COMPRESS-632: Fixes and tests for ArInputStream (#440) * Fixes and tests for AR parsing issues * added another test * another test --------- Co-authored-by: Yakov Shafranovich <yako...@amazon.com> --- .../archivers/ar/ArArchiveInputStream.java | 60 ++++++++++++++++----- .../archivers/ar/ArArchiveInputStreamTest.java | 29 ++++++++++ .../compress/ar/number_parsing/bad_group.ar | 8 +++ .../compress/ar/number_parsing/bad_length.ar | 8 +++ .../ar/number_parsing/bad_long_namelen_bsd.ar | 5 ++ .../ar/number_parsing/bad_long_namelen_gnu1.ar | 8 +++ .../ar/number_parsing/bad_long_namelen_gnu2.ar | 6 +++ .../ar/number_parsing/bad_long_namelen_gnu3.ar | Bin 0 -> 274 bytes .../compress/ar/number_parsing/bad_modified.ar | 8 +++ .../ar/number_parsing/bad_table_length_gnu.ar | 8 +++ .../commons/compress/ar/number_parsing/bad_user.ar | 8 +++ 11 files changed, 135 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java index ddb9e6c6c..aba0f155a 100644 --- a/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java +++ b/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java @@ -205,8 +205,13 @@ public class ArArchiveInputStream extends ArchiveInputStream<ArArchiveEntry> { * @since 1.3 */ private String getBSDLongName(final String bsdLongName) throws IOException { - final int nameLen = - Integer.parseInt(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN)); + final int nameLen; + try { + nameLen = Integer.parseInt(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN)); + } catch (NumberFormatException ex) { + throw new IOException("Broken archive, unable to parse BSD long name length field as a number", ex); + } + final byte[] name = IOUtils.readRange(input, nameLen); final int read = name.length; trackReadBytes(read); @@ -229,10 +234,19 @@ public class ArArchiveInputStream extends ArchiveInputStream<ArArchiveEntry> { } for (int i = offset; i < namebuffer.length; i++) { if (namebuffer[i] == '\012' || namebuffer[i] == 0) { - if (namebuffer[i - 1] == '/') { + // Avoid array errors + if (i == 0) { + break; + } else if (namebuffer[i - 1] == '/') { i--; // drop trailing / } - return ArchiveUtils.toAsciiString(namebuffer, offset, i - offset); + + // Check there is a something to return, otherwise break out of the loop + if (i - offset > 0) { + return ArchiveUtils.toAsciiString(namebuffer, offset, i - offset); + } else { + break; + } } } throw new IOException("Failed to read entry: " + offset); @@ -311,11 +325,21 @@ public class ArArchiveInputStream extends ArchiveInputStream<ArArchiveEntry> { return getNextArEntry(); } - long len = asLong(metaData, LENGTH_OFFSET, LENGTH_LEN); + long len; + try { + len = asLong(metaData, LENGTH_OFFSET, LENGTH_LEN); + } catch (NumberFormatException ex) { + throw new IOException("Broken archive, unable to parse ar_size field as a number", ex); + } if (temp.endsWith("/")) { // GNU terminator temp = temp.substring(0, temp.length() - 1); } else if (isGNULongName(temp)) { - final int off = Integer.parseInt(temp.substring(1));// get the offset + int off; + try { + off = Integer.parseInt(temp.substring(1));// get the offset + } catch (NumberFormatException ex) { + throw new IOException("Broken archive, unable to parse GNU long name offset field as a number", ex); + } temp = getExtendedName(off); // convert to the long name } else if (isBSDLongName(temp)) { temp = getBSDLongName(temp); @@ -331,12 +355,16 @@ public class ArArchiveInputStream extends ArchiveInputStream<ArArchiveEntry> { throw new IOException("broken archive, entry with negative size"); } - currentEntry = new ArArchiveEntry(temp, len, - asInt(metaData, USER_ID_OFFSET, USER_ID_LEN, true), - asInt(metaData, GROUP_ID_OFFSET, GROUP_ID_LEN, true), - asInt(metaData, FILE_MODE_OFFSET, FILE_MODE_LEN, 8), - asLong(metaData, LAST_MODIFIED_OFFSET, LAST_MODIFIED_LEN)); - return currentEntry; + try { + currentEntry = new ArArchiveEntry(temp, len, + asInt(metaData, USER_ID_OFFSET, USER_ID_LEN, true), + asInt(metaData, GROUP_ID_OFFSET, GROUP_ID_LEN, true), + asInt(metaData, FILE_MODE_OFFSET, FILE_MODE_LEN, 8), + asLong(metaData, LAST_MODIFIED_OFFSET, LAST_MODIFIED_LEN)); + return currentEntry; + } catch (final NumberFormatException ex) { + throw new IOException("Broken archive, unable to parse entry metadata fields as numbers", ex); + } } /* @@ -389,7 +417,13 @@ public class ArArchiveInputStream extends ArchiveInputStream<ArArchiveEntry> { * @see #isGNUStringTable */ private ArArchiveEntry readGNUStringTable(final byte[] length, final int offset, final int len) throws IOException { - final int bufflen = asInt(length, offset, len); // Assume length will fit in an int + int bufflen; + try { + bufflen = asInt(length, offset, len); // Assume length will fit in an int + } catch (NumberFormatException ex) { + throw new IOException("Broken archive, unable to parse GNU string table length field as a number", ex); + } + namebuffer = IOUtils.readRange(input, bufflen); final int read = namebuffer.length; trackReadBytes(read); diff --git a/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java index ae86a45c1..2b2bd7d3e 100644 --- a/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java @@ -34,6 +34,8 @@ import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.utils.ArchiveUtils; import org.apache.commons.compress.utils.IOUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; public class ArArchiveInputStreamTest extends AbstractTest { @@ -134,4 +136,31 @@ public class ArArchiveInputStreamTest extends AbstractTest { assertEquals(-1, archive.read()); } } + + @ParameterizedTest + @ValueSource(strings = {"bad_group.ar", "bad_length.ar", "bad_modified.ar", "bad_user.ar"}) + public void testInvalidNumericFields(final String testFileName) throws Exception { + try (InputStream in = newInputStream("org/apache/commons/compress/ar/number_parsing/" + testFileName); + ArArchiveInputStream archive = new ArArchiveInputStream(in)) { + assertThrows(IOException.class, archive::getNextEntry); + } + } + + @ParameterizedTest + @ValueSource(strings = {"bad_long_namelen_bsd.ar", "bad_long_namelen_gnu1.ar", "bad_long_namelen_gnu2.ar", "bad_long_namelen_gnu3.ar", "bad_table_length_gnu.ar"}) + public void testInvalidLongNameLength(final String testFileName) throws Exception { + try (InputStream in = newInputStream("org/apache/commons/compress/ar/number_parsing/" + testFileName); + ArArchiveInputStream archive = new ArArchiveInputStream(in)) { + assertThrows(IOException.class, archive::getNextEntry); + } + } + + @Test + public void testInvalidBadTableLength() throws Exception { + try (InputStream in = newInputStream("org/apache/commons/compress/ar/number_parsing/bad_table_length_gnu.ar"); + ArArchiveInputStream archive = new ArArchiveInputStream(in)) { + assertThrows(IOException.class, archive::getNextEntry); + } + } + } diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_group.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_group.ar new file mode 100644 index 000000000..1cffcf1b3 --- /dev/null +++ b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_group.ar @@ -0,0 +1,8 @@ +!<arch> +// 68 ` +this_is_a_long_file_name.txt/ +this_is_a_long_file_name_as_well.txt/ +/0 1454693980 1000 1.23 100664 14 ` +Hello, world! +/30 1454694016 1000 1000 100664 4 ` +Bye diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_length.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_length.ar new file mode 100644 index 000000000..259991876 --- /dev/null +++ b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_length.ar @@ -0,0 +1,8 @@ +!<arch> +// 68 ` +this_is_a_long_file_name.txt/ +this_is_a_long_file_name_as_well.txt/ +/0 1454693980 1000 1000 100664 1.23 ` +Hello, world! +/30 1454694016 1000 1000 100664 4 ` +Bye diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_bsd.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_bsd.ar new file mode 100644 index 000000000..cd0f3b334 --- /dev/null +++ b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_bsd.ar @@ -0,0 +1,5 @@ +!<arch> +#1/123456789012 1311256511 1000 1000 100644 42 ` +this_is_a_long_file_name.txtHello, world! +#1/36 1454694016 1000 1000 100664 40 ` +this_is_a_long_file_name_as_well.txtBye diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu1.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu1.ar new file mode 100644 index 000000000..5779e549e --- /dev/null +++ b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu1.ar @@ -0,0 +1,8 @@ +!<arch> +// 68 ` +this_is_a_long_file_name.txt/ +this_is_a_long_file_name_as_well.txt/ +/9999999999 1454693980 1000 1000 100664 14 ` +Hello, world! +/30 1454694016 1000 1000 100664 4 ` +Bye diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu2.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu2.ar new file mode 100644 index 000000000..51e998d1f --- /dev/null +++ b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu2.ar @@ -0,0 +1,6 @@ +!<arch> +// 68 ` +this_is_a_long_file_name.txt/ +this_is_a_long_file_name_as_well.txt/ +/29 1454694016 1000 1000 100664 4 ` +Bye diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu3.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu3.ar new file mode 100644 index 000000000..81a31e6db Binary files /dev/null and b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_long_namelen_gnu3.ar differ diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_modified.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_modified.ar new file mode 100644 index 000000000..f4e12bbcd --- /dev/null +++ b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_modified.ar @@ -0,0 +1,8 @@ +!<arch> +// 68 ` +this_is_a_long_file_name.txt/ +this_is_a_long_file_name_as_well.txt/ +/0 9e99999999 1000 1000 100664 14 ` +Hello, world! +/30 1454694016 1000 1000 100664 4 ` +Bye diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_table_length_gnu.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_table_length_gnu.ar new file mode 100644 index 000000000..9fd61f458 --- /dev/null +++ b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_table_length_gnu.ar @@ -0,0 +1,8 @@ +!<arch> +// 1.23 ` +this_is_a_long_file_name.txt/ +this_is_a_long_file_name_as_well.txt/ +/0 1454693980 1000 1000 100664 14 ` +Hello, world! +/30 1454694016 1000 1000 100664 4 ` +Bye diff --git a/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_user.ar b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_user.ar new file mode 100644 index 000000000..fc46fed87 --- /dev/null +++ b/src/test/resources/org/apache/commons/compress/ar/number_parsing/bad_user.ar @@ -0,0 +1,8 @@ +!<arch> +// 68 ` +this_is_a_long_file_name.txt/ +this_is_a_long_file_name_as_well.txt/ +/0 1454693980 9e99 1000 100664 14 ` +Hello, world! +/30 1454694016 1000 1000 100664 4 ` +Bye