Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Open
Assignees
Labels
extension-modulesC modules in the Modules dir topic-sqlite3 type-crashA hard crash of the interpreter, possibly with a core dump
@jackfromeast

Description

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:

Details
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]

Metadata

Metadata

Assignees

Labels

extension-modulesC modules in the Modules dir topic-sqlite3 type-crashA hard crash of the interpreter, possibly with a core dump

Projects

Status

No status

Milestone

No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      AltStyle によって変換されたページ (->オリジナル) /