diff --git a/src/blocks.c b/src/blocks.c index 292e563bb..a2f35b42e 100644 --- a/src/blocks.c +++ b/src/blocks.c @@ -224,18 +224,18 @@ static void remove_trailing_blank_lines(cmark_strbuf *ln) { // Check to see if a node ends with a blank line, descending // if needed into lists and sublists. static bool S_ends_with_blank_line(cmark_node *node) { - if (S_last_line_checked(node)) { - return(S_last_line_blank(node)); - } else if ((S_type(node) == CMARK_NODE_LIST || - S_type(node) == CMARK_NODE_ITEM) && node->last_child) { + while (!S_last_line_checked(node)) { S_set_last_line_checked(node); - return(S_ends_with_blank_line(node->last_child)); - } else { - S_set_last_line_checked(node); - return (S_last_line_blank(node)); + if (S_type(node) != CMARK_NODE_LIST && S_type(node) != CMARK_NODE_ITEM) + break; + if (!node->last_child) + break; + node = node->last_child; } + return S_last_line_blank(node); } + // returns true if content remains after link defs are resolved. static bool resolve_reference_link_definitions(cmark_parser *parser) { bufsize_t pos; diff --git a/src/buffer.c b/src/buffer.c index f3159948c..80d09c24f 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -199,7 +199,8 @@ void cmark_strbuf_unescape(cmark_strbuf *buf) { bufsize_t r, w; for (r = 0, w = 0; r < buf->size; ++r) { - if (buf->ptr[r] == '\\' && cmark_ispunct(buf->ptr[r + 1])) + if (buf->ptr[r] == '\\' && r + 1 < buf->size && + cmark_ispunct(buf->ptr[r + 1])) r++; buf->ptr[w++] = buf->ptr[r]; diff --git a/src/inlines.c b/src/inlines.c index ab82ca74e..9143bbb5f 100644 --- a/src/inlines.c +++ b/src/inlines.c @@ -934,9 +934,11 @@ static cmark_node *handle_pointy_brace(subject *subj, int options) { if (c == '!' && (subj->flags & FLAG_SKIP_HTML_COMMENT) == 0) { c = subj->input.data[subj->pos+1]; if (c == '-' && subj->input.data[subj->pos+2] == '-') { - if (subj->input.data[subj->pos+3] == '>') { + if (subj->pos + 3 < subj->input.len && + subj->input.data[subj->pos+3] == '>') { matchlen = 4; - } else if (subj->input.data[subj->pos+3] == '-' && + } else if (subj->pos + 4 < subj->input.len && + subj->input.data[subj->pos+3] == '-' && subj->input.data[subj->pos+4] == '>') { matchlen = 5; } else { diff --git a/src/scanners.c b/src/scanners.c index 7338c75b7..57d86f4dd 100644 --- a/src/scanners.c +++ b/src/scanners.c @@ -13,9 +13,20 @@ bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c, } else { unsigned char lim = ptr[c->len]; - ptr[c->len] = '\0'; - res = scanner(ptr + offset); - ptr[c->len] = lim; + // The re2c scanners are built with yyfill:enable = 0, so they + // require a NUL sentinel at ptr[c->len]. In the common case the + // chunk is backed by a cmark_strbuf which is already NUL-terminated + // at that position, so we avoid the transient write entirely (it + // would otherwise mutate shared backing storage and break both + // const-correctness and reentrancy). Only when the sentinel is + // missing do we fall back to patching the byte around the call. + if (lim == '\0') { + res = scanner(ptr + offset); + } else { + ptr[c->len] = '\0'; + res = scanner(ptr + offset); + ptr[c->len] = lim; + } } return res; diff --git a/src/scanners.re b/src/scanners.re index 16238b774..5070d4578 100644 --- a/src/scanners.re +++ b/src/scanners.re @@ -12,9 +12,20 @@ bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c, } else { unsigned char lim = ptr[c->len]; - ptr[c->len] = '\0'; - res = scanner(ptr + offset); - ptr[c->len] = lim; + // The re2c scanners are built with yyfill:enable = 0, so they + // require a NUL sentinel at ptr[c->len]. In the common case the + // chunk is backed by a cmark_strbuf which is already NUL-terminated + // at that position, so we avoid the transient write entirely (it + // would otherwise mutate shared backing storage and break both + // const-correctness and reentrancy). Only when the sentinel is + // missing do we fall back to patching the byte around the call. + if (lim == '\0') { + res = scanner(ptr + offset); + } else { + ptr[c->len] = '\0'; + res = scanner(ptr + offset); + ptr[c->len] = lim; + } } return res;