
Standalone proof of concept for CVE-2026-86547, a NULL pointer dereference in mrubyc op_enter() through 4.0.0.
OP_ENTER NULL Pointer DereferenceStandalone proof of concept for CVE-2026-86547, a NULL pointer dereference in mrubyc's op_enter() handler through release 4.0.0.
mrubyc is a lightweight Ruby implementation for embedded systems. Its bytecode VM keeps the current call-frame pointer in mrbc_vm.callinfo_tail. At top level, mrbc_vm_begin() initializes this pointer to NULL because no method call frame exists yet.
In the vulnerable op_enter() implementation in release4.0.0, the handler reads callinfo->reg_offset without first checking whether vm->callinfo_tail is NULL:
mrbc_callinfo *callinfo = vm->callinfo_tail;
int reg_offset = callinfo->reg_offset;
A crafted .mrb bytecode program can place OP_ENTER at top level, causing the interpreter to reach this code with a NULL callinfo_tail and crash through a NULL pointer dereference.
CVE: CVE-2026-86547
Type: CWE-476 — NULL Pointer Dereference
Impact: Availability / denial of service
Affected: mrubyc through 4.0.0
Severity: Medium, CVSS 6.9 (per the advisory)
The PoC mirrors the relevant mrbc_callinfo and mrbc_vm layouts and reproduces the vulnerable memory access independently of the full mrubyc runtime.
It demonstrates two paths:
callinfo_tail is NULL and callinfo->reg_offset is dereferenced without a guard, producing a segmentation fault.OP_ENTER safely, followed by a control test showing that a valid call frame still works.The harness uses volatile on the vulnerable pointer and __builtin_trap() in its description to make the reproducer suitable for sanitizer-based observation. The actual vulnerable access is the callinfo->reg_offset read.
gcc -O0 -g -o poc poc.c
./poc
Expected result on the vulnerable path is a segmentation fault after:
[VULNERABLE PATH] op_enter without NULL guard
vm->callinfo_tail = NULL (top-level frame)
About to dereference NULL...
gcc -O0 -g -fsanitize=address -fno-omit-frame-pointer -o poc-asan poc.c
./poc-asan
The sanitizer should report a SEGV on an address corresponding to the reg_offset field from a NULL mrbc_callinfo base. With the structure layout used here, offsetof(mrbc_callinfo, reg_offset) is 0x14.
./poc fixed
Expected output includes:
[FIXED PATH] op_enter with NULL guard
[GUARD] top-level OP_ENTER — rejected safely
[control, valid frame] reg_offset = 5
Fixed path: no crash.
The original vulnerable handler is in src/vm.c in mrubyc release4.0.0, around line 1537. The relevant operation is the direct dereference of callinfo->reg_offset after assigning vm->callinfo_tail to callinfo without a NULL check.
The post-fix source referenced in the research is commit 4261cf5e5ae5579e3110dab98a04b91c7d919429.
The standalone harness demonstrates the underlying memory error. In mrubyc itself, the trigger requires a crafted .mrb bytecode file containing an OP_ENTER instruction at top level, outside a method definition. If an application loads and executes untrusted .mrb files, an attacker-controlled bytecode file can therefore crash the interpreter process.
This is a denial-of-service condition. The research does not claim code execution, information disclosure, or memory corruption beyond the NULL dereference.
src/vm.csrc/vm.cOP_SUPER NULL-check issue that motivated the audit of op_enter()The discovery story is documented in: I Read Someone Else's Bug Report, Then Found The Same Missing Check In The Next Function Over.
The key research observation was that op_super() had a missing NULL guard around the same vm->callinfo_tail invariant. Checking the neighboring op_enter() handler showed that the same assumption was present there without the corresponding guard.