# Type confusion in mrb_exc_set leading to memory corruption

mruby's E_*_ERROR macros resolve exception classes at runtime, so redefining NotImplementedError to String makes mrb_raise set a non-exception, corrupting memory and enabling code execution.

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

**Target:** mruby interpreter / mrb_exc_set and E_*_ERROR macros
**Root cause:** E_*_ERROR macros call mrb_class_get at runtime rather than being constants, so user code can rebind exception class names to arbitrary classes; mrb_exc_set does not verify the object is an exception before treating it as one.
**Steps:**
1. Note that `#define E_NOTIMP_ERROR (mrb_class_get(mrb, "NotImplementedError"))` resolves the class dynamically.
2. In mruby-engine, run `NotImplementedError = String`.
3. Trigger a path that calls mrb_raise with E_NOTIMP_ERROR, e.g. `Module.constants`.
4. mrb_exc_set receives a String instead of an exception, causing a native crash / memory corruption.
**How found:** Observed that E_*_ERROR macros are runtime lookups rather than constants (similar to prior report #181871), hypothesized exception types could be redefined, and confirmed with a minimal snippet causing a native crash.
**Exploitation:** Attacker-controlled mruby code rebinds an exception constant to a non-exception class, then invokes a builtin that raises that error, producing type confusion in mrb_exc_set and memory corruption potentially leading to arbitrary code execution.
**Escalation:** Generalizes beyond one macro: any mrb_raise call using an E_*_ERROR macro is reachable, widening the attack surface.
**Insight:** When auditing embedded interpreters, check whether 'constant' error/exception identifiers are actually runtime lookups; if user code can rebind them, every raise site becomes a type-confusion sink.

