This is an automated email from the ASF dual-hosted git repository.

snuyanzin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git

commit 96a45c1234dd4592852b9a93f86b4a36b40bf23a
Author: Ramin Gharib <[email protected]>
AuthorDate: Wed Jul 22 18:00:43 2026 +0200

    [FLINK-40217][core] Reject non-finite numbers in `VARIANT` JSON conversion
    
    PARSE_JSON accepted JSON numbers outside the double range, such as 1e400, 
and silently stored them as +/-Infinity. Variant.toJson() then emitted bare 
Infinity/-Infinity tokens, which are invalid JSON and cannot be parsed back by 
PARSE_JSON, so the round trip was broken.
    
    parseFloatingPoint now rejects a non-finite result from getDoubleValue() 
with a clear parse error. PARSE_JSON surfaces the failure and TRY_PARSE_JSON 
returns NULL, so a parsed Variant can never hold a non-finite value.
    
    As a defensive safeguard for the builder API, which can still inject 
non-finite values, toJson() now throws for non-finite DOUBLE and FLOAT values 
instead of emitting invalid tokens.
---
 .../apache/flink/types/variant/BinaryVariant.java  | 25 ++++++++++++++++++----
 .../variant/BinaryVariantInternalBuilder.java      | 15 +++++++++++--
 .../variant/BinaryVariantInternalBuilderTest.java  | 11 ++++++++++
 .../flink/types/variant/BinaryVariantTest.java     | 18 ++++++++++++++++
 4 files changed, 63 insertions(+), 6 deletions(-)

diff --git 
a/flink-core/src/main/java/org/apache/flink/types/variant/BinaryVariant.java 
b/flink-core/src/main/java/org/apache/flink/types/variant/BinaryVariant.java
index ce5664d34e3..c5dd0bf3efe 100644
--- a/flink-core/src/main/java/org/apache/flink/types/variant/BinaryVariant.java
+++ b/flink-core/src/main/java/org/apache/flink/types/variant/BinaryVariant.java
@@ -356,8 +356,16 @@ public final class BinaryVariant implements Variant {
                 sb.append(escapeJson(BinaryVariantUtil.getString(value, pos)));
                 break;
             case DOUBLE:
-                sb.append(BinaryVariantUtil.getDouble(value, pos));
-                break;
+                {
+                    final double d = BinaryVariantUtil.getDouble(value, pos);
+                    if (Double.isInfinite(d) || Double.isNaN(d)) {
+                        throw new VariantTypeException(
+                                String.format(
+                                        "Non-finite value %s cannot be 
serialized to JSON.", d));
+                    }
+                    sb.append(d);
+                    break;
+                }
             case DECIMAL:
                 sb.append(BinaryVariantUtil.getDecimal(value, 
pos).toPlainString());
                 break;
@@ -382,8 +390,17 @@ public final class BinaryVariant implements Variant {
                                         .atZone(ZoneOffset.UTC)));
                 break;
             case FLOAT:
-                sb.append(BinaryVariantUtil.getFloat(value, pos));
-                break;
+                {
+                    final float f = BinaryVariantUtil.getFloat(value, pos);
+                    if (Float.isInfinite(f) || Float.isNaN(f)) {
+                        throw new VariantTypeException(
+                                String.format(
+                                        "Non-finite value %s cannot be 
serialized to JSON.",
+                                        (double) f));
+                    }
+                    sb.append(f);
+                    break;
+                }
             case BYTES:
                 appendQuoted(
                         sb,
diff --git 
a/flink-core/src/main/java/org/apache/flink/types/variant/BinaryVariantInternalBuilder.java
 
b/flink-core/src/main/java/org/apache/flink/types/variant/BinaryVariantInternalBuilder.java
index 928c267f365..1674d166266 100644
--- 
a/flink-core/src/main/java/org/apache/flink/types/variant/BinaryVariantInternalBuilder.java
+++ 
b/flink-core/src/main/java/org/apache/flink/types/variant/BinaryVariantInternalBuilder.java
@@ -91,6 +91,7 @@ public class BinaryVariantInternalBuilder {
             new VariantTypeException("VARIANT_SIZE_LIMIT");
     public static final VariantTypeException VARIANT_DUPLICATE_KEY_EXCEPTION =
             new VariantTypeException("VARIANT_DUPLICATE_KEY");
+    private static final JsonFactory JSON_FACTORY = new JsonFactory();
 
     public BinaryVariantInternalBuilder(boolean allowDuplicateKeys) {
         this.allowDuplicateKeys = allowDuplicateKeys;
@@ -103,7 +104,7 @@ public class BinaryVariantInternalBuilder {
      */
     public static BinaryVariant parseJson(String json, boolean 
allowDuplicateKeys)
             throws IOException {
-        try (JsonParser parser = new JsonFactory().createParser(json)) {
+        try (JsonParser parser = JSON_FACTORY.createParser(json)) {
             parser.nextToken();
             return parseJson(parser, allowDuplicateKeys);
         }
@@ -622,7 +623,17 @@ public class BinaryVariantInternalBuilder {
 
     private void parseFloatingPoint(JsonParser parser) throws IOException {
         if (!tryParseDecimal(parser.getText())) {
-            appendDouble(parser.getDoubleValue());
+            final double d = parser.getDoubleValue();
+            // Jackson coerces out-of-range numbers like 1e400 to +/-Infinity. 
Reject them instead
+            // of storing a non-finite double that toJson() could not render 
as valid JSON.
+            if (Double.isInfinite(d) || Double.isNaN(d)) {
+                throw new JsonParseException(
+                        parser,
+                        String.format(
+                                "Numeric value '%s' is out of the range of 
double precision and cannot be stored as a Variant.",
+                                parser.getText()));
+            }
+            appendDouble(d);
         }
     }
 
diff --git 
a/flink-core/src/test/java/org/apache/flink/types/variant/BinaryVariantInternalBuilderTest.java
 
b/flink-core/src/test/java/org/apache/flink/types/variant/BinaryVariantInternalBuilderTest.java
index 924e538f40a..cec12149ceb 100644
--- 
a/flink-core/src/test/java/org/apache/flink/types/variant/BinaryVariantInternalBuilderTest.java
+++ 
b/flink-core/src/test/java/org/apache/flink/types/variant/BinaryVariantInternalBuilderTest.java
@@ -19,6 +19,8 @@
 package org.apache.flink.types.variant;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import java.io.IOException;
 import java.math.BigDecimal;
@@ -120,6 +122,15 @@ class BinaryVariantInternalBuilderTest {
         
assertThat(variant.getField("k2").getDecimal()).isEqualTo(BigDecimal.valueOf(1.5));
     }
 
+    @ParameterizedTest
+    @ValueSource(strings = {"NaN", "Infinity", "-Infinity", "1e400", "-1e400"})
+    void testParseJsonRejectsNonFiniteNumbers(final String nonFiniteNumber) {
+        // NaN and the infinities are not valid JSON; 1e400 is valid JSON but 
overflows the double
+        // range. Both must be rejected so PARSE_JSON errors and 
TRY_PARSE_JSON returns NULL.
+        assertThatThrownBy(() -> 
BinaryVariantInternalBuilder.parseJson(nonFiniteNumber, false))
+                .isInstanceOf(IOException.class);
+    }
+
     @Test
     void testAppendFloat() {
         BinaryVariantInternalBuilder builder = new 
BinaryVariantInternalBuilder(false);
diff --git 
a/flink-core/src/test/java/org/apache/flink/types/variant/BinaryVariantTest.java
 
b/flink-core/src/test/java/org/apache/flink/types/variant/BinaryVariantTest.java
index 83896ec53e1..77235e968ee 100644
--- 
a/flink-core/src/test/java/org/apache/flink/types/variant/BinaryVariantTest.java
+++ 
b/flink-core/src/test/java/org/apache/flink/types/variant/BinaryVariantTest.java
@@ -20,6 +20,8 @@ package org.apache.flink.types.variant;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import java.math.BigDecimal;
 import java.time.Instant;
@@ -236,6 +238,22 @@ class BinaryVariantTest {
                 .isEqualTo("{" + "\"list\":[\"hello\",1]," + 
"\"object\":{\"ff\":10.0,\"ss\":1}}");
     }
 
+    @ParameterizedTest
+    @ValueSource(doubles = {Double.POSITIVE_INFINITY, 
Double.NEGATIVE_INFINITY, Double.NaN})
+    void testToJsonRejectsNonFiniteDouble(final double nonFinite) {
+        assertThatThrownBy(() -> builder.of(nonFinite).toJson())
+                .isInstanceOf(VariantTypeException.class)
+                .hasMessageContaining("cannot be serialized to JSON");
+    }
+
+    @ParameterizedTest
+    @ValueSource(floats = {Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, 
Float.NaN})
+    void testToJsonRejectsNonFiniteFloat(final float nonFinite) {
+        assertThatThrownBy(() -> builder.of(nonFinite).toJson())
+                .isInstanceOf(VariantTypeException.class)
+                .hasMessageContaining("cannot be serialized to JSON");
+    }
+
     @Test
     void testVariantException() {
         assertThatThrownBy(() -> new BinaryVariant(new byte[0], new byte[0]))

Reply via email to