Skip to content

Commit 9151773

Browse files
nwellnhofkevinbackhouse
authored andcommitted
Fix quadratic behavior when parsing inlines
The inline parsing code would call cmark_node_append_child to append nodes. This public function has a sanity check which is linear in the depth of the tree. Repeated calls could show quadratic behavior in degenerate trees. Use a special function to append nodes without this check. Fixes commonmark#373. Found by OSS-Fuzz.
1 parent 9d57d8a commit 9151773

File tree

1 file changed

+24
-4
lines changed

1 file changed

+24
-4
lines changed

src/inlines.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,24 @@ static cmark_node *make_str_with_entities(subject *subj,
111111
}
112112
}
113113

114+
// Like cmark_node_append_child but without costly sanity checks.
115+
// Assumes that child was newly created.
116+
static void append_child(cmark_node *node, cmark_node *child) {
117+
cmark_node *old_last_child = node->last_child;
118+
119+
child->next = NULL;
120+
child->prev = old_last_child;
121+
child->parent = node;
122+
node->last_child = child;
123+
124+
if (old_last_child) {
125+
old_last_child->next = child;
126+
} else {
127+
// Also set first_child if node previously had no children.
128+
node->first_child = child;
129+
}
130+
}
131+
114132
// Duplicate a chunk by creating a copy of the buffer not by reusing the
115133
// buffer like cmark_chunk_dup does.
116134
static cmark_chunk chunk_clone(cmark_mem *mem, cmark_chunk *src) {
@@ -154,7 +172,7 @@ static CMARK_INLINE cmark_node *make_autolink(subject *subj,
154172
link->start_line = link->end_line = subj->line;
155173
link->start_column = start_column + 1;
156174
link->end_column = end_column + 1;
157-
cmark_node_append_child(link, make_str_with_entities(subj, start_column + 1, end_column - 1, &url));
175+
append_child(link, make_str_with_entities(subj, start_column + 1, end_column - 1, &url));
158176
return link;
159177
}
160178

@@ -768,7 +786,8 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener,
768786
tmp = opener_inl->next;
769787
while (tmp && tmp != closer_inl) {
770788
tmpnext = tmp->next;
771-
cmark_node_append_child(emph, tmp);
789+
cmark_node_unlink(tmp);
790+
append_child(emph, tmp);
772791
tmp = tmpnext;
773792
}
774793
cmark_node_insert_after(opener_inl, emph);
@@ -1238,7 +1257,8 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
12381257
tmp = opener->inl_text->next;
12391258
while (tmp) {
12401259
tmpnext = tmp->next;
1241-
cmark_node_append_child(inl, tmp);
1260+
cmark_node_unlink(tmp);
1261+
append_child(inl, tmp);
12421262
tmp = tmpnext;
12431263
}
12441264

@@ -1451,7 +1471,7 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
14511471
new_inl = make_str(subj, startpos, endpos - 1, contents);
14521472
}
14531473
if (new_inl != NULL) {
1454-
cmark_node_append_child(parent, new_inl);
1474+
append_child(parent, new_inl);
14551475
}
14561476

14571477
return 1;

0 commit comments

Comments
 (0)