22001:限制 JSON 嵌套深度
题目
在本章 JSON parser 的单次递归下降过程中限制数组和对象的嵌套深度。超限时必须报告准确位置,并保持原有失败清理语义。
解析
深度检查应当发生在 parse_value 即将分派到 parse_array 或 parse_object 时。此刻 p->cur 仍指向开放符号,正好可以作为超限位置。
把“检查、增加深度、解析容器、恢复深度”封装在一个同步辅助函数中,可以避免在 parse_array 和 parse_object 的众多返回路径里分别维护计数:
text
检查限制
depth += 1
调用容器解析函数
depth -= 1
返回解析结果1
2
3
4
5
2
3
4
5
容器解析函数无论成功还是失败都会正常返回,所以恢复步骤只需写一次。
解析
首先扩展解析器状态:
c
typedef struct {
const char *cur;
const char *end;
const char *msg;
size_t depth;
size_t max_depth;
} json_parser;1
2
3
4
5
6
7
2
3
4
5
6
7
在 parse_array、parse_object 定义之后,parse_value 定义之前加入:
c
static json_value *parse_container(
json_parser *p,
char opener
) {
if (p->depth == p->max_depth) {
set_error(p, "maximum nesting depth exceeded");
return NULL;
}
++p->depth;
json_value *result = opener == '{'
? parse_object(p)
: parse_array(p);
--p->depth;
return result;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
把 parse_value 中原有的两个容器分支:
c
if (c == '{') {
return parse_object(p);
}
if (c == '[') {
return parse_array(p);
}1
2
3
4
5
6
2
3
4
5
6
替换为:
c
if (c == '{' || c == '[') {
return parse_container(p, c);
}1
2
3
2
3
增加带限制的完整入口:
c
static json_value *json_parse_n_with_limit(
const char *text,
size_t len,
size_t max_depth,
const char **err_pos,
const char **err_msg
) {
if (text == NULL) {
if (err_pos != NULL) {
*err_pos = NULL;
}
if (err_msg != NULL) {
*err_msg = "invalid input";
}
return NULL;
}
json_parser p = {0};
p.cur = text;
p.end = text + len;
p.msg = NULL;
p.depth = 0;
p.max_depth = max_depth;
skip_ws(&p);
json_value *root = parse_value(&p);
if (root == NULL) {
if (err_pos != NULL) {
*err_pos = p.cur;
}
if (err_msg != NULL) {
*err_msg = p.msg == NULL
? "parse failed"
: p.msg;
}
return NULL;
}
skip_ws(&p);
if (p.cur != p.end) {
json_free(root);
if (err_pos != NULL) {
*err_pos = p.cur;
}
if (err_msg != NULL) {
*err_msg = "trailing characters";
}
return NULL;
}
if (err_pos != NULL) {
*err_pos = p.cur;
}
if (err_msg != NULL) {
*err_msg = NULL;
}
return root;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
若要保留原入口及其无限制语义,可以让它转发到新入口:
c
static json_value *json_parse_n(
const char *text,
size_t len,
const char **err_pos,
const char **err_msg
) {
return json_parse_n_with_limit(
text,
len,
SIZE_MAX,
err_pos,
err_msg
);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14