Tk Source Code

View Ticket
Login
Ticket UUID: 1ab7a5f29956725c6513663c9a3f889aebdd98d4
Title: Aqua: use-after-free in handleReopenApplicationEvent:withReplyEvent:
Type: Bug Version: 8.6.13
Submitter: chrstphrchvz Created on: 2023-04-25 13:48:24
Subsystem: 66. Aqua Window Operations Assigned To: nobody
Priority: 5 Medium Severity: Minor
Status: Open Last Modified: 2026-06-25 22:02:02
Resolution: None Closed By: nobody
    Closed on:
Description:

I observe this with 8.7 (edit: and 8.6.13). While trying to debug an IDLE test which hangs on Aqua during a call to update (see https://github.com/python/cpython/issues/88496; I think update idletasks might work instead), clicking the Tk icon in the dock triggers a use-after-free:

==889==ERROR: AddressSanitizer: heap-use-after-free on address 0x61a0000888a8 at pc 0x00010b6dc1aa bp 0x7ff7bbbc5af0 sp 0x7ff7bbbc5ae8
READ of size 8 at 0x61a0000888a8 thread T0
    #0 0x10b6dc1a9 in Tcl_FindCommand tclNamesp.c:2596
    #1 0x10cf7eac8 in -[TKApplication(TKHLEvents) handleReopenApplicationEvent:withReplyEvent:] tkMacOSXHLEvents.c:132
    #2 0x7ff808a5e723 in -[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:]+0x133 (Foundation:x86_64+0x34723) (BuildId: cecd498e32483fbaa3c3c2a12369af0e32000000200000000100000000060c00)
    #3 0x7ff808a5e595 in _NSAppleEventManagerGenericHandler+0x4f (Foundation:x86_64+0x34595) (BuildId: cecd498e32483fbaa3c3c2a12369af0e32000000200000000100000000060c00)
    #4 0x7ff80e2e546f  (AE:x86_64+0xb46f) (BuildId: 2176fd2a75363417abeb4b4190c429a932000000200000000100000000060c00)
    #5 0x7ff80e2e4cd9  (AE:x86_64+0xacd9) (BuildId: 2176fd2a75363417abeb4b4190c429a932000000200000000100000000060c00)
    #6 0x7ff80e2de31e in aeProcessAppleEvent+0x1a2 (AE:x86_64+0x431e) (BuildId: 2176fd2a75363417abeb4b4190c429a932000000200000000100000000060c00)
    #7 0x7ff8108ecce1 in AEProcessAppleEvent+0x35 (HIToolbox:x86_64+0x3fce1) (BuildId: 06fdecd69f69397bb1e2a8226c0ba7ed32000000200000000100000000060c00)
    #8 0x7ff80a6663c1 in _DPSNextEvent+0x7f3 (AppKit:x86_64+0x3f3c1) (BuildId: f02d178c79143fc4b9f49881945b316532000000200000000100000000060c00)
    #9 0x7ff80a664629 in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]+0x571 (AppKit:x86_64+0x3d629) (BuildId: f02d178c79143fc4b9f49881945b316532000000200000000100000000060c00)
    #10 0x10cff8045 in TkMacOSXDrawAllViews tkMacOSXNotify.c:394
    #11 0x10b8afea9 in TclServiceIdle tclTimer.c:751
    #12 0x10b707903 in Tcl_DoOneEvent tclNotify.c:1020
    #13 0x10c3dc779 in Tk_UpdateObjCmd tkCmds.c:1211
    #14 0x10a9b5cc8 in Dispatch tclBasic.c:4997
    #15 0x10a99423e in TclNRRunCallbacks tclBasic.c:5037
    #16 0x10a9925ab in Tcl_EvalObjv tclBasic.c:4756
    #17 0x108be4899 in Tkapp_Call _tkinter.c:1437
    #18 0x104593887 in method_vectorcall_VARARGS descrobject.c:331
    #19 0x10455c4d9 in PyObject_Vectorcall call.c:301
    #20 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #21 0x104567ad2 in _PyObject_VectorcallTstate pycore_call.h:92
    #22 0x104564595 in method_vectorcall classobject.c:89
    #23 0x10455c2cf in _PyVectorcall_Call call.c:247
    #24 0x1049bc5c9 in _PyEval_EvalFrameDefault bytecodes.c:3062
    #25 0x10455a413 in _PyObject_FastCallDictTstate call.c:143
    #26 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #27 0x1047565f6 in slot_tp_call typeobject.c:8196
    #28 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #29 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #30 0x104567ad2 in _PyObject_VectorcallTstate pycore_call.h:92
    #31 0x104564595 in method_vectorcall classobject.c:89
    #32 0x10455c2cf in _PyVectorcall_Call call.c:247
    #33 0x1049bc5c9 in _PyEval_EvalFrameDefault bytecodes.c:3062
    #34 0x10455a413 in _PyObject_FastCallDictTstate call.c:143
    #35 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #36 0x1047565f6 in slot_tp_call typeobject.c:8196
    #37 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #38 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #39 0x104567ad2 in _PyObject_VectorcallTstate pycore_call.h:92
    #40 0x104564595 in method_vectorcall classobject.c:89
    #41 0x10455c2cf in _PyVectorcall_Call call.c:247
    #42 0x1049bc5c9 in _PyEval_EvalFrameDefault bytecodes.c:3062
    #43 0x10455a413 in _PyObject_FastCallDictTstate call.c:143
    #44 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #45 0x1047565f6 in slot_tp_call typeobject.c:8196
    #46 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #47 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #48 0x10455a3cd in _PyObject_FastCallDictTstate call.c:154
    #49 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #50 0x104758bd1 in slot_tp_init typeobject.c:8434
    #51 0x10474a99e in type_call typeobject.c:1303
    #52 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #53 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #54 0x10499ab87 in PyEval_EvalCode ceval.c:556
    #55 0x104991758 in builtin_exec bltinmodule.c.h:586
    #56 0x1046d9539 in cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:438
    #57 0x10455c4d9 in PyObject_Vectorcall call.c:301
    #58 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #59 0x10455c2cf in _PyVectorcall_Call call.c:247
    #60 0x104c426a5 in pymain_run_module main.c:300
    #61 0x104c403a9 in Py_RunMain main.c:689
    #62 0x104c41bd9 in pymain_main main.c:719
    #63 0x104c42010 in Py_BytesMain main.c:743
    #64 0x1106b952d in start+0x1cd (dyld:x86_64+0x552d) (BuildId: 6376ba1620f73c31a5c5a10648ae7b5632000000200000000100000000060c00)

0x61a0000888a8 is located 40 bytes inside of 1304-byte region [0x61a000088880,0x61a000088d98)
freed by thread T0 here:
    #0 0x106053149 in wrap_free+0xa9 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x49149) (BuildId: bb241bbae85430e08df0156a18518bb92400000010000000000a0a0000030c00)
    #1 0x10a90f9a4 in TclpFree tclAlloc.c:722
    #2 0x10a97ab86 in DeleteInterpProc tclBasic.c:2131
    #3 0x10b7c0b8f in Tcl_EventuallyFree tclPreserve.c:295
    #4 0x10a974214 in Tcl_DeleteInterp tclBasic.c:1803
    #5 0x108be3d24 in Tkapp_Dealloc _tkinter.c:2841
    #6 0x10469ea9e in _PyObject_FreeInstanceAttributes dictobject.c:5560
    #7 0x10473f9c2 in subtype_dealloc typeobject.c:1641
    #8 0x104ad1066 in _PyFrame_ClearExceptCode frame.c:140
    #9 0x104a0223b in clear_thread_frame ceval.c:1430
    #10 0x1049e3685 in _PyEval_EvalFrameDefault bytecodes.c:640
    #11 0x104567ad2 in _PyObject_VectorcallTstate pycore_call.h:92
    #12 0x104564595 in method_vectorcall classobject.c:89
    #13 0x10455c2cf in _PyVectorcall_Call call.c:247
    #14 0x1049bc5c9 in _PyEval_EvalFrameDefault bytecodes.c:3062
    #15 0x10455a413 in _PyObject_FastCallDictTstate call.c:143
    #16 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #17 0x1047565f6 in slot_tp_call typeobject.c:8196
    #18 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #19 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #20 0x104567ad2 in _PyObject_VectorcallTstate pycore_call.h:92
    #21 0x104564595 in method_vectorcall classobject.c:89
    #22 0x10455c2cf in _PyVectorcall_Call call.c:247
    #23 0x1049bc5c9 in _PyEval_EvalFrameDefault bytecodes.c:3062
    #24 0x10455a413 in _PyObject_FastCallDictTstate call.c:143
    #25 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #26 0x1047565f6 in slot_tp_call typeobject.c:8196
    #27 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #28 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #29 0x10455a3cd in _PyObject_FastCallDictTstate call.c:154
    #30 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #31 0x104758bd1 in slot_tp_init typeobject.c:8434
    #32 0x10474a99e in type_call typeobject.c:1303
    #33 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #34 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #35 0x10499ab87 in PyEval_EvalCode ceval.c:556
    #36 0x104991758 in builtin_exec bltinmodule.c.h:586
    #37 0x1046d9539 in cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:438
    #38 0x10455c4d9 in PyObject_Vectorcall call.c:301
    #39 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #40 0x10455c2cf in _PyVectorcall_Call call.c:247
    #41 0x104c426a5 in pymain_run_module main.c:300
    #42 0x104c403a9 in Py_RunMain main.c:689
    #43 0x104c41bd9 in pymain_main main.c:719
    #44 0x104c42010 in Py_BytesMain main.c:743
    #45 0x1106b952d in start+0x1cd (dyld:x86_64+0x552d) (BuildId: 6376ba1620f73c31a5c5a10648ae7b5632000000200000000100000000060c00)

previously allocated by thread T0 here:
    #0 0x106053000 in wrap_malloc+0xa0 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x49000) (BuildId: bb241bbae85430e08df0156a18518bb92400000010000000000a0a0000030c00)
    #1 0x10a90f984 in TclpAlloc tclAlloc.c:699
    #2 0x10aa20172 in Tcl_Alloc tclCkalloc.c:1037
    #3 0x10a956bbd in Tcl_CreateInterp tclBasic.c:805
    #4 0x108be105e in Tkapp_New _tkinter.c:564
    #5 0x108bdee92 in _tkinter_create _tkinter.c.h:797
    #6 0x1046d9438 in cfunction_vectorcall_FASTCALL methodobject.c:422
    #7 0x10455c4d9 in PyObject_Vectorcall call.c:301
    #8 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #9 0x10455a413 in _PyObject_FastCallDictTstate call.c:143
    #10 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #11 0x104758bd1 in slot_tp_init typeobject.c:8434
    #12 0x10474a99e in type_call typeobject.c:1303
    #13 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #14 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #15 0x104567ad2 in _PyObject_VectorcallTstate pycore_call.h:92
    #16 0x104564595 in method_vectorcall classobject.c:89
    #17 0x10455c2cf in _PyVectorcall_Call call.c:247
    #18 0x1049bc5c9 in _PyEval_EvalFrameDefault bytecodes.c:3062
    #19 0x10455a413 in _PyObject_FastCallDictTstate call.c:143
    #20 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #21 0x1047565f6 in slot_tp_call typeobject.c:8196
    #22 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #23 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #24 0x104567ad2 in _PyObject_VectorcallTstate pycore_call.h:92
    #25 0x104564595 in method_vectorcall classobject.c:89
    #26 0x10455c2cf in _PyVectorcall_Call call.c:247
    #27 0x1049bc5c9 in _PyEval_EvalFrameDefault bytecodes.c:3062
    #28 0x10455a413 in _PyObject_FastCallDictTstate call.c:143
    #29 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #30 0x1047565f6 in slot_tp_call typeobject.c:8196
    #31 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #32 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #33 0x10455a3cd in _PyObject_FastCallDictTstate call.c:154
    #34 0x10455d5a1 in _PyObject_Call_Prepend call.c:486
    #35 0x104758bd1 in slot_tp_init typeobject.c:8434
    #36 0x10474a99e in type_call typeobject.c:1303
    #37 0x10455a85f in _PyObject_MakeTpCall call.c:216
    #38 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #39 0x10499ab87 in PyEval_EvalCode ceval.c:556
    #40 0x104991758 in builtin_exec bltinmodule.c.h:586
    #41 0x1046d9539 in cfunction_vectorcall_FASTCALL_KEYWORDS methodobject.c:438
    #42 0x10455c4d9 in PyObject_Vectorcall call.c:301
    #43 0x1049bac73 in _PyEval_EvalFrameDefault bytecodes.c:2533
    #44 0x10455c2cf in _PyVectorcall_Call call.c:247
    #45 0x104c426a5 in pymain_run_module main.c:300
    #46 0x104c403a9 in Py_RunMain main.c:689
    #47 0x104c41bd9 in pymain_main main.c:719
    #48 0x104c42010 in Py_BytesMain main.c:743
    #49 0x1106b952d in start+0x1cd (dyld:x86_64+0x552d) (BuildId: 6376ba1620f73c31a5c5a10648ae7b5632000000200000000100000000060c00)

SUMMARY: AddressSanitizer: heap-use-after-free tclNamesp.c:2596 in Tcl_FindCommand … ==889==ABORTING

Although the use-after-free can be avoided by doing Tcl_Preserve(interp) from _setup:, there should then also be a Tcl_Release(_eventInterp) call somewhere to avoid a leak (along with setting _eventInterp to NULL to avoid a stale reference), but I do not know where to do this.

User Comments: marc_culler (claiming to be Marc Culler) added on 2026-06-25 22:02:02:
This bug has appeared again in Python, in a more serious way which
is causing segfaults during the tkinter tests.

I have created a bug-fix branch with one possible "fix".  This does not
preserve the main (i.e. first) interpreter, whose address is stored in
the private NSApplication attribute named _eventInterp.  Instead it adds
a Destroy proc the that interpreter which sets the _eventInterp to NULL
when the main interpreter is destroyed.  I think that all uses of
_eventInterp are guarded by a test for NULL.  So I think this prevents
crashes.  It prevents the crash described in this report, anyway.

Possibly this is not as good as preserving the main interpreter.  I am
not sure.  It wasn't clear to me that interpreters are reference counted.
(I had lost track of this ticket and was responding to the Python
ticket.)

It is also not clear to me what the correct behavior should be for the
macOS Application Menu when the main interpreter is destroyed.

chrstphrchvz added on 2025-11-04 21:19:18:

Another issue which I believe has the same cause (and workaround) is the one reported by Marc at https://github.com/python/cpython/issues/123204

It can be triggered from tclsh using:

interp create i
interp eval i package require Tk
package require Tk
interp delete i

Then switch to Tk and click on the File menu. This reliably crashes even for builds without -DPURIFY -fsanitize=address because tclsh will abort instead.


jan.nijtmans added on 2025-11-04 21:07:10:

Ping


chrstphrchvz added on 2025-11-04 20:53:18:

One way of triggering this issue from tclsh (reliably crashes a -DPURIFY -fsanitize=address build):

interp create i
interp eval i package require Tk
interp delete i
package require Tk

Then click on the dock icon for Tk.