Skip to content

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

@jackfromeast

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtopic-sqlite3type-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