This is an automated email from the ASF dual-hosted git repository.
JackieTien97 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 8940ba0e3f5 fix(jdbc): prevent BigDecimal DoS in
StringUtils.consistentToString (#17508)
8940ba0e3f5 is described below
commit 8940ba0e3f5b199af170193128395ec2c5e262fa
Author: Le Yang <[email protected]>
AuthorDate: Mon Jul 6 12:22:48 2026 +0800
fix(jdbc): prevent BigDecimal DoS in StringUtils.consistentToString (#17508)
---
.../java/org/apache/iotdb/jdbc/StringUtils.java | 21 +++++-
.../org/apache/iotdb/jdbc/StringUtilsTest.java | 74 ++++++++++++++++++++++
2 files changed, 94 insertions(+), 1 deletion(-)
diff --git
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/StringUtils.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/StringUtils.java
index a7bdaab0e67..845190a013c 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/StringUtils.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/StringUtils.java
@@ -44,6 +44,18 @@ public class StringUtils {
static final int WILD_COMPARE_NO_MATCH = -1;
+ /**
+ * Maximum absolute {@link BigDecimal#scale()} accepted by {@link
#consistentToString(BigDecimal)}
+ * before it falls back to scientific notation.
+ *
+ * <p>{@link BigDecimal#toPlainString()} expands the value to a
non-scientific decimal string
+ * whose length grows with {@code |scale|}. A value such as {@code
1e1000000000} would therefore
+ * materialize a ~1GB {@link String}, causing {@link OutOfMemoryError} /
denial of service on the
+ * client. Any scale beyond this bound is rejected for expansion and the
caller gets the
+ * scientific form instead, which is a few dozen characters at most.
+ */
+ static final int MAX_PLAIN_STRING_SCALE = 10_000;
+
static {
for (int i = -128; i <= 127; i++) {
allBytes[i - -128] = (byte) i;
@@ -65,9 +77,16 @@ public class StringUtils {
if (decimal == null) {
return null;
}
+ // Guard against CVE-style DoS: BigDecimal values constructed from extreme
scientific
+ // notation (e.g. "1e1000000000") have a huge |scale| and would otherwise
cause
+ // toPlainString() to allocate a multi-GB String and OOM the JVM. Fall
back to the
+ // scientific form, which is always short.
+ if (decimal.scale() > MAX_PLAIN_STRING_SCALE || decimal.scale() <
-MAX_PLAIN_STRING_SCALE) {
+ return decimal.toString();
+ }
if (toPlainStringMethod != null) {
try {
- return (String) toPlainStringMethod.invoke(decimal, null);
+ return (String) toPlainStringMethod.invoke(decimal, (Object[]) null);
} catch (InvocationTargetException | IllegalAccessException e) {
LOGGER.warn(JdbcMessages.CONSISTENT_TO_STRING_ERROR, e);
}
diff --git
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/StringUtilsTest.java
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/StringUtilsTest.java
new file mode 100644
index 00000000000..906eeef8e08
--- /dev/null
+++ b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/StringUtilsTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.jdbc;
+
+import org.junit.Test;
+
+import java.math.BigDecimal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class StringUtilsTest {
+
+ @Test
+ public void consistentToStringHandlesNull() {
+ assertNull(StringUtils.consistentToString(null));
+ }
+
+ @Test
+ public void consistentToStringExpandsNormalDecimal() {
+ assertEquals("123.45", StringUtils.consistentToString(new
BigDecimal("123.45")));
+ // Plain-string form, not scientific notation.
+ assertEquals("100", StringUtils.consistentToString(new
BigDecimal("1E+2")));
+ }
+
+ @Test
+ public void consistentToStringExpandsUpToLimit() {
+ // scale right at the boundary must still be expanded to plain form.
+ BigDecimal atLimit = new BigDecimal("1E+" +
StringUtils.MAX_PLAIN_STRING_SCALE);
+ String out = StringUtils.consistentToString(atLimit);
+ assertEquals(StringUtils.MAX_PLAIN_STRING_SCALE + 1, out.length());
+ assertTrue(out.startsWith("1"));
+ }
+
+ /**
+ * Regression test for the BigDecimal DoS: {@code new
BigDecimal("1e1000000000")} used to cause
+ * {@link BigDecimal#toPlainString()} to allocate a ~1GB string and OOM the
JVM. The guard must
+ * refuse to expand such values and return the compact scientific form
instead.
+ */
+ @Test
+ public void consistentToStringRefusesToExpandHugeScale() {
+ BigDecimal malicious = new BigDecimal("1e1000000000");
+ String out = StringUtils.consistentToString(malicious);
+ // Must be short (scientific notation), never the ~1e9-char plain form.
+ assertTrue("expected scientific form, got length " + out.length(),
out.length() < 64);
+ assertTrue(out.contains("E") || out.contains("e"));
+ }
+
+ @Test
+ public void consistentToStringRefusesToExpandHugePositiveScale() {
+ // Very large positive scale -> also produces a long plain string; must be
refused.
+ BigDecimal malicious = new BigDecimal("1e-1000000000");
+ String out = StringUtils.consistentToString(malicious);
+ assertTrue("expected scientific form, got length " + out.length(),
out.length() < 64);
+ }
+}