11use crate :: arena_tree:: Node ;
22use crate :: nodes;
3- use crate :: nodes:: { Ast , AstNode , NodeValue , TableAlignment } ;
3+ use crate :: nodes:: { Ast , AstNode , NodeTable , NodeValue , TableAlignment } ;
44use crate :: parser:: Parser ;
55use crate :: scanners;
66use crate :: strings:: trim;
@@ -9,14 +9,17 @@ use std::cmp::min;
99
1010use super :: inlines:: count_newlines;
1111
12+ // Limit to prevent a malicious input from causing a denial of service.
13+ const MAX_AUTOCOMPLETED_CELLS : usize = 500_000 ;
14+
1215pub fn try_opening_block < ' a > (
1316 parser : & mut Parser < ' a , ' _ , ' _ > ,
1417 container : & ' a AstNode < ' a > ,
1518 line : & [ u8 ] ,
1619) -> Option < ( & ' a AstNode < ' a > , bool , bool ) > {
1720 let aligns = match container. data . borrow ( ) . value {
1821 NodeValue :: Paragraph => None ,
19- NodeValue :: Table ( ref aligns ) => Some ( aligns . clone ( ) ) ,
22+ NodeValue :: Table ( NodeTable { ref alignments , .. } ) => Some ( alignments . clone ( ) ) ,
2023 _ => return None ,
2124 } ;
2225
@@ -74,7 +77,15 @@ fn try_opening_header<'a>(
7477 }
7578
7679 let start = container. data . borrow ( ) . sourcepos . start ;
77- let child = Ast :: new ( NodeValue :: Table ( alignments) , start) ;
80+ let child = Ast :: new (
81+ NodeValue :: Table ( NodeTable {
82+ alignments,
83+ num_columns : header_row. cells . len ( ) ,
84+ num_rows : 0 ,
85+ num_nonempty_cells : 0 ,
86+ } ) ,
87+ start,
88+ ) ;
7889 let table = parser. arena . alloc ( Node :: new ( RefCell :: new ( child) ) ) ;
7990 container. append ( table) ;
8091
@@ -88,7 +99,10 @@ fn try_opening_header<'a>(
8899 ) ;
89100 }
90101
91- for cell in header_row. cells {
102+ let mut i = 0 ;
103+
104+ while i < header_row. cells . len ( ) {
105+ let cell = & header_row. cells [ i] ;
92106 let ast_cell = parser. add_child (
93107 header,
94108 NodeValue :: TableCell ,
@@ -100,8 +114,12 @@ fn try_opening_header<'a>(
100114 start. column_add ( ( cell. end_offset - header_row. paragraph_offset ) as isize ) ;
101115 ast. internal_offset = cell. internal_offset ;
102116 ast. content = cell. content . clone ( ) ;
117+
118+ i += 1 ;
103119 }
104120
121+ incr_table_row_count ( container, i) ;
122+
105123 let offset = line. len ( ) - 1 - parser. offset ;
106124 parser. advance_offset ( line, offset, false ) ;
107125
@@ -117,6 +135,11 @@ fn try_opening_row<'a>(
117135 if parser. blank {
118136 return None ;
119137 }
138+
139+ if get_num_autocompleted_cells ( container) > MAX_AUTOCOMPLETED_CELLS {
140+ return None ;
141+ }
142+
120143 let sourcepos = container. data . borrow ( ) . sourcepos ;
121144 let this_row = match row ( & line[ parser. first_nonspace ..] ) {
122145 Some ( this_row) => this_row,
@@ -148,9 +171,12 @@ fn try_opening_row<'a>(
148171 cell_ast. content = cell. content . clone ( ) ;
149172
150173 last_column = cell_ast. sourcepos . end . column ;
174+
151175 i += 1 ;
152176 }
153177
178+ incr_table_row_count ( container, i) ;
179+
154180 while i < alignments. len ( ) {
155181 parser. add_child ( new_row, NodeValue :: TableCell , last_column) ;
156182 i += 1 ;
@@ -305,6 +331,40 @@ fn unescape_pipes(string: &[u8]) -> Vec<u8> {
305331 v
306332}
307333
334+ // Increment the number of rows in the table. Also update n_nonempty_cells,
335+ // which keeps track of the number of cells which were parsed from the
336+ // input file. (If one of the rows is too short, then the trailing cells
337+ // are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.)
338+ // The purpose of this is to prevent a malicious input from generating a very
339+ // large number of autocompleted cells, which could cause a denial of service
340+ // vulnerability.
341+ fn incr_table_row_count < ' a > ( container : & ' a AstNode < ' a > , i : usize ) -> bool {
342+ return match container. data . borrow_mut ( ) . value {
343+ NodeValue :: Table ( ref mut node_table) => {
344+ node_table. num_rows += 1 ;
345+ node_table. num_nonempty_cells += i;
346+ true
347+ }
348+ _ => false ,
349+ } ;
350+ }
351+
352+ // Calculate the number of autocompleted cells.
353+ fn get_num_autocompleted_cells < ' a > ( container : & ' a AstNode < ' a > ) -> usize {
354+ return match container. data . borrow ( ) . value {
355+ NodeValue :: Table ( ref node_table) => {
356+ let num_cells = node_table. num_columns * node_table. num_rows ;
357+
358+ if num_cells < node_table. num_nonempty_cells {
359+ 0
360+ } else {
361+ ( node_table. num_columns * node_table. num_rows ) - node_table. num_nonempty_cells
362+ }
363+ }
364+ _ => 0 ,
365+ } ;
366+ }
367+
308368pub fn matches ( line : & [ u8 ] ) -> bool {
309369 row ( line) . is_some ( )
310370}
0 commit comments