rdiscount has an Out-of-bounds Read
Published: April 06, 2026
SECURITY IDENTIFIERS
- CVE: CVE-2026-35201 (NVD)
- GHSA: GHSA-6r34-94wq-jhrc
- Vendor Advisory: https://github.com/davidfstr/rdiscount/security/advisories/GHSA-6r34-94wq-jhrc
GEM
SEVERITY
CVSS v3.x: 5.9 (Medium)
UNAFFECTED VERSIONS
< 1.3.1.1
PATCHED VERSIONS
>= 2.2.7.4
DESCRIPTION
Summary
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.
Details
In both public entry points:
ext/rdiscount.c:97ext/rdiscount.c:136
RSTRING_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:174
Document
* 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:205
struct string_stream {
const
char *data;
int size;
};
The read loop stops only when size == 0:
ext/mkdio.c:161
int __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_content
Impact
This is an out-of-bounds read with the main issue being reliable denial-of-service. Impacted is limited to deployments parses attacker-controlled Markdown and permits multi-GB inputs.
Fix
just add a checked length guard before the mkd_string()
call in both public entry points:
ext/rdiscount.c:97ext/rdiscount.c:136ex:
VALUE text = rb_funcall(self, rb_intern(\"text\"), 0);
long text_len = RSTRING_LEN(text);
VALUE buf = rb_str_buf_new(1024);
Check_Type(text, T_STRING);
if (text_len > INT_MAX) {
rb_raise(rb_eArgError, \"markdown input too large\");
}
MMIOT *doc = mkd_string(RSTRING_PTR(text), (int)text_len, flags);
The same guard should be applied in rb_rdiscount_toc_content()
before its mkd_string() call.
RELATED
- https://nvd.nist.gov/vuln/detail/CVE-2026-35201
- https://github.com/davidfstr/rdiscount/security/advisories/GHSA-6r34-94wq-jhrc
- http://github.com/davidfstr/rdiscount/releases/tag/2.2.7.4
- https://github.com/davidfstr/rdiscount/commit/b1a16445e92e0d12c07594dedcdc56f80b317761
- https://github.com/advisories/GHSA-6r34-94wq-jhrc
