CPython UaF during index callbacks
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 77.2k
- Fork
- 35.9k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
Bug report
Bug description:
The _PyNumber_Index, PyFloat_AsDouble and PyNumber_Long functions use a borrowed reference for error formatting after calling Python slots (index/float), but the slot's frame cleanup can free the object before the error is formatted.
A use-after-free vulnerability exists in CPython's type conversion functions (_PyNumber_Index, PyFloat_AsDouble and PyNumber_Long) where a borrowed reference is used for error message formatting after a Python callback that can free the referenced object.
For example, the vulnerable code pattern could be observed in _PyNumber_Index implementation (Objects/abstract.c):
PyObject *
_PyNumber_Index(PyObject *item) // item is a BORROWED reference
{
if (item == NULL) {
return null_error();
}
if (PyLong_Check(item)) {
return Py_NewRef(item);
}
if (!_PyIndex_Check(item)) {
PyErr_Format(PyExc_TypeError,
"'%.200s' object cannot be interpreted "
"as an integer", Py_TYPE(item)->tp_name);
return NULL;
}
// === PYTHON CODE EXECUTES HERE ===
PyObject *result = Py_TYPE(item)->tp_as_number->nb_index(item);
// === item MAY NOW BE FREED ===
if (!result || PyLong_CheckExact(result)) {
return result;
}
if (!PyLong_Check(result)) {
PyErr_Format(PyExc_TypeError,
"%T.__index__() must return an int, not %T",
item, result); // <== UAF: item may be freed
Py_DECREF(result);
return NULL;
}
// ... similar pattern continues
}
Similar patterns can be found in the PyFloat_AsDouble and PyNumber_Long functions.
The Python array.fromlist() method provides an ideal attack vector because it:
Iterates over a list using PyList_GET_ITEM() (returns borrowed reference)
Passes the borrowed reference directly to type-specific setitem functions
These functions call _PyNumber_Index() or PyFloat_AsDouble() for conversion.
Running with a python interpreter compiled with ASAN (./configure --with-address-sanitizer):
import array
class Evil:
def __init__(self, lst):
self.lst = lst
def __index__(self):
self.lst.clear()
return "not an int"
lst = []
e = Evil(lst)
lst.append(e)
del e
arr = array.array('I')
arr.fromlist(lst)
You'll get the following:
==73525==ERROR: AddressSanitizer: heap-use-after-free on address 0x6130000300b8 at pc 0x0001013458a4 bp 0x00016ee396f0 sp 0x00016ee396e8
READ of size 8 at 0x6130000300b8 thread T0
#0 0x0001013458a0 in unicode_from_format unicodeobject.c:3075
#1 0x0001013436a8 in PyUnicode_FromFormatV unicodeobject.c:3109
#2 0x00010154d5c8 in PyErr_Format errors.c:1243
#3 0x0001010fe4e8 in _PyNumber_Index abstract.c:1433
#4 0x000109338334 in II_setitem arraymodule.c:411
#5 0x0001093427d4 in array_array_fromlist arraymodule.c.h:495
#6 0x00010114aeb0 in _PyObject_VectorcallTstate pycore_call.h:136
#7 0x0001014828ac in _Py_VectorCallInstrumentation_StackRefSteal ceval.c:1094
#8 0x0001014b4718 in _PyEval_EvalFrameDefault generated_cases.c.h:1785
#9 0x00010148180c in _PyEval_Vector ceval.c:2516
#10 0x00010148105c in PyEval_EvalCode ceval.c:1005
#11 0x000101646118 in run_mod pythonrun.c:1469
#12 0x00010163e6a0 in _PyRun_SimpleFileObject pythonrun.c:518
#13 0x00010163da30 in _PyRun_AnyFileObject pythonrun.c:81
#14 0x0001016ac948 in pymain_run_file main.c:429
#15 0x0001016aabcc in Py_RunMain main.c:772
#16 0x0001016ab9fc in pymain_main main.c:802
#17 0x0001016abba8 in Py_BytesMain main.c:826
#18 0x00019e4edd50 (<unknown module>)
CPython versions tested on:
3.15
Operating systems tested on:
macOS
Linked PRs
- gh-144138
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia in Objects/abstract.c, in corrispondenza di _PyNumber_Index, PyFloat_AsDouble e PyNumber_Long, poi esamina il percorso delle chiamate di arraymodule.c e riproduci il problema con l’esempio ASAN. Confronta la gestione correlata e i test nell’albero corrente; il lavoro è completato quando il use-after-free segnalato è risolto in tutte le funzioni di conversione nominate e il riproduttore ASAN non fallisce più.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- c, python
- Ambito
- security
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Ferma
- Chiarezza
- Specificata chiaramente
- Idoneità per principianti
- 25/100