From: Lennart Poettering Date: Fri, 24 Oct 2025 07:57:48 +0000 (+0200) Subject: sd-json: make sure all dispatch helpers do something sensible in case of "null" JSON... X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e01f8990f1e535c367bbab9018ed2d8def1dd6fa;p=thirdparty%2Fsystemd.git sd-json: make sure all dispatch helpers do something sensible in case of "null" JSON value 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. --- diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c index 556d4c786bc..6f42239b91e 100644 --- a/src/libsystemd/sd-json/json-util.c +++ b/src/libsystemd/sd-json/json-util.c @@ -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; diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c index 79cfa4cc60d..b13960897ed 100644 --- a/src/libsystemd/sd-json/sd-json.c +++ b/src/libsystemd/sd-json/sd-json.c @@ -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;