Skip to content

Commit 38d1cfe

Browse files
authored
Merge pull request from GHSA-w4qg-3vf7-m9x5
Fix GHSL-2023-117, GHSL-2023-118, GHSL-2023-119
2 parents a4cf959 + 580f021 commit 38d1cfe

File tree

3 files changed

+74
-27
lines changed

3 files changed

+74
-27
lines changed

extensions/table.c

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
#include "table.h"
1212
#include "cmark-gfm-core-extensions.h"
1313

14+
// Limit to prevent a malicious input from causing a denial of service.
15+
#define MAX_AUTOCOMPLETED_CELLS 0x80000
16+
1417
// Custom node flag, initialized in `create_table_extension`.
1518
static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED;
1619

@@ -31,6 +34,8 @@ typedef struct {
3134
typedef struct {
3235
uint16_t n_columns;
3336
uint8_t *alignments;
37+
int n_rows;
38+
int n_nonempty_cells;
3439
} node_table;
3540

3641
typedef struct {
@@ -83,6 +88,33 @@ static int set_n_table_columns(cmark_node *node, uint16_t n_columns) {
8388
return 1;
8489
}
8590

91+
// Increment the number of rows in the table. Also update n_nonempty_cells,
92+
// which keeps track of the number of cells which were parsed from the
93+
// input file. (If one of the rows is too short, then the trailing cells
94+
// are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.)
95+
// The purpose of this is to prevent a malicious input from generating a very
96+
// large number of autocompleted cells, which could cause a denial of service
97+
// vulnerability.
98+
static int incr_table_row_count(cmark_node *node, int i) {
99+
if (!node || node->type != CMARK_NODE_TABLE) {
100+
return 0;
101+
}
102+
103+
((node_table *)node->as.opaque)->n_rows++;
104+
((node_table *)node->as.opaque)->n_nonempty_cells += i;
105+
return 1;
106+
}
107+
108+
// Calculate the number of autocompleted cells.
109+
static int get_n_autocompleted_cells(cmark_node *node) {
110+
if (!node || node->type != CMARK_NODE_TABLE) {
111+
return 0;
112+
}
113+
114+
const node_table *nt = (node_table *)node->as.opaque;
115+
return (nt->n_columns * nt->n_rows) - nt->n_nonempty_cells;
116+
}
117+
86118
static uint8_t *get_table_alignments(cmark_node *node) {
87119
if (!node || node->type != CMARK_NODE_TABLE)
88120
return 0;
@@ -98,6 +130,23 @@ static int set_table_alignments(cmark_node *node, uint8_t *alignments) {
98130
return 1;
99131
}
100132

133+
static uint8_t get_cell_alignment(cmark_node *node) {
134+
if (!node || node->type != CMARK_NODE_TABLE_CELL)
135+
return 0;
136+
137+
const uint8_t *alignments = get_table_alignments(node->parent->parent);
138+
int i = node->as.cell_index;
139+
return alignments[i];
140+
}
141+
142+
static int set_cell_index(cmark_node *node, int i) {
143+
if (!node || node->type != CMARK_NODE_TABLE_CELL)
144+
return 0;
145+
146+
node->as.cell_index = i;
147+
return 1;
148+
}
149+
101150
static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len)
102151
{
103152
cmark_strbuf *res = (cmark_strbuf *)mem->calloc(1, sizeof(cmark_strbuf));
@@ -353,19 +402,20 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
353402
table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
354403
ntr->is_header = true;
355404

356-
{
357-
for (i = 0; i < header_row->n_columns; ++i) {
358-
node_cell *cell = &header_row->cells[i];
359-
cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
360-
CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
361-
header_cell->start_line = header_cell->end_line = parent_container->start_line;
362-
header_cell->internal_offset = cell->internal_offset;
363-
header_cell->end_column = parent_container->start_column + cell->end_offset;
364-
cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
365-
cmark_node_set_syntax_extension(header_cell, self);
366-
}
405+
for (i = 0; i < header_row->n_columns; ++i) {
406+
node_cell *cell = &header_row->cells[i];
407+
cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
408+
CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
409+
header_cell->start_line = header_cell->end_line = parent_container->start_line;
410+
header_cell->internal_offset = cell->internal_offset;
411+
header_cell->end_column = parent_container->start_column + cell->end_offset;
412+
cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
413+
cmark_node_set_syntax_extension(header_cell, self);
414+
set_cell_index(header_cell, i);
367415
}
368416

417+
incr_table_row_count(parent_container, i);
418+
369419
cmark_parser_advance_offset(
370420
parser, (char *)input,
371421
(int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);
@@ -385,6 +435,10 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
385435
if (cmark_parser_is_blank(parser))
386436
return NULL;
387437

438+
if (get_n_autocompleted_cells(parent_container) > MAX_AUTOCOMPLETED_CELLS) {
439+
return NULL;
440+
}
441+
388442
table_row_block =
389443
cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
390444
parent_container->start_column);
@@ -412,12 +466,16 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
412466
node->end_column = parent_container->start_column + cell->end_offset;
413467
cmark_node_set_string_content(node, (char *) cell->buf->ptr);
414468
cmark_node_set_syntax_extension(node, self);
469+
set_cell_index(node, i);
415470
}
416471

472+
incr_table_row_count(parent_container, i);
473+
417474
for (; i < table_columns; ++i) {
418475
cmark_node *node = cmark_parser_add_child(
419476
parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
420477
cmark_node_set_syntax_extension(node, self);
478+
set_cell_index(node, i);
421479
}
422480
}
423481

@@ -602,13 +660,7 @@ static const char *xml_attr(cmark_syntax_extension *extension,
602660
cmark_node *node) {
603661
if (node->type == CMARK_NODE_TABLE_CELL) {
604662
if (cmark_gfm_extensions_get_table_row_is_header(node->parent)) {
605-
uint8_t *alignments = get_table_alignments(node->parent->parent);
606-
int i = 0;
607-
cmark_node *n;
608-
for (n = node->parent->first_child; n; n = n->next, ++i)
609-
if (n == node)
610-
break;
611-
switch (alignments[i]) {
663+
switch (get_cell_alignment(node)) {
612664
case 'l': return " align=\"left\"";
613665
case 'c': return " align=\"center\"";
614666
case 'r': return " align=\"right\"";
@@ -696,7 +748,6 @@ static void html_render(cmark_syntax_extension *extension,
696748
cmark_event_type ev_type, int options) {
697749
bool entering = (ev_type == CMARK_EVENT_ENTER);
698750
cmark_strbuf *html = renderer->html;
699-
cmark_node *n;
700751

701752
// XXX: we just monopolise renderer->opaque.
702753
struct html_table_state *table_state =
@@ -745,7 +796,6 @@ static void html_render(cmark_syntax_extension *extension,
745796
}
746797
}
747798
} else if (node->type == CMARK_NODE_TABLE_CELL) {
748-
uint8_t *alignments = get_table_alignments(node->parent->parent);
749799
if (entering) {
750800
cmark_html_render_cr(html);
751801
if (table_state->in_table_header) {
@@ -754,12 +804,7 @@ static void html_render(cmark_syntax_extension *extension,
754804
cmark_strbuf_puts(html, "<td");
755805
}
756806

757-
int i = 0;
758-
for (n = node->parent->first_child; n; n = n->next, ++i)
759-
if (n == node)
760-
break;
761-
762-
switch (alignments[i]) {
807+
switch (get_cell_alignment(node)) {
763808
case 'l': html_table_add_align(html, "left", options); break;
764809
case 'c': html_table_add_align(html, "center", options); break;
765810
case 'r': html_table_add_align(html, "right", options); break;

src/blocks.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,8 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
12171217
parser->first_nonspace + 1);
12181218
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
12191219
} else if (!indented &&
1220-
parser->options & CMARK_OPT_FOOTNOTES &&
1220+
(parser->options & CMARK_OPT_FOOTNOTES) &&
1221+
depth < MAX_LIST_DEPTH &&
12211222
(matched = scan_footnote_definition(input, parser->first_nonspace))) {
12221223
cmark_chunk c = cmark_chunk_dup(input, parser->first_nonspace + 2, matched - 2);
12231224
cmark_chunk_to_cstr(parser->mem, &c);

src/node.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ struct cmark_node {
105105
cmark_link link;
106106
cmark_custom custom;
107107
int html_block_type;
108+
int cell_index; // For keeping track of TABLE_CELL table alignments
108109
void *opaque;
109110
} as;
110111
};

0 commit comments

Comments
 (0)