Skip to content

gh-50966: Fix unbounded recursion in turtle drag handlers#152626

Merged
serhiy-storchaka merged 1 commit into
python:mainfrom
serhiy-storchaka:gh-50966-turtle-update-reentrancy
Jun 30, 2026
Merged

gh-50966: Fix unbounded recursion in turtle drag handlers#152626
serhiy-storchaka merged 1 commit into
python:mainfrom
serhiy-storchaka:gh-50966-turtle-update-reentrancy

Conversation

@serhiy-storchaka

Copy link
Copy Markdown
Member

turtle.TurtleScreenBase._update() redraws by calling cv.update(), which also reprocesses pending input events.
When a mouse handler moves the turtle — the canonical example being screen.ondrag(turtle.goto) — the move triggers another _update(), whose cv.update() dispatches the next queued motion event and reenters the handler.
With a continuous drag this recurses without bound (each level adds Python and Tcl C-stack frames), eventually crashing the interpreter (Tcl_Panic / segfault).

Guard _update() so that a reentrant call only flushes pending drawing with update_idletasks() instead of reprocessing input, turning the recursion into bounded iteration.
All drag callbacks still run (the turtle still follows the cursor); the non-reentrant path is unchanged.

Verified with a reproduction (reentrancy depth drops from dozens to 2 for a burst of queued drag events, all callbacks still processed) and a fast non-GUI regression test.

TurtleScreenBase._update() redraws with cv.update(), which also reprocesses
input events, so a handler that moves the turtle (such as
screen.ondrag(turtle.goto)) reenters _update() for every queued event until
the interpreter crashes.  A reentrant _update() now only flushes drawing with
update_idletasks().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@serhiy-storchaka serhiy-storchaka added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes labels Jun 29, 2026
@serhiy-storchaka serhiy-storchaka merged commit 6f103fa into python:main Jun 30, 2026
101 of 103 checks passed
@miss-islington-app

Copy link
Copy Markdown

Thanks @serhiy-storchaka for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14, 3.15.
🐍🍒⛏🤖

@serhiy-storchaka serhiy-storchaka deleted the gh-50966-turtle-update-reentrancy branch June 30, 2026 09:19
@bedevere-app

bedevere-app Bot commented Jun 30, 2026

Copy link
Copy Markdown

GH-152657 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 pre-release feature fixes, bugs and security fixes label Jun 30, 2026
@miss-islington-app

Copy link
Copy Markdown

Sorry, @serhiy-storchaka, I could not cleanly backport this to 3.13 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker 6f103fab178c07cbb5f701b8ad97e275b6eb6c4c 3.13

@bedevere-app

bedevere-app Bot commented Jun 30, 2026

Copy link
Copy Markdown

GH-152658 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.14 bugs and security fixes label Jun 30, 2026
serhiy-storchaka added a commit that referenced this pull request Jun 30, 2026
…152626) (GH-152657)

TurtleScreenBase._update() redraws with cv.update(), which also reprocesses
input events, so a handler that moves the turtle (such as
screen.ondrag(turtle.goto)) reenters _update() for every queued event until
the interpreter crashes.  A reentrant _update() now only flushes drawing with
update_idletasks().
(cherry picked from commit 6f103fa)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@bedevere-app

bedevere-app Bot commented Jun 30, 2026

Copy link
Copy Markdown

GH-152667 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.13 bugs and security fixes label Jun 30, 2026
serhiy-storchaka added a commit that referenced this pull request Jun 30, 2026
…152626) (GH-152658)

TurtleScreenBase._update() redraws with cv.update(), which also reprocesses
input events, so a handler that moves the turtle (such as
screen.ondrag(turtle.goto)) reenters _update() for every queued event until
the interpreter crashes.  A reentrant _update() now only flushes drawing with
update_idletasks().
(cherry picked from commit 6f103fa)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant