]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
varlink: limit the maximum nesting depth
authorFrantisek Sumsal <frantisek@sumsal.cz>
Thu, 2 Nov 2023 17:55:50 +0000 (18:55 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 2 Nov 2023 20:43:38 +0000 (20:43 +0000)
Let's limit the maximum nesting depth for structure definitions to 64 to
avoid stack overflows with very deep definitions.

Resolves: #29589

src/shared/varlink-idl.c
src/test/test-varlink-idl.c

index 78d8435ea31c96272bb58b94f9815819c7ce7248..f871c8e632b5bbdd92489afb8eb7cc25b412c2ef 100644 (file)
@@ -6,6 +6,8 @@
 #include "varlink-idl.h"
 #include "set.h"
 
+#define DEPTH_MAX 64U
+
 enum {
         COLOR_SYMBOL_TYPE,   /* interface, method, type, error */
         COLOR_FIELD_TYPE,    /* string, bool, … */
@@ -562,13 +564,14 @@ static int varlink_idl_subparse_whitespace(
         return 1;
 }
 
-static int varlink_idl_subparse_struct_or_enum(const char **p, unsigned *line, unsigned *column, VarlinkSymbol **symbol, size_t *n_fields, VarlinkFieldDirection direction);
+static int varlink_idl_subparse_struct_or_enum(const char **p, unsigned *line, unsigned *column, VarlinkSymbol **symbol, size_t *n_fields, VarlinkFieldDirection direction, unsigned depth);
 
 static int varlink_idl_subparse_field_type(
                 const char **p,
                 unsigned *line,
                 unsigned *column,
-                VarlinkField *field) {
+                VarlinkField *field,
+                unsigned depth) {
 
         size_t l;
         int r;
@@ -638,7 +641,8 @@ static int varlink_idl_subparse_field_type(
                                 column,
                                 &symbol,
                                 &n_fields,
-                                VARLINK_REGULAR);
+                                VARLINK_REGULAR,
+                                depth + 1);
                 if (r < 0)
                         return r;
 
@@ -677,7 +681,8 @@ static int varlink_idl_subparse_struct_or_enum(
                 unsigned *column,
                 VarlinkSymbol **symbol,
                 size_t *n_fields,
-                VarlinkFieldDirection direction) {
+                VarlinkFieldDirection direction,
+                unsigned depth) {
 
         enum {
                 STATE_OPEN,
@@ -698,6 +703,9 @@ static int varlink_idl_subparse_struct_or_enum(
         assert(*symbol);
         assert(n_fields);
 
+        if (depth > DEPTH_MAX)
+                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Maximum nesting depth reached (%u).", *line, *column, DEPTH_MAX);
+
         while (state != STATE_DONE) {
                 _cleanup_free_ char *token = NULL;
 
@@ -765,7 +773,7 @@ static int varlink_idl_subparse_struct_or_enum(
                                         .field_direction = direction,
                                 };
 
-                                r = varlink_idl_subparse_field_type(p, line, column, field);
+                                r = varlink_idl_subparse_field_type(p, line, column, field, depth);
                                 if (r < 0)
                                         return r;
 
@@ -992,7 +1000,7 @@ int varlink_idl_parse(
                         symbol->symbol_type = VARLINK_METHOD;
                         symbol->name = TAKE_PTR(token);
 
-                        r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_INPUT);
+                        r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_INPUT, 0);
                         if (r < 0)
                                 return r;
 
@@ -1009,7 +1017,7 @@ int varlink_idl_parse(
                         if (!streq(token, "->"))
                                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%u:%u: Unexpected token '%s'.", *line, *column, token);
 
-                        r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_OUTPUT);
+                        r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_OUTPUT, 0);
                         if (r < 0)
                                 return r;
 
@@ -1037,7 +1045,7 @@ int varlink_idl_parse(
                         symbol->symbol_type = _VARLINK_SYMBOL_TYPE_INVALID; /* don't know yet if enum or struct, will be field in by varlink_idl_subparse_struct_or_enum() */
                         symbol->name = TAKE_PTR(token);
 
-                        r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_REGULAR);
+                        r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_REGULAR, 0);
                         if (r < 0)
                                 return r;
 
@@ -1065,7 +1073,7 @@ int varlink_idl_parse(
                         symbol->symbol_type = VARLINK_ERROR;
                         symbol->name = TAKE_PTR(token);
 
-                        r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_REGULAR);
+                        r = varlink_idl_subparse_struct_or_enum(&text, line, column, &symbol, &n_fields, VARLINK_REGULAR, 0);
                         if (r < 0)
                                 return r;
 
index 167e8eaff686d1850fc2d706ee30a75602f78c16..cbdb9c64fbb35cc764e815a7f85bdf49b3df9684 100644 (file)
@@ -249,6 +249,35 @@ TEST(validate_json) {
         assert_se(varlink_idl_validate_method_call(symbol, v, NULL) >= 0);
 }
 
+static int test_recursive_one(unsigned depth) {
+        _cleanup_(varlink_interface_freep) VarlinkInterface *parsed = NULL;
+        _cleanup_free_ char *pre = NULL, *post = NULL, *text = NULL;
+        static const char header[] =
+                "interface recursive.test\n"
+                "type Foo (\n";
+
+        /* Generate a chain of nested structures, i.e. a: (a: (... (int))...) */
+        pre = strrep("a:(", depth);
+        post = strrep(")", depth);
+        if (!pre || !post)
+                return log_oom();
+
+        text = strjoin(header, pre, "int", post, ")");
+        if (!text)
+                return log_oom();
+
+        return varlink_idl_parse(text, NULL, NULL, &parsed);
+}
+
+TEST(recursive) {
+        assert_se(test_recursive_one(32) >= 0);
+        assert_se(test_recursive_one(64) >= 0);
+
+        /* We should handle this gracefully without a stack overflow */
+        assert_se(test_recursive_one(65) < 0);
+        assert_se(test_recursive_one(20000) < 0 );
+}
+
 static int test_method(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
         JsonVariant *foo = json_variant_by_key(parameters, "foo"), *bar = json_variant_by_key(parameters, "bar");