Ticket UUID: | 14a9b62e1db54af41b4a39a3741885faf25c672c | |||
Title: | crash when closing toplevel window with menus | |||
Type: | Bug | Version: | 8.7a6 | |
Submitter: | emiliano | Created on: | 2023-08-18 15:39:56 | |
Subsystem: | 10. Generic Menus | Assigned To: | sbron | |
Priority: | 5 Medium | Severity: | Critical | |
Status: | Closed | Last Modified: | 2023-09-04 15:25:00 | |
Resolution: | Fixed | Closed By: | jan.nijtmans | |
Closed on: | 2023-09-04 15:25:00 | |||
Description: |
$ cat menucrash.tcl package require Tk toplevel .t1 -menu .t1.mbar set m [menu .t1.mbar] $m add cascade -label "menu" -menu $m.c menu $m.c $m.c add command -label "close" -command [list destroy .t1] upon invoking the "close " menu $ tclsh8.7 menucrash.tcl alloc: invalid block: 0x11abf40: 20 0 Aborted (core dumped) gdb backtrace (gdb) bt #0 0xb7fd7bb5 in __kernel_vsyscall () #1 0xb7bc1a02 in __libc_signal_restore_set (set=0xbfffe5ac) at ../sysdeps/unix/sysv/linux/nptl-signals.h:80 #2 __GI_raise (sig=6) at ../sysdeps/unix/sysv/linux/raise.c:48 #3 0xb7bc2e91 in __GI_abort () at abort.c:79 #4 0xb7eef26c in Tcl_PanicVA ( format=0xb7f906b8 "alloc: invalid block: %p: %x %x", argList=0xbfffe894 "") at /home/carla/src/tcl/generic/tclPanic.c:125 #5 0xb7eef2a6 in Tcl_Panic ( format=0xb7f906b8 "alloc: invalid block: %p: %x %x") at /home/carla/src/tcl/generic/tclPanic.c:160 #6 0xb7f19ee4 in Ptr2Block (ptr=0x733c28) at /home/carla/src/tcl/generic/tclThreadAlloc.c:832 #7 0xb7f1955c in TclpFree (ptr=0x733c28) at /home/carla/src/tcl/generic/tclThreadAlloc.c:398 #8 0xb7eaad29 in Tcl_DeleteHashEntry (entryPtr=0x733c28) at /home/carla/src/tcl/generic/tclHash.c:440 #9 0xb69d8250 in DestroyMenuEntry (memPtr=0x7c1508) at /home/carla/src/tk/unix/../generic/tkMenu.c:1485 #10 0xb7efe09e in Tcl_Release (clientData=0x7c1508) at /home/carla/src/tcl/generic/tclPreserve.c:228 #11 0xb69d7abc in TkInvokeMenu (interp=0x40dde8, menuPtr=0x7a2fc8, index=0) at /home/carla/src/tk/unix/../generic/tkMenu.c:1092 #12 0xb69d7222 in MenuWidgetObjCmd (clientData=0x7a2fc8, interp=0x40dde8, objc=3, objv=0x628a6c) at /home/carla/src/tk/unix/../generic/tkMenu.c:889 #13 0xb7dbeaac in Dispatch (data=0x72ac44, interp=0x40dde8, dummy4958=0) at /home/carla/src/tcl/generic/tclBasic.c:4996 #14 0xb7dbeb2b in TclNRRunCallbacks (interp=0x40dde8, result=0, rootPtr=0x0) at /home/carla/src/tcl/generic/tclBasic.c:5036 #15 0xb7dbe3ea in Tcl_EvalObjv (interp=0x40dde8, objc=3, objv=0x418bf8, flags=2097168) at /home/carla/src/tcl/generic/tclBasic.c:4755 #16 0xb7dc0840 in TclEvalEx (interp=0x40dde8, script=0xbfffee34 "\n tk::MenuInvoke .t1.#t1#mbar.#t1#mbar#c 1\n", numBytes=45, flags=131072, line=2, clNextOuter=0x0, outerScript=0xbfffee34 "\n tk::MenuInvoke .t1.#t1#mbar.#t1#mbar#c 1\n") at /home/carla/src/tcl/generic/tclBasic.c:5912 #17 0xb7dbfd27 in Tcl_EvalEx (interp=0x40dde8, script=0xbfffee34 "\n tk::MenuInvoke .t1.#t1#mbar.#t1#mbar#c 1\n", numBytes=45, flags=131072) at /home/carla/src/tcl/generic/tclBasic.c:5576 #18 0xb697fd63 in Tk_BindEvent (bindPtr=0x51bb48, eventPtr=0x709ab0, tkwin=0x626998, numObjects=4, objArr=0xbfffef6c) at /home/carla/src/tk/unix/../generic/tkBind.c:2624 #19 0xb698a822 in TkBindEventProc (winPtr=0x626998, eventPtr=0x709ab0) at /home/carla/src/tk/unix/../generic/tkCmds.c:320 #20 0xb69969e1 in Tk_HandleEvent (eventPtr=0x709ab0) at /home/carla/src/tk/unix/../generic/tkEvent.c:1307 #21 0xb699705e in WindowEventProc (evPtr=0x709aa8, flags=-3) at /home/carla/src/tk/unix/../generic/tkEvent.c:1738 #22 0xb7ee9447 in Tcl_ServiceEvent (flags=-3) at /home/carla/src/tcl/generic/tclNotify.c:710 #23 0xb7ee972f in Tcl_DoOneEvent (flags=-3) at /home/carla/src/tcl/generic/tclNotify.c:943 #24 0xb699766a in Tk_MainLoop () at /home/carla/src/tk/unix/../generic/tkEvent.c:2124 #25 0xb7ee1f18 in Tcl_MainEx (argc=-1, argv=0xbffff274, appInitProc=0x40085a <Tcl_AppInit>, interp=0x40dde8) at /home/carla/src/tcl/generic/tclMain.c:592 #26 0x00400847 in main (argc=2, argv=0xbffff274) at /home/carla/src/tcl/unix/tclAppInit.c:94 The pointed out line #9 0xb69d8250 in DestroyMenuEntry (memPtr=0x7c1508) at /home/carla/src/tk/unix/../generic/tkMenu.c:1485 comes from commit https://core.tcl-lang.org/tk/info/e2b00bfd293c89ea | |||
User Comments: |
jan.nijtmans added on 2023-09-04 15:25:00:
Fixed [85d17152d854339b|here]. Many thanks! nab added on 2023-09-04 11:32:39: Hi, this works for me on macOS Aqua. thanks, ++ sbron added on 2023-09-04 10:36:13: I have added test menu-6.17 to Tk 8.6 for this issue and merged it to 8.7. I then created a bug-14a9b62e1d branch with the proposed fix. emiliano added on 2023-08-20 15:48:32: As Christopher well pointed out, the triggering script can be shortened to package require Tk menu .m -tearoff 0 .m add command -command {destroy .m} .m invoke 0 chrstphrchvz added on 2023-08-19 14:37:02: Making sure that mePtr->entryPtr is NULL rather than stale is indeed a better fix. The crash can also be triggered by appending $m.c invoke end to the example code. So I would suggest adding a test for this to menu.test in 8.6 (since the example uses nothing specific to 8.7). sbron added on 2023-08-19 10:03:56: Then I guess at least the hash entries need to be cleaned up in the loop in DestroyMenuInstance() that used to destroy the menu entries: Index: generic/tkMenu.c ================================================================== --- generic/tkMenu.c +++ generic/tkMenu.c @@ -1197,10 +1197,21 @@ * Free up all the stuff that requires special handling, then let * Tk_FreeConfigOptions handle all the standard option-related stuff. */ for (i = menuPtr->numEntries; --i >= 0; ) { + /* + * Clean up the hash entry for the menu item ID. + * This cannot be done when the entry is eventually freed, because + * the hash table may already have been deleted by then. + */ + + if (menuPtr->entries[i]->entryPtr) { + Tcl_DeleteHashEntry(menuPtr->entries[i]->entryPtr); + menuPtr->entries[i]->entryPtr = NULL; + } + /* * As each menu entry is deleted from the end of the array of entries, * decrement menuPtr->numEntries. Otherwise, the act of deleting menu * entry i will dereference freed memory attempting to queue a redraw * for menu entries (i+1)...numEntries. sbron added on 2023-08-19 09:06:43: Indeed, reverting [499c0467b3] (as a test) makes the crash disappear. chrstphrchvz added on 2023-08-19 08:37:57: Tcl ticket b6257b8283 does not appear to involve Tk, so I think it is unrelated to this ticket. The issue here is an interaction between the recently merged patch [499c0467b3] and the TIP 658 implementation which results in a use-after-free. Previously, DestroyMenuInstance() would call DestroyMenuEntry() and in turn Tcl_DeleteHashEntry() immediately for each entry, and before Tcl_DeleteHashTable() is called. But now Tcl_EventuallyFree() is called instead, and because this is occurring during MenuInvokeEntry() which has called Tcl_Preserve() on the invoked entry, DestroyMenuEntry() and in turn Tcl_DeleteHashEntry() are not called for the invoked entry until MenuInvokeEntry() calls Tcl_Release(), after the hash table was deleted. As reported by AddressSanitizer (-DPURIFY -fsanitize=address): ==11516==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000751158 at pc 0x000113e05572 bp 0x7ff7b1400ea0 sp 0x7ff7b1400e98 READ of size 8 at 0x604000751158 thread T0 #0 0x113e05571 in Tcl_DeleteHashEntry tclHash.c:374 #1 0x11054f2b5 in DestroyMenuEntry tkMenu.c:1485 #2 0x1140d528e in Tcl_Release tclPreserve.c:228 #3 0x11053605e in TkInvokeMenu tkMenu.c:1092 #4 0x110dd881a in -[TKMenu(TKMenuActions) tkMenuItemInvoke:] tkMacOSXMenu.c:370 #5 0x7ff81648542b in -[NSApplication(NSResponder) sendAction:to:from:]+0x142 (AppKit:x86_64+0x23042b) #6 0x7ff81657506b in -[NSMenuItem _corePerformAction]+0x19c (AppKit:x86_64+0x32006b) #7 0x7ff816574d78 in -[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:]+0x8d (AppKit:x86_64+0x31fd78) #8 0x7ff8165bd112 in -[NSMenu performActionForItemAtIndex:]+0x70 (AppKit:x86_64+0x368112) #9 0x7ff8165bd098 in -[NSMenu _internalPerformActionForItemAtIndex:]+0x51 (AppKit:x86_64+0x368098) #10 0x7ff8165bcee0 in -[NSCarbonMenuImpl _carbonCommandProcessEvent:handlerCallRef:]+0x64 (AppKit:x86_64+0x367ee0) #11 0x7ff816559a83 in NSSLMMenuEventHandler+0x3dd (AppKit:x86_64+0x304a83) #12 0x7ff81cc51d3e in DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*)+0x564 (HIToolbox:x86_64+0x8d3e) #13 0x7ff81cc51187 in SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*)+0x14c (HIToolbox:x86_64+0x8187) #14 0x7ff81cc65ce4 in SendEventToEventTarget+0x26 (HIToolbox:x86_64+0x1cce4) #15 0x7ff81ccc195a in SendHICommandEvent(unsigned int, HICommand const*, unsigned int, unsigned int, unsigned char, void const*, OpaqueEventTargetRef*, OpaqueEventTargetRef*, OpaqueEventRef**)+0x168 (HIToolbox:x86_64+0x7895a) #16 0x7ff81cce5d4c in SendMenuCommandWithContextAndModifiers+0x2d (HIToolbox:x86_64+0x9cd4c) #17 0x7ff81cce5cf0 in SendMenuItemSelectedEvent+0x157 (HIToolbox:x86_64+0x9ccf0) #18 0x7ff81cce5b38 in FinishMenuSelection(SelectionData*, MenuResult*, MenuResult*)+0x5f (HIToolbox:x86_64+0x9cb38) #19 0x7ff81cce654b in MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*)+0x270 (HIToolbox:x86_64+0x9d54b) #20 0x7ff81cce6240 in _HandleMenuSelection2+0x1ca (HIToolbox:x86_64+0x9d240) #21 0x7ff816421534 in _NSHandleCarbonMenuEvent+0xd6 (AppKit:x86_64+0x1cc534) #22 0x7ff8164213a1 in _DPSEventHandledByCarbon+0x35 (AppKit:x86_64+0x1cc3a1) #23 0x7ff8162927ae in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]+0xb66 (AppKit:x86_64+0x3d7ae) #24 0x110e1986e in TkMacOSXEventsCheckProc tkMacOSXNotify.c:560 #25 0x11402ea27 in Tcl_DoOneEvent tclNotify.c:999 #26 0x1102838af in Tk_MainLoop tkEvent.c:2124 #27 0x110354688 in Tk_MainEx tkMain.c:388 #28 0x10eb02bc2 in main tkAppInit.c:96 #29 0x7ff812dc341e in start+0x76e (dyld:x86_64+0xfffffffffff6e41e) 0x604000751158 is located 8 bytes inside of 40-byte region [0x604000751150,0x604000751178) freed by thread T0 here: #0 0x10f032889 in wrap_free+0xa9 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0xdd889) #1 0x1132eea74 in TclpFree tclAlloc.c:723 #2 0x113e070c9 in Tcl_DeleteHashTable tclHash.c:465 #3 0x11053987a in DestroyMenuInstance tkMenu.c:1217 #4 0x110536c53 in TkDestroyMenu tkMenu.c:1301 #5 0x110575bbb in TkMenuEventProc tkMenuDraw.c:778 #6 0x1102770a9 in Tk_HandleEvent tkEvent.c:1285 #7 0x11041f82c in Tk_DestroyWindow tkWindow.c:1498 #8 0x110536c40 in TkDestroyMenu tkMenu.c:1296 #9 0x110575bbb in TkMenuEventProc tkMenuDraw.c:778 #10 0x1102770a9 in Tk_HandleEvent tkEvent.c:1285 #11 0x11041f82c in Tk_DestroyWindow tkWindow.c:1498 #12 0x11041e868 in Tk_DestroyWindow tkWindow.c:1439 #13 0x11041e868 in Tk_DestroyWindow tkWindow.c:1439 #14 0x11020329b in Tk_DestroyObjCmd tkCmds.c:501 #15 0x11338af43 in Dispatch tclBasic.c:4629 #16 0x11336b5ca in TclNRRunCallbacks tclBasic.c:4645 #17 0x11337f8e7 in TclEvalObjEx tclBasic.c:6105 #18 0x11337f759 in Tcl_EvalObjEx tclBasic.c:6086 #19 0x110535db5 in TkInvokeMenu tkMenu.c:1089 #20 0x110dd881a in -[TKMenu(TKMenuActions) tkMenuItemInvoke:] tkMacOSXMenu.c:370 #21 0x7ff81648542b in -[NSApplication(NSResponder) sendAction:to:from:]+0x142 (AppKit:x86_64+0x23042b) #22 0x7ff81657506b in -[NSMenuItem _corePerformAction]+0x19c (AppKit:x86_64+0x32006b) #23 0x7ff816574d78 in -[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:]+0x8d (AppKit:x86_64+0x31fd78) #24 0x7ff8165bd112 in -[NSMenu performActionForItemAtIndex:]+0x70 (AppKit:x86_64+0x368112) #25 0x7ff8165bd098 in -[NSMenu _internalPerformActionForItemAtIndex:]+0x51 (AppKit:x86_64+0x368098) #26 0x7ff8165bcee0 in -[NSCarbonMenuImpl _carbonCommandProcessEvent:handlerCallRef:]+0x64 (AppKit:x86_64+0x367ee0) #27 0x7ff816559a83 in NSSLMMenuEventHandler+0x3dd (AppKit:x86_64+0x304a83) #28 0x7ff81cc51d3e in DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*)+0x564 (HIToolbox:x86_64+0x8d3e) #29 0x7ff81cc51187 in SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*)+0x14c (HIToolbox:x86_64+0x8187) #30 0x7ff81cc65ce4 in SendEventToEventTarget+0x26 (HIToolbox:x86_64+0x1cce4) #31 0x7ff81ccc195a in SendHICommandEvent(unsigned int, HICommand const*, unsigned int, unsigned int, unsigned char, void const*, OpaqueEventTargetRef*, OpaqueEventTargetRef*, OpaqueEventRef**)+0x168 (HIToolbox:x86_64+0x7895a) #32 0x7ff81cce5d4c in SendMenuCommandWithContextAndModifiers+0x2d (HIToolbox:x86_64+0x9cd4c) #33 0x7ff81cce5cf0 in SendMenuItemSelectedEvent+0x157 (HIToolbox:x86_64+0x9ccf0) #34 0x7ff81cce5b38 in FinishMenuSelection(SelectionData*, MenuResult*, MenuResult*)+0x5f (HIToolbox:x86_64+0x9cb38) #35 0x7ff81cce654b in MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*)+0x270 (HIToolbox:x86_64+0x9d54b) #36 0x7ff81cce6240 in _HandleMenuSelection2+0x1ca (HIToolbox:x86_64+0x9d240) #37 0x7ff816421534 in _NSHandleCarbonMenuEvent+0xd6 (AppKit:x86_64+0x1cc534) #38 0x7ff8164213a1 in _DPSEventHandledByCarbon+0x35 (AppKit:x86_64+0x1cc3a1) #39 0x7ff8162927ae in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]+0xb66 (AppKit:x86_64+0x3d7ae) #40 0x110e1986e in TkMacOSXEventsCheckProc tkMacOSXNotify.c:560 #41 0x11402ea27 in Tcl_DoOneEvent tclNotify.c:999 #42 0x1102838af in Tk_MainLoop tkEvent.c:2124 #43 0x110354688 in Tk_MainEx tkMain.c:388 #44 0x10eb02bc2 in main tkAppInit.c:96 #45 0x7ff812dc341e in start+0x76e (dyld:x86_64+0xfffffffffff6e41e) previously allocated by thread T0 here: #0 0x10f032740 in wrap_malloc+0xa0 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0xdd740) #1 0x1132eea54 in TclpAlloc tclAlloc.c:699 #2 0x1133ed224 in Tcl_Alloc tclCkalloc.c:1038 #3 0x113e023ac in AllocStringEntry tclHash.c:767 #4 0x113e04ae9 in CreateHashEntry tclHash.c:322 #5 0x1105412d7 in MenuAddOrInsert tkMenu.c:2477 #6 0x11051c42e in MenuWidgetObjCmd tkMenu.c:659 #7 0x11338af43 in Dispatch tclBasic.c:4629 #8 0x11336b5ca in TclNRRunCallbacks tclBasic.c:4645 #9 0x113369acd in Tcl_EvalObjv tclBasic.c:4388 #10 0x113377e1b in TclEvalEx tclBasic.c:5484 #11 0x113f59017 in Tcl_FSEvalFileEx tclIOUtil.c:1783 #12 0x11035368e in Tk_MainEx tkMain.c:340 #13 0x10eb02bc2 in main tkAppInit.c:96 #14 0x7ff812dc341e in start+0x76e (dyld:x86_64+0xfffffffffff6e41e) I currently do not think [499c0467b3] should be reverted. This one line change avoids the crash, although maybe it is not the best solution either: --- generic/tkMenu.c +++ generic/tkMenu.c @@ -1481,7 +1481,7 @@ DestroyMenuEntry( TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, MenuVarProc, mePtr); } - if (mePtr->entryPtr) { + if (mePtr->entryPtr && (menuPtr->numEntries != 0)) { Tcl_DeleteHashEntry(mePtr->entryPtr); mePtr->entryPtr = NULL; } fvogel added on 2023-08-18 17:49:08: Possible reference: https://core.tcl-lang.org/tcl/tktview/b6257b82833b69673486ae253be7550489642e42 fvogel added on 2023-08-18 16:07:54: Thanks Schelte! I have added your login to the list of possible assignees and have assigned this ticket to you. sbron added on 2023-08-18 15:55:18: As author of the implicated code, I will investigate. However, I cannot assign the bug to myself, as I'm not on the list of possible assignees. |
