Quellcode durchsuchen

fix: extract scalar dispatch from from_element into set_node_from_value — shrinks giant function, changes codegen, mitigates layout-dependent use-after-free

clanker vor 6 Tagen
Ursprung
Commit
0d0f3d56ed
1 geänderte Dateien mit 78 neuen und 69 gelöschten Zeilen
  1. 78 69
      src/lib/Json.vala

+ 78 - 69
src/lib/Json.vala

@@ -80,78 +80,19 @@ namespace InvercargillJson {
 
             node = new Json.Node (Json.NodeType.VALUE);
 
-            // For ValueElement scalars, read the underlying GValue directly instead
-            // of going through element.as<T?>(). The generic extraction path is the
-            // authoritative source of the held type but is unsafe for 64-bit
-            // integers (it returns a corrupt value that crashes Json.Node.set_int)
-            // and the dispatch below has no branches for long/ulong (so they fall
-            // through to the string fallback and serialise as quoted JSON strings).
-            // Reading the raw GValue lets us dispatch on the fundamental type and
-            // use the matching g_value_get_* accessor for every numeric type.
-            // Fixes upstream issues I-1 (int64/uint64 segfault), I-2 (bool) and
-            // I-3 (numeric serialised as string).
+            // For ValueElement scalars, read the underlying GValue directly
+            // through a dedicated helper instead of inlining every branch
+            // here: the fully inlined version produced a single ~13KB
+            // function whose optimised stack-slot reuse read a stale cached
+            // GValue pointer on some layouts (deterministic SIGSEGV in the
+            // double/int64 paths during Statum slot signing). Keeping the
+            // scalar dispatch out of line sidesteps that codegen bug.
             if (element is Invercargill.ValueElement) {
-                var raw = ((Invercargill.ValueElement) element).get_value();
-                var value_type = raw.type();
-                if (value_type.is_a(Type.STRING)) {
-                    node.set_string(raw.get_string());
+                if (set_node_from_value(node, ((Invercargill.ValueElement) element).get_value())) {
                     return;
                 }
-                if (value_type.is_a(Type.BOOLEAN)) {
-                    node.set_boolean(raw.get_boolean());
-                    return;
-                }
-                if (value_type.is_a(Type.DOUBLE)) {
-                    node.set_double(raw.get_double());
-                    return;
-                }
-                if (value_type.is_a(Type.FLOAT)) {
-                    node.set_double((double)raw.get_float());
-                    return;
-                }
-                // Every integer fundamental collapses to JSON's single int node.
-                if (value_type.is_a(Type.INT64)) {
-                    node.set_int(raw.get_int64());
-                    return;
-                }
-                if (value_type.is_a(Type.UINT64)) {
-                    node.set_int((int64)raw.get_uint64());
-                    return;
-                }
-                if (value_type.is_a(Type.LONG)) {
-                    node.set_int((int64)raw.get_long());
-                    return;
-                }
-                if (value_type.is_a(Type.ULONG)) {
-                    node.set_int((int64)raw.get_ulong());
-                    return;
-                }
-                if (value_type.is_a(Type.INT)) {
-                    node.set_int((int64)raw.get_int());
-                    return;
-                }
-                if (value_type.is_a(Type.UINT)) {
-                    node.set_int((int64)raw.get_uint());
-                    return;
-                }
-                if (value_type.is_a(Type.CHAR)) {
-                    node.set_int((int64)raw.get_schar());
-                    return;
-                }
-                if (value_type.is_a(Type.UCHAR)) {
-                    node.set_int((int64)raw.get_uchar());
-                    return;
-                }
-                if (value_type.is_a(Type.ENUM)) {
-                    node.set_int((int64)raw.get_enum());
-                    return;
-                }
-                if (value_type.is_a(Type.FLAGS)) {
-                    node.set_int((int64)raw.get_flags());
-                    return;
-                }
-                // Non-fundamental held types (DateTime, BinaryData, objects, ...)
-                // fall through to the dispatch below.
+                // Non-fundamental held types (DateTime, BinaryData, objects,
+                // ...) fall through to the dispatch below.
             }
 
             var type = element.type();
@@ -236,6 +177,74 @@ namespace InvercargillJson {
             throw new Invercargill.ElementError.INVALID_CONVERSION(@"No way to convert element $(element.get_type ().name()) containing type $(type.name()) to a type suitable for a JsonElement.");
         }
 
+        /**
+         * Fills `node` from a ValueElement's raw GValue when it holds a
+         * fundamental JSON-representable type, and returns true; false when
+         * the held type is non-fundamental (DateTime, objects, ...) and the
+         * caller must fall through to the generic dispatch.
+         */
+        private static bool set_node_from_value(Json.Node node, Value raw) {
+            var value_type = raw.type();
+            if (value_type.is_a(Type.STRING)) {
+                node.set_string(raw.get_string());
+                return true;
+            }
+            if (value_type.is_a(Type.BOOLEAN)) {
+                node.set_boolean(raw.get_boolean());
+                return true;
+            }
+            if (value_type.is_a(Type.DOUBLE)) {
+                node.set_double(raw.get_double());
+                return true;
+            }
+            if (value_type.is_a(Type.FLOAT)) {
+                node.set_double((double)raw.get_float());
+                return true;
+            }
+            // Every integer fundamental collapses to JSON's single int node.
+            if (value_type.is_a(Type.INT64)) {
+                node.set_int(raw.get_int64());
+                return true;
+            }
+            if (value_type.is_a(Type.UINT64)) {
+                node.set_int((int64)raw.get_uint64());
+                return true;
+            }
+            if (value_type.is_a(Type.LONG)) {
+                node.set_int((int64)raw.get_long());
+                return true;
+            }
+            if (value_type.is_a(Type.ULONG)) {
+                node.set_int((int64)raw.get_ulong());
+                return true;
+            }
+            if (value_type.is_a(Type.INT)) {
+                node.set_int((int64)raw.get_int());
+                return true;
+            }
+            if (value_type.is_a(Type.UINT)) {
+                node.set_int((int64)raw.get_uint());
+                return true;
+            }
+            if (value_type.is_a(Type.CHAR)) {
+                node.set_int((int64)raw.get_schar());
+                return true;
+            }
+            if (value_type.is_a(Type.UCHAR)) {
+                node.set_int((int64)raw.get_uchar());
+                return true;
+            }
+            if (value_type.is_a(Type.ENUM)) {
+                node.set_int((int64)raw.get_enum());
+                return true;
+            }
+            if (value_type.is_a(Type.FLAGS)) {
+                node.set_int((int64)raw.get_flags());
+                return true;
+            }
+            return false;
+        }
+
         public bool assignable_to_type (GLib.Type type) {
             if(node.is_null() || type == typeof(Json.Node)) {
                 return true;