Skip to content

Commit c3f11f2

Browse files
authored
Fix overflow bug in SortingNumericDocValues (#70154)
1 parent 5e487fd commit c3f11f2

File tree

2 files changed

+85
-4
lines changed

2 files changed

+85
-4
lines changed

server/src/main/java/org/elasticsearch/index/fielddata/SortingNumericDocValues.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,35 @@ protected final void resize(int newSize) {
6060
count = newSize;
6161
valuesCursor = 0;
6262

63-
if (newSize <= values.length) {
63+
if (newSize <= getArrayLength()) {
6464
return;
6565
}
6666

6767
// Array is expected to grow so increment the circuit breaker
6868
// to include both the additional bytes used by the grown array
6969
// as well as the overhead of keeping both arrays in memory while
7070
// copying.
71-
long oldValuesSizeInBytes = values.length * Long.BYTES;
71+
long oldValuesSizeInBytes = (long) getArrayLength() * Long.BYTES;
7272
int newValuesLength = ArrayUtil.oversize(newSize, Long.BYTES);
73-
circuitBreakerConsumer.accept(newValuesLength * Long.BYTES);
73+
circuitBreakerConsumer.accept((long) newValuesLength * Long.BYTES);
7474

7575
// resize
76-
values = ArrayUtil.growExact(values, newValuesLength);
76+
growExact(newValuesLength);
7777

7878
// account for freeing the old values array
7979
circuitBreakerConsumer.accept(-oldValuesSizeInBytes);
8080
}
8181

82+
/** Grow the array in a method so we can override it during testing */
83+
protected void growExact(int newValuesLength) {
84+
values = ArrayUtil.growExact(values, newValuesLength);
85+
}
86+
87+
/** Get the size of the internal array using a method so we can override it during testing */
88+
protected int getArrayLength() {
89+
return values.length;
90+
}
91+
8292
/**
8393
* Sort values that are stored between offsets <code>0</code> and
8494
* {@link #count} of {@link #values}.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.index.fielddata;
10+
11+
import org.apache.lucene.util.ArrayUtil;
12+
import org.elasticsearch.test.ESTestCase;
13+
import org.hamcrest.Matchers;
14+
15+
import java.util.concurrent.atomic.AtomicLong;
16+
import java.util.function.LongConsumer;
17+
18+
public class SortingNumericDocValuesTests extends ESTestCase {
19+
20+
public void testResize() {
21+
final int oldSize = Integer.MAX_VALUE - 200;
22+
final int newSize = Integer.MAX_VALUE - 100;
23+
// This counter should account for the initialization of the array (size == 1)
24+
// and the diff between newSize (over-sized) and oldSize.
25+
final AtomicLong counter = new AtomicLong();
26+
LongConsumer consumer = value -> {
27+
long total = counter.addAndGet(value);
28+
assertThat(total, Matchers.greaterThanOrEqualTo(0L));
29+
};
30+
SortingNumericDocValues docValues = new SortingNumericDocValues(consumer) {
31+
32+
@Override
33+
protected void growExact(int newValuesLength) {
34+
// don't grow the array
35+
}
36+
37+
/** Get the size of the internal array using a method so we can override it during testing */
38+
protected int getArrayLength() {
39+
return oldSize;
40+
}
41+
42+
@Override
43+
public boolean advanceExact(int target) {
44+
return false;
45+
}
46+
47+
@Override
48+
public int docID() {
49+
return 0;
50+
}
51+
52+
@Override
53+
public int nextDoc() {
54+
return 0;
55+
}
56+
57+
@Override
58+
public int advance(int target) {
59+
return 0;
60+
}
61+
62+
@Override
63+
public long cost() {
64+
return 0;
65+
}
66+
};
67+
docValues.resize(newSize);
68+
final long diff = ArrayUtil.oversize(newSize, Long.BYTES) - oldSize;
69+
assertThat(counter.get(), Matchers.equalTo((diff + 1) * Long.BYTES));
70+
}
71+
}

0 commit comments

Comments
 (0)