From: Frantisek Sumsal Date: Tue, 12 May 2026 15:09:41 +0000 (+0200) Subject: sd-bus: handle non-string keys in dictionaries in JSON dump X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7cecff8c9949d4bfd85b06c6dba3691e82ec86d4;p=thirdparty%2Fsystemd.git sd-bus: handle non-string keys in dictionaries in JSON dump JSON only supports string keys in objects, but D-Bus specification is a bit more lenient and allows dict entries to have any basic type as key. Let's stringify allowed non-string keys so that we can represent them as JSON objects. Relevant snippet from the D-Bus specification: A DICT_ENTRY works exactly like a struct, but rather than parentheses it uses curly braces, and it has more restrictions. The restrictions are: it occurs only as an array element type; it has exactly two single complete types inside the curly braces; the first single complete type (the "key") must be a basic type rather than a container type. Implementations must not accept dict entries outside of arrays, must not accept dict entries with zero, one, or more than two fields, and must not accept dict entries with non-basic-typed keys. A dict entry is always a key-value pair. Resolves: #32904 --- diff --git a/src/libsystemd/sd-bus/bus-dump-json.c b/src/libsystemd/sd-bus/bus-dump-json.c index 8238ac587e9..ae3e8061946 100644 --- a/src/libsystemd/sd-bus/bus-dump-json.c +++ b/src/libsystemd/sd-bus/bus-dump-json.c @@ -72,6 +72,7 @@ static int json_transform_dict_array(sd_bus_message *m, sd_json_variant **ret) { CLEANUP_ARRAY(elements, n_elements, sd_json_variant_unref_many); for (;;) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *key = NULL; const char *contents; char type; @@ -94,11 +95,35 @@ static int json_transform_dict_array(sd_bus_message *m, sd_json_variant **ret) { if (r < 0) return r; - r = json_transform_one(m, elements + n_elements); + r = json_transform_one(m, &key); if (r < 0) return r; - n_elements++; + /* JSON only supports string keys in objects, but D-Bus specification is a bit more lenient + * and allows dict entries to have any basic type as key. Let's stringify allowed non-string + * keys so that we can represent them as JSON objects. */ + if (!sd_json_variant_is_string(key)) { + _cleanup_free_ char *s = NULL; + + if (!IN_SET(sd_json_variant_type(key), + SD_JSON_VARIANT_BOOLEAN, + SD_JSON_VARIANT_INTEGER, + SD_JSON_VARIANT_REAL, + SD_JSON_VARIANT_UNSIGNED)) + return -EINVAL; + + r = sd_json_variant_format(key, /* flags= */ 0, &s); + if (r < 0) + return r; + + key = sd_json_variant_unref(key); + + r = sd_json_variant_new_string(&key, s); + if (r < 0) + return r; + } + + elements[n_elements++] = TAKE_PTR(key); r = json_transform_one(m, elements + n_elements); if (r < 0) diff --git a/test/units/TEST-74-AUX-UTILS.busctl.sh b/test/units/TEST-74-AUX-UTILS.busctl.sh index 875f353b00e..54170cf5b55 100755 --- a/test/units/TEST-74-AUX-UTILS.busctl.sh +++ b/test/units/TEST-74-AUX-UTILS.busctl.sh @@ -43,6 +43,12 @@ busctl call --json=short \ busctl call -j \ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.DBus.Properties \ GetAll s "org.freedesktop.systemd1.Manager" | jq -c +# Do the same with D-Bus' org.freedesktop.DBus.Debug.Stats interface which also provides some +# complex signatures that caused issues during the JSON conversion in the past +# See: https://github.com/systemd/systemd/issues/32904 +busctl call --json=pretty \ + org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.Debug.Stats \ + GetStats | jq -c busctl call --verbose --timeout=60 --expect-reply=yes \ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \ ListUnitsByPatterns asas 1 "active" 2 "systemd-*.socket" "*.mount"