2626#include "util.h"
2727#include "clinic/cursor.c.h"
2828
29+ static inline int
30+ check_cursor_locked (pysqlite_Cursor * cur )
31+ {
32+ if (cur -> locked ) {
33+ PyErr_SetString (pysqlite_ProgrammingError ,
34+ "Recursive use of cursors not allowed." );
35+ return 0 ;
36+ }
37+ return 1 ;
38+ }
39+
2940/*[clinic input]
3041module _sqlite3
3142class _sqlite3.Cursor "pysqlite_Cursor *" "pysqlite_CursorType"
@@ -47,6 +58,10 @@ pysqlite_cursor_init_impl(pysqlite_Cursor *self,
4758 pysqlite_Connection * connection )
4859/*[clinic end generated code: output=ac59dce49a809ca8 input=a8a4f75ac90999b2]*/
4960{
61+ if (!check_cursor_locked (self )) {
62+ return -1 ;
63+ }
64+
5065 Py_INCREF (connection );
5166 Py_XSETREF (self -> connection , connection );
5267 Py_CLEAR (self -> statement );
@@ -407,12 +422,9 @@ static int check_cursor(pysqlite_Cursor* cur)
407422 return 0 ;
408423 }
409424
410- if (cur -> locked ) {
411- PyErr_SetString (pysqlite_ProgrammingError , "Recursive use of cursors not allowed." );
412- return 0 ;
413- }
414-
415- return pysqlite_check_thread (cur -> connection ) && pysqlite_check_connection (cur -> connection );
425+ return (pysqlite_check_thread (cur -> connection )
426+ && pysqlite_check_connection (cur -> connection )
427+ && check_cursor_locked (cur ));
416428}
417429
418430static PyObject *
@@ -810,27 +822,29 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self)
810822 if (self -> statement ) {
811823 rc = pysqlite_step (self -> statement -> st , self -> connection );
812824 if (PyErr_Occurred ()) {
813- (void )pysqlite_statement_reset (self -> statement );
814- Py_DECREF (next_row );
815- return NULL ;
825+ goto error ;
816826 }
817827 if (rc != SQLITE_DONE && rc != SQLITE_ROW ) {
818- (void )pysqlite_statement_reset (self -> statement );
819- Py_DECREF (next_row );
820828 _pysqlite_seterror (self -> connection -> db , NULL );
821- return NULL ;
829+ goto error ;
822830 }
823831
824832 if (rc == SQLITE_ROW ) {
833+ self -> locked = 1 ; // GH-80254: Prevent recursive use of cursors.
825834 self -> next_row = _pysqlite_fetch_one_row (self );
835+ self -> locked = 0 ;
826836 if (self -> next_row == NULL ) {
827- (void )pysqlite_statement_reset (self -> statement );
828- return NULL ;
837+ goto error ;
829838 }
830839 }
831840 }
832841
833842 return next_row ;
843+
844+ error :
845+ (void )pysqlite_statement_reset (self -> statement );
846+ Py_DECREF (next_row );
847+ return NULL ;
834848}
835849
836850/*[clinic input]
@@ -973,6 +987,10 @@ static PyObject *
973987pysqlite_cursor_close_impl (pysqlite_Cursor * self )
974988/*[clinic end generated code: output=b6055e4ec6fe63b6 input=08b36552dbb9a986]*/
975989{
990+ if (!check_cursor_locked (self )) {
991+ return NULL ;
992+ }
993+
976994 if (!self -> connection ) {
977995 PyErr_SetString (pysqlite_ProgrammingError ,
978996 "Base Cursor.__init__ not called." );
0 commit comments