GHSA-6R34-94WQ-JHRC

Vulnerability from github – Published: 2026-04-06 17:53 – Updated: 2026-05-13 16:20
VLAI
Summary
rdiscount has an Out-of-bounds Read
Details

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():

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_html
  • RDiscount.new(input).toc_content

PoC

Crash 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
  • Ruby terminates with [BUG] Segmentation fault
  • top control frame: CFUNC :to_html

same result with 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:
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.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "rdiscount"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.3.1.1"
            },
            {
              "fixed": "2.2.7.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-35201"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-125"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-06T17:53:59Z",
    "nvd_published_at": "2026-04-06T20:16:27Z",
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nA 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\n\n### Details\n\nIn both public entry points:\n\n- `ext/rdiscount.c:97`\n- `ext/rdiscount.c:136`\n\n`RSTRING_LEN(text)` is passed directly into `mkd_string()`:\n\n```c\nMMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags);\n```\n\n`mkd_string()` accepts `int len`:\n\n- `ext/mkdio.c:174`\n\n```c\nDocument * mkd_string(const char *buf, int len, mkd_flag_t flags)\n{\n    struct string_stream about;\n\n    about.data = buf;\n    about.size = len;\n\n    return populate((getc_func)__mkd_io_strget, \u0026about, flags \u0026 INPUT_MASK);\n}\n```\n\nThe parser stores the remaining input length in a signed `int`:\n\n- `ext/markdown.h:205`\n\n```c\nstruct string_stream {\n    const char *data;\n    int   size;\n};\n```\n\nThe read loop stops only when `size == 0`:\n\n- `ext/mkdio.c:161`\n\n```c\nint __mkd_io_strget(struct string_stream *in)\n{\n    if ( !in-\u003esize ) return EOF;\n\n    --(in-\u003esize);\n\n    return *(in-\u003edata)++;\n}\n```\n\nIf 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.\n\nAffected APIs:\n\n- `RDiscount.new(input).to_html`\n- `RDiscount.new(input).toc_content`\n\n### PoC\n\nCrash via `to_html`:\n\n```sh\nRUBYLIB=lib:ext ruby -e \u0027require \"rdiscount\"; n=2_200_000_000; s = \"a\" * n; warn \"built=#{s.bytesize}\"; RDiscount.new(s).to_html\"\u0027\n```\nresult:\n\n- `built=2200000000`\n- Ruby terminates with `[BUG] Segmentation fault`\n- top control frame: `CFUNC :to_html`\n\nsame result with `toc_content`\n\n### Impact\n\nThis 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.\n\n### Fix\n\njust add a checked length guard before the `mkd_string()` call in both public entry points:\n\n- `ext/rdiscount.c:97`\n- `ext/rdiscount.c:136`\nex: \n```c\nVALUE text = rb_funcall(self, rb_intern(\"text\"), 0);\nlong text_len = RSTRING_LEN(text);\nVALUE buf = rb_str_buf_new(1024);\nCheck_Type(text, T_STRING);\n\nif (text_len \u003e INT_MAX) {\n    rb_raise(rb_eArgError, \"markdown input too large\");\n}\n\nMMIOT *doc = mkd_string(RSTRING_PTR(text), (int)text_len, flags);\n```\n\nThe same guard should be applied in `rb_rdiscount_toc_content()` before its `mkd_string()` call.",
  "id": "GHSA-6r34-94wq-jhrc",
  "modified": "2026-05-13T16:20:38Z",
  "published": "2026-04-06T17:53:59Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/davidfstr/rdiscount/security/advisories/GHSA-6r34-94wq-jhrc"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35201"
    },
    {
      "type": "WEB",
      "url": "https://github.com/davidfstr/rdiscount/commit/b1a16445e92e0d12c07594dedcdc56f80b317761"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/davidfstr/rdiscount"
    },
    {
      "type": "WEB",
      "url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/rdiscount/CVE-2026-35201.yml"
    },
    {
      "type": "WEB",
      "url": "http://github.com/davidfstr/rdiscount/releases/tag/2.2.7.4"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "rdiscount has an Out-of-bounds Read"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…