Skip to content

Commit fa6e16b

Browse files
sbrannenBenjamin Reed
authored and
Benjamin Reed
committed
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 spring-projectsgh-28257
1 parent 36d0015 commit fa6e16b

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,8 @@
3434
* if it is known.
3535
*
3636
* @author Andy Clement
37+
* @author Juergen Hoeller
38+
* @author Sam Brannen
3739
* @since 3.0
3840
*/
3941
public enum SpelMessage {
@@ -249,7 +251,19 @@ public enum SpelMessage {
249251
"Problem parsing left operand"),
250252

251253
MISSING_SELECTION_EXPRESSION(Kind.ERROR, 1071,
252-
"A required selection expression has not been specified");
254+
"A required selection expression has not been specified"),
255+
256+
/** @since 4.1 */
257+
EXCEPTION_RUNNING_COMPILED_EXPRESSION(Kind.ERROR, 1072,
258+
"An exception occurred whilst evaluating a compiled expression"),
259+
260+
/** @since 4.3.17 */
261+
FLAWED_PATTERN(Kind.ERROR, 1073,
262+
"Failed to efficiently evaluate pattern ''{0}'': consider redesigning it"),
263+
264+
/** @since 5.2.20 */
265+
MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075,
266+
"Array declares too many elements, exceeding the threshold of ''{0}''");
253267

254268

255269
private final Kind kind;

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -47,11 +47,19 @@
4747
*
4848
* @author Andy Clement
4949
* @author Juergen Hoeller
50+
* @author Sam Brannen
5051
* @since 3.0
5152
*/
5253
public class ConstructorReference extends SpelNodeImpl {
5354

54-
private boolean isArrayConstructor = false;
55+
/**
56+
* Maximum number of elements permitted in an array declaration, applying
57+
* to one-dimensional as well as multi-dimensional arrays.
58+
* @since 5.2.20
59+
*/
60+
private static final int MAX_ARRAY_ELEMENTS = 256 * 1024; // 256K
61+
62+
private final boolean isArrayConstructor;
5563

5664
private SpelNodeImpl[] dimensions;
5765

@@ -250,14 +258,19 @@ private TypedValue createArray(ExpressionState state) throws EvaluationException
250258
if (this.dimensions.length == 1) {
251259
TypedValue o = this.dimensions[0].getTypedValue(state);
252260
int arraySize = ExpressionUtils.toInt(typeConverter, o);
261+
checkNumElements(arraySize);
253262
newArray = Array.newInstance(componentType, arraySize);
254263
}
255264
else {
256265
// Multi-dimensional - hold onto your hat!
257266
int[] dims = new int[this.dimensions.length];
267+
long numElements = 1;
258268
for (int d = 0; d < this.dimensions.length; d++) {
259269
TypedValue o = this.dimensions[d].getTypedValue(state);
260-
dims[d] = ExpressionUtils.toInt(typeConverter, o);
270+
int arraySize = ExpressionUtils.toInt(typeConverter, o);
271+
dims[d] = arraySize;
272+
numElements *= arraySize;
273+
checkNumElements(numElements);
261274
}
262275
newArray = Array.newInstance(componentType, dims);
263276
}
@@ -317,6 +330,13 @@ else if (arrayTypeCode == TypeCode.BYTE) {
317330
return new TypedValue(newArray);
318331
}
319332

333+
private void checkNumElements(long numElements) {
334+
if (numElements >= MAX_ARRAY_ELEMENTS) {
335+
throw new SpelEvaluationException(getStartPosition(),
336+
SpelMessage.MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED, MAX_ARRAY_ELEMENTS);
337+
}
338+
}
339+
320340
private void populateReferenceTypeArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
321341
InlineList initializer, Class<?> componentType) {
322342
TypeDescriptor toTypeDescriptor = TypeDescriptor.valueOf(componentType);

0 commit comments

Comments
 (0)