Skip to content

Commit 99be5a0

Browse files
committed
Allow InputStreamStreamInput array size validation where applicable (#26692)
Today we can't validate the array length in `InputStreamStreamInput` since we can't rely on `InputStream.available` yet in some situations we know the size of the stream and can apply additional validation.
1 parent 0b38499 commit 99be5a0

File tree

5 files changed

+44
-4
lines changed

5 files changed

+44
-4
lines changed

core/src/main/java/org/elasticsearch/common/io/stream/InputStreamStreamInput.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,28 @@
2828
public class InputStreamStreamInput extends StreamInput {
2929

3030
private final InputStream is;
31+
private final long sizeLimit;
3132

33+
/**
34+
* Creates a new InputStreamStreamInput with unlimited size
35+
* @param is the input stream to wrap
36+
*/
3237
public InputStreamStreamInput(InputStream is) {
38+
this(is, Long.MAX_VALUE);
39+
}
40+
41+
/**
42+
* Creates a new InputStreamStreamInput with a size limit
43+
* @param is the input stream to wrap
44+
* @param sizeLimit a hard limit of the number of bytes in the given input stream. This is used for internal input validation
45+
*/
46+
public InputStreamStreamInput(InputStream is, long sizeLimit) {
3347
this.is = is;
48+
if (sizeLimit < 0) {
49+
throw new IllegalArgumentException("size limit must be positive");
50+
}
51+
this.sizeLimit = sizeLimit;
52+
3453
}
3554

3655
@Override
@@ -98,6 +117,8 @@ public long skip(long n) throws IOException {
98117

99118
@Override
100119
protected void ensureCanReadBytes(int length) throws EOFException {
101-
// TODO what can we do here?
120+
if (length > sizeLimit) {
121+
throw new EOFException("tried to read: " + length + " bytes but this stream is limited to: " + sizeLimit);
122+
}
102123
}
103124
}

core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ public static StreamInput wrap(byte[] bytes) {
928928
}
929929

930930
public static StreamInput wrap(byte[] bytes, int offset, int length) {
931-
return new InputStreamStreamInput(new ByteArrayInputStream(bytes, offset, length));
931+
return new InputStreamStreamInput(new ByteArrayInputStream(bytes, offset, length), length);
932932
}
933933

934934
/**

core/src/main/java/org/elasticsearch/index/translog/TranslogReader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ public static TranslogReader open(
7979
final FileChannel channel, final Path path, final Checkpoint checkpoint, final String translogUUID) throws IOException {
8080

8181
try {
82-
InputStreamStreamInput headerStream = new InputStreamStreamInput(java.nio.channels.Channels.newInputStream(channel)); // don't close
82+
InputStreamStreamInput headerStream = new InputStreamStreamInput(java.nio.channels.Channels.newInputStream(channel),
83+
channel.size()); // don't close
8384
// Lucene's CodecUtil writes a magic number of 0x3FD76C17 with the
8485
// header, in binary this looks like:
8586
//

core/src/test/java/org/elasticsearch/common/io/stream/StreamTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.test.ESTestCase;
2727

2828
import java.io.ByteArrayInputStream;
29+
import java.io.EOFException;
2930
import java.io.IOException;
3031
import java.util.ArrayList;
3132
import java.util.Arrays;
@@ -192,6 +193,22 @@ public void testInputStreamStreamInputDelegatesAvailable() throws IOException {
192193
assertEquals(streamInput.available(), length - bytesToRead);
193194
}
194195

196+
public void testReadArraySize() throws IOException {
197+
BytesStreamOutput stream = new BytesStreamOutput();
198+
byte[] array = new byte[randomIntBetween(1, 10)];
199+
for (int i = 0; i < array.length; i++) {
200+
array[i] = randomByte();
201+
}
202+
stream.writeByteArray(array);
203+
InputStreamStreamInput streamInput = new InputStreamStreamInput(StreamInput.wrap(BytesReference.toBytes(stream.bytes())), array
204+
.length-1);
205+
expectThrows(EOFException.class, streamInput::readByteArray);
206+
streamInput = new InputStreamStreamInput(StreamInput.wrap(BytesReference.toBytes(stream.bytes())), BytesReference.toBytes(stream
207+
.bytes()).length);
208+
209+
assertArrayEquals(array, streamInput.readByteArray());
210+
}
211+
195212
public void testWritableArrays() throws IOException {
196213

197214
final String[] strings = generateRandomStringArray(10, 10, false, true);

modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,8 @@ static PercolateQuery.QueryStore createStore(MappedFieldType queryBuilderFieldTy
599599
if (binaryDocValues.advanceExact(docId)) {
600600
BytesRef qbSource = binaryDocValues.binaryValue();
601601
try (InputStream in = new ByteArrayInputStream(qbSource.bytes, qbSource.offset, qbSource.length)) {
602-
try (StreamInput input = new NamedWriteableAwareStreamInput(new InputStreamStreamInput(in), registry)) {
602+
try (StreamInput input = new NamedWriteableAwareStreamInput(
603+
new InputStreamStreamInput(in, qbSource.length), registry)) {
603604
input.setVersion(indexVersion);
604605
// Query builder's content is stored via BinaryFieldMapper, which has a custom encoding
605606
// to encode multiple binary values into a single binary doc values field.

0 commit comments

Comments
 (0)