python / python/cpython

Null pointer dereference in `array_repeat` via re-entrant `__index__` during array multiplication

Abierto
#142,883 1 comentario 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

extension-modules type-crash
Lenguaje dominante
Python
Estrellas
77.2k
Forks
35.9k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

[!IMPORTANT]
This issue is not for newcomers.

What happened?

A custom __index__ can swap the MRO of an array.array subclass mid multiplication, so array_repeat calls find_array_state_by_type on a type without the array module and receives a NULL module state. The subsequent dereference of state->ArrayType then hits freed memory and triggers a null pointer read.

Proof of Concept:

import array

class Good(array.array):
    pass

class Hide(type):
    def mro(cls):
        return (cls, object)

class Bad(Good, metaclass=Hide):
    pass

arr = Good('b', b'x')

class Count:
    def __index__(self):
        arr.__class__ = Bad     # Drop array.array from the MRO mid-op
        return 2

arr * Count()

Affected Versions:

Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 27 2025, 21:34:13) OK 0
Python 3.10.19+ (heads/3.10:014261980b1, Oct 27 2025, 21:19:00) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 27 2025, 21:20:35) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 27 2025, 21:27:07) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 27 2025, 21:28:49) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.14.0+ (heads/3.14:2e216728038, Oct 27 2025, 21:30:55) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 27 2025, 21:32:37) [Clang 18.1.3 (1ubuntu1)] ASAN 1

Related Code Snippet

#define find_array_state_by_type(tp) \
      (get_array_state(PyType_GetModuleByDef(tp, &arraymodule)))   // requires array.array in the MRO

static array_state *
get_array_state(PyObject *module)
{
    return (array_state *)_PyModule_GetState(module);            // assumes module is non-NULL
}

static PyObject *
array_repeat(PyObject *op, Py_ssize_t n)
{
    arrayobject *a = arrayobject_CAST(op);
    array_state *state = find_array_state_by_type(Py_TYPE(a));

    if (n < 0)
        n = 0;
    const Py_ssize_t array_length = Py_SIZE(a);
    if ((array_length != 0) && (n > PY_SSIZE_T_MAX / array_length)) {
        return PyErr_NoMemory();
    }
    Py_ssize_t size = array_length * n;
    arrayobject* np = (arrayobject *) newarrayobject(state->ArrayType, size, a->ob_descr);
    if (np == NULL)
        return NULL;
    if (size == 0)
        return (PyObject *)np;

    const Py_ssize_t oldbytes = array_length * a->ob_descr->itemsize;
    const Py_ssize_t newbytes = oldbytes * n;
    _PyBytes_Repeat(np->ob_item, newbytes, a->ob_item, oldbytes);

    return (PyObject *)np;
}

Sanitizer Report

=================================================================
==105120==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x77b5659592d8 bp 0x7ffe28c17cf0 sp 0x7ffe28c17cf0 T0)
==105120==The signal is caused by a READ memory access.
==105120==Hint: address points to the zero page.
    #0 0x77b5659592d8 in _Py_TYPE Include/object.h:277
    #1 0x77b5659592d8 in Py_IS_TYPE Include/object.h:302
    #2 0x77b5659592d8 in PyObject_TypeCheck Include/object.h:432
    #3 0x77b565959317 in _PyModule_GetState Include/internal/pycore_moduleobject.h:38
    #4 0x77b56595936b in get_array_state Modules/arraymodule.c:75
    #5 0x77b56595f09d in array_repeat Modules/arraymodule.c:970
    #6 0x584ac280512b in wrap_indexargfunc Objects/typeobject.c:9651
    #7 0x584ac270d606 in wrapperdescr_raw_call Objects/descrobject.c:532
    #8 0x584ac270df48 in wrapperdescr_call Objects/descrobject.c:570
    #9 0x584ac26eec71 in _PyObject_MakeTpCall Objects/call.c:242
    #10 0x584ac2801605 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167
    #11 0x584ac2801680 in vectorcall_unbound Objects/typeobject.c:3033
    #12 0x584ac281def4 in vectorcall_maybe Objects/typeobject.c:3130
    #13 0x584ac2821819 in slot_nb_multiply Objects/typeobject.c:10397
    #14 0x584ac26b8613 in binary_op1 Objects/abstract.c:966
    #15 0x584ac26bcc64 in PyNumber_Multiply Objects/abstract.c:1170
    #16 0x584ac29638f8 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:62
    #17 0x584ac29b0e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #18 0x584ac29b1148 in _PyEval_Vector Python/ceval.c:2001
    #19 0x584ac29b13f8 in PyEval_EvalCode Python/ceval.c:884
    #20 0x584ac2aa8507 in run_eval_code_obj Python/pythonrun.c:1365
    #21 0x584ac2aa8723 in run_mod Python/pythonrun.c:1459
    #22 0x584ac2aa957a in pyrun_file Python/pythonrun.c:1293
    #23 0x584ac2aac220 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #24 0x584ac2aac4f6 in _PyRun_AnyFileObject Python/pythonrun.c:81
    #25 0x584ac2afd74d in pymain_run_file_obj Modules/main.c:410
    #26 0x584ac2afd9b4 in pymain_run_file Modules/main.c:429
    #27 0x584ac2aff1b2 in pymain_run_python Modules/main.c:691
    #28 0x584ac2aff842 in Py_RunMain Modules/main.c:772
    #29 0x584ac2affa2e in pymain_main Modules/main.c:802
    #30 0x584ac2affdb3 in Py_BytesMain Modules/main.c:826
    #31 0x584ac2583645 in main Programs/python.c:15
    #32 0x77b56622a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #33 0x77b56622a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #34 0x584ac2583574 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x2dd574) (BuildId: 202d5dbb945f6d5f5a66ad50e2688d56affd6ecb)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV Include/object.h:277 in _Py_TYPE
==105120==ABORTING
Linked PRs
  • gh-143776

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Empieza por Modules/arraymodule.c, especialmente por array_repeat y la ruta find_array_state_by_type, y luego ejecuta el reproductor re-entrant index proporcionado bajo la configuración de sanitizer. Revisa el PR vinculado gh-143776 para conocer la dirección actual; se considera terminado cuando el reproductor ya no activa el fallo de sanitizer indicado y las pruebas relevantes pasan.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
c, python
Área
backend
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bien especificado
Aptitud para principiantes
20/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.