Skip to content

Commit a1d171a

Browse files
committed
Fix for use-after-free bug introduced in 71e27f2
Sometimes, when cleaning up unusued footnotes, two footnote->nodes may end up referencing each other. As they get free()'d up, this can lead to problems. Instead, first we unlink every node and _then_ free them up.
1 parent d3a819c commit a1d171a

File tree

4 files changed

+37
-0
lines changed

4 files changed

+37
-0
lines changed

src/blocks.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ static void process_footnotes(cmark_parser *parser) {
523523
}
524524
}
525525

526+
cmark_unlink_footnotes_map(map);
526527
cmark_map_free(map);
527528
}
528529

src/footnotes.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,26 @@ void cmark_footnote_create(cmark_map *map, cmark_node *node) {
3838
cmark_map *cmark_footnote_map_new(cmark_mem *mem) {
3939
return cmark_map_new(mem, footnote_free);
4040
}
41+
42+
// Before calling `cmark_map_free` on a map with `cmark_footnotes`, first
43+
// unlink all of the footnote nodes before freeing their memory.
44+
//
45+
// Sometimes, two (unused) footnote nodes can end up referencing each other,
46+
// which as they get freed up by calling `cmark_map_free` -> `footnote_free` ->
47+
// etc, can lead to a use-after-free error.
48+
//
49+
// Better to `unlink` every footnote node first, setting their next, prev, and
50+
// parent pointers to NULL, and only then walk thru & free them up.
51+
void cmark_unlink_footnotes_map(cmark_map *map) {
52+
cmark_map_entry *ref;
53+
cmark_map_entry *next;
54+
55+
ref = map->refs;
56+
while(ref) {
57+
next = ref->next;
58+
if (((cmark_footnote *)ref)->node) {
59+
cmark_node_unlink(((cmark_footnote *)ref)->node);
60+
}
61+
ref = next;
62+
}
63+
}

src/footnotes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ typedef struct cmark_footnote cmark_footnote;
1818
void cmark_footnote_create(cmark_map *map, cmark_node *node);
1919
cmark_map *cmark_footnote_map_new(cmark_mem *mem);
2020

21+
void cmark_unlink_footnotes_map(cmark_map *map);
22+
2123
#ifdef __cplusplus
2224
}
2325
#endif

test/regression.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,3 +354,14 @@ Footnotes interacting with strikethrough should not lead to a use-after-free pt2
354354
.
355355
<p>[^~~is~~1]</p>
356356
````````````````````````````````
357+
358+
Adjacent unused footnotes definitions should not lead to a use after free
359+
360+
```````````````````````````````` example footnotes autolink strikethrough table
361+
Hello world
362+
363+
364+
[^a]:[^b]:
365+
.
366+
<p>Hello world</p>
367+
````````````````````````````````

0 commit comments

Comments
 (0)