# StringIO strio_getline() can divulge arbitrary memory

Integer overflow in StringIO#gets limit lets a 32-bit Ruby process read out-of-bounds memory, potentially leaking secrets like private keys.

- Payout: hidden
- Severity: medium
- Program: Ruby
- Found by: guido
- Disclosed: 2016-06-21
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/144482
- Library page: https://bugbountylibrary.com/r/h1%3A144482
- Tags: area:oss domain:oss impact:data-leak kind:bounty platform:hackerone sev:medium tech:ruby technique:blind technique:source-review vuln:info-disclosure vuln:memory-corruption

**Target:** ext/stringio/stringio.c strio_getline()
**Root cause:** `s + limit` pointer arithmetic overflows when limit is huge and the buffer sits high in the 32-bit address space, so the computed end pointer `e` points to unrelated memory; subsequent memchr/bm_search scan past the real buffer.
**Steps:**
1. `require "stringio"; s = StringIO.new; s.puts("abc"); s.rewind()`
2. `x = s.gets('xxx', 0x7FFFFFFFFFFFFFF0)`
3. `puts(s.pos)` reveals relative offset where the searched string was found in memory
4. Vary `limit` and the search string to walk memory and deduce contents
**How found:** Code review of strio_getline() noticing `s + limit < e` can overflow; traced each branch (nil/empty/1-byte/multi-byte str) to strio_substr and memchr/bm_search to see which paths read OOB, then reasoned about using `pos` as an oracle.
**Exploitation:** Attacker who controls the `limit` argument and search string to `StringIO#gets` repeatedly invokes it; the overflowed `e` makes memchr/bm_search scan arbitrary virtual memory, and `pos` reveals where the searched string was located, allowing byte-by-byte reconstruction of secrets such as base64 PGP private keys.
**Insight:** When auditing length/limit parameters, check pointer+length arithmetic for wraparound on 32-bit; a returned position/offset field can turn a blind OOB scan into a memory-disclosure oracle.

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

**Ruby trigger for StringIO getline overflow**

```
require "stringio"
s = StringIO.new
s.puts("abc")
s.rewind()
x = s.gets('xxx', 0x7FFFFFFFFFFFFFF0)
puts(s.pos)
```

