LUCENE-7777: fix AIOOBE from ByteBlockPool.readBytes when byte block exceeds 32 KB

This commit is contained in:
Mike McCandless 2017-04-11 11:47:31 -04:00
parent 9c00fc6795
commit e386ec973b
4 changed files with 61 additions and 42 deletions

View File

@ -91,6 +91,12 @@ Other
======================= Lucene 6.6.0 ======================= ======================= Lucene 6.6.0 =======================
Bug Fixes
* LUCENE-7777: ByteBlockPool.readBytes sometimes throws
ArrayIndexOutOfBoundsException when byte blocks larger than 32 KB
were added (Mike McCandless)
Other Other
* LUCENE-7754: Inner classes should be static whenever possible. * LUCENE-7754: Inner classes should be static whenever possible.

View File

@ -106,7 +106,7 @@ public final class CommonGramsFilter extends TokenFilter {
saveTermBuffer(); saveTermBuffer();
return true; return true;
} else if (!input.incrementToken()) { } else if (!input.incrementToken()) {
return false; return false;
} }
/* We build n-grams before and after stopwords. /* We build n-grams before and after stopwords.

View File

@ -324,28 +324,25 @@ public final class ByteBlockPool {
* the current position. * the current position.
*/ */
public void append(final BytesRef bytes) { public void append(final BytesRef bytes) {
int length = bytes.length; int bytesLeft = bytes.length;
if (length == 0) {
return;
}
int offset = bytes.offset; int offset = bytes.offset;
int overflow = (length + byteUpto) - BYTE_BLOCK_SIZE; while (bytesLeft > 0) {
do { int bufferLeft = BYTE_BLOCK_SIZE - byteUpto;
if (overflow <= 0) { if (bytesLeft < bufferLeft) {
System.arraycopy(bytes.bytes, offset, buffer, byteUpto, length); // fits within current buffer
byteUpto += length; System.arraycopy(bytes.bytes, offset, buffer, byteUpto, bytesLeft);
byteUpto += bytesLeft;
break; break;
} else { } else {
final int bytesToCopy = length-overflow; // fill up this buffer and move to next one
if (bytesToCopy > 0) { if (bufferLeft > 0) {
System.arraycopy(bytes.bytes, offset, buffer, byteUpto, bytesToCopy); System.arraycopy(bytes.bytes, offset, buffer, byteUpto, bufferLeft);
offset += bytesToCopy;
length -= bytesToCopy;
} }
nextBuffer(); nextBuffer();
overflow = overflow - BYTE_BLOCK_SIZE; bytesLeft -= bufferLeft;
offset += bufferLeft;
} }
} while(true); }
} }
/** /**
@ -353,30 +350,18 @@ public final class ByteBlockPool {
* length into the given byte array at offset <tt>off</tt>. * length into the given byte array at offset <tt>off</tt>.
* <p>Note: this method allows to copy across block boundaries.</p> * <p>Note: this method allows to copy across block boundaries.</p>
*/ */
public void readBytes(final long offset, final byte bytes[], final int off, final int length) { public void readBytes(final long offset, final byte bytes[], int bytesOffset, int bytesLength) {
if (length == 0) { int bytesLeft = bytesLength;
return;
}
int bytesOffset = off;
int bytesLength = length;
int bufferIndex = (int) (offset >> BYTE_BLOCK_SHIFT); int bufferIndex = (int) (offset >> BYTE_BLOCK_SHIFT);
byte[] buffer = buffers[bufferIndex];
int pos = (int) (offset & BYTE_BLOCK_MASK); int pos = (int) (offset & BYTE_BLOCK_MASK);
int overflow = (pos + length) - BYTE_BLOCK_SIZE; while (bytesLeft > 0) {
do { byte[] buffer = buffers[bufferIndex++];
if (overflow <= 0) { int chunk = Math.min(bytesLeft, BYTE_BLOCK_SIZE - pos);
System.arraycopy(buffer, pos, bytes, bytesOffset, bytesLength); System.arraycopy(buffer, pos, bytes, bytesOffset, chunk);
break; bytesOffset += chunk;
} else { bytesLeft -= chunk;
final int bytesToCopy = length - overflow; pos = 0;
System.arraycopy(buffer, pos, bytes, bytesOffset, bytesToCopy); }
pos = 0;
bytesLength -= bytesToCopy;
bytesOffset += bytesToCopy;
buffer = buffers[++bufferIndex];
overflow = overflow - BYTE_BLOCK_SIZE;
}
} while (true);
} }
/** /**

View File

@ -18,6 +18,7 @@ package org.apache.lucene.util;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
public class TestByteBlockPool extends LuceneTestCase { public class TestByteBlockPool extends LuceneTestCase {
@ -34,8 +35,7 @@ public class TestByteBlockPool extends LuceneTestCase {
final int numValues = atLeast(100); final int numValues = atLeast(100);
BytesRefBuilder ref = new BytesRefBuilder(); BytesRefBuilder ref = new BytesRefBuilder();
for (int i = 0; i < numValues; i++) { for (int i = 0; i < numValues; i++) {
final String value = TestUtil.randomRealisticUnicodeString(random(), final String value = TestUtil.randomRealisticUnicodeString(random(), maxLength);
maxLength);
list.add(new BytesRef(value)); list.add(new BytesRef(value));
ref.copyChars(value); ref.copyChars(value);
pool.append(ref.get()); pool.append(ref.get());
@ -76,5 +76,33 @@ public class TestByteBlockPool extends LuceneTestCase {
pool.nextBuffer(); // prepare for next iter pool.nextBuffer(); // prepare for next iter
} }
} }
} }
public void testLargeRandomBlocks() throws IOException {
Counter bytesUsed = Counter.newCounter();
ByteBlockPool pool = new ByteBlockPool(new ByteBlockPool.DirectTrackingAllocator(bytesUsed));
pool.nextBuffer();
List<byte[]> items = new ArrayList<>();
for (int i=0;i<100;i++) {
int size;
if (random().nextBoolean()) {
size = TestUtil.nextInt(random(), 100, 1000);
} else {
size = TestUtil.nextInt(random(), 50000, 100000);
}
byte[] bytes = new byte[size];
random().nextBytes(bytes);
items.add(bytes);
pool.append(new BytesRef(bytes));
}
long position = 0;
for (byte[] expected : items) {
byte[] actual = new byte[expected.length];
pool.readBytes(position, actual, 0, actual.length);
assertTrue(Arrays.equals(expected, actual));
position += expected.length;
}
}
} }