]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: add test for variant recursion depth limit in message_skip_fields() 42164/head
authorTristanInSec <tristan.mtn@gmail.com>
Tue, 19 May 2026 21:33:11 +0000 (17:33 -0400)
committerTristanInSec <tristan.mtn@gmail.com>
Tue, 19 May 2026 21:33:11 +0000 (17:33 -0400)
Craft a raw D-Bus message with an unknown header field containing
BUS_CONTAINER_DEPTH+1 nested variants and verify that message parsing
rejects it with -EBADMSG rather than recursing until stack overflow.

src/libsystemd/sd-bus/test-bus-marshal.c

index e26ca43344713ae6872f2a2ce98a55cb013f529f..7544b1adc66a5dbed5fbe7a2da6f31c0922fb0ed 100644 (file)
@@ -216,6 +216,58 @@ static void test_bus_fds_truncated(void) {
         log_info("All fd truncation tests passed");
 }
 
+static void test_bus_nested_variant_depth_limit(void) {
+        /* Craft a raw D-Bus message with an unknown header field whose value is a variant
+         * containing a variant containing a variant... nested beyond BUS_CONTAINER_DEPTH.
+         * Without the depth limit in message_skip_fields(), this causes unbounded recursion
+         * and stack overflow. With the fix, it should be rejected with -EBADMSG. */
+
+        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        const unsigned depth = BUS_CONTAINER_DEPTH + 1; /* one past the limit */
+
+        /* Each nesting level in the fields area is: 1 byte sig_len + 1 byte 'v' + 1 byte NUL = 3 bytes.
+         * The innermost level has sig_len=1, 'u', NUL, then 4 bytes for the uint32 value.
+         * The field header is: 1 byte field_code + 1 byte sig_len + 1 byte 'v' + 1 byte NUL = 4 bytes. */
+        size_t fields_size = 4 + (depth * 3) + 4; /* field header + nested variant sigs + uint32 */
+        size_t padded_fields = ALIGN8(fields_size);
+        size_t total = sizeof(BusMessageHeader) + padded_fields;
+
+        _cleanup_free_ void *buf = ASSERT_PTR(malloc0(total));
+
+        BusMessageHeader *h = buf;
+        *h = (BusMessageHeader) {
+                .endian = BUS_NATIVE_ENDIAN,
+                .type = SD_BUS_MESSAGE_METHOD_CALL,
+                .version = 1,
+                .serial = 1,
+                .fields_size = (uint32_t) fields_size,
+        };
+
+        uint8_t *p = (uint8_t *) buf + sizeof(BusMessageHeader);
+
+        /* Unknown field code (triggers default: in message_parse_fields) */
+        *p++ = 0xFF;
+        /* Field signature: variant */
+        *p++ = 1;    /* sig length */
+        *p++ = 'v';
+        *p++ = '\0';
+
+        /* Nested variant signatures: each level declares its content is another variant */
+        for (unsigned i = 0; i < depth; i++) {
+                *p++ = 1;    /* sig length */
+                *p++ = 'v';
+                *p++ = '\0';
+        }
+
+        /* Innermost value: a uint32 */
+        memset(p, 0, 4);
+
+        ASSERT_OK(sd_bus_new(&bus));
+
+        ASSERT_ERROR(bus_message_from_malloc(bus, buf, total, NULL, 0, false, NULL, &m), EBADMSG);
+}
+
 static void test_bus_label_escape(void) {
         test_bus_label_escape_one("foo123bar", "foo123bar");
         test_bus_label_escape_one("foo.bar", "foo_2ebar");
@@ -533,6 +585,7 @@ int main(int argc, char *argv[]) {
         test_bus_path_encode_unique();
         test_bus_path_encode_many();
         test_bus_fds_truncated();
+        test_bus_nested_variant_depth_limit();
 
         return 0;
 }