parser BUGFIX stack-oveflow protection
In yang/json format by tracking the right and left braces.
In xml by tracking open and close element.
diff --git a/src/json.c b/src/json.c
index 2ef1b5a..77fdf74 100644
--- a/src/json.c
+++ b/src/json.c
@@ -545,6 +545,8 @@
LY_CHECK_RET(skip_ws(jsonctx));
if (*jsonctx->in->current == '}') {
+ assert(jsonctx->depth);
+ jsonctx->depth--;
/* empty object */
ly_in_skip(jsonctx->in, 1);
lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
@@ -626,6 +628,12 @@
LY_CHECK_RET(lyjson_array(jsonctx));
} else if (*jsonctx->in->current == '{') {
+ jsonctx->depth++;
+ if (jsonctx->depth > LY_MAX_BLOCK_DEPTH) {
+ LOGERR(jsonctx->ctx, LY_EINVAL,
+ "The maximum number of block nestings has been exceeded.");
+ return LY_EINVAL;
+ }
/* object */
ly_in_skip(jsonctx->in, 1);
LY_CHECK_RET(lyjson_object(jsonctx));
@@ -697,6 +705,7 @@
jsonctx->backup.value_len = jsonctx->value_len;
jsonctx->backup.input = jsonctx->in->current;
jsonctx->backup.dynamic = jsonctx->dynamic;
+ jsonctx->backup.depth = jsonctx->depth;
jsonctx->dynamic = 0;
}
@@ -712,6 +721,7 @@
jsonctx->value_len = jsonctx->backup.value_len;
jsonctx->in->current = jsonctx->backup.input;
jsonctx->dynamic = jsonctx->backup.dynamic;
+ jsonctx->depth = jsonctx->backup.depth;
jsonctx->backup.dynamic = 0;
}
@@ -768,6 +778,10 @@
ret = lyjson_value(jsonctx);
}
} else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) || ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
+ if (*jsonctx->in->current == '}') {
+ assert(jsonctx->depth);
+ jsonctx->depth--;
+ }
ly_in_skip(jsonctx->in, 1);
JSON_POP_STATUS_RET(jsonctx);
JSON_PUSH_STATUS_RET(jsonctx, prev + 1);
diff --git a/src/json.h b/src/json.h
index ba22884..a43f90c 100644
--- a/src/json.h
+++ b/src/json.h
@@ -59,6 +59,7 @@
const char *value; /* LYJSON_STRING, LYJSON_NUMBER, LYJSON_OBJECT */
size_t value_len; /* LYJSON_STRING, LYJSON_NUMBER, LYJSON_OBJECT */
ly_bool dynamic; /* LYJSON_STRING, LYJSON_NUMBER, LYJSON_OBJECT */
+ uint32_t depth; /* current number of nested blocks, see ::LY_MAX_BLOCK_DEPTH */
struct {
enum LYJSON_PARSER_STATUS status;
@@ -66,6 +67,7 @@
const char *value;
size_t value_len;
ly_bool dynamic;
+ uint32_t depth;
const char *input;
} backup;
};
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 4c76d43..7e5cc98 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -751,7 +751,18 @@
word_start = ctx->in->current;
*kw = lysp_match_kw(ctx->in, &ctx->indent);
- if ((*kw == LY_STMT_SYNTAX_SEMICOLON) || (*kw == LY_STMT_SYNTAX_LEFT_BRACE) || (*kw == LY_STMT_SYNTAX_RIGHT_BRACE)) {
+ if (*kw == LY_STMT_SYNTAX_SEMICOLON) {
+ goto success;
+ } else if (*kw == LY_STMT_SYNTAX_LEFT_BRACE) {
+ ctx->depth++;
+ if (ctx->depth > LY_MAX_BLOCK_DEPTH) {
+ LOGERR(ctx->parsed_mod->mod->ctx, LY_EINVAL,
+ "The maximum number of block nestings has been exceeded.");
+ return LY_EINVAL;
+ }
+ goto success;
+ } else if (*kw == LY_STMT_SYNTAX_RIGHT_BRACE) {
+ ctx->depth--;
goto success;
}
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 9847099..b5d6986 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -35,6 +35,14 @@
#define LY_PCRE2_MSG_LIMIT 256
/**
+ * @brief The maximum depth at which the last nested block is located.
+ * Designed to protect against corrupted input that causes a stack-overflow error.
+ * For yang language and json format, the block is bounded by "{ }".
+ * For the xml format, the opening and closing element tag is considered as the block.
+ */
+#define LY_MAX_BLOCK_DEPTH 500
+
+/**
* @brief Informational structure for YANG statements
*/
struct stmt_info_s {
@@ -153,6 +161,7 @@
struct lys_glob_unres *unres; /**< global unres structure */
struct ly_in *in; /**< input handler for the parser */
uint64_t indent; /**< current position on the line for YANG indentation */
+ uint32_t depth; /**< current number of nested blocks, see ::LY_MAX_BLOCK_DEPTH */
};
/**
diff --git a/src/xml.c b/src/xml.c
index ac1655b..907058d 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -598,7 +598,14 @@
e->prefix = prefix;
e->name_len = name_len;
e->prefix_len = prefix_len;
+
LY_CHECK_RET(ly_set_add(&xmlctx->elements, e, 1, NULL));
+ if (xmlctx->elements.count > LY_MAX_BLOCK_DEPTH) {
+ LOGERR(xmlctx->ctx, LY_EINVAL,
+ "The maximum number of open elements has been exceeded.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
/* skip WS */
ign_xmlws(xmlctx);