# Type confusion in FutureIter_throw() which may potentially lead to an arbitrary code execution

FutureIter_throw() in CPython's _asynciomodule passed unchecked type/val objects to PyErr_Restore(), causing type confusion and potential pointer control via PyErr_Fetch().

- Payout: $500
- Severity: low
- Program: Internet Bug Bounty
- Credited: artem
- Disclosed: 2016-12-03
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/182169
- Library page: https://bugbountylibrary.com/r/h1%3A182169
- Tags: domain:oss impact:dos impact:rce kind:bounty platform:hackerone sev:low tech:python technique:source-review vuln:memory-corruption

**Target:** _asynciomodule.c FutureIter_throw() / PyErr_Restore
**Root cause:** FutureIter_throw() does not validate the types of its 'type' and 'val' arguments before passing them to PyErr_Restore(), which stores them unchecked into tstate->curexc_type/value; later PyErr_Fetch() callers assume a real exception object.
**Steps:**
1. Call `future_iter.throw(obj, obj)` with non-exception objects (e.g. custom class instances) instead of exception type/value
2. PyErr_Restore stores attacker objects into tstate->curexc_type and curexc_value without type checks
3. Trigger PyErr_Fetch() via an except handler; the fetched value is treated as an exception object
4. POC 1: causes segfault when repr/str of the confused object is invoked
5. POC 2: craft object whose ob_type->tp_repr points to attacker-controlled address; PyObject_Repr dereferences it at line 485, giving potential instruction pointer control
**How found:** Code review of _asynciomodule.c: noticed FutureIter_throw() passes parsed args straight to PyErr_Restore() with no type validation, then traced PyErr_Restore into errors.c and observed it stores objects as-is into tstate->curexc_type/value, which PyErr_Fetch() consumers assume are real exceptions.
**Exploitation:** Attacker supplies crafted Python objects as the exception type/value to FutureIter_throw(); the confused curexc_value is later dereferenced as an exception, and in POC 2 the object's ob_type->tp_repr field (0x50607020304050c8 in the crash) is attacker-controlled, potentially redirecting the indirect call at PyObject_Repr line 485 to achieve code execution.
**Escalation:** From a simple crash (POC 1) to potential arbitrary code execution by controlling the tp_repr function pointer dereferenced in PyObject_Repr (POC 2).
**Insight:** When auditing C extension argument parsing, follow every PyArg_ParseTuple result into APIs like PyErr_Restore/PyErr_Fetch that assume specific object types; unchecked 'O' format args flowing into exception state create type confusion exploitable via function-pointer fields like tp_repr.
