mirror of
https://github.com/elastic/logstash.git
synced 2025-04-19 04:15:23 -04:00
Added test to verify the int overflow happen (#17353)
Use long instead of int type to keep the length of the first token.
The size limit validation requires to sum two integers, one with the length of the accumulated chars till now plus the next fragment head part. If any of the two sizes is close to the max integer it generates an overflow and could successfully fail the test 9c0e50faac/logstash-core/src/main/java/org/logstash/common/BufferedTokenizerExt.java (L123)
.
To fall in this case it's required that sizeLimit is bigger then 2^32 bytes (2GB) and data fragments without any line delimiter is pushed to the tokenizer with a total size close to 2^32 bytes.
This commit is contained in:
parent
9c0e50faac
commit
afde43f918
3 changed files with 34 additions and 2 deletions
|
@ -124,6 +124,8 @@ tasks.register("javaTests", Test) {
|
|||
exclude '/org/logstash/plugins/factory/PluginFactoryExtTest.class'
|
||||
exclude '/org/logstash/execution/ObservedExecutionTest.class'
|
||||
|
||||
maxHeapSize = "12g"
|
||||
|
||||
jacoco {
|
||||
enabled = true
|
||||
destinationFile = layout.buildDirectory.file('jacoco/test.exec').get().asFile
|
||||
|
|
|
@ -48,7 +48,7 @@ public class BufferedTokenizerExt extends RubyObject {
|
|||
private RubyString delimiter = NEW_LINE;
|
||||
private int sizeLimit;
|
||||
private boolean hasSizeLimit;
|
||||
private int inputSize;
|
||||
private long inputSize;
|
||||
private boolean bufferFullErrorNotified = false;
|
||||
private String encodingName;
|
||||
|
||||
|
|
|
@ -38,14 +38,19 @@ import static org.logstash.RubyUtil.RUBY;
|
|||
@SuppressWarnings("unchecked")
|
||||
public final class BufferedTokenizerExtWithSizeLimitTest extends RubyTestBase {
|
||||
|
||||
public static final int GB = 1024 * 1024 * 1024;
|
||||
private BufferedTokenizerExt sut;
|
||||
private ThreadContext context;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
initSUTWithSizeLimit(10);
|
||||
}
|
||||
|
||||
private void initSUTWithSizeLimit(int sizeLimit) {
|
||||
sut = new BufferedTokenizerExt(RubyUtil.RUBY, RubyUtil.BUFFERED_TOKENIZER);
|
||||
context = RUBY.getCurrentContext();
|
||||
IRubyObject[] args = {RubyUtil.RUBY.newString("\n"), RubyUtil.RUBY.newFixnum(10)};
|
||||
IRubyObject[] args = {RubyUtil.RUBY.newString("\n"), RubyUtil.RUBY.newFixnum(sizeLimit)};
|
||||
sut.init(context, args);
|
||||
}
|
||||
|
||||
|
@ -108,4 +113,29 @@ public final class BufferedTokenizerExtWithSizeLimitTest extends RubyTestBase {
|
|||
RubyArray<RubyString> tokens = (RubyArray<RubyString>) sut.extract(context, RubyUtil.RUBY.newString("ccc\nddd\n"));
|
||||
assertEquals(List.of("ccccc", "ddd"), tokens);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMaliciousInputExtractDoesntOverflow() {
|
||||
assertEquals("Xmx must equals to what's defined in the Gradle's javaTests task",
|
||||
12L * GB, Runtime.getRuntime().maxMemory());
|
||||
|
||||
// re-init the tokenizer with big sizeLimit
|
||||
initSUTWithSizeLimit((int) (2L * GB) - 3);
|
||||
// Integer.MAX_VALUE is 2 * GB
|
||||
String bigFirstPiece = generateString("a", Integer.MAX_VALUE - 1024);
|
||||
sut.extract(context, RubyUtil.RUBY.newString(bigFirstPiece));
|
||||
|
||||
// add another small fragment to trigger int overflow
|
||||
// sizeLimit is (2^32-1)-3 first segment length is (2^32-1) - 1024 second is 1024 +2
|
||||
// so the combined length of first and second is > sizeLimit and should throw an expection
|
||||
// but because of overflow it's negative and happens to be < sizeLimit
|
||||
Exception thrownException = assertThrows(IllegalStateException.class, () -> {
|
||||
sut.extract(context, RubyUtil.RUBY.newString(generateString("a", 1024 + 2)));
|
||||
});
|
||||
assertThat(thrownException.getMessage(), containsString("input buffer full"));
|
||||
}
|
||||
|
||||
private String generateString(String fill, int size) {
|
||||
return fill.repeat(size);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue