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 ddb221c3b2)
(cherry picked from commit 50df1d7659)
(cherry picked from commit 527eb93d4b)
This commit is contained in:
TristanInSec authored and Luca Boccassi committed 2026-05-27 15:29:28 +01:00
1 parent 5f939b1e8a
commit 23bd58ceb4
1 file changed
+54
+54
View File
@@ -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;
}