You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2019/03/09 22:59:20 UTC
[sis] 04/05: Tests against some integer overflows.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 141af4766b5317672ee530c0ad1deb6ec6302611
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Mar 8 15:06:54 2019 +0100
Tests against some integer overflows.
---
.../org/apache/sis/coverage/grid/GridExtent.java | 4 +-
.../main/java/org/apache/sis/math/ArrayVector.java | 8 +--
.../main/java/org/apache/sis/math/Fraction.java | 7 +-
.../src/main/java/org/apache/sis/math/Vector.java | 2 +-
.../main/java/org/apache/sis/util/ArraysExt.java | 82 ++++++++++++++++++++--
.../java/org/apache/sis/util/resources/Errors.java | 5 ++
.../apache/sis/util/resources/Errors.properties | 1 +
.../apache/sis/util/resources/Errors_fr.properties | 1 +
.../java/org/apache/sis/util/ArraysExtTest.java | 19 +++++
.../java/org/apache/sis/internal/netcdf/Axis.java | 2 +-
.../org/apache/sis/internal/netcdf/CRSBuilder.java | 2 +-
.../org/apache/sis/internal/netcdf/Variable.java | 11 ++-
12 files changed, 125 insertions(+), 19 deletions(-)
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index d16598b..2dd3bf1 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -995,7 +995,9 @@ public class GridExtent implements Serializable {
final int j = i + m;
long low = coordinates[i];
long size = coordinates[j] - low + 1; // Result is an unsigned number.
- if (size == 0) throw new ArithmeticException("long overflow");
+ if (size == 0) {
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE));
+ }
long r = Long.divideUnsigned(size, s);
if (r*s == size) r--; // Make inclusive if the division did not already rounded toward 0.
sub.coordinates[i] = low /= s;
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java b/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
index d5dd4a2..8f66146 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java
@@ -919,7 +919,7 @@ abstract class ArrayVector<E extends Number> extends Vector implements CheckedCo
@Override public long longValue(final int index) {
final long value = super.longValue(index);
if (value >= 0) return value;
- throw new ArithmeticException();
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE));
}
/** Returns the string representation at the given index. */
@@ -960,7 +960,7 @@ abstract class ArrayVector<E extends Number> extends Vector implements CheckedCo
@Override public int intValue(int index) {
final int value = super.intValue(index);
if (value >= 0) return value;
- throw new ArithmeticException();
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Integer.SIZE));
}
/** Uses a larger type if the value exceed integer capacity. */
@@ -1016,7 +1016,7 @@ abstract class ArrayVector<E extends Number> extends Vector implements CheckedCo
@Override public short shortValue(int index) {
final short value = super.shortValue(index);
if (value >= 0) return value;
- throw new ArithmeticException();
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Short.SIZE));
}
/** Uses a larger type if the value exceed short integer capacity. */
@@ -1073,7 +1073,7 @@ abstract class ArrayVector<E extends Number> extends Vector implements CheckedCo
@Override public byte byteValue(int index) {
final byte value = super.byteValue(index);
if (value >= 0) return value;
- throw new ArithmeticException();
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Byte.SIZE));
}
/** Uses a larger type if the value exceed integer capacity. */
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java b/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java
index 92846f5..e33eb30 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java
@@ -18,6 +18,7 @@ package org.apache.sis.math;
import java.io.Serializable;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.collection.WeakHashSet;
@@ -115,7 +116,7 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri
*/
private Fraction simplify(long num, long den) {
if (num == Long.MIN_VALUE || den == Long.MIN_VALUE) {
- throw new ArithmeticException();
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE));
}
if (num == 0) {
den = Long.signum(den); // Simplify 0/x as 0/±1 or 0/0.
@@ -357,7 +358,7 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri
public short shortValue() {
final int n = intValue();
if ((n & ~0xFFFF) == 0) return (short) n;
- throw new ArithmeticException();
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Short.SIZE));
}
/**
@@ -370,7 +371,7 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri
public byte byteValue() {
final int n = intValue();
if ((n & ~0xFF) == 0) return (byte) n;
- throw new ArithmeticException();
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Byte.SIZE));
}
/**
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
index f25ed0d..abce606 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
@@ -687,7 +687,7 @@ search: for (;;) {
final long inc = a - b;
// The sign of the difference shall be the same than the sign of Long.compare(…).
if ((((isUnsigned() ? Long.compareUnsigned(a, b) : Long.compare(a, b)) ^ inc) & Long.MIN_VALUE) != 0) {
- throw new ArithmeticException();
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE));
}
return inc;
}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java b/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
index 51c80b0..a6dd7a7 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Objects;
import java.util.Comparator;
import java.lang.reflect.Array;
+import org.apache.sis.util.resources.Errors;
/**
@@ -1258,22 +1259,89 @@ public final class ArraysExt extends Static {
}
/**
- * Returns a sequence of increasing values of the given length. Each value is increased by 1.
- * For example {@code sequence(-1, 4)} returns {@code {-1, 0, 1, 2}}. This method is a convenience for
- * enumerating a subset of dimensions in a coordinate reference system or a subset of bands in an image.
+ * Returns a finite arithmetic progression of the given length. Each value is increased by 1.
+ * For example {@code sequence(-1, 4)} returns {@code {-1, 0, 1, 2}}.
+ *
+ * <div class="note"><b>Purpose:</b>
+ * this method is convenient for enumerating dimensions in a coordinate reference system or bands in an image.
+ * Some methods in the Java library or in Apache SIS want dimensions or bands to be specified by their indices.
+ * An example in the Java library is the {@code bankIndices} argument in
+ * <code>{@linkplain java.awt.image.Raster#createBandedRaster(int, int, int, int, int[], int[], java.awt.Point)
+ * Raster.createBandedRaster}(…, bankIndices, …)</code>.
+ * An example in Apache SIS is the {@code range} argument in
+ * <code>{@linkplain org.apache.sis.storage.GridCoverageResource#read GridCoverageResource.read}(…, range)</code>.</div>
+ *
+ * For any array returned by this method, <code>{@linkplain #isSequence(int, int[]) isSequence}(start, array)</code>
+ * is guaranteed to return {@code true}.
*
* @param start first value in the array to return.
* @param length number of values to return.
* @return a sequence of increasing integers starting at {@code start} and having {@code length} values.
+ * @throws ArithmeticException if {@code start + length} overflows 32 bits integer.
+ *
+ * @see java.util.stream.IntStream#range(int, int)
*
* @since 1.0
*/
public static int[] sequence(final int start, final int length) {
- final int[] array = new int[length];
- for (int i=0; i<length; i++) {
- array[i] = start + i;
+ if (length > 0) {
+ if (start + (length - 1) >= start) {
+ final int[] array = new int[length];
+ for (int i=0; i<length; i++) {
+ array[i] = start + i;
+ }
+ return array;
+ } else {
+ throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Integer.SIZE));
+ }
+ } else if (length == 0) {
+ return EMPTY_INT;
+ } else {
+ throw new NegativeArraySizeException(Errors.format(Errors.Keys.NegativeArgument_2, "length", length));
}
- return array;
+ }
+
+ /**
+ * Returns {@code true} if the given array is a finite arithmetic progression starting at the given value.
+ * More specifically:
+ *
+ * <ul>
+ * <li>If {@code array} is {@code null}, then return {@code false}.</li>
+ * <li>Otherwise if {@code array} is empty, then return {@code true} for consistency
+ * with <code>{@linkplain #sequence(int, int) sequence}(start, 0)</code>.</li>
+ * <li>Otherwise for any index 0 ≦ <var>i</var> {@literal <} {@code array.length}, if {@code array[i]}
+ * is equal to {@code start + i} (computed as if no overflow occurs), then return {@code true}.</li>
+ * <li>Otherwise return {@code false}.</li>
+ * </ul>
+ *
+ * <div class="note"><b>Example:</b>
+ * {@code isSequence(1, array)} returns {@code true} if the given array is {@code {1, 2, 3, 4}}
+ * but {@code false} if the array is {@code {1, 2, 4}} (missing 3).</div>
+ *
+ * @param start first value expected in the given {@code array}.
+ * @param array the array to test, or {@code null}.
+ * @return {@code true} if the given array is non-null and equal to
+ * <code>{@linkplain #sequence(int, int) sequence}(start, array.length)</code>.
+ *
+ * @see #sequence(int, int)
+ *
+ * @since 1.0
+ */
+ public static boolean isSequence(final int start, final int[] array) {
+ if (array == null) {
+ return false;
+ }
+ if (array.length != 0) {
+ if (start + (array.length - 1) < start) {
+ return false; // Overflow.
+ }
+ for (int i=0; i<array.length; i++) {
+ if (array[i] != start + i) {
+ return false;
+ }
+ }
+ }
+ return true;
}
/**
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
index 7ca6176..52589b4 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
@@ -489,6 +489,11 @@ public final class Errors extends IndexedResourceBundle {
public static final short InfiniteArgumentValue_1 = 73;
/**
+ * Integer overflow during {0} bits arithmetic operation.
+ */
+ public static final short IntegerOverflow_1 = 188;
+
+ /**
* “{0}” is an invalid version identifier.
*/
public static final short InvalidVersionIdentifier_1 = 179;
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
index 75be8f1..6007f78 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
@@ -108,6 +108,7 @@ InconsistentUnitsForCS_1 = Unit of measurement \u201c{0}\u201d is incon
IndexOutOfBounds_1 = Index {0} is out of bounds.
IndicesOutOfBounds_2 = Indices ({0}, {1}) are out of bounds.
InfiniteArgumentValue_1 = Argument \u2018{0}\u2019 can not take an infinite value.
+IntegerOverflow_1 = Integer overflow during {0} bits arithmetic operation.
InvalidVersionIdentifier_1 = \u201c{0}\u201d is an invalid version identifier.
KeyCollision_1 = Key \u201c{0}\u201d is associated twice to different values.
MandatoryAttribute_2 = Attribute \u201c{0}\u201d is mandatory for an object of type \u2018{1}\u2019.
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
index f87e89b..fe58d55 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
@@ -105,6 +105,7 @@ InconsistentUnitsForCS_1 = L\u2019unit\u00e9 de mesure \u00ab\u202f{0}\
IndexOutOfBounds_1 = L\u2019index {0} est en dehors des limites permises.
IndicesOutOfBounds_2 = Les index ({0}, {1}) sont en dehors des limites permises.
InfiniteArgumentValue_1 = L\u2019argument \u2018{0}\u2019 ne peut pas prendre une valeur infinie.
+IntegerOverflow_1 = D\u00e9passement d\u2019entier lors d\u2019une op\u00e9ration arithm\u00e9tique sur {0} bits.
InvalidVersionIdentifier_1 = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas un identifiant de version valide.
KeyCollision_1 = La cl\u00e9 \u00ab\u202f{0}\u202f\u00bb est associ\u00e9e deux fois \u00e0 des valeurs diff\u00e9rentes.
MandatoryAttribute_2 = L\u2019attribut \u00ab\u202f{0}\u202f\u00bb est obligatoire pour un objet de type \u2018{1}\u2019.
diff --git a/core/sis-utility/src/test/java/org/apache/sis/util/ArraysExtTest.java b/core/sis-utility/src/test/java/org/apache/sis/util/ArraysExtTest.java
index 295eb80..df015a2 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/util/ArraysExtTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/util/ArraysExtTest.java
@@ -58,6 +58,25 @@ public final strictfp class ArraysExtTest extends TestCase {
}
/**
+ * Tests {@link ArraysExt#sequence(int, int)} and {@link ArraysExt#isSequence(int, int[])}.
+ */
+ @Test
+ public void testSequence() {
+ int[] sequence = ArraysExt.sequence(-1, 4);
+ assertArrayEquals("sequence", new int[] {-1, 0, 1, 2}, sequence);
+ assertTrue ("isSequence", ArraysExt.isSequence(-1, sequence));
+ assertFalse("isSequence", ArraysExt.isSequence(-2, sequence));
+ assertTrue ("isSequence", ArraysExt.isSequence(1, new int[] {1, 2, 3, 4}));
+ assertFalse("isSequence", ArraysExt.isSequence(1, new int[] {1, 2, 4}));
+ try {
+ ArraysExt.sequence(Integer.MAX_VALUE - 10, 12);
+ fail("Expected ArithmeticException.");
+ } catch (ArithmeticException e) {
+ assertNotNull(e.getMessage());
+ }
+ }
+
+ /**
* Tests {@link ArraysExt#unionOfSorted(int[], int[])}.
*/
@Test
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
index 84879da..9f24ba7 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
@@ -349,7 +349,7 @@ public final class Axis extends NamedElement {
private int getSize(final int i) {
final int n = sourceSizes[i];
if (n >= 0) return n;
- throw new ArithmeticException("signed integer overflow");
+ throw new ArithmeticException(coordinates.errors().getString(Errors.Keys.IntegerOverflow_1, Integer.SIZE));
}
/**
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
index a2367f4..4c2579a 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
@@ -235,7 +235,7 @@ previous: for (int i=components.size(); --i >= 0;) {
*/
private void add(final Axis axis) throws DataStoreContentException {
if (dimension == Byte.MAX_VALUE) {
- throw new DataStoreContentException(Errors.getResources(getFirstAxis().coordinates.getLocale())
+ throw new DataStoreContentException(getFirstAxis().coordinates.errors()
.getString(Errors.Keys.ExcessiveListSize_2, "axes", (short) (Byte.MAX_VALUE + 1)));
}
if (dimension >= axes.length) {
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
index 1438698..19f7d4a 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
@@ -882,6 +882,15 @@ public abstract class Variable extends NamedElement {
}
/**
+ * Returns the resources to use for error messages.
+ *
+ * @return the resources for error messages using the locales specified to the decoder.
+ */
+ final Errors errors() {
+ return Errors.getResources(getLocale());
+ }
+
+ /**
* Reports a warning to the listeners specified at construction time.
* This method is for Apache SIS internal purpose only since resources may change at any time.
*
@@ -904,7 +913,7 @@ public abstract class Variable extends NamedElement {
* @param arguments values to be formatted in the {@link java.text.MessageFormat} pattern.
*/
final void error(final Class<?> caller, final String method, final Exception exception, final short key, final Object... arguments) {
- warning(decoder.listeners, caller, method, exception, Errors.getResources(getLocale()), key, arguments);
+ warning(decoder.listeners, caller, method, exception, errors(), key, arguments);
}
/**