GHSA-VWM4-62GF-X745

Vulnerability from github – Published: 2026-06-19 20:47 – Updated: 2026-06-19 20:47
VLAI
Summary
Oj: Use-After-Free in Oj::Parser array_class/hash_class GC Marking
Details

Summary

Oj::Parser in usual mode does not mark array_class and hash_class references during garbage collection. If GC runs after the class is assigned but before a parse, the class object is reclaimed, leaving the parser holding a dangling VALUE. The subsequent parse call dereferences the freed object, producing a segfault.

Version

  • Software: oj gem
  • Affected: all versions with ext/oj/usual.c / ext/oj/parser.c
  • Latest tested: 3.17.1 (confirmed present)

Details

The parser_mark function in ext/oj/parser.c is registered as the GC mark callback for the parser's TypedData. If array_class (stored as d->array_class in the Usual struct) is not passed to rb_gc_mark, the GC does not know it is referenced and may collect it.

When close_array_class (usual.c:405) later calls rb_funcallv on the collected class VALUE, it accesses freed memory, crashing at RIP: 0x7f... / 0x0000000000000000.

Crash output:

array_class finalized
about to parse
[BUG] Segmentation fault at 0x0000000000000000
    close_array_class+0x194  /ext/oj/usual.c:405
    parse+0x17b3             /ext/oj/parser.c:715
    parser_parse+0x10b       /ext/oj/parser.c:1408
RIP: 0x7fd1b46d68b7  RBP: 0x0000000000000000

Reproduce

require 'oj'
p = Oj::Parser.new(:usual,
  array_class: (ac = Class.new { def <<(_x); end }))
ObjectSpace.define_finalizer(ac, proc { warn 'array_class finalized' })
ac = nil
GC.start(full_mark: true, immediate_sweep: true)  # collect the class
p.parse('[1]')  # segfault
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c 3.17.2"
      },
      "package": {
        "ecosystem": "RubyGems",
        "name": "oj"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.17.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54901"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-416"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-19T20:47:25Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\n`Oj::Parser` in usual mode does not mark `array_class` and `hash_class` references during garbage collection. If GC runs after the class is assigned but before a parse, the class object is reclaimed, leaving the parser holding a dangling VALUE. The subsequent `parse` call dereferences the freed object, producing a segfault.\n\n### Version\n\n- **Software**: oj gem\n- **Affected**: all versions with `ext/oj/usual.c` / `ext/oj/parser.c`\n- **Latest tested**: 3.17.1 (confirmed present)\n\n### Details\n\nThe `parser_mark` function in `ext/oj/parser.c` is registered as the GC mark callback for the parser\u0027s `TypedData`. If `array_class` (stored as `d-\u003earray_class` in the `Usual` struct) is not passed to `rb_gc_mark`, the GC does not know it is referenced and may collect it.\n\nWhen `close_array_class` (`usual.c:405`) later calls `rb_funcallv` on the collected class VALUE, it accesses freed memory, crashing at `RIP: 0x7f... / 0x0000000000000000`.\n\nCrash output:\n```\narray_class finalized\nabout to parse\n[BUG] Segmentation fault at 0x0000000000000000\n    close_array_class+0x194  /ext/oj/usual.c:405\n    parse+0x17b3             /ext/oj/parser.c:715\n    parser_parse+0x10b       /ext/oj/parser.c:1408\nRIP: 0x7fd1b46d68b7  RBP: 0x0000000000000000\n```\n\n### Reproduce\n\n```ruby\nrequire \u0027oj\u0027\np = Oj::Parser.new(:usual,\n  array_class: (ac = Class.new { def \u003c\u003c(_x); end }))\nObjectSpace.define_finalizer(ac, proc { warn \u0027array_class finalized\u0027 })\nac = nil\nGC.start(full_mark: true, immediate_sweep: true)  # collect the class\np.parse(\u0027[1]\u0027)  # segfault\n```",
  "id": "GHSA-vwm4-62gf-x745",
  "modified": "2026-06-19T20:47:25Z",
  "published": "2026-06-19T20:47:25Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/ohler55/oj/security/advisories/GHSA-vwm4-62gf-x745"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/ohler55/oj"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Oj: Use-After-Free in Oj::Parser array_class/hash_class GC Marking"
}


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…

Detection rules are retrieved from Rulezet.

Loading…

Loading…