# Recursion causing uninitialized memory reads leading to a segfault

Recursive ensure blocks in mruby sandbox cause use-after-pop of callinfo, leading to uninitialized memory reads and segfault at recursion depth 24.

- Payout: $2,000
- Severity: low
- Program: shopify-scripts
- Credited: dgaletic
- Disclosed: 2017-02-28
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/201897
- Library page: https://bugbountylibrary.com/r/h1%3A201897
- Tags: domain:oss impact:dos kind:bounty platform:hackerone sev:low tech:ruby technique:source-review vuln:dos vuln:memory-corruption

**Target:** mruby VM (vm.c) sandbox interpreter
**Root cause:** In vm.c, cipop(mrb) frees/pops the callinfo before acc = ci->acc and mrb->c->stack = ci->stackent read from the now-stale ci pointer, causing uninitialized reads.
**Steps:**
1. Run `def fn(n); return; ensure; if n == 0; else fn(n-1); end; end; fn(24)` in the sandbox
2. Observe segfault without stack overflow
3. Run under memory sanitizer: uninitialized reads appear at n>=15
4. Trace to vm.c:1702 reading ci->acc after cipop(mrb)
5. Apply patch: reassign ci = mrb->c->ci before reading acc/stackent, then cipop
**How found:** Researcher noticed segfault occurred without a stack overflow, then used a memory sanitizer to pinpoint uninitialized reads and traced the read to use of ci after cipop in vm.c.
**Exploitation:** Attacker submits crafted Ruby script with recursive ensure blocks to crash the sandboxed interpreter process, causing denial of service.
**Insight:** When a crash happens without the expected precondition (here no stack overflow), run a memory sanitizer to find the true faulting read; look for use-after-free/pop patterns where a pointer is dereferenced after its backing structure is released.
