python / python/cpython

`_sqlite`: NULL dereference when connection is closed from within a callback

オープン
#145,040 コメント 11 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

extension-modules topic-sqlite3 type-bug type-crash
主要言語
Python
スター
77.2k
フォーク
35.9k
PR マージ指標
PR 指標を取得中

説明

Bug report

Bug description:

A segmentation fault occurs in the _sqlite module when a connection is closed inside an aggregate step() callback. After sqlite3_step() returns, _pysqlite_query_execute() calls sqlite3_last_insert_rowid(self->connection->db) without verifying that self->connection->db is still valid.

If the connection was closed during the callback, self->connection->db becomes NULL, leading to a NULL pointer dereference inside sqlite3_last_insert_rowid().

Affected Code

Modules/_sqlite/cursor.c:974

lastrowid = sqlite3_last_insert_rowid(self->connection->db);

Reproducer

import sqlite3

conn = sqlite3.connect(":memory:", autocommit=True)
conn.execute("CREATE TABLE t(x INTEGER)")

for i in range(50):
    conn.execute("INSERT INTO t VALUES (?)", (i,))

class MyAgg:
    def __init__(self):
        self.total = 0

    def step(self, value):
        self.total += value
        conn.close()

    def finalize(self):
        return self.total


conn.create_aggregate("agg_sum", 1, MyAgg)
conn.execute("SELECT agg_sum(x) FROM t").fetchone()

Result

AddressSanitizer:DEADLYSIGNAL
=================================================================
==19418==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000038 (pc 0x742d9562d034 bp 0x7fffdf6c6360 sp 0x7fffdf6c62f8 T0)
==19418==The signal is caused by a READ memory access.
==19418==Hint: address points to the zero page.
    #0 0x742d9562d034 in sqlite3_last_insert_rowid (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xaa034) (BuildId: f5959b839b5d4de9879dc3630ecde4d76d4be713)
    #1 0x742d96bd55c5 in _pysqlite_query_execute Modules/_sqlite/cursor.c:974
    #2 0x742d96bcdbc5 in pysqlite_connection_execute_impl Modules/_sqlite/connection.c:1848
    #3 0x742d96bcdd60 in pysqlite_connection_execute Modules/_sqlite/clinic/connection.c.h:967
    #4 0x5f5ad4a35cd6 in method_vectorcall_FASTCALL Objects/descrobject.c:402
    #5 0x5f5ad4a15ccc in _PyObject_VectorcallTstate Include/internal/pycore_call.h:136
    #6 0x5f5ad4a15dbf in PyObject_Vectorcall Objects/call.c:327
    #7 0x5f5ad4c948b7 in _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:769
    #8 0x5f5ad4ca487b in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1817
    #9 0x5f5ad4cdbab0 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:118
    #10 0x5f5ad4cdbe16 in _PyEval_Vector Python/ceval.c:2132
    #11 0x5f5ad4cdc0cc in PyEval_EvalCode Python/ceval.c:680
    #12 0x5f5ad4ddf771 in run_eval_code_obj Python/pythonrun.c:1366
    #13 0x5f5ad4ddfab7 in run_mod Python/pythonrun.c:1469
    #14 0x5f5ad4de09ec in pyrun_file Python/pythonrun.c:1294
    #15 0x5f5ad4de3822 in _PyRun_SimpleFileObject Python/pythonrun.c:518
    #16 0x5f5ad4de3ace in _PyRun_AnyFileObject Python/pythonrun.c:81
    #17 0x5f5ad4e38d76 in pymain_run_file_obj Modules/main.c:410
    #18 0x5f5ad4e38fe3 in pymain_run_file Modules/main.c:429
    #19 0x5f5ad4e3a7e1 in pymain_run_python Modules/main.c:691
    #20 0x5f5ad4e3ae77 in Py_RunMain Modules/main.c:772
    #21 0x5f5ad4e3b063 in pymain_main Modules/main.c:802
    #22 0x5f5ad4e3b3e8 in Py_BytesMain Modules/main.c:826
    #23 0x5f5ad48a0675 in main Programs/python.c:15
    #24 0x742d9782a3b7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #25 0x742d9782a47a in __libc_start_main_impl ../csu/libc-start.c:360
    #26 0x5f5ad48a05a4 in _start (/home/raminfp/Projects/cpython/python+0x2ee5a4) (BuildId: fde840bca26b2112d86f1a7490e122abaa6caef7)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libsqlite3.so.0+0xaa034) (BuildId: f5959b839b5d4de9879dc3630ecde4d76d4be713) in sqlite3_last_insert_rowid
==19418==ABORTING

Suggested Fix

Add a NULL check before calling sqlite3_last_insert_rowid():

if (self->connection->db != NULL) {
    lastrowid = sqlite3_last_insert_rowid(self->connection->db);
}
CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-145041
  • gh-151031

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

Modules/_sqlite/cursor.c:974 から開始し、AddressSanitizer 下で集約 callback のシナリオを再現します。callback 後の接続状態を確認し、step() から conn.close() が呼び出されたときにクエリがクラッシュしなくなったことを検証します。周辺のテスト構成から適切な場所が特定できる場合は、リグレッションテストのカバレッジを追加または更新します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
c, python, sqlite
領域
databases
issue の種類
バグ
難易度
2/5
見積もり時間
1〜3時間
活発さ
停滞
明瞭さ
明確に書かれている
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。