| Ticket UUID: | 43573999cae70c8db2b7a234dd4ba0da63adb37a | |||
| Title: | Problem with tkBind.c since Tk 8.6.10 (various assertions fail) | |||
| Type: | Bug | Version: | 8.6.10 | |
| Submitter: | robgcs | Created on: | 2022-09-27 14:20:08 | |
| Subsystem: | 01. Bindings | Assigned To: | fvogel | |
| Priority: | 5 Medium | Severity: | Critical | |
| Status: | Closed | Last Modified: | 2022-10-26 19:42:16 | |
| Resolution: | Fixed | Closed By: | fvogel | |
| Closed on: | 2022-10-26 19:42:16 | |||
| Description: |
Hello, everybody
We have noticed that since Tk 8.6.10 our application crashes in connection with bindings. Different kinds of assertions fail in tkBind.c, for instance:
tkBind.c:2825: MatchPatterns: Assertion `(psPtr->object == NULL) == (physPtrPtr != NULL)'
tkBind.c:2489: Tk_BindEvent: Assertion `i + 1 < psEntry->psPtr->numPats'
tkBind.c:2827: MatchPatterns: Assertion `psPtr->numPats > patIndex'
tkBind.c:2824: MatchPatterns: Assertion `TEST_PSENTRY(psPtr)'
From what I can tell at a glance, major changes were made to tkBind.c between 8.6.9 and 8.6.10.
I have attached a small script that can be used to reproduce the crashes (see below).
Could you guys please take another look at this?
Kind regards,
Robert
#!/bin/sh
#\
exec wish "$0" ${1+"$@"}
# If you draw a line, the program crashes with Tk since 8.6.10.
#
# Tested under
# Ubuntu 18.04/Tk 8.6.8 (works)
# Ubuntu 20.04/Tk 8.6.10 (crashes)
# Ubuntu 22.04/Tk 8.6.12 (crashes)
# openSUSE 15.4/Tk 8.6.12 (crashes)
# among others
#
# (8.6.9 works, 8.6.11 crashes)
#
# If Tk was compiled without debugging information, it may take several
# attempts to crash the program.
proc startLin {c} {
unbindCan $c; # try without this (shouldn't crash then)
# Some array (move mouse after <ButtonRelease-1>):
set arr($c,foofoofoo) ""
set arr($c,barbarbar) 1
# or try this instead (and move mouse, leave canvas or draw another line after <ButtonRelease-1>):
# set arr($c,foo) ""
# set arr($c,bar) 1
# Try this (shouldn't crash then)
# unset arr
bindCan $c [array get arr]
}
proc bindCan {c args} {
bind $c <ButtonPress-1> "createLin %W %x %y"
bind $c <B1-Motion> "drawLin %W %x %y"
# Try without the next two lines (shouldn't crash then)
bind $c <B1-Motion><Leave> "leaveCan %W"
bind $c <B1-Motion><Enter> "enterCan %W"
bind $c <ButtonRelease-1> "startLin %W"
}
proc unbindCan {c} {
bind $c <ButtonPress-1> {}
bind $c <B1-Motion> {}
# Try without the next two lines (shouldn't crash then)
bind $c <B1-Motion><Leave> {}
bind $c <B1-Motion><Enter> {}
bind $c <ButtonRelease-1> {}
}
proc createLin {c x y} {
set x0 [$c canvasx $x]
set y0 [$c canvasy $y]
$c delete linTag
$c create line $x0 $y0 $x0 $y0 -tags linTag
}
proc drawLin {c x y} {
lassign [$c coords linTag] x0 y0
$c coords linTag [list $x0 $y0 [$c canvasx $x] [$c canvasy $y]]
}
proc leaveCan {c} {
# nothing
}
proc enterCan {c} {
# nothing
}
set c [canvas .c]
pack $c
startLin $c
| |||
| User Comments: |
fvogel added on 2022-10-26 19:42:16:
Thanks ;-) Now merged in core-8-6-branch and trunk. Closing. marc_culler (claiming to be Marc Culler) added on 2022-10-25 13:06:14: Well done, François! fvogel added on 2022-10-24 22:19:30: I have a fix, see [aac99dcf401]. All bind tests still pass in the test suite, and the new bind-37.1 stops crashing and is passing now. fvogel added on 2022-10-24 21:29:21: I have committed a testcase demonstrating the crash (or assertion failed in debug mode), see branch bug-43573999ca. I'm now having an understanding of what's happening. Basically, the successive <B1-Motion><Enter> pattern sequences get all promoted because they are partially matching the <B1-Motion> event. When the binding gets deleted in proc A only one instance of the promoted <B1-Motion><Enter> pattern sequences gets deleted in RemovePatSeqFromPromotionLists(), not all promoted (identical) patterne sequences. The next <Motion> event crashes in MatchPattern because of stale pattern sequences still in the promotion array. Stay tuned, I'm working on the fix. fvogel added on 2022-10-23 08:34:56: Oh, I realize I didn't give the recipe to reproduce the crash with either of my two trimmed down examples below: you have to run the code in tclsh, then, in the "." window, click with left button, move the mouse a bit while button-1 is still down, and release the button. At this point it has not yet crashed. Now move the mouse: the next <Motion> event crashes Tk (assertion failed). fvogel added on 2022-10-22 15:30:17: Another trimmed down version, not making use of 'after idle' this time:
package require Tk
proc A {} {
bind .c <B1-Motion><Enter> {}
set myv(a) 1
set b [array get myv]
# unset b ; # doesn't crash if present
bind .c <B1-Motion><Enter> "puts Trigger"
}
pack [canvas .c]
bind .c <ButtonRelease-1> "A"
A
It is a very strange bug. In proc A above, even if array 'myv' is not used this code is needed to make assertions fail in the bindings code (on Windows and Linux at least - my first experiments on macOS tend to show it's not the case on the mac with -fsanitize=address). Add "unset b" and the failed asserts in the binding code stop. This is very puzzling. To the OP: a simple workaround for your application is to bind to a script that does nothing in proc unbindCan, e.g. bind to {#} instead of bind to {} (which internally deletes the binding). This stops the crash from happening since the binding is no longer deleted but replaced by a void script. fvogel added on 2022-09-27 20:07:08: Very interesting. Here is a trimmed down script triggering the bug:
package require Tk
proc A {} {
bind .c <B1-Motion><Enter> {}
after idle [list bind .c <B1-Motion><Enter> "puts Trigger"]
}
pack [canvas .c]
bind .c <ButtonRelease-1> "A"
A
| |||
Attachments:
- Address sanitizer output.txt [download] added by fvogel on 2022-09-27 20:20:58. [details]
