From e7446d47b4b694e7b9d66bcf187fdb8fa45390a8 Mon Sep 17 00:00:00 2001 From: yangjie01 Date: Sat, 5 Sep 2026 03:52:49 +0800 Subject: [PATCH] [common] Range-check the digit guard in DateTimeUtils parseDate and parseTime return null for unparseable input and guard every Integer.parseInt with isInteger, which only checked that the characters were digits. An eleven-digit component passed it and parseInt threw NumberFormatException out of a method whose other failure paths return null. Check the range in the guard, in front of all eleven parseInt calls. --- .../org/apache/paimon/utils/DateTimeUtils.java | 15 +++++++++++---- .../apache/paimon/utils/DateTimeUtilsTest.java | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/paimon-common/src/main/java/org/apache/paimon/utils/DateTimeUtils.java b/paimon-common/src/main/java/org/apache/paimon/utils/DateTimeUtils.java index d202a5bc4708..45a53a3f8607 100644 --- a/paimon-common/src/main/java/org/apache/paimon/utils/DateTimeUtils.java +++ b/paimon-common/src/main/java/org/apache/paimon/utils/DateTimeUtils.java @@ -349,15 +349,22 @@ public static Integer parseTime(String v) { + milli; } + /** + * Whether the string is a non-negative decimal integer that fits in an {@code int}. Callers + * hand the string straight to {@link Integer#parseInt}, so the range matters as much as the + * characters. + */ private static boolean isInteger(String s) { - boolean isInt = s.length() > 0; + if (s.isEmpty() || s.length() > 10) { + return false; + } for (int i = 0; i < s.length(); i++) { if (s.charAt(i) < '0' || s.charAt(i) > '9') { - isInt = false; - break; + return false; } } - return isInt; + // ten digits still reach past Integer.MAX_VALUE + return s.length() < 10 || Long.parseLong(s) <= Integer.MAX_VALUE; } private static boolean isIllegalDate(int y, int m, int d) { diff --git a/paimon-common/src/test/java/org/apache/paimon/utils/DateTimeUtilsTest.java b/paimon-common/src/test/java/org/apache/paimon/utils/DateTimeUtilsTest.java index 6a0f88a367ff..9d459c7236fc 100644 --- a/paimon-common/src/test/java/org/apache/paimon/utils/DateTimeUtilsTest.java +++ b/paimon-common/src/test/java/org/apache/paimon/utils/DateTimeUtilsTest.java @@ -33,6 +33,24 @@ /** Test for {@link DateTimeUtils}. */ public class DateTimeUtilsTest { + @Test + public void testParseDateAndTimeOverflowReturnsNull() { + // A component too large for an int is an invalid date or time, not a crash: the + // contract of parseDate/parseTime is null for unparseable input. 2147483648 is + // Integer.MAX_VALUE + 1, the smallest ten-digit value that does not fit. + assertThat(DateTimeUtils.parseDate("2147483648-01-01")).isNull(); + assertThat(DateTimeUtils.parseDate("2147483647-01-01")) + .isNull(); // in range, but not a year + assertThat(DateTimeUtils.parseDate("99999999999-01-01")).isNull(); + assertThat(DateTimeUtils.parseDate("2024-99999999999-01")).isNull(); + assertThat(DateTimeUtils.parseDate("2024-01-99999999999")).isNull(); + assertThat(DateTimeUtils.parseTime("99999999999:00:00")).isNull(); + + // Sanity: valid values still parse. + assertThat(DateTimeUtils.parseDate("2024-01-15")).isNotNull(); + assertThat(DateTimeUtils.parseTime("12:30:00")).isNotNull(); + } + @Test public void testFormatLocalDateTime() { LocalDateTime time = LocalDateTime.of(2023, 8, 30, 12, 30, 59, 999_999_999);