Skip to content

Use-after-free in sqlite3 progress handler via re-entrant __bool__ #142830

Closed

Description

@jackfromeast

What happened?

A progress handler that returns an object with a custom __bool__ can call conn.set_progress_handler(None, …) while progress_callback is still running, so the callback context is freed before print_or_clear_traceback(ctx) runs, producing a use-after-free when SQLite aborts the query after the raised exception.

Proof of Concept:

import sqlite3

conn = sqlite3.connect(":memory:")

class Evil:
    def __bool__(self):
        conn.set_progress_handler(None, 1)  # frees the callback context
        raise RuntimeError                  # force PyObject_IsTrue to fail

conn.set_progress_handler(lambda: Evil(), 1)
conn.execute("select 1")

Affected Versions:

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

Details
static int
progress_callback(void *ctx)
{
    PyGILState_STATE gilstate = PyGILState_Ensure();

    int rc;
    PyObject *ret;

    assert(ctx != NULL);
    PyObject *callable = ((callback_context *)ctx)->callable;
    ret = PyObject_CallNoArgs(callable);
    if (!ret) {
        /* abort query if error occurred */
        rc = -1;
    }
    else {
        // Side Effect: Trigger our __bool__ method
        rc = PyObject_IsTrue(ret);
        Py_DECREF(ret);
    }
    if (rc < 0) {
        // Bug: ctx is now freed.
        print_or_clear_traceback(ctx);
    }

    PyGILState_Release(gilstate);
    return rc;
}


print_or_clear_traceback(callback_context *ctx)
{
    assert(ctx != NULL);
    assert(ctx->state != NULL);
    if (ctx->state->enable_callback_tracebacks) {
        PyErr_FormatUnraisable("Exception ignored on sqlite3 callback %R",
                                ctx->callable);
    }
    else {
        PyErr_Clear();
    }
}

Sanitizer Output:

Details
=================================================================
==1658202==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000020cf0 at pc 0x7d94b4dac8b5 bp 0x7ffe6ff7e260 sp 0x7ffe6ff7e258
READ of size 8 at 0x503000020cf0 thread T0
    #0 0x7d94b4dac8b4 in print_or_clear_traceback /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:900:14
    #1 0x7d94b4daf3fd in progress_callback /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:1472:9
    #2 0x7d94b34e46d2 in sqlite3VdbeExec (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xf56d2) (BuildId: ac2bec9c45eec6feab0928b3b1373b467aa51339)
    #3 0x7d94b34ed690 in sqlite3_step (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xfe690) (BuildId: ac2bec9c45eec6feab0928b3b1373b467aa51339)
    #4 0x7d94b4db3c02 in stmt_step /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/cursor.c:571:10
    #5 0x7d94b4db3c02 in _pysqlite_query_execute /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/cursor.c:934:14
    #6 0x7d94b4da6276 in pysqlite_connection_execute_impl /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:1863:14
    #7 0x7d94b4da6276 in pysqlite_connection_execute /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/clinic/connection.c.h:967:20
    #8 0x566ed5f38117 in _PyObject_VectorcallTstate /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Include/internal/pycore_call.h:169:11
    #9 0x566ed5f38117 in PyObject_Vectorcall /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/call.c:327:12
    #10 0x566ed6535c62 in _PyEval_EvalFrameDefault /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/generated_cases.c.h:1620:35
    #11 0x566ed6504bf4 in _PyEval_Vector /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:2005:12
    #12 0x566ed6504bf4 in PyEval_EvalCode /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:888:21
    #13 0x566ed67c94d4 in run_mod /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1459:19
    #14 0x566ed67c302d in pyrun_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1293:15
    #15 0x566ed67c02d3 in _PyRun_SimpleFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:521:13
    #16 0x566ed67bf89e in _PyRun_AnyFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:81:15
    #17 0x566ed6884b13 in pymain_run_file_obj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:410:15
    #18 0x566ed6884b13 in pymain_run_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:429:15
    #19 0x566ed6881bcb in pymain_run_python /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:691:21
    #20 0x566ed6881bcb in Py_RunMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:772:5
    #21 0x566ed68837fb in pymain_main /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:802:12
    #22 0x566ed6883aa2 in Py_BytesMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:826:12
    #23 0x7d94b562a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #24 0x7d94b562a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #25 0x566ed5c40114 in _start (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x6b0114) (BuildId: 0aee20a59f1c25de22733bd0e5f8259ab04406c4)

0x503000020cf0 is located 16 bytes inside of 24-byte region [0x503000020ce0,0x503000020cf8)
freed by thread T0 here:
    #0 0x566ed5cdacca in free (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x74acca) (BuildId: 0aee20a59f1c25de22733bd0e5f8259ab04406c4)
    #1 0x7d94b4daf0e6 in set_callback_context /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:1133:9
    #2 0x7d94b4daf0e6 in pysqlite_connection_set_progress_handler_impl /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c
    #3 0x7d94b4da7940 in pysqlite_connection_set_progress_handler /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/clinic/connection.c.h:749:20
    #4 0x566ed5f38117 in _PyObject_VectorcallTstate /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Include/internal/pycore_call.h:169:11
    #5 0x566ed5f38117 in PyObject_Vectorcall /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/call.c:327:12
    #6 0x566ed6535c62 in _PyEval_EvalFrameDefault /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/generated_cases.c.h:1620:35
    #7 0x566ed650565c in _PyEval_Vector /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:2005:12
    #8 0x566ed5f3850c in _PyObject_VectorcallTstate /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Include/internal/pycore_call.h:169:11
    #9 0x566ed5f3850c in PyObject_CallOneArg /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/call.c:395:12
    #10 0x566ed61cc25e in call_unbound_noarg /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/typeobject.c:3041:16
    #11 0x566ed61cc25e in maybe_call_special_no_args /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/typeobject.c:3154:15
    #12 0x566ed625239f in slot_nb_bool /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/typeobject.c:10470:23
    #13 0x566ed6116f86 in PyObject_IsTrue /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/object.c:2060:15
    #14 0x7d94b4daf3a0 in progress_callback /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:1468:14
    #15 0x7d94b34e46d2 in sqlite3VdbeExec (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xf56d2) (BuildId: ac2bec9c45eec6feab0928b3b1373b467aa51339)
    #16 0x7d94b34ed690 in sqlite3_step (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xfe690) (BuildId: ac2bec9c45eec6feab0928b3b1373b467aa51339)
    #17 0x7d94b4db3c02 in stmt_step /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/cursor.c:571:10
    #18 0x7d94b4db3c02 in _pysqlite_query_execute /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/cursor.c:934:14
    #19 0x7d94b4da6276 in pysqlite_connection_execute_impl /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:1863:14
    #20 0x7d94b4da6276 in pysqlite_connection_execute /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/clinic/connection.c.h:967:20
    #21 0x566ed5f38117 in _PyObject_VectorcallTstate /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Include/internal/pycore_call.h:169:11
    #22 0x566ed5f38117 in PyObject_Vectorcall /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/call.c:327:12
    #23 0x566ed6535c62 in _PyEval_EvalFrameDefault /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/generated_cases.c.h:1620:35
    #24 0x566ed6504bf4 in _PyEval_Vector /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:2005:12
    #25 0x566ed6504bf4 in PyEval_EvalCode /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:888:21
    #26 0x566ed67c94d4 in run_mod /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1459:19
    #27 0x566ed67c302d in pyrun_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1293:15
    #28 0x566ed67c02d3 in _PyRun_SimpleFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:521:13
    #29 0x566ed67bf89e in _PyRun_AnyFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:81:15
    #30 0x566ed6884b13 in pymain_run_file_obj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:410:15
    #31 0x566ed6884b13 in pymain_run_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:429:15
    #32 0x566ed6881bcb in pymain_run_python /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:691:21
    #33 0x566ed6881bcb in Py_RunMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:772:5
    #34 0x566ed68837fb in pymain_main /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:802:12
    #35 0x566ed6883aa2 in Py_BytesMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:826:12
    #36 0x7d94b562a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #37 0x7d94b562a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #38 0x566ed5c40114 in _start (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x6b0114) (BuildId: 0aee20a59f1c25de22733bd0e5f8259ab04406c4)

previously allocated by thread T0 here:
    #0 0x566ed5cdaf63 in malloc (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x74af63) (BuildId: 0aee20a59f1c25de22733bd0e5f8259ab04406c4)
    #1 0x7d94b4dab19a in create_callback_context /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:1107:29
    #2 0x7d94b4daf03e in pysqlite_connection_set_progress_handler_impl /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:1610:33
    #3 0x7d94b4da7940 in pysqlite_connection_set_progress_handler /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/clinic/connection.c.h:749:20
    #4 0x566ed5f38117 in _PyObject_VectorcallTstate /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Include/internal/pycore_call.h:169:11
    #5 0x566ed5f38117 in PyObject_Vectorcall /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Objects/call.c:327:12
    #6 0x566ed6535c62 in _PyEval_EvalFrameDefault /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/generated_cases.c.h:1620:35
    #7 0x566ed6504bf4 in _PyEval_Vector /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:2005:12
    #8 0x566ed6504bf4 in PyEval_EvalCode /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/ceval.c:888:21
    #9 0x566ed67c94d4 in run_mod /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1459:19
    #10 0x566ed67c302d in pyrun_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:1293:15
    #11 0x566ed67c02d3 in _PyRun_SimpleFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:521:13
    #12 0x566ed67bf89e in _PyRun_AnyFileObject /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Python/pythonrun.c:81:15
    #13 0x566ed6884b13 in pymain_run_file_obj /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:410:15
    #14 0x566ed6884b13 in pymain_run_file /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:429:15
    #15 0x566ed6881bcb in pymain_run_python /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:691:21
    #16 0x566ed6881bcb in Py_RunMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:772:5
    #17 0x566ed68837fb in pymain_main /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:802:12
    #18 0x566ed6883aa2 in Py_BytesMain /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/Modules/main.c:826:12
    #19 0x7d94b562a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #20 0x7d94b562a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #21 0x566ed5c40114 in _start (/home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/python+0x6b0114) (BuildId: 0aee20a59f1c25de22733bd0e5f8259ab04406c4)

SUMMARY: AddressSanitizer: heap-use-after-free /home/jackfromeast/Desktop/entropy/tasks/reproducexx/targets/cpython-main/./Modules/_sqlite/connection.c:900:14 in print_or_clear_traceback
Shadow bytes around the buggy address:
  0x503000020a00: fa fa fd fd fd fa fa fa 00 00 00 00 fa fa 00 00
  0x503000020a80: 00 fa fa fa 00 00 00 00 fa fa 00 00 00 00 fa fa
  0x503000020b00: 00 00 00 fa fa fa 00 00 00 00 fa fa 00 00 00 fa
  0x503000020b80: fa fa 00 00 00 00 fa fa 00 00 00 fa fa fa 00 00
  0x503000020c00: 00 00 fa fa 00 00 00 fa fa fa 00 00 00 00 fa fa
=>0x503000020c80: 00 00 00 00 fa fa 00 00 00 00 fa fa fd fd[fd]fa
  0x503000020d00: fa fa 00 00 00 00 fa fa 00 00 00 00 fa fa fa fa
  0x503000020d80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x503000020e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x503000020e80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x503000020f00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==1658202==ABORTING

Linked PRs

Activity

  1. self-assigned this
    on Dec 27, 2025
  2. picnixz commented on Dec 28, 2025

    @picnixz
    Member

    I haven't looked at the code in detail but I'm worried that this one could be tricky because ctx is not a PyObject. I'm tempted to convert it into a PyObject to rely on the GC though.

  3. added 8 commits that reference this issue on Dec 28, 2025
  4. picnixz commented on Jan 1, 2026

    @picnixz
    Member

    Thanks for the report!

  5. added a commit that references this issue on Feb 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

extension-modulesC modules in the Modules dirtopic-sqlite3type-crashA hard crash of the interpreter, possibly with a core dump

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions