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 adda4c182 COMPRESS-632: Fixes for CPIO bugs (#441)
adda4c182 is described below

commit adda4c1829aaae45939e370f47b478ef7f3e7e33
Author: Yakov Shafranovich <[email protected]>
AuthorDate: Tue Dec 5 13:55:50 2023 -0500

    COMPRESS-632: Fixes for CPIO bugs (#441)
    
    * Fixes for CPIO bugs
    
    * Fixes
    
    * fixed formatting
    
    * fixing checkstyle
    
    * moving overflow check to utility class
    
    * added unit test
    
    * made variables final
    
    * refactor Integer and Long parsing
    
    * split the methods into two: with and without radix
    
    * minor refactoring
    
    * changed test assertions
---
 .../archivers/ar/ArArchiveInputStream.java         |  30 +++-----
 .../archivers/cpio/CpioArchiveInputStream.java     |   3 +-
 .../compress/archivers/tar/TarArchiveEntry.java    |  27 +++----
 .../commons/compress/archivers/tar/TarUtils.java   |  22 +-----
 .../harmony/pack200/NewAttributeBands.java         |   3 +-
 .../compress/harmony/pack200/Pack200Adapter.java   |  14 +++-
 .../harmony/pack200/Pack200PackerAdapter.java      |   7 +-
 .../harmony/unpack200/NewAttributeBands.java       |   3 +-
 .../apache/commons/compress/utils/ExactMath.java   |   9 ++-
 .../commons/compress/utils/ParsingUtils.java       |  83 +++++++++++++++++++++
 .../archivers/cpio/CpioArchiveEntryTest.java}      |  32 +++-----
 .../archivers/cpio/CpioArchiveInputStreamTest.java |  10 +++
 .../commons/compress/utils/ExactMathTest.java      |  31 ++++++++
 .../commons/compress/utils/ParsingUtilsTest.java   |  59 +++++++++++++++
 .../commons/compress/cpio/bad_long_value.cpio      | Bin 0 -> 1536 bytes
 15 files changed, 244 insertions(+), 89 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 85d5144ad..d0f32954d 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
@@ -27,6 +27,7 @@ import java.util.regex.Pattern;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 import org.apache.commons.compress.utils.ArchiveUtils;
 import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.compress.utils.ParsingUtils;
 
 /**
  * Implements the "ar" archive format as an input stream.
@@ -152,28 +153,28 @@ public class ArArchiveInputStream extends 
ArchiveInputStream<ArArchiveEntry> {
         this.input = inputStream;
     }
 
-    private int asInt(final byte[] byteArray, final int offset, final int len) 
{
+    private int asInt(final byte[] byteArray, final int offset, final int len) 
throws IOException {
         return asInt(byteArray, offset, len, 10, false);
     }
 
-    private int asInt(final byte[] byteArray, final int offset, final int len, 
final boolean treatBlankAsZero) {
+    private int asInt(final byte[] byteArray, final int offset, final int len, 
final boolean treatBlankAsZero) throws IOException {
         return asInt(byteArray, offset, len, 10, treatBlankAsZero);
     }
 
-    private int asInt(final byte[] byteArray, final int offset, final int len, 
final int base) {
+    private int asInt(final byte[] byteArray, final int offset, final int len, 
final int base) throws IOException {
         return asInt(byteArray, offset, len, base, false);
     }
 
-    private int asInt(final byte[] byteArray, final int offset, final int len, 
final int base, final boolean treatBlankAsZero) {
+    private int asInt(final byte[] byteArray, final int offset, final int len, 
final int base, final boolean treatBlankAsZero) throws IOException {
         final String string = ArchiveUtils.toAsciiString(byteArray, offset, 
len).trim();
         if (string.isEmpty() && treatBlankAsZero) {
             return 0;
         }
-        return Integer.parseInt(string, base);
+        return ParsingUtils.parseIntValue(string, base);
     }
 
-    private long asLong(final byte[] byteArray, final int offset, final int 
len) {
-        return Long.parseLong(ArchiveUtils.toAsciiString(byteArray, offset, 
len).trim());
+    private long asLong(final byte[] byteArray, final int offset, final int 
len) throws IOException {
+        return 
ParsingUtils.parseLongValue(ArchiveUtils.toAsciiString(byteArray, offset, 
len).trim());
     }
 
     /*
@@ -198,13 +199,7 @@ public class ArArchiveInputStream extends 
ArchiveInputStream<ArArchiveEntry> {
      * @since 1.3
      */
     private String getBSDLongName(final String bsdLongName) throws IOException 
{
-        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 int nameLen = 
ParsingUtils.parseIntValue(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN));
         final byte[] name = IOUtils.readRange(input, nameLen);
         final int read = name.length;
         trackReadBytes(read);
@@ -326,12 +321,7 @@ public class ArArchiveInputStream extends 
ArchiveInputStream<ArArchiveEntry> {
         if (temp.endsWith("/")) { // GNU terminator
             temp = temp.substring(0, temp.length() - 1);
         } else if (isGNULongName(temp)) {
-            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);
-            }
+            final int off = ParsingUtils.parseIntValue(temp.substring(1));// 
get the offset
             temp = getExtendedName(off); // convert to the long name
         } else if (isBSDLongName(temp)) {
             temp = getBSDLongName(temp);
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 0e17d3202..42898831a 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
@@ -28,6 +28,7 @@ import 
org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
 import org.apache.commons.compress.utils.ArchiveUtils;
 import org.apache.commons.compress.utils.CharsetNames;
 import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.compress.utils.ParsingUtils;
 
 /**
  * CpioArchiveInputStream is a stream for reading cpio streams. All formats of 
cpio are supported (old ascii, old binary, new portable format and the new
@@ -362,7 +363,7 @@ public class CpioArchiveInputStream extends 
ArchiveInputStream<CpioArchiveEntry>
 
     private long readAsciiLong(final int length, final int radix) throws 
IOException {
         final byte[] tmpBuffer = readRange(length);
-        return Long.parseLong(ArchiveUtils.toAsciiString(tmpBuffer), radix);
+        return 
ParsingUtils.parseLongValue(ArchiveUtils.toAsciiString(tmpBuffer), radix);
     }
 
     private long readBinaryLong(final int length, final boolean swapHalfWord) 
throws IOException {
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java 
b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java
index e9e94000b..02b4ed449 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java
@@ -50,6 +50,7 @@ import 
org.apache.commons.compress.archivers.EntryStreamOffsets;
 import org.apache.commons.compress.archivers.zip.ZipEncoding;
 import org.apache.commons.compress.utils.ArchiveUtils;
 import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.compress.utils.ParsingUtils;
 import org.apache.commons.compress.utils.TimeUtils;
 
 /**
@@ -759,9 +760,9 @@ public class TarArchiveEntry implements ArchiveEntry, 
TarConstants, EntryStreamO
         return fill((byte) value, offset, outbuf, length);
     }
 
-    void fillGNUSparse0xData(final Map<String, String> headers) {
+    void fillGNUSparse0xData(final Map<String, String> headers) throws 
IOException {
         paxGNUSparse = true;
-        realSize = Integer.parseInt(headers.get(TarGnuSparseKeys.SIZE));
+        realSize = 
ParsingUtils.parseIntValue(headers.get(TarGnuSparseKeys.SIZE));
         if (headers.containsKey(TarGnuSparseKeys.NAME)) {
             // version 0.1
             name = headers.get(TarGnuSparseKeys.NAME);
@@ -775,22 +776,14 @@ public class TarArchiveEntry implements ArchiveEntry, 
TarConstants, EntryStreamO
             name = headers.get(TarGnuSparseKeys.NAME);
         }
         if (headers.containsKey(TarGnuSparseKeys.REALSIZE)) {
-            try {
-                realSize = 
Integer.parseInt(headers.get(TarGnuSparseKeys.REALSIZE));
-            } catch (final NumberFormatException ex) {
-                throw new IOException("Corrupted TAR archive. 
GNU.sparse.realsize header for " + name + " contains non-numeric value");
-            }
+            realSize = 
ParsingUtils.parseIntValue(headers.get(TarGnuSparseKeys.REALSIZE));
         }
     }
 
     void fillStarSparseData(final Map<String, String> headers) throws 
IOException {
         starSparse = true;
         if (headers.containsKey("SCHILY.realsize")) {
-            try {
-                realSize = Long.parseLong(headers.get("SCHILY.realsize"));
-            } catch (final NumberFormatException ex) {
-                throw new IOException("Corrupted TAR archive. SCHILY.realsize 
header for " + name + " contains non-numeric value");
-            }
+            realSize = 
ParsingUtils.parseLongValue(headers.get("SCHILY.realsize"));
         }
     }
 
@@ -1634,19 +1627,19 @@ public class TarArchiveEntry implements ArchiveEntry, 
TarConstants, EntryStreamO
             setLinkName(val);
             break;
         case "gid":
-            setGroupId(Long.parseLong(val));
+            setGroupId(ParsingUtils.parseLongValue(val));
             break;
         case "gname":
             setGroupName(val);
             break;
         case "uid":
-            setUserId(Long.parseLong(val));
+            setUserId(ParsingUtils.parseLongValue(val));
             break;
         case "uname":
             setUserName(val);
             break;
         case "size":
-            final long size = Long.parseLong(val);
+            final long size = ParsingUtils.parseLongValue(val);
             if (size < 0) {
                 throw new IOException("Corrupted TAR archive. Entry size is 
negative");
             }
@@ -1665,14 +1658,14 @@ public class TarArchiveEntry implements ArchiveEntry, 
TarConstants, EntryStreamO
             
setCreationTime(FileTime.from(parseInstantFromDecimalSeconds(val)));
             break;
         case "SCHILY.devminor":
-            final int devMinor = Integer.parseInt(val);
+            final int devMinor = ParsingUtils.parseIntValue(val);
             if (devMinor < 0) {
                 throw new IOException("Corrupted TAR archive. Dev-Minor is 
negative");
             }
             setDevMinor(devMinor);
             break;
         case "SCHILY.devmajor":
-            final int devMajor = Integer.parseInt(val);
+            final int devMajor = ParsingUtils.parseIntValue(val);
             if (devMajor < 0) {
                 throw new IOException("Corrupted TAR archive. Dev-Major is 
negative");
             }
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java 
b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
index 4c3741776..ef4538ee4 100644
--- a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
+++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
@@ -36,6 +36,7 @@ import org.apache.commons.compress.archivers.zip.ZipEncoding;
 import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
 import org.apache.commons.compress.utils.CharsetNames;
 import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.compress.utils.ParsingUtils;
 
 /**
  * This class provides static utility methods to work with byte streams.
@@ -396,21 +397,11 @@ public class TarUtils {
         }
 
         for (int i = 0; i < sparseHeaderStrings.length; i += 2) {
-            long sparseOffset;
-            try {
-                sparseOffset = Long.parseLong(sparseHeaderStrings[i]);
-            } catch (final NumberFormatException ex) {
-                throw new IOException("Corrupted TAR archive." + " Sparse 
struct offset contains a non-numeric value");
-            }
+            final long sparseOffset = 
ParsingUtils.parseLongValue(sparseHeaderStrings[i]);
             if (sparseOffset < 0) {
                 throw new IOException("Corrupted TAR archive." + " Sparse 
struct offset contains negative value");
             }
-            long sparseNumbytes;
-            try {
-                sparseNumbytes = Long.parseLong(sparseHeaderStrings[i + 1]);
-            } catch (final NumberFormatException ex) {
-                throw new IOException("Corrupted TAR archive." + " Sparse 
struct numbytes contains a non-numeric value");
-            }
+            final long sparseNumbytes = 
ParsingUtils.parseLongValue(sparseHeaderStrings[i + 1]);
             if (sparseNumbytes < 0) {
                 throw new IOException("Corrupted TAR archive." + " Sparse 
struct numbytes contains negative value");
             }
@@ -750,12 +741,7 @@ public class TarUtils {
                                         throw new IOException(
                                                 "Failed to read Paxheader." + 
TarGnuSparseKeys.OFFSET + " is expected before GNU.sparse.numbytes shows up.");
                                     }
-                                    long numbytes;
-                                    try {
-                                        numbytes = Long.parseLong(value);
-                                    } catch (final NumberFormatException ex) {
-                                        throw new IOException("Failed to read 
Paxheader." + TarGnuSparseKeys.NUMBYTES + " contains a non-numeric value.");
-                                    }
+                                    final long numbytes = 
ParsingUtils.parseLongValue(value);
                                     if (numbytes < 0) {
                                         throw new IOException("Failed to read 
Paxheader." + TarGnuSparseKeys.NUMBYTES + " contains negative value");
                                     }
diff --git 
a/src/main/java/org/apache/commons/compress/harmony/pack200/NewAttributeBands.java
 
b/src/main/java/org/apache/commons/compress/harmony/pack200/NewAttributeBands.java
index 6dc17e85a..46c06fc8d 100644
--- 
a/src/main/java/org/apache/commons/compress/harmony/pack200/NewAttributeBands.java
+++ 
b/src/main/java/org/apache/commons/compress/harmony/pack200/NewAttributeBands.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Map;
 
 import 
org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
+import org.apache.commons.compress.utils.ParsingUtils;
 import org.objectweb.asm.Label;
 
 /**
@@ -824,7 +825,7 @@ public class NewAttributeBands extends BandSet {
         if (read != digits.length) {
             throw new IOException("Error reading from the input stream");
         }
-        return Integer.valueOf(Integer.parseInt((negative ? "-" : "") + new 
String(digits)));
+        return ParsingUtils.parseIntValue((negative ? "-" : "") + new 
String(digits));
     }
 
     /**
diff --git 
a/src/main/java/org/apache/commons/compress/harmony/pack200/Pack200Adapter.java 
b/src/main/java/org/apache/commons/compress/harmony/pack200/Pack200Adapter.java
index 44471ed9e..1f53117be 100644
--- 
a/src/main/java/org/apache/commons/compress/harmony/pack200/Pack200Adapter.java
+++ 
b/src/main/java/org/apache/commons/compress/harmony/pack200/Pack200Adapter.java
@@ -18,6 +18,7 @@ package org.apache.commons.compress.harmony.pack200;
 
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
+import java.io.IOException;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
@@ -40,12 +41,21 @@ public abstract class Pack200Adapter {
      * Completion between 0..1.
      *
      * @param value Completion between 0..1.
+     * @throws IOException if the value cannot be parsed
      */
-    protected void completed(final double value) {
+    protected void completed(final double value) throws IOException {
         firePropertyChange("pack.progress", null, String.valueOf((int) (100 * 
value))); //$NON-NLS-1$
     }
 
-    protected void firePropertyChange(final String propertyName, final Object 
oldValue, final Object newValue) {
+    /**
+     * Reports a property update to listeners
+     *
+     * @param propertyName name of property being updated
+     * @param oldValue old property value
+     * @param newValue new property value
+     * @throws IOException if the values cannot be parsed
+     */
+    protected void firePropertyChange(final String propertyName, final Object 
oldValue, final Object newValue) throws IOException {
         support.firePropertyChange(propertyName, oldValue, newValue);
     }
 
diff --git 
a/src/main/java/org/apache/commons/compress/harmony/pack200/Pack200PackerAdapter.java
 
b/src/main/java/org/apache/commons/compress/harmony/pack200/Pack200PackerAdapter.java
index c4723368f..8e5debe65 100644
--- 
a/src/main/java/org/apache/commons/compress/harmony/pack200/Pack200PackerAdapter.java
+++ 
b/src/main/java/org/apache/commons/compress/harmony/pack200/Pack200PackerAdapter.java
@@ -22,6 +22,7 @@ import java.util.jar.JarFile;
 import java.util.jar.JarInputStream;
 
 import org.apache.commons.compress.java.util.jar.Pack200.Packer;
+import org.apache.commons.compress.utils.ParsingUtils;
 
 /**
  * This class provides the binding between the standard Pack200 interface and 
the internal interface for (un)packing. As this uses generics for the SortedMap,
@@ -32,7 +33,7 @@ public class Pack200PackerAdapter extends Pack200Adapter 
implements Packer {
     private final PackingOptions options = new PackingOptions();
 
     @Override
-    protected void firePropertyChange(final String propertyName, final Object 
oldValue, final Object newValue) {
+    protected void firePropertyChange(final String propertyName, final Object 
oldValue, final Object newValue) throws IOException {
         super.firePropertyChange(propertyName, oldValue, newValue);
         if (newValue != null && !newValue.equals(oldValue)) {
             if (propertyName.startsWith(CLASS_ATTRIBUTE_PFX)) {
@@ -44,7 +45,7 @@ public class Pack200PackerAdapter extends Pack200Adapter 
implements Packer {
             } else if (propertyName.equals(DEFLATE_HINT)) {
                 options.setDeflateHint((String) newValue);
             } else if (propertyName.equals(EFFORT)) {
-                options.setEffort(Integer.parseInt((String) newValue));
+                options.setEffort(ParsingUtils.parseIntValue((String) 
newValue));
             } else if (propertyName.startsWith(FIELD_ATTRIBUTE_PFX)) {
                 final String attributeName = 
propertyName.substring(FIELD_ATTRIBUTE_PFX.length());
                 options.addFieldAttributeAction(attributeName, (String) 
newValue);
@@ -61,7 +62,7 @@ public class Pack200PackerAdapter extends Pack200Adapter 
implements Packer {
                 }
                 options.addPassFile((String) newValue);
             } else if (propertyName.equals(SEGMENT_LIMIT)) {
-                options.setSegmentLimit(Long.parseLong((String) newValue));
+                options.setSegmentLimit(ParsingUtils.parseLongValue((String) 
newValue));
             } else if (propertyName.equals(UNKNOWN_ATTRIBUTE)) {
                 options.setUnknownAttributeAction((String) newValue);
             }
diff --git 
a/src/main/java/org/apache/commons/compress/harmony/unpack200/NewAttributeBands.java
 
b/src/main/java/org/apache/commons/compress/harmony/unpack200/NewAttributeBands.java
index bca63fd98..cc0a339dc 100644
--- 
a/src/main/java/org/apache/commons/compress/harmony/unpack200/NewAttributeBands.java
+++ 
b/src/main/java/org/apache/commons/compress/harmony/unpack200/NewAttributeBands.java
@@ -39,6 +39,7 @@ import 
org.apache.commons.compress.harmony.unpack200.bytecode.CPNameAndType;
 import org.apache.commons.compress.harmony.unpack200.bytecode.CPString;
 import org.apache.commons.compress.harmony.unpack200.bytecode.CPUTF8;
 import org.apache.commons.compress.harmony.unpack200.bytecode.NewAttribute;
+import org.apache.commons.compress.utils.ParsingUtils;
 
 /**
  * Sets of bands relating to a non-predefined attribute
@@ -884,7 +885,7 @@ public class NewAttributeBands extends BandSet {
         if (read != digits.length) {
             throw new IOException("Error reading from the input stream");
         }
-        return Integer.valueOf(Integer.parseInt((negative ? "-" : "") + new 
String(digits)));
+        return ParsingUtils.parseIntValue((negative ? "-" : "") + new 
String(digits));
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/compress/utils/ExactMath.java 
b/src/main/java/org/apache/commons/compress/utils/ExactMath.java
index 579fd7517..6d72acd9a 100644
--- a/src/main/java/org/apache/commons/compress/utils/ExactMath.java
+++ b/src/main/java/org/apache/commons/compress/utils/ExactMath.java
@@ -32,11 +32,14 @@ public class ExactMath {
      * @param x the first value, an int.
      * @param y the second value, a long,
      * @return the addition of both values.
-     * @throws ArithmeticException when y overflow an int.
-     * @throws ArithmeticException if the result overflows an int.
+     * @throws IllegalArgumentException when y or the result overflows an int
      */
     public static int add(final int x, final long y) {
-        return Math.addExact(x, Math.toIntExact(y));
+        try {
+            return Math.addExact(x, Math.toIntExact(y));
+        } catch (final ArithmeticException exp) {
+            throw new IllegalArgumentException("Argument too large or result 
overflows", exp);
+        }
     }
 
     private ExactMath() {
diff --git a/src/main/java/org/apache/commons/compress/utils/ParsingUtils.java 
b/src/main/java/org/apache/commons/compress/utils/ParsingUtils.java
new file mode 100644
index 000000000..0fc320209
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/utils/ParsingUtils.java
@@ -0,0 +1,83 @@
+/*
+ * 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
+ *
+ *   http://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.utils;
+
+import java.io.IOException;
+
+/**
+ * Utility methods for parsing data and converting it to other formats.
+ *
+ * @since 1.26.0
+ */
+public final class ParsingUtils {
+    /**
+     * Parses the provided string value to an Integer, assuming a base-10 radix
+     *
+     * @param value string value to parse
+     * @return parsed value as an int
+     * @throws IOException when the value cannot be parsed
+     */
+    public static int parseIntValue(final String value) throws IOException {
+        return parseIntValue(value, 10);
+    }
+
+    /**
+     * Parse the provided string value to an Integer with a provided radix
+     *
+     * @param value string value to parse
+     * @param radix radix value to use for parsing
+     * @return parsed value as an int
+     * @throws IOException when the value cannot be parsed
+     */
+    public static int parseIntValue(final String value, final int radix) 
throws IOException {
+        try {
+            return Integer.parseInt(value, radix);
+        } catch (final NumberFormatException exp) {
+            throw new IOException("Unable to parse int from string value: " + 
value);
+        }
+    }
+
+    /**
+     * Parses the provided string value to a Long, assuming a base-10 radix
+     *
+     * @param value string value to parse
+     * @return parsed value as a long
+     * @throws IOException when the value cannot be parsed
+     */
+    public static long parseLongValue(final String value) throws IOException {
+        return parseLongValue(value, 10);
+    }
+
+    /**
+     * Parses the provided string value to a Long with a provided radix
+     *
+     * @param value string value to parse
+     * @param radix radix value to use for parsing
+     * @return parsed value as a long
+     * @throws IOException when the value cannot be parsed
+     */
+    public static long parseLongValue(final String value, final int radix) 
throws IOException {
+        try {
+            return Long.parseLong(value, radix);
+        } catch (final NumberFormatException exp) {
+            throw new IOException("Unable to parse long from string value: " + 
value);
+        }
+    }
+
+    private ParsingUtils() {
+        /* no instances */ }
+}
diff --git a/src/main/java/org/apache/commons/compress/utils/ExactMath.java 
b/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveEntryTest.java
similarity index 53%
copy from src/main/java/org/apache/commons/compress/utils/ExactMath.java
copy to 
src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveEntryTest.java
index 579fd7517..8b5eb33c7 100644
--- a/src/main/java/org/apache/commons/compress/utils/ExactMath.java
+++ 
b/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveEntryTest.java
@@ -16,31 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.commons.compress.archivers.cpio;
 
-package org.apache.commons.compress.utils;
+import static org.junit.Assert.assertThrows;
 
-/**
- * PRIVATE.
- *
- * Performs exact math through {@link Math} "exact" APIs.
- */
-public class ExactMath {
+import org.junit.jupiter.api.Test;
 
-    /**
-     * Returns the int result of adding an int and a long, and throws an 
exception if the result overflows an int.
-     *
-     * @param x the first value, an int.
-     * @param y the second value, a long,
-     * @return the addition of both values.
-     * @throws ArithmeticException when y overflow an int.
-     * @throws ArithmeticException if the result overflows an int.
-     */
-    public static int add(final int x, final long y) {
-        return Math.addExact(x, Math.toIntExact(y));
+public class CpioArchiveEntryTest {
+    @Test
+    public void testGetHeaderPadCountOverflow() throws Exception {
+        final CpioArchiveEntry entry = new 
CpioArchiveEntry(CpioConstants.FORMAT_NEW);
+        entry.setName("test name");
+        assertThrows(IllegalArgumentException.class, () -> 
entry.getHeaderPadCount(Long.MAX_VALUE));
     }
-
-    private ExactMath() {
-        // no instances
-    }
-
 }
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 9c0d712a0..6fb2ed6ef 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
@@ -20,7 +20,9 @@ package org.apache.commons.compress.archivers.cpio;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import java.io.IOException;
 import java.io.InputStream;
 
 import org.apache.commons.compress.AbstractTest;
@@ -105,4 +107,12 @@ public class CpioArchiveInputStreamTest extends 
AbstractTest {
         }
     }
 
+    @Test
+    public void testInvalidLongValueInMetadata() throws Exception {
+        try (InputStream in = 
newInputStream("org/apache/commons/compress/cpio/bad_long_value.cpio");
+             CpioArchiveInputStream archive = new CpioArchiveInputStream(in)) {
+            assertThrows(IOException.class, archive::getNextEntry);
+        }
+    }
+
 }
diff --git a/src/test/java/org/apache/commons/compress/utils/ExactMathTest.java 
b/src/test/java/org/apache/commons/compress/utils/ExactMathTest.java
new file mode 100644
index 000000000..808a2cd93
--- /dev/null
+++ b/src/test/java/org/apache/commons/compress/utils/ExactMathTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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
+ *
+ *   http://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.utils;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class ExactMathTest {
+
+    @Test
+    public void testOverflow() {
+        assertThrows(IllegalArgumentException.class, () -> ExactMath.add(1, 
Long.MAX_VALUE / 1000));
+        assertThrows(IllegalArgumentException.class, () -> 
ExactMath.add(Integer.MAX_VALUE, Integer.MAX_VALUE));
+    }
+
+}
diff --git 
a/src/test/java/org/apache/commons/compress/utils/ParsingUtilsTest.java 
b/src/test/java/org/apache/commons/compress/utils/ParsingUtilsTest.java
new file mode 100644
index 000000000..1800a6b7a
--- /dev/null
+++ b/src/test/java/org/apache/commons/compress/utils/ParsingUtilsTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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
+ *
+ *   http://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.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Unit tests for class {@link ParsingUtils}.
+ *
+ * @see ParsingUtils
+ */
+public class ParsingUtilsTest {
+
+    @ParameterizedTest
+    @ValueSource(strings = {Integer.MIN_VALUE + "1", "x.x", "9e999", "1.1", 
"one", Integer.MAX_VALUE + "1"})
+    public void testParseIntValueInvalidValues(final String value) {
+        assertThrows(IOException.class, () -> 
ParsingUtils.parseIntValue(value, 10));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {Integer.MIN_VALUE + "", "-1", "1", "123456", 
Integer.MAX_VALUE + ""})
+    public void testParseIntValueValidValues(final String value) throws 
Exception {
+        assertEquals(Long.parseLong(value), ParsingUtils.parseIntValue(value, 
10));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {Long.MIN_VALUE + "1", "x.x", "9e999", "1.1", 
"one", Long.MAX_VALUE + "1"})
+    public void testParseLongValueInvalidValues(final String value) {
+        assertThrows(IOException.class, () -> 
ParsingUtils.parseLongValue(value, 10));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {Long.MIN_VALUE + "", "-1", "1", "12345678901234", 
Long.MAX_VALUE + ""})
+    public void testParseLongValueValidValues(final String value) throws 
Exception {
+        assertEquals(Long.parseLong(value), ParsingUtils.parseLongValue(value, 
10));
+    }
+
+}
diff --git 
a/src/test/resources/org/apache/commons/compress/cpio/bad_long_value.cpio 
b/src/test/resources/org/apache/commons/compress/cpio/bad_long_value.cpio
new file mode 100644
index 000000000..d497cd75c
Binary files /dev/null and 
b/src/test/resources/org/apache/commons/compress/cpio/bad_long_value.cpio differ

Reply via email to