# Segfault and/or potential unwanted (byte)code execution with "break" and "||=" inside a loop

Malformed Ruby with `||=` on a constant inside a while/break loop miscompiles to an out-of-bounds OP_JMP, crashing mruby and potentially executing spurious bytecode.

- Payout: $10,000
- Severity: high
- Program: shopify-scripts
- Credited: dkasak
- Disclosed: 2016-12-16
- Write-up quality: 9/10
- Original: https://hackerone.com/reports/183356
- Library page: https://bugbountylibrary.com/r/h1%3A183356
- Tags: domain:oss impact:dos impact:rce kind:bounty platform:hackerone sev:high tech:ruby technique:regression technique:source-review vuln:dos vuln:memory-corruption

**Target:** mruby compiler (codegen.c) / mruby VM
**Root cause:** Codegen for `||=` on a constant pushes a LOOP_RESCUE context that is never popped, so the enclosing while loop's loop_pop/dispatch_linked patches jump offsets against the wrong loop context, producing an OP_JMP past the end of the iseq.
**Steps:**
1. Write `A ||= break while break` to crash.rb
2. Run `mruby crash.rb` or `sandbox crash.rb`
3. Observe segfault; disassemble to see OP_JMP at 010 jumping beyond last instruction (018)
4. Add more `break` tokens to grow the invalid jump offset up to 32767
5. Apply patch adding `loop_pop(s, val)` after the NODE_OP_ASGN rescue codegen to fix
**How found:** Noticed that `A ||= break while break` crashes but lowercase `a ||= break while break` and `A &&= break while break` do not; the constant/`||` distinction pointed to the special-case branch in codegen.c line 1724 that creates a LOOP_RESCUE context; debugger inspection of `s->loop` in loop_pop showed two nested loops instead of one.
**Exploitation:** Attacker submits a crafted Ruby script to the sandbox; the miscompiled out-of-bounds OP_JMP reads memory past the iseq, which may contain valid opcodes or an opcode index beyond optable, causing mrb_vm_exec to jump through an out-of-table function pointer — limited unwanted code execution or crash of the parent MRI VM.
**Escalation:** Inserting additional `break` keywords increases the invalid jump argument arbitrarily (up to 0x7fff), widening the reach past the iseq into adjacent memory.
**Insight:** When a compiler special-cases an operator (here `||=` on constants) by emitting an exception-handling loop, verify that the synthetic loop context is popped before the enclosing construct's jump patching runs — differential testing of near-identical programs (const vs local, `||` vs `&&`) localizes such codegen bugs fast.
