Fix integer overflows when dealing with templates. (#21628)
The overflows were happening in two places, the parsing of the template that implicitly truncates the `order` when its value does not fall into the `integer` range, and the comparator that sorts templates in ascending order, since it returns `order2-order1`, which might overflow. Closes #21622
This commit is contained in:
parent
90247446aa
commit
23d5293f82
|
@ -474,12 +474,7 @@ public class MetaDataCreateIndexService extends AbstractComponent {
|
|||
}
|
||||
}
|
||||
|
||||
CollectionUtil.timSort(templateMetadata, new Comparator<IndexTemplateMetaData>() {
|
||||
@Override
|
||||
public int compare(IndexTemplateMetaData o1, IndexTemplateMetaData o2) {
|
||||
return o2.order() - o1.order();
|
||||
}
|
||||
});
|
||||
CollectionUtil.timSort(templateMetadata, Comparator.comparingInt(IndexTemplateMetaData::order).reversed());
|
||||
return templateMetadata;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ package org.elasticsearch.common;
|
|||
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* A set of utilities for numbers.
|
||||
*/
|
||||
|
@ -178,4 +181,56 @@ public final class Numbers {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Return the long that {@code n} stores, or throws an exception if the
|
||||
* stored value cannot be converted to a long that stores the exact same
|
||||
* value. */
|
||||
public static long toLongExact(Number n) {
|
||||
if (n instanceof Byte || n instanceof Short || n instanceof Integer
|
||||
|| n instanceof Long) {
|
||||
return n.longValue();
|
||||
} else if (n instanceof Float || n instanceof Double) {
|
||||
double d = n.doubleValue();
|
||||
if (d != Math.round(d)) {
|
||||
throw new IllegalArgumentException(n + " is not an integer value");
|
||||
}
|
||||
return n.longValue();
|
||||
} else if (n instanceof BigDecimal) {
|
||||
return ((BigDecimal) n).toBigIntegerExact().longValueExact();
|
||||
} else if (n instanceof BigInteger) {
|
||||
return ((BigInteger) n).longValueExact();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot check whether [" + n + "] of class [" + n.getClass().getName()
|
||||
+ "] is actually a long");
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the int that {@code n} stores, or throws an exception if the
|
||||
* stored value cannot be converted to an int that stores the exact same
|
||||
* value. */
|
||||
public static int toIntExact(Number n) {
|
||||
return Math.toIntExact(toLongExact(n));
|
||||
}
|
||||
|
||||
/** Return the short that {@code n} stores, or throws an exception if the
|
||||
* stored value cannot be converted to a short that stores the exact same
|
||||
* value. */
|
||||
public static short toShortExact(Number n) {
|
||||
long l = toLongExact(n);
|
||||
if (l != (short) l) {
|
||||
throw new ArithmeticException("short overflow: " + l);
|
||||
}
|
||||
return (short) l;
|
||||
}
|
||||
|
||||
/** Return the byte that {@code n} stores, or throws an exception if the
|
||||
* stored value cannot be converted to a byte that stores the exact same
|
||||
* value. */
|
||||
public static byte toByteExact(Number n) {
|
||||
long l = toLongExact(n);
|
||||
if (l != (byte) l) {
|
||||
throw new ArithmeticException("byte overflow: " + l);
|
||||
}
|
||||
return (byte) l;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.apache.lucene.util.automaton.Automaton;
|
|||
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
|
||||
import org.apache.lucene.util.automaton.Operations;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.Numbers;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
@ -357,7 +358,7 @@ public class XContentMapValues {
|
|||
|
||||
public static int nodeIntegerValue(Object node) {
|
||||
if (node instanceof Number) {
|
||||
return ((Number) node).intValue();
|
||||
return Numbers.toIntExact((Number) node);
|
||||
}
|
||||
return Integer.parseInt(node.toString());
|
||||
}
|
||||
|
@ -366,10 +367,7 @@ public class XContentMapValues {
|
|||
if (node == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (node instanceof Number) {
|
||||
return ((Number) node).intValue();
|
||||
}
|
||||
return Integer.parseInt(node.toString());
|
||||
return nodeIntegerValue(node);
|
||||
}
|
||||
|
||||
public static short nodeShortValue(Object node, short defaultValue) {
|
||||
|
@ -381,7 +379,7 @@ public class XContentMapValues {
|
|||
|
||||
public static short nodeShortValue(Object node) {
|
||||
if (node instanceof Number) {
|
||||
return ((Number) node).shortValue();
|
||||
return Numbers.toShortExact((Number) node);
|
||||
}
|
||||
return Short.parseShort(node.toString());
|
||||
}
|
||||
|
@ -395,7 +393,7 @@ public class XContentMapValues {
|
|||
|
||||
public static byte nodeByteValue(Object node) {
|
||||
if (node instanceof Number) {
|
||||
return ((Number) node).byteValue();
|
||||
return Numbers.toByteExact((Number) node);
|
||||
}
|
||||
return Byte.parseByte(node.toString());
|
||||
}
|
||||
|
@ -409,7 +407,7 @@ public class XContentMapValues {
|
|||
|
||||
public static long nodeLongValue(Object node) {
|
||||
if (node instanceof Number) {
|
||||
return ((Number) node).longValue();
|
||||
return Numbers.toLongExact((Number) node);
|
||||
}
|
||||
return Long.parseLong(node.toString());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch 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.elasticsearch.common;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class NumbersTests extends ESTestCase {
|
||||
|
||||
public void testToLongExact() {
|
||||
assertEquals(3L, Numbers.toLongExact(Long.valueOf(3L)));
|
||||
assertEquals(3L, Numbers.toLongExact(Integer.valueOf(3)));
|
||||
assertEquals(3L, Numbers.toLongExact(Short.valueOf((short) 3)));
|
||||
assertEquals(3L, Numbers.toLongExact(Byte.valueOf((byte) 3)));
|
||||
assertEquals(3L, Numbers.toLongExact(3d));
|
||||
assertEquals(3L, Numbers.toLongExact(3f));
|
||||
assertEquals(3L, Numbers.toLongExact(BigInteger.valueOf(3L)));
|
||||
assertEquals(3L, Numbers.toLongExact(BigDecimal.valueOf(3L)));
|
||||
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(3.1d));
|
||||
assertEquals("3.1 is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(Double.NaN));
|
||||
assertEquals("NaN is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(Double.POSITIVE_INFINITY));
|
||||
assertEquals("Infinity is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(3.1f));
|
||||
assertEquals("3.1 is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(new AtomicInteger(3))); // not supported
|
||||
assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage());
|
||||
}
|
||||
|
||||
public void testToIntExact() {
|
||||
assertEquals(3L, Numbers.toIntExact(Long.valueOf(3L)));
|
||||
assertEquals(3L, Numbers.toIntExact(Integer.valueOf(3)));
|
||||
assertEquals(3L, Numbers.toIntExact(Short.valueOf((short) 3)));
|
||||
assertEquals(3L, Numbers.toIntExact(Byte.valueOf((byte) 3)));
|
||||
assertEquals(3L, Numbers.toIntExact(3d));
|
||||
assertEquals(3L, Numbers.toIntExact(3f));
|
||||
assertEquals(3L, Numbers.toIntExact(BigInteger.valueOf(3L)));
|
||||
assertEquals(3L, Numbers.toIntExact(BigDecimal.valueOf(3L)));
|
||||
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toIntExact(3.1d));
|
||||
assertEquals("3.1 is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(Double.NaN));
|
||||
assertEquals("NaN is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(Double.POSITIVE_INFINITY));
|
||||
assertEquals("Infinity is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toIntExact(3.1f));
|
||||
assertEquals("3.1 is not an integer value", e.getMessage());
|
||||
ArithmeticException ae = expectThrows(ArithmeticException.class,
|
||||
() -> Numbers.toIntExact(1L << 40));
|
||||
assertEquals("integer overflow", ae.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toIntExact(new AtomicInteger(3))); // not supported
|
||||
assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage());
|
||||
}
|
||||
|
||||
public void testToShortExact() {
|
||||
assertEquals(3L, Numbers.toShortExact(Long.valueOf(3L)));
|
||||
assertEquals(3L, Numbers.toShortExact(Integer.valueOf(3)));
|
||||
assertEquals(3L, Numbers.toShortExact(Short.valueOf((short) 3)));
|
||||
assertEquals(3L, Numbers.toShortExact(Byte.valueOf((byte) 3)));
|
||||
assertEquals(3L, Numbers.toShortExact(3d));
|
||||
assertEquals(3L, Numbers.toShortExact(3f));
|
||||
assertEquals(3L, Numbers.toShortExact(BigInteger.valueOf(3L)));
|
||||
assertEquals(3L, Numbers.toShortExact(BigDecimal.valueOf(3L)));
|
||||
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toShortExact(3.1d));
|
||||
assertEquals("3.1 is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(Double.NaN));
|
||||
assertEquals("NaN is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(Double.POSITIVE_INFINITY));
|
||||
assertEquals("Infinity is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toShortExact(3.1f));
|
||||
assertEquals("3.1 is not an integer value", e.getMessage());
|
||||
ArithmeticException ae = expectThrows(ArithmeticException.class,
|
||||
() -> Numbers.toShortExact(100000));
|
||||
assertEquals("short overflow: " + 100000, ae.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toShortExact(new AtomicInteger(3))); // not supported
|
||||
assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage());
|
||||
}
|
||||
|
||||
public void testToByteExact() {
|
||||
assertEquals(3L, Numbers.toByteExact(Long.valueOf(3L)));
|
||||
assertEquals(3L, Numbers.toByteExact(Integer.valueOf(3)));
|
||||
assertEquals(3L, Numbers.toByteExact(Short.valueOf((short) 3)));
|
||||
assertEquals(3L, Numbers.toByteExact(Byte.valueOf((byte) 3)));
|
||||
assertEquals(3L, Numbers.toByteExact(3d));
|
||||
assertEquals(3L, Numbers.toByteExact(3f));
|
||||
assertEquals(3L, Numbers.toByteExact(BigInteger.valueOf(3L)));
|
||||
assertEquals(3L, Numbers.toByteExact(BigDecimal.valueOf(3L)));
|
||||
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toByteExact(3.1d));
|
||||
assertEquals("3.1 is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(Double.NaN));
|
||||
assertEquals("NaN is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toLongExact(Double.POSITIVE_INFINITY));
|
||||
assertEquals("Infinity is not an integer value", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toByteExact(3.1f));
|
||||
assertEquals("3.1 is not an integer value", e.getMessage());
|
||||
ArithmeticException ae = expectThrows(ArithmeticException.class,
|
||||
() -> Numbers.toByteExact(300));
|
||||
assertEquals("byte overflow: " + 300, ae.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> Numbers.toByteExact(new AtomicInteger(3))); // not supported
|
||||
assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue