# Use after free vulnerability in mruby Array#to_h causing DOS possible RCE

mruby Array#to_h fails to recheck array length after to_ary callback shrinks it, causing OOB read and potential RCE.

- Payout: hidden
- Severity: critical
- Program: Shopify
- Found by: isra17
- Disclosed: 2016-12-16
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/181321
- Library page: https://bugbountylibrary.com/r/h1%3A181321
- Tags: area:oss domain:oss impact:dos impact:rce kind:bounty platform:hackerone sev:critical tech:ruby technique:chained technique:source-review vuln:dos vuln:memory-corruption vuln:rce

**Target:** mruby interpreter Array#to_h (mruby-array-ext array.c)
**Root cause:** mrb_ary_to_h iterates the array and calls to_ary on non-array elements, but does not revalidate the array length/pointer after the callback; if to_ary clears the array, RARRAY_PTR(ary)[i] dereferences freed/null data.
**Steps:**
1. Define class A with to_ary that clears a global array $a and returns nil.
2. Set $a = [A.new].
3. Call $a.to_h.
4. mrb_check_array_type invokes A#to_ary, which clears $a setting ptr=0 and len=0.
5. to_ary returns nil, so mrb_raisef evaluates RARRAY_PTR(ary)[i] on the now-empty array, dereferencing 0[i] and crashing.
**How found:** Code review of mruby-array-ext array.c: noticed to_ary callback can mutate the array mid-iteration and length is not rechecked before RARRAY_PTR(ary)[i] in the exception path.
**Exploitation:** Second PoC: build array of 7 elements with A.new last so OOB index lands 3 pointers past; in to_ary clear $bb and $a, push back <=4 elements to avoid capacity growth, then push crafted strings so their data sits after array data; mrb_obj_classname reads attacker-controlled bytes as a class pointer, enabling crafted mrb_value and further memory corruption/RCE.
**Escalation:** Chains with report #181319 for memory disclosure to build a full RCE PoC; crafted mrb_value via controlled string data yields arbitrary write.
**Insight:** When a C loop calls back into user Ruby (to_ary, to_s, etc.), the callback can mutate the container; check for reentrancy and revalidate length/pointer after every callback, especially in error/exception paths that re-index.

## Proof of concept (copied exactly from the original report)

**Ruby PoC crashing mruby via Array#to_h**

```
class A
  def to_ary
    $a.clear
    nil
  end
end
$a = [A.new]
$a.to_h
```

