#include "varlink-idl.h"
#include "set.h"
+#define DEPTH_MAX 64U
+
enum {
COLOR_SYMBOL_TYPE, /* interface, method, type, error */
COLOR_FIELD_TYPE, /* string, bool, … */
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;
column,
&symbol,
&n_fields,
- VARLINK_REGULAR);
+ VARLINK_REGULAR,
+ depth + 1);
if (r < 0)
return r;
unsigned *column,
VarlinkSymbol **symbol,
size_t *n_fields,
- VarlinkFieldDirection direction) {
+ VarlinkFieldDirection direction,
+ unsigned depth) {
enum {
STATE_OPEN,
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;
.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;
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;
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;
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;
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;
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");