From 23bd58ceb4dfbb798b1ca03460ffebca484d727d Mon Sep 17 00:00:00 2001 From: TristanInSec Date: Tue, 19 May 2026 17:33:11 -0400 Subject: [PATCH] test: add test for variant recursion depth limit in message_skip_fields() 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. (cherry picked from commit ddb221c3b26d561286518da99d477e2f580076b0) (cherry picked from commit 50df1d765907656b4f9667ac36c7545891b3358a) (cherry picked from commit 527eb93d4b94f20079bd08e8f3b4f4e790d67959) --- src/libsystemd/sd-bus/test-bus-marshal.c | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c index d52683ea87e..67cccdac5d3 100644 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ b/src/libsystemd/sd-bus/test-bus-marshal.c @@ -19,6 +19,7 @@ REENABLE_WARNING #include "sd-bus.h" #include "alloc-util.h" +#include "bus-internal.h" #include "bus-label.h" #include "bus-message.h" #include "bus-util.h" @@ -99,6 +100,58 @@ static void test_bus_label_escape_one(const char *a, const char *b) { assert_se(streq(a, y)); } +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, 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"); @@ -415,6 +468,7 @@ int main(int argc, char *argv[]) { test_bus_path_encode(); test_bus_path_encode_unique(); test_bus_path_encode_many(); + test_bus_nested_variant_depth_limit(); return 0; }