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`.
1518static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED ;
1619
@@ -31,6 +34,8 @@ typedef struct {
3134typedef struct {
3235 uint16_t n_columns ;
3336 uint8_t * alignments ;
37+ int n_rows ;
38+ int n_nonempty_cells ;
3439} node_table ;
3540
3641typedef 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+
86118static 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+
101150static 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 ;
0 commit comments