# Read after free in mrb_vm_exec with OP_ARYCAT reading R(B)

Crafted mruby source triggers heap use-after-free in mrb_vm_exec OP_ARYCAT when reading R(B) after realloc, crashing the interpreter and mruby-engine sandbox at raised limits.

- Payout: $1,000
- Severity: high
- Program: shopify-scripts
- Credited: haquaman
- Disclosed: 2016-12-18
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/184715
- Library page: https://bugbountylibrary.com/r/h1%3A184715
- Tags: domain:oss impact:dos impact:integrity kind:bounty platform:hackerone sev:high tech:ruby technique:fuzzing technique:source-review vuln:dos vuln:memory-corruption

**Target:** mruby VM interpreter (OP_ARYCAT) / mruby-engine sandbox
**Root cause:** In OP_ARYCAT, mrb_ary_splat(mrb, regs[GETARG_B(i)]) can trigger a realloc of the register stack, invalidating the regs pointer; the subsequent mrb_ary_concat reads R(A)/R(B) from freed memory.
**Steps:**
1. Build mruby 1.2.0 with ASAN
2. Save the crafted class Klazz snippet with `f@thing.f@thing.name *nil` to crash.rb
3. Run `./dev/bin/mruby crash.rb`
4. Observe ASAN heap-use-after-free at vm.c:2137 in mrb_vm_exec OP_ARYCAT
5. For mruby-engine: raise REASONABLE_MEMORY_QUOTA to 22MB and REASONABLE_INSTRUCTION_QUOTA to 525000, then run sandbox_eval on the payload
**How found:** Researcher fuzzed/experimented with unusual mruby syntax (instance-variable-like `f@thing` and splat `*nil` in method args) and ran under ASAN, observing a heap-use-after-free in OP_ARYCAT; then tested whether mruby-engine's quotas masked it by raising limits.
**Exploitation:** Attacker submits crafted mruby source to the mruby-engine sandbox; with default quotas it is blocked, but if memory/instruction limits are raised the OP_ARYCAT path reallocs the register stack and dereferences freed memory, causing a segfault in the host Ruby process (DoS / potential memory corruption).
**Escalation:** Impact scales from interpreter crash to host-process crash of mruby-engine when resource quotas are increased.
**Insight:** VM opcodes that call allocating helpers (like mrb_ary_splat) between fetching and using register operands can invalidate the regs pointer after realloc; look for opcodes reading R(A)/R(B) across an allocation call, and test sandboxes at raised resource quotas since limits can mask memory-safety bugs.
