Skip to content

Avoid stack-overflow and add some defensive conditions#604

Merged
jgm merged 4 commits intomasterfrom
security-fixes
Apr 17, 2026
Merged

Avoid stack-overflow and add some defensive conditions#604
jgm merged 4 commits intomasterfrom
security-fixes

Conversation

@jgm
Copy link
Copy Markdown
Member

@jgm jgm commented Apr 17, 2026

These are some changes suggested by Claude in response to a prompt to provide a security audit. They all seem reasonable and correct to me. Some of them aren't strictly necessary, given certain assumptions about how the rest of the code works, but they are probably a good idea in the spirit of defensive coding.

@nwellnhof comments welcome.

@nwellnhof
Copy link
Copy Markdown
Contributor

Something like the following seems more readable to me:

static bool S_ends_with_blank_line(cmark_node *node) {
  while (!S_last_line_checked(node)) {
    S_set_last_line_checked(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);
}

jgm and others added 4 commits April 17, 2026 12:44
The recursive descent through nested LIST/ITEM nodes could exhaust
the call stack on deeply nested list input. Convert to a loop that
walks node->last_child; the last_line_checked memoization and all
branch semantics are preserved.

Co-Authored-By: Claude <noreply@anthropic.com>
The outer condition only guarantees pos+2 <= input.len, but the
branches indexed data[pos+3] and data[pos+4]. Correctness relied
on the strbuf-backed null terminator and on data[pos+2] == '-'
ruling out the terminator. Make the bounds explicit so the code
does not depend on those invariants.

Co-Authored-By: Claude <noreply@anthropic.com>
When ptr[r] is a trailing backslash, the code read ptr[r+1] (the
strbuf's null terminator). Safe in practice since cmark_ispunct('\0')
is false, but the read depends on the null-termination invariant.
Guard with r + 1 < buf->size to keep the read within the logical
buffer.

Co-Authored-By: Claude <noreply@anthropic.com>
_scan_at patched the byte at ptr[c->len] with '\0' around every
scanner invocation so that the re2c scanners (built with
yyfill:enable = 0) could use it as a stopping sentinel. In the
common case the chunk is backed by a cmark_strbuf whose data[size]
is already '\0', so the write was an idempotent mutation of shared
backing storage. Skip it when the byte is already '\0' to preserve
const-correctness and reentrancy on that path; retain the patch
fallback for chunks that are not already NUL-terminated.

Co-Authored-By: Claude <noreply@anthropic.com>
@jgm jgm force-pushed the security-fixes branch from bc6d834 to 0de9f64 Compare April 17, 2026 10:46
@jgm jgm merged commit 4e47eba into master Apr 17, 2026
29 checks passed
@jgm
Copy link
Copy Markdown
Member Author

jgm commented Apr 17, 2026

Thanks, I agree, and I adopted your suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants