Browse Source

Improve diagnostics in SpEL for large array creation

Attempting to create a large array in a SpEL expression can result in
an OutOfMemoryError. Although the JVM recovers from that, the error
message is not very helpful to the user.

This commit improves the diagnostics in SpEL for large array creation
by throwing a SpelEvaluationException with a meaningful error message
in order to improve diagnostics for the user.

Closes gh-28257
pull/28694/head
Sam Brannen 4 years ago committed by Brian Clozel
parent
commit
90cfde985e
  1. 9
      spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java
  2. 24
      spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java

9
spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,6 +31,7 @@ import java.text.MessageFormat; @@ -31,6 +31,7 @@ import java.text.MessageFormat;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public enum SpelMessage {
@ -255,7 +256,11 @@ public enum SpelMessage { @@ -255,7 +256,11 @@ public enum SpelMessage {
/** @since 4.3.17 */
FLAWED_PATTERN(Kind.ERROR, 1073,
"Failed to efficiently evaluate pattern ''{0}'': consider redesigning it");
"Failed to efficiently evaluate pattern ''{0}'': consider redesigning it"),
/** @since 5.2.20 */
MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075,
"Array declares too many elements, exceeding the threshold of ''{0}''");
private final Kind kind;

24
spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -53,10 +53,18 @@ import org.springframework.util.Assert; @@ -53,10 +53,18 @@ import org.springframework.util.Assert;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class ConstructorReference extends SpelNodeImpl {
/**
* Maximum number of elements permitted in an array declaration, applying
* to one-dimensional as well as multi-dimensional arrays.
* @since 5.2.20
*/
private static final int MAX_ARRAY_ELEMENTS = 256 * 1024; // 256K
private final boolean isArrayConstructor;
@Nullable
@ -259,14 +267,19 @@ public class ConstructorReference extends SpelNodeImpl { @@ -259,14 +267,19 @@ public class ConstructorReference extends SpelNodeImpl {
// Shortcut for 1-dimensional
TypedValue o = this.dimensions[0].getTypedValue(state);
int arraySize = ExpressionUtils.toInt(typeConverter, o);
checkNumElements(arraySize);
newArray = Array.newInstance(componentType, arraySize);
}
else {
// Multi-dimensional - hold onto your hat!
int[] dims = new int[this.dimensions.length];
long numElements = 1;
for (int d = 0; d < this.dimensions.length; d++) {
TypedValue o = this.dimensions[d].getTypedValue(state);
dims[d] = ExpressionUtils.toInt(typeConverter, o);
int arraySize = ExpressionUtils.toInt(typeConverter, o);
dims[d] = arraySize;
numElements *= arraySize;
checkNumElements(numElements);
}
newArray = Array.newInstance(componentType, dims);
}
@ -327,6 +340,13 @@ public class ConstructorReference extends SpelNodeImpl { @@ -327,6 +340,13 @@ public class ConstructorReference extends SpelNodeImpl {
return new TypedValue(newArray);
}
private void checkNumElements(long numElements) {
if (numElements >= MAX_ARRAY_ELEMENTS) {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED, MAX_ARRAY_ELEMENTS);
}
}
private void populateReferenceTypeArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
InlineList initializer, Class<?> componentType) {

Loading…
Cancel
Save