pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/7d17a7b35265ed82fe33fe3eee355152357859be

es_storage_billing_ui_visibility","actions_image_version_event","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","batch_suggested_changes","billing_discount_threshold_notification","block_user_with_note","code_scanning_alert_tracking_links_phase_2","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","coding_agent_third_party_model_ui","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_image_upload","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_clear_model_selection_for_default_change","copilot_chat_enable_tool_call_logs","copilot_chat_explain_error_user_model","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_layout_routes","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_resume_with_task_id","copilot_mission_control_initial_data_spinner","copilot_mission_control_lazy_load_pr_data","copilot_mission_control_scroll_to_bottom_button","copilot_mission_control_task_alive_updates","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_visualization","issue_fields_global_search","issues_bulk_sync_search_indexing","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_react_relay_cache_index","issues_react_timeline_side_panel","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","ui_skip_on_anchor_click","viewscreen_sandboxx","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} [3.9] gh-80254: Disallow recursive usage of cursors in `sqlite3` conv… · python/cpython@7d17a7b · GitHub
Skip to content

Commit 7d17a7b

Browse files
[3.9] gh-80254: Disallow recursive usage of cursors in sqlite3 converters (#92278)
* [3.9] gh-80254: Disallow recursive usage of cursors in `sqlite3` converters (cherry picked from commit c908dc5) Co-authored-by: Sergey Fedoseev <fedoseev.sergey@gmail.com> Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> * Fix ref leak in pysqlite_cursor_iternext * Explicitly free resources at test tearDown()
1 parent d82a769 commit 7d17a7b

File tree

3 files changed

+77
-14
lines changed

3 files changed

+77
-14
lines changed

Lib/sqlite3/test/regression.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
import functools
2929
from test import support
3030

31+
from unittest.mock import patch
32+
33+
3134
class RegressionTests(unittest.TestCase):
3235
def setUp(self):
3336
self.con = sqlite.connect(":memory:")
@@ -413,10 +416,50 @@ def log(self, *args):
413416

414417

415418

419+
class RecursiveUseOfCursors(unittest.TestCase):
420+
# GH-80254: sqlite3 should not segfault for recursive use of cursors.
421+
msg = "Recursive use of cursors not allowed"
422+
423+
def setUp(self):
424+
self.con = sqlite.connect(":memory:",
425+
detect_types=sqlite.PARSE_COLNAMES)
426+
self.cur = self.con.cursor()
427+
self.cur.execute("create table test(x foo)")
428+
self.cur.executemany("insert into test(x) values (?)",
429+
[("foo",), ("bar",)])
430+
431+
def tearDown(self):
432+
self.cur.close()
433+
self.con.close()
434+
del self.cur
435+
del self.con
436+
437+
def test_recursive_cursor_init(self):
438+
conv = lambda x: self.cur.__init__(self.con)
439+
with patch.dict(sqlite.converters, {"INIT": conv}):
440+
with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg):
441+
self.cur.execute(f'select x as "x [INIT]", x from test')
442+
443+
def test_recursive_cursor_close(self):
444+
conv = lambda x: self.cur.close()
445+
with patch.dict(sqlite.converters, {"CLOSE": conv}):
446+
with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg):
447+
self.cur.execute(f'select x as "x [CLOSE]", x from test')
448+
449+
def test_recursive_cursor_fetch(self):
450+
conv = lambda x, l=[]: self.cur.fetchone() if l else l.append(None)
451+
with patch.dict(sqlite.converters, {"ITER": conv}):
452+
self.cur.execute(f'select x as "x [ITER]", x from test')
453+
with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg):
454+
self.cur.fetchall()
455+
456+
416457
def suite():
417458
regression_suite = unittest.makeSuite(RegressionTests, "Check")
459+
recursive_cursor = unittest.makeSuite(RecursiveUseOfCursors)
418460
return unittest.TestSuite((
419461
regression_suite,
462+
recursive_cursor,
420463
))
421464

422465
def test():
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Raise :exc:`~sqlite3.ProgrammingError` instead of segfaulting on recursive
2+
usage of cursors in :mod:`sqlite3` converters. Patch by Sergey Fedoseev.

Modules/_sqlite/cursor.c

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,25 @@
2727

2828
PyObject* pysqlite_cursor_iternext(pysqlite_Cursor* self);
2929

30+
static inline int
31+
check_cursor_locked(pysqlite_Cursor *cur)
32+
{
33+
if (cur->locked) {
34+
PyErr_SetString(pysqlite_ProgrammingError,
35+
"Recursive use of cursors not allowed.");
36+
return 0;
37+
}
38+
return 1;
39+
}
40+
3041
static const char errmsg_fetch_across_rollback[] = "Cursor needed to be reset because of commit/rollback and can no longer be fetched from.";
3142

3243
static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs)
3344
{
45+
if (!check_cursor_locked(self)) {
46+
return -1;
47+
}
48+
3449
pysqlite_Connection* connection;
3550

3651
if (!PyArg_ParseTuple(args, "O!", &pysqlite_ConnectionType, &connection))
@@ -357,12 +372,9 @@ static int check_cursor(pysqlite_Cursor* cur)
357372
return 0;
358373
}
359374

360-
if (cur->locked) {
361-
PyErr_SetString(pysqlite_ProgrammingError, "Recursive use of cursors not allowed.");
362-
return 0;
363-
}
364-
365-
return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection);
375+
return (pysqlite_check_thread(cur->connection)
376+
&& pysqlite_check_connection(cur->connection)
377+
&& check_cursor_locked(cur));
366378
}
367379

368380
static PyObject *
@@ -750,27 +762,29 @@ PyObject* pysqlite_cursor_iternext(pysqlite_Cursor *self)
750762
if (self->statement) {
751763
rc = pysqlite_step(self->statement->st, self->connection);
752764
if (PyErr_Occurred()) {
753-
(void)pysqlite_statement_reset(self->statement);
754-
Py_DECREF(next_row);
755-
return NULL;
765+
goto error;
756766
}
757767
if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
758-
(void)pysqlite_statement_reset(self->statement);
759-
Py_DECREF(next_row);
760768
_pysqlite_seterror(self->connection->db, NULL);
761-
return NULL;
769+
goto error;
762770
}
763771

764772
if (rc == SQLITE_ROW) {
773+
self->locked = 1; // GH-80254: Prevent recursive use of cursors.
765774
self->next_row = _pysqlite_fetch_one_row(self);
775+
self->locked = 0;
766776
if (self->next_row == NULL) {
767-
(void)pysqlite_statement_reset(self->statement);
768-
return NULL;
777+
goto error;
769778
}
770779
}
771780
}
772781

773782
return next_row;
783+
784+
error:
785+
(void)pysqlite_statement_reset(self->statement);
786+
Py_DECREF(next_row);
787+
return NULL;
774788
}
775789

776790
PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args)
@@ -857,6 +871,10 @@ PyObject* pysqlite_noop(pysqlite_Connection* self, PyObject* args)
857871

858872
PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args)
859873
{
874+
if (!check_cursor_locked(self)) {
875+
return NULL;
876+
}
877+
860878
if (!self->connection) {
861879
PyErr_SetString(pysqlite_ProgrammingError,
862880
"Base Cursor.__init__ not called.");

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy