python / python/cpython

Null pointer dereference in `_sqlite` cursor cache via re-entrant parameter iterator

Abierto
#143,198 2 comentarios 0 reacciones 1 asignado Ver en GitHub

@picnixz ya está trabajando en esto.

Desde el 27/12/2025.

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

Descripción

What happened?

cursor.executemany fetches parameters by calling PyObject_GetIter, so a crafted iterable can close the connection during iteration and clear self->connection->db. The cursor then continues into get_statement_from_cache with a NULL connection handle, leading _Py_TYPE to dereference a null pointer when trying to reuse the cached statement.

Proof of Concept:

import sqlite3

class Params:
    def __iter__(self):
        conn.close()
        return iter([(1,)])

conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.executemany('SELECT ?', Params())
Affected Versions
Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 28 2025, 16:51:20) Exception 1
Python 3.10.19+ (heads/3.10:014261980b1, Oct 28 2025, 16:52:08) [Clang 18.1.3 (1ubuntu1)] Exception 1
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 28 2025, 16:53:08) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 28 2025, 16:54:14) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 28 2025, 16:55:18) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.14.0+ (heads/3.14:2e216728038, Oct 28 2025, 16:56:16) [Clang 18.1.3 (1ubuntu1)] ASAN 1
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0] ASAN 1
Vulnerable Code
PyObject *
_pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation, PyObject* second_argument)
{
    PyObject* parameters_list = NULL;
    PyObject* parameters_iter = NULL;
    PyObject* parameters = NULL;
    int i;
    int rc;
    int numcols;
    PyObject* column_name;

    if (!check_cursor(self)) {
        goto error;
    }

    self->locked = 1;

    if (multiple) {
        if (PyIter_Check(second_argument)) {
            /* iterator */
            parameters_iter = Py_NewRef(second_argument);
        } else {
            /* sequence */
            // Bug: Trigger __iter__ method and set self->connection->db to null
            parameters_iter = PyObject_GetIter(second_argument);
            if (!parameters_iter) {
                goto error;
            }
        }
    } else {
        parameters_list = PyList_New(0);
        if (!parameters_list) {
            goto error;
        }

        if (second_argument == NULL) {
            second_argument = PyTuple_New(0);
            if (!second_argument) {
                goto error;
            }
        } else {
            Py_INCREF(second_argument);
        }
        if (PyList_Append(parameters_list, second_argument) != 0) {
            Py_DECREF(second_argument);
            goto error;
        }
        Py_DECREF(second_argument);

        parameters_iter = PyObject_GetIter(parameters_list);
        if (!parameters_iter) {
            goto error;
        }
    }

    /* reset description */
    Py_INCREF(Py_None);
    Py_SETREF(self->description, Py_None);

    if (self->statement) {
        // Reset pending statements on this cursor.
        if (stmt_reset(self->statement) != SQLITE_OK) {
            goto reset_failure;
        }
    }

    // Crash: Use the null pointer without anycheck
    PyObject *stmt = get_statement_from_cache(self, operation);
    Py_XSETREF(self->statement, (pysqlite_Statement *)stmt);
    if (!self->statement) {
        goto error;
    }

    /* ... */
Sanitizer Output
=================================================================
==1701025==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x5d9f40309b11 bp 0x7fff345709e0 sp 0x7fff345709d0 T0)
==1701025==The signal is caused by a READ memory access.
==1701025==Hint: address points to the zero page.
    #0 0x5d9f40309b11 in _Py_TYPE Include/object.h:277
    #1 0x5d9f40309b11 in _PyVectorcall_FunctionInline Include/internal/pycore_call.h:120
    #2 0x5d9f40309b11 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:164
    #3 0x5d9f40309b11 in PyObject_Vectorcall Objects/call.c:327
    #4 0x76a658353bb4 in get_statement_from_cache Modules/_sqlite/cursor.c:562
    #5 0x76a658353bb4 in _pysqlite_query_execute Modules/_sqlite/cursor.c:884
    #6 0x5d9f40309bc7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #7 0x5d9f40309bc7 in PyObject_Vectorcall Objects/call.c:327
    #8 0x5d9f401abb82 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #9 0x5d9f4068a256 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #10 0x5d9f4068a256 in _PyEval_Vector Python/ceval.c:2005
    #11 0x5d9f4068a256 in PyEval_EvalCode Python/ceval.c:888
    #12 0x5d9f407cff8e in run_eval_code_obj Python/pythonrun.c:1365
    #13 0x5d9f407cff8e in run_mod Python/pythonrun.c:1459
    #14 0x5d9f407d4c37 in pyrun_file Python/pythonrun.c:1293
    #15 0x5d9f407d4c37 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #16 0x5d9f407d575c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #17 0x5d9f40847ecc in pymain_run_file_obj Modules/main.c:410
    #18 0x5d9f40847ecc in pymain_run_file Modules/main.c:429
    #19 0x5d9f40847ecc in pymain_run_python Modules/main.c:691
    #20 0x5d9f408497ae in Py_RunMain Modules/main.c:772
    #21 0x5d9f408497ae in pymain_main Modules/main.c:802
    #22 0x5d9f408497ae in Py_BytesMain Modules/main.c:826
    #23 0x76a658a2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #24 0x76a658a2a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #25 0x5d9f401e1694 in _start (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x20e694) (BuildId: 9b8528ade1939b4eac85ea0eb0dad4843ee8ca4b)

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

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.

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.