python / python/cpython

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

Ouverte
#145,040 11 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

extension-modules topic-sqlite3 type-bug type-crash
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

Description

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

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez à Modules/_sqlite/cursor.c:974 et reproduisez le scénario du callback d’agrégation avec AddressSanitizer. Vérifiez l’état de la connexion après le callback et assurez-vous que la requête ne plante plus lorsque conn.close() est appelé depuis step() ; ajoutez ou mettez à jour la couverture de régression si la structure de test environnante indique un emplacement approprié.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
c, python, sqlite
Domaine
databases
Type d'issue
Bug
Difficulté
2/5
Temps estimé
1-3 heures
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.