From f8af0e16ea7478aa6ee6ad1aa42b265e920b0f0e Mon Sep 17 00:00:00 2001 From: martin Date: Wed, 16 Oct 2024 11:07:13 +0100 Subject: [PATCH] KAFKA-17792 header parsing times out processing and using large quantities of memory if the string looks like a number --- .../main/java/org/apache/kafka/connect/data/Values.java | 6 ++++++ .../java/org/apache/kafka/connect/data/ValuesTest.java | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/connect/api/src/main/java/org/apache/kafka/connect/data/Values.java b/connect/api/src/main/java/org/apache/kafka/connect/data/Values.java index a528271d1ab44..b6850a166c949 100644 --- a/connect/api/src/main/java/org/apache/kafka/connect/data/Values.java +++ b/connect/api/src/main/java/org/apache/kafka/connect/data/Values.java @@ -71,6 +71,8 @@ public class Values { static final String ISO_8601_DATE_FORMAT_PATTERN = "yyyy-MM-dd"; static final String ISO_8601_TIME_FORMAT_PATTERN = "HH:mm:ss.SSS'Z'"; static final String ISO_8601_TIMESTAMP_FORMAT_PATTERN = ISO_8601_DATE_FORMAT_PATTERN + "'T'" + ISO_8601_TIME_FORMAT_PATTERN; + private static BigDecimal TOO_BIG = new BigDecimal("1e1000000"); + private static BigDecimal TOO_SMALL = new BigDecimal("1e-1000000"); private static final Pattern TWO_BACKSLASHES = Pattern.compile("\\\\"); @@ -1041,6 +1043,10 @@ private static SchemaAndValue parseAsNumber(String token) { } private static SchemaAndValue parseAsExactDecimal(BigDecimal decimal) { + BigDecimal abs = decimal.abs(); + if (abs.compareTo(TOO_BIG) > 0 || (abs.compareTo(TOO_SMALL) < 0 && BigDecimal.ZERO.compareTo(abs) != 0)) { + throw new NumberFormatException("outside efficient parsing range"); + } BigDecimal ceil = decimal.setScale(0, RoundingMode.CEILING); BigDecimal floor = decimal.setScale(0, RoundingMode.FLOOR); if (ceil.equals(floor)) { diff --git a/connect/api/src/test/java/org/apache/kafka/connect/data/ValuesTest.java b/connect/api/src/test/java/org/apache/kafka/connect/data/ValuesTest.java index 9a96882d976e3..c4bb1998edd09 100644 --- a/connect/api/src/test/java/org/apache/kafka/connect/data/ValuesTest.java +++ b/connect/api/src/test/java/org/apache/kafka/connect/data/ValuesTest.java @@ -1154,7 +1154,14 @@ public void shouldParseFractionalPartsAsIntegerWhenNoFractionalPart() { assertEquals(new SchemaAndValue(Schema.INT32_SCHEMA, 66000), Values.parseString("66000.0")); assertEquals(new SchemaAndValue(Schema.FLOAT32_SCHEMA, 66000.0008f), Values.parseString("66000.0008")); } + @Test + public void avoidCpuAndMemoryIssuesConvertingExtremeBigDecimals() { + String PARSING_BIG = "1e+100000000"; // new BigDecimal().setScale(0, RoundingMode.FLOOR) takes around two minutes and uses 3GB; + assertEquals(new SchemaAndValue(Schema.STRING_SCHEMA, PARSING_BIG), Values.parseString(PARSING_BIG)); + String PARSING_SMALL = "1e-100000000"; + assertEquals(new SchemaAndValue(Schema.STRING_SCHEMA, PARSING_SMALL), Values.parseString(PARSING_SMALL)); + } protected void assertParsed(String input) { assertParsed(input, input); }