Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions gems/rdiscount/CVE-2026-35201.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
gem: rdiscount
cve: 2026-35201
ghsa: 6r34-94wq-jhrc
url: https://github.com/davidfstr/rdiscount/security/advisories/GHSA-6r34-94wq-jhrc
title: rdiscount has an Out-of-bounds Read
date: 2026-04-06
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:97`
- `ext/rdiscount.c:136`

`RSTRING_LEN(text)` is passed directly into `mkd_string()`:

```c
MMIOT *doc = mkd_string(RSTRING_PTR(text),
RSTRING_LEN(text), flags);
```

`mkd_string()` accepts `int len`:

- `ext/mkdio.c:174`

```c
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`

```c
struct string_stream {
const
char *data;
int size;
};
```

The read loop stops only when `size == 0`:

- `ext/mkdio.c:161`

```c
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_html`
- `RDiscount.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:97`
- `ext/rdiscount.c:136`
ex:

```c
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.
cvss_v3: 5.9
unaffected_versions:
- "< 1.3.1.1"
patched_versions:
- ">= 2.2.7.4"
related:
url:
- 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
Loading