python / python/cpython

Null pointer dereference in `cursor.fetchone` after re-entrant `text_factory` closes connection

Abierto
#143,662 3 comentarios 0 reacciones 1 asignado Ver en GitHub

@picnixz ya está trabajando en esto.

Desde el 10/1/2026.

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

A user-provided text_factory runs while _pysqlite_fetch_one_row converts TEXT columns. If it calls Connection.close, connection->db is set to NULL, but pysqlite_cursor_iternext still calls sqlite3_changes(self->connection->db) once iteration advances. The SQLite helper dereferences the cleared pointer and segfaults.

Proof of Concept:

import sqlite3

conn = sqlite3.connect(":memory:")
conn.execute("create table t(x)")
cur = conn.cursor()
cur.execute("insert into t values ('a') returning x")

def text_factory(val, _conn=conn):
    if not getattr(text_factory, "armed", False):
        text_factory.armed = True
        _conn.close()
    return "x"

conn.text_factory = text_factory
cur.fetchone()

Vulnerable Code Snippet:

Click to expand
/* Buggy Re-entrant Path */
static PyObject *
pysqlite_cursor_fetchone_impl(pysqlite_Cursor *self)
{
    return pysqlite_cursor_iternext((PyObject *)self);
}

static PyObject *
pysqlite_cursor_iternext(PyObject *op)
{
    pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
    /* ... */
    PyObject *row = _pysqlite_fetch_one_row(self);
    /* ... */
    if (rc == SQLITE_DONE) {
        if (self->statement->is_dml) {
            sqlite3 *db = self->connection->db;  /* crashing pointer derived */
            self->rowcount = (long)sqlite3_changes(db);  /* Crash site */
        }
        /* ... */
    }
    return row;
}

static PyObject *
_pysqlite_fetch_one_row(pysqlite_Cursor* self)
{
    /* ... */
    converted = PyObject_CallFunction(self->connection->text_factory,
                                      "y#", text, nbytes);  /* Reentrant call site */
    /* ... */
}

/* Clobbering Path */
static int
connection_close(pysqlite_Connection *self)
{
    sqlite3 *db = self->db;
    self->db = NULL;  /* state mutate site */
    (void)sqlite3_close_v2(db);
    /* ... */
    return rc;
}

Sanitizer Output:

Click to expand
AddressSanitizer:DEADLYSIGNAL
=================================================================
==431550==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000078 (pc 0x7fbfd133c1b4 bp 0x7ffed0387610 sp 0x7ffed0387608 T0)
==431550==The signal is caused by a READ memory access.
==431550==Hint: address points to the zero page.
    #0 0x7fbfd133c1b4 in sqlite3_changes64 (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xa41b4) (BuildId: ac2bec9c45eec6feab0928b3b1373b467aa51339)
    #1 0x7fbfd133c1cc in sqlite3_changes (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xa41cc) (BuildId: ac2bec9c45eec6feab0928b3b1373b467aa51339)
    #2 0x7fbfd3bdbb1c in pysqlite_cursor_iternext Modules/_sqlite/cursor.c:1121
    #3 0x7fbfd3bdbb1c in pysqlite_cursor_fetchone_impl Modules/_sqlite/cursor.c:1154
    #4 0x7fbfd3bdbb1c in pysqlite_cursor_fetchone Modules/_sqlite/clinic/cursor.c.h:169
    #5 0x6390ee26e3e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
    #6 0x6390ee26e3e7 in PyObject_Vectorcall Objects/call.c:327
    #7 0x6390ee1225a2 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
    #8 0x6390ee5ecad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
    #9 0x6390ee5ecad6 in _PyEval_Vector Python/ceval.c:2001
    #10 0x6390ee5ecad6 in PyEval_EvalCode Python/ceval.c:884
    #11 0x6390ee73216e in run_eval_code_obj Python/pythonrun.c:1365
    #12 0x6390ee73216e in run_mod Python/pythonrun.c:1459
    #13 0x6390ee736e17 in pyrun_file Python/pythonrun.c:1293
    #14 0x6390ee736e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521
    #15 0x6390ee73793c in _PyRun_AnyFileObject Python/pythonrun.c:81
    #16 0x6390ee7aae3c in pymain_run_file_obj Modules/main.c:410
    #17 0x6390ee7aae3c in pymain_run_file Modules/main.c:429
    #18 0x6390ee7aae3c in pymain_run_python Modules/main.c:691
    #19 0x6390ee7ac71e in Py_RunMain Modules/main.c:772
    #20 0x6390ee7ac71e in pymain_main Modules/main.c:802
    #21 0x6390ee7ac71e in Py_BytesMain Modules/main.c:826
    #22 0x7fbfd442a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #23 0x7fbfd442a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #24 0x6390ee146634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xa41b4) (BuildId: ac2bec9c45eec6feab0928b3b1373b467aa51339) in sqlite3_changes64
==431550==ABORTING
Affected Versions:
Python Version Status Exit Code
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 28 2025, 16:51:20) OK 0
Python 3.10.19+ (heads/3.10:014261980b1, Oct 28 2025, 16:52:08) [Clang 18.1.3 (1ubuntu1)] OK 0
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
CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0]

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.