Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kafka 17792 header parsing times out processing and using large quantities of memory if the string looks like a number #17510

Open
wants to merge 7 commits into
base: trunk
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.text.CharacterIterator;
import java.text.DateFormat;
Expand Down Expand Up @@ -1012,6 +1011,7 @@ private SchemaAndValue parseMultipleTokensAsTemporal(String token) {
return parseAsTemporal(token);
}


private static SchemaAndValue parseAsNumber(String token) {
// Try to parse as a number ...
BigDecimal decimal = new BigDecimal(token);
Expand All @@ -1032,12 +1032,20 @@ private static SchemaAndValue parseAsNumber(String token) {
}
}

private static boolean isWholeNumber(BigDecimal bd) {
return bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0;
}

private static final BigDecimal BIGGER_THAN_LONG = new BigDecimal("1e19");

private static SchemaAndValue parseAsExactDecimal(BigDecimal decimal) {
BigDecimal ceil = decimal.setScale(0, RoundingMode.CEILING);
BigDecimal floor = decimal.setScale(0, RoundingMode.FLOOR);
if (ceil.equals(floor)) {
BigInteger num = ceil.toBigIntegerExact();
if (ceil.precision() >= 19 && (num.compareTo(LONG_MIN) < 0 || num.compareTo(LONG_MAX) > 0)) {
BigDecimal abs = decimal.abs();
Copy link
Contributor

Choose a reason for hiding this comment

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

This value is unused.

Choose a reason for hiding this comment

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

I think I've addressed this now

if (abs.compareTo(BIGGER_THAN_LONG) > 0 || (abs.compareTo(BigDecimal.ONE) < 0 && abs.compareTo(BigDecimal.ZERO) != 0)) {
return null;
}
if (isWholeNumber(decimal)) {
BigInteger num = decimal.toBigIntegerExact();
if (num.compareTo(LONG_MIN) < 0 || num.compareTo(LONG_MAX) > 0) {
return null;
}
long integral = num.longValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,17 @@ public void shouldParseFractionalPartsAsIntegerWhenNoFractionalPart() {
assertEquals(new SchemaAndValue(Schema.FLOAT32_SCHEMA, 66000.0008f), Values.parseString("66000.0008"));
}

@Test
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: missing a newline above this test.

public void avoidCpuAndMemoryIssuesConvertingExtremeBigDecimals() {
String parsingBig = "1e+100000000"; // new BigDecimal().setScale(0, RoundingMode.FLOOR) takes around two minutes and uses 3GB;
BigDecimal valueBig = new BigDecimal(parsingBig);
assertEquals(new SchemaAndValue(Decimal.schema(-100000000), valueBig), Values.parseString(parsingBig), "parsing number that's too big");

String parsingSmall = "1e-100000000";
BigDecimal valueSmall = new BigDecimal(parsingSmall);
assertEquals(new SchemaAndValue(Schema.FLOAT32_SCHEMA, (float) valueSmall.doubleValue()), Values.parseString(parsingSmall), "parsing number that's too big, strictly this should return a bigdecimal");
}

protected void assertParsed(String input) {
assertParsed(input, input);
}
Expand Down
Loading