]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-json: make sure all dispatch helpers do something sensible in case of "null" JSON...
authorLennart Poettering <lennart@poettering.net>
Fri, 24 Oct 2025 07:57:48 +0000 (09:57 +0200)
committerLennart Poettering <lennart@poettering.net>
Sat, 1 Nov 2025 20:43:37 +0000 (21:43 +0100)
Most of our dispatch helpers already do something useful in case they
are invoked on a null JSON value: they translate this to the appropriate
niche value for the type, if there is one.

Add the same for *all* dispatchers we have, to make this fully
systematic.

For various types it's not always clear which niche value to pick. I
opted for UINT{8,16,32,64}_MAX for the various unsigned integers, which
maps our own use in most cases. I opted for -1 for the various signed
integer types. For arrays/blobs of stuff I opted for the empty
array/blob, and for booleans I opted for false.

Of course, in various cases this is not going to be the right niche
value, but that's entirely fine, after all before a json value reaches a
dispatcher function it must pass one of two type checks first:

1. Either the .type field of sd_json_dispatch_field must be
   _SD_JSON_VARIANT_TYPE_INVALID to not do a type check at all

2. Or the .type field is set, but then the SD_JSON_NULLABLE flag must be
   set in .flags.

This means, accidentally generating the niche values on null is not
really likely.

src/libsystemd/sd-json/json-util.c
src/libsystemd/sd-json/sd-json.c

index 556d4c786bc4df5b7b9cef33bf0d4200e7a8fe63..6f42239b91ecec3a1558ee61f9cedcf27e0047e1 100644 (file)
@@ -31,6 +31,11 @@ int json_dispatch_unhex_iovec(const char *name, sd_json_variant *variant, sd_jso
         size_t sz;
         int r;
 
+        if (sd_json_variant_is_null(variant)) {
+                iovec_done(iov);
+                return 0;
+        }
+
         if (!sd_json_variant_is_string(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
 
@@ -49,6 +54,11 @@ int json_dispatch_unbase64_iovec(const char *name, sd_json_variant *variant, sd_
         size_t sz;
         int r;
 
+        if (sd_json_variant_is_null(variant)) {
+                iovec_done(iov);
+                return 0;
+        }
+
         if (!sd_json_variant_is_string(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
 
@@ -68,6 +78,11 @@ int json_dispatch_byte_array_iovec(const char *name, sd_json_variant *variant, s
 
         assert(variant);
 
+        if (sd_json_variant_is_null(variant)) {
+                iovec_done(iov);
+                return 0;
+        }
+
         if (!sd_json_variant_is_array(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
 
@@ -169,6 +184,11 @@ int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_di
         _cleanup_(iovec_done) struct iovec iov = {};
         int r;
 
+        if (sd_json_variant_is_null(variant)) {
+                *address = (struct in_addr) {};
+                return 0;
+        }
+
         r = json_dispatch_byte_array_iovec(name, variant, flags, &iov);
         if (r < 0)
                 return r;
index 79cfa4cc60ddf1b66cab653a029113972622d5d7..b13960897eda247788cda5b481ed41a3fdfded83 100644 (file)
@@ -5292,6 +5292,11 @@ _public_ int sd_json_dispatch_stdbool(const char *name, sd_json_variant *variant
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *b = false;
+                return 0;
+        }
+
         if (!sd_json_variant_is_boolean(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name));
 
@@ -5305,6 +5310,11 @@ _public_ int sd_json_dispatch_intbool(const char *name, sd_json_variant *variant
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *b = false;
+                return 0;
+        }
+
         if (!sd_json_variant_is_boolean(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name));
 
@@ -5336,6 +5346,11 @@ _public_ int sd_json_dispatch_int64(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *i = -1;
+                return 0;
+        }
+
         /* Also accept numbers formatted as string, to increase compatibility with less capable JSON
          * implementations that cannot do 64bit integers. */
         if (sd_json_variant_is_string(variant) && safe_atoi64(sd_json_variant_string(variant), i) >= 0)
@@ -5354,6 +5369,11 @@ _public_ int sd_json_dispatch_uint64(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *u = UINT64_MAX;
+                return 0;
+        }
+
         /* Since 64bit values (in particular unsigned ones) in JSON are problematic, let's also accept them
          * formatted as strings. If this is not desired make sure to set the .type field in
          * sd_json_dispatch_field to SD_JSON_UNSIGNED rather than _SD_JSON_VARIANT_TYPE_INVALID, so that
@@ -5377,6 +5397,11 @@ _public_ int sd_json_dispatch_uint32(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *u = UINT32_MAX;
+                return 0;
+        }
+
         r = sd_json_dispatch_uint64(name, variant, flags, &u64);
         if (r < 0)
                 return r;
@@ -5399,6 +5424,11 @@ _public_ int sd_json_dispatch_int32(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *i = -1;
+                return 0;
+        }
+
         r = sd_json_dispatch_int64(name, variant, flags, &i64);
         if (r < 0)
                 return r;
@@ -5421,6 +5451,11 @@ _public_ int sd_json_dispatch_int16(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *i = -1;
+                return 0;
+        }
+
         r = sd_json_dispatch_int64(name, variant, flags, &i64);
         if (r < 0)
                 return r;
@@ -5440,6 +5475,11 @@ _public_ int sd_json_dispatch_uint16(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *u = UINT16_MAX;
+                return 0;
+        }
+
         r = sd_json_dispatch_uint64(name, variant, flags, &u64);
         if (r < 0)
                 return r;
@@ -5459,6 +5499,11 @@ _public_ int sd_json_dispatch_int8(const char *name, sd_json_variant *variant, s
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *i = -1;
+                return 0;
+        }
+
         r = sd_json_dispatch_int64(name, variant, flags, &i64);
         if (r < 0)
                 return r;
@@ -5478,6 +5523,11 @@ _public_ int sd_json_dispatch_uint8(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *u = UINT8_MAX;
+                return 0;
+        }
+
         r = sd_json_dispatch_uint64(name, variant, flags, &u64);
         if (r < 0)
                 return r;
@@ -5495,6 +5545,11 @@ _public_ int sd_json_dispatch_double(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *d = NAN;
+                return 0;
+        }
+
         /* Note, this will take care of parsing NaN, -Infinity, Infinity for us */
         if (sd_json_variant_is_string(variant) && safe_atod(sd_json_variant_string(variant), d) >= 0)
                 return 0;
@@ -5514,6 +5569,11 @@ _public_ int sd_json_dispatch_string(const char *name, sd_json_variant *variant,
         assert_return(variant, -EINVAL);
         assert_return(userdata, -EINVAL);
 
+        if (sd_json_variant_is_null(variant)) {
+                *s = mfree(*s);
+                return 0;
+        }
+
         r = sd_json_dispatch_const_string(name, variant, flags, &n);
         if (r < 0)
                 return r;