Skip to content

Commit 5bd1108

Browse files
prevent integer overflow in row_from_string (#36)
* added explicit check for UINT16_MAX boundary on row->n_columns * added additional checks for row_from_string NULL returns to prevent NULL dereferences on error cases * added additional check to ensure n_columns between marker and header rows always match prior to any alignment processing * allocate alignment array based on marker rows rather than header rows * prevent memory leak on dangling node when encountering row_from_string error in try_opening_table_row * add explicit integer overflow error marker to not overload offset semantics in row_from_string with other implied error conditions rdar://89864410 Co-authored-by: Bas Alberts <[email protected]>
1 parent 6ddaa39 commit 5bd1108

File tree

1 file changed

+28
-2
lines changed

1 file changed

+28
-2
lines changed

extensions/table.c

+28-2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ static table_row *row_from_string(cmark_syntax_extension *self,
129129
bufsize_t cell_matched = 1, pipe_matched = 1, offset;
130130
int expect_more_cells = 1;
131131
int row_end_offset = 0;
132+
int int_overflow_abort = 0;
132133

133134
row = (table_row *)parser->mem->calloc(1, sizeof(table_row));
134135
row->n_columns = 0;
@@ -161,6 +162,12 @@ static table_row *row_from_string(cmark_syntax_extension *self,
161162
++cell->internal_offset;
162163
}
163164

165+
// make sure we never wrap row->n_columns
166+
// offset will != len and our exit will clean up as intended
167+
if (row->n_columns == UINT16_MAX) {
168+
int_overflow_abort = 1;
169+
break;
170+
}
164171
row->n_columns += 1;
165172
row->cells = cmark_llist_append(parser->mem, row->cells, cell);
166173
}
@@ -194,7 +201,7 @@ static table_row *row_from_string(cmark_syntax_extension *self,
194201
}
195202
}
196203

197-
if (offset != len || row->n_columns == 0) {
204+
if (offset != len || row->n_columns == 0 || int_overflow_abort) {
198205
free_table_row(parser->mem, row);
199206
row = NULL;
200207
}
@@ -241,6 +248,11 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
241248
marker_row = row_from_string(self, parser,
242249
input + cmark_parser_get_first_nonspace(parser),
243250
len - cmark_parser_get_first_nonspace(parser));
251+
// assert may be optimized out, don't rely on it for security boundaries
252+
if (!marker_row) {
253+
return parent_container;
254+
}
255+
244256
assert(marker_row);
245257

246258
cmark_arena_push();
@@ -264,6 +276,12 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
264276
len - cmark_parser_get_first_nonspace(parser));
265277
header_row = row_from_string(self, parser, (unsigned char *)parent_string,
266278
(int)strlen(parent_string));
279+
// row_from_string can return NULL, add additional check to ensure n_columns match
280+
if (!marker_row || !header_row || header_row->n_columns != marker_row->n_columns) {
281+
free_table_row(parser->mem, marker_row);
282+
free_table_row(parser->mem, header_row);
283+
return parent_container;
284+
}
267285
}
268286

269287
if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
@@ -281,8 +299,10 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
281299
parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
282300
set_n_table_columns(parent_container, header_row->n_columns);
283301

302+
// allocate alignments based on marker_row->n_columns
303+
// since we populate the alignments array based on marker_row->cells
284304
uint8_t *alignments =
285-
(uint8_t *)parser->mem->calloc(header_row->n_columns, sizeof(uint8_t));
305+
(uint8_t *)parser->mem->calloc(marker_row->n_columns, sizeof(uint8_t));
286306
cmark_llist *it = marker_row->cells;
287307
for (i = 0; it; it = it->next, ++i) {
288308
node_cell *node = (node_cell *)it->data;
@@ -351,6 +371,12 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
351371
row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser),
352372
len - cmark_parser_get_first_nonspace(parser));
353373

374+
if (!row) {
375+
// clean up the dangling node
376+
cmark_node_free(table_row_block);
377+
return NULL;
378+
}
379+
354380
{
355381
cmark_llist *tmp;
356382
int i, table_columns = get_n_table_columns(parent_container);

0 commit comments

Comments
 (0)