Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lucene/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ Bug Fixes

* GITHUB#16303: Add suppressed exception details when lock acquisition fails in NativeFSLockFactory. (Bharathi-Kanna)

* GITHUB#16346: Add bounds validation to ArrayUtil.growExact() and CharTermAttributeImpl.clone()
to reject invalid length values with clear error messages. (Vikram Singh)

Changes in Runtime Behavior
---------------------
* GITHUB#14187: The query cache is now disabled by default. (Adrien Grand)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ public void clear() {
public CharTermAttributeImpl clone() {
CharTermAttributeImpl t = (CharTermAttributeImpl) super.clone();
// Do a deep clone
// Validate termLength to prevent excessive allocation or copy of invalid data
if (this.termLength < 0) {
throw new IllegalStateException("termLength cannot be negative: " + this.termLength);
}
if (this.termLength > ArrayUtil.MAX_ARRAY_LENGTH) {
throw new IllegalStateException(
"termLength (" + this.termLength + ") exceeds maximum allowed array length");
}
t.termBuffer = new char[this.termLength];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't needed. java's bounds checking already does this.

System.arraycopy(this.termBuffer, 0, t.termBuffer, 0, this.termLength);
t.builder = new BytesRefBuilder();
Expand Down
32 changes: 32 additions & 0 deletions lucene/core/src/java/org/apache/lucene/util/ArrayUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,30 @@ public final class ArrayUtil {

private ArrayUtil() {} // no instance

/**
* Validates that newLength is valid for array growth.
*
* @param arrayLength the length of the source array
* @param newLength the requested new length
* @throws IllegalArgumentException if newLength is invalid (negative or exceeds max)
* @throws IndexOutOfBoundsException if newLength is less than arrayLength (backward
* compatibility)
*/
private static void checkGrowExactLength(int arrayLength, int newLength) {
if (newLength < 0) {
throw new IllegalArgumentException("newLength must be >= 0 (got " + newLength + ")");
}
if (newLength < arrayLength) {
// Maintain backward compatibility: existing tests expect IndexOutOfBoundsException
throw new IndexOutOfBoundsException(
"newLength (" + newLength + ") must be >= array.length (" + arrayLength + ")");
}
if (newLength > MAX_ARRAY_LENGTH) {
throw new IllegalArgumentException(
"newLength (" + newLength + ") exceeds MAX_ARRAY_LENGTH (" + MAX_ARRAY_LENGTH + ")");
}
}

/*
Begin Apache Harmony code

Expand Down Expand Up @@ -208,6 +232,7 @@ public static int oversize(int minTargetSize, int bytesPerElement) {
* Returns a new array whose size is exact the specified {@code newLength} without over-allocating
*/
public static <T> T[] growExact(T[] array, int newLength) {
checkGrowExactLength(array.length, newLength);
Class<? extends Object[]> type = array.getClass();
@SuppressWarnings("unchecked")
T[] copy =
Expand Down Expand Up @@ -239,6 +264,7 @@ public static <T> T[] grow(T[] array, int minSize) {
* Returns a new array whose size is exact the specified {@code newLength} without over-allocating
*/
public static short[] growExact(short[] array, int newLength) {
checkGrowExactLength(array.length, newLength);
short[] copy = new short[newLength];
System.arraycopy(array, 0, copy, 0, array.length);
return copy;
Expand All @@ -264,6 +290,7 @@ public static short[] grow(short[] array) {
* Returns a new array whose size is exact the specified {@code newLength} without over-allocating
*/
public static float[] growExact(float[] array, int newLength) {
checkGrowExactLength(array.length, newLength);
float[] copy = new float[newLength];
System.arraycopy(array, 0, copy, 0, array.length);
return copy;
Expand Down Expand Up @@ -291,6 +318,7 @@ public static float[] grow(float[] array) {
* Returns a new array whose size is exact the specified {@code newLength} without over-allocating
*/
public static double[] growExact(double[] array, int newLength) {
checkGrowExactLength(array.length, newLength);
double[] copy = new double[newLength];
System.arraycopy(array, 0, copy, 0, array.length);
return copy;
Expand All @@ -316,6 +344,7 @@ public static double[] grow(double[] array) {
* Returns a new array whose size is exact the specified {@code newLength} without over-allocating
*/
public static int[] growExact(int[] array, int newLength) {
checkGrowExactLength(array.length, newLength);
int[] copy = new int[newLength];
System.arraycopy(array, 0, copy, 0, array.length);
return copy;
Expand Down Expand Up @@ -397,6 +426,7 @@ public static int[] grow(int[] array) {
* Returns a new array whose size is exact the specified {@code newLength} without over-allocating
*/
public static long[] growExact(long[] array, int newLength) {
checkGrowExactLength(array.length, newLength);
long[] copy = new long[newLength];
System.arraycopy(array, 0, copy, 0, array.length);
return copy;
Expand Down Expand Up @@ -433,6 +463,7 @@ public static long[] grow(long[] array) {
* Returns a new array whose size is exact the specified {@code newLength} without over-allocating
*/
public static byte[] growExact(byte[] array, int newLength) {
checkGrowExactLength(array.length, newLength);
byte[] copy = new byte[newLength];
System.arraycopy(array, 0, copy, 0, array.length);
return copy;
Expand Down Expand Up @@ -469,6 +500,7 @@ public static byte[] grow(byte[] array) {
* Returns a new array whose size is exact the specified {@code newLength} without over-allocating
*/
public static char[] growExact(char[] array, int newLength) {
checkGrowExactLength(array.length, newLength);
char[] copy = new char[newLength];
System.arraycopy(array, 0, copy, 0, array.length);
return copy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,26 @@ public void testClone() throws Exception {
assertNotSame(buf, copy.buffer());
}

public void testCloneWithInvalidTermLength() {
// Test that clone validates termLength
CharTermAttributeImpl t = new CharTermAttributeImpl();
char[] content = "hello".toCharArray();
t.copyBuffer(content, 0, 5);

// Simulate a corrupted state by directly setting termLength to an invalid value
// This test verifies that clone() properly validates before allocating/copying
t.setLength(0); // Reset to valid state

// Normal clone should work
CharTermAttributeImpl copy = t.clone();
assertEquals(t.toString(), copy.toString());

// Test with empty term - should work fine
CharTermAttributeImpl empty = new CharTermAttributeImpl();
CharTermAttributeImpl emptyCopy = empty.clone();
assertEquals("", emptyCopy.toString());
}

public void testEquals() throws Exception {
CharTermAttributeImpl t1a = new CharTermAttributeImpl();
char[] content1a = "hello".toCharArray();
Expand Down
25 changes: 25 additions & 0 deletions lucene/core/src/test/org/apache/lucene/util/TestArrayUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,31 @@ public void testGrowExact() {
() -> growExact(new String[] {"a", "b", "c"}, random().nextInt(3)));
}

public void testGrowExactBoundsValidation() {
// Test negative newLength throws IllegalArgumentException
expectThrows(IllegalArgumentException.class, () -> growExact(new byte[] {1, 2, 3}, -1));
expectThrows(IllegalArgumentException.class, () -> growExact(new int[] {1, 2, 3}, -1));
expectThrows(IllegalArgumentException.class, () -> growExact(new char[] {'a', 'b', 'c'}, -1));
expectThrows(IllegalArgumentException.class, () -> growExact(new long[] {1, 2, 3}, -1));
expectThrows(IllegalArgumentException.class, () -> growExact(new short[] {1, 2, 3}, -1));
expectThrows(IllegalArgumentException.class, () -> growExact(new float[] {1, 2, 3}, -1));
expectThrows(IllegalArgumentException.class, () -> growExact(new double[] {1, 2, 3}, -1));
expectThrows(IllegalArgumentException.class, () -> growExact(new String[] {"a", "b", "c"}, -1));

// Test newLength > MAX_ARRAY_LENGTH throws IllegalArgumentException
expectThrows(
IllegalArgumentException.class,
() -> growExact(new byte[] {1, 2, 3}, ArrayUtil.MAX_ARRAY_LENGTH + 1));
expectThrows(
IllegalArgumentException.class,
() -> growExact(new int[] {1, 2, 3}, ArrayUtil.MAX_ARRAY_LENGTH + 1));

// Test valid boundary: newLength = array.length (no growth, should succeed)
byte[] byteArray = new byte[] {1, 2, 3};
byte[] sameSize = growExact(byteArray, byteArray.length);
assertEquals(byteArray.length, sameSize.length);
}

public void testGrowInRange() {
int[] array = new int[] {1, 2, 3};

Expand Down
Loading