Tk Source Code

View Ticket
Login
Ticket UUID: 5d991b822e934bff738ed0a27c80b377e8cf6e20
Title: segmentation violation in TclObjLookupVarEx
Type: Bug Version: 8.6.6
Submitter: bll Created on: 2017-06-30 20:21:57
Subsystem: 69. Events Assigned To: dgp
Priority: 5 Medium Severity: Important
Status: Closed Last Modified: 2019-05-17 12:43:27
Resolution: Fixed Closed By: dgp
    Closed on: 2019-05-17 12:43:27
Description:
I can reproduce the problem with a large amount of code, but have been
unable to whittle it down so far.  (I have a checkButtonScaled package and
it happens when both -textvariable and -variable are pointing to the same
variable and the variable gets unset;  checkButtonScaled is pure Tcl).

It has something to do with multiple traces on the same variable and the variable being unset.

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b5a8ef in TclObjLookupVarEx ()
   from /home/bll/local/lib/libtcl8.6.so
(gdb) bt
#0  0x00007ffff7b5a8ef in TclObjLookupVarEx ()
   from /home/bll/local/lib/libtcl8.6.so
#1  0x00007ffff7b5bacf in Tcl_ObjSetVar2 ()
   from /home/bll/local/lib/libtcl8.6.so
#2  0x00007ffff3d75471 in ButtonTextVarProc ()
   from /home/bll/local/lib/libtk8.6.so
#3  0x00007ffff7b50bb1 in TclCallVarTraces ()
   from /home/bll/local/lib/libtcl8.6.so
#4  0x00007ffff7b57f2d in UnsetVarStruct ()
   from /home/bll/local/lib/libtcl8.6.so
#5  0x00007ffff7b5944d in TclPtrUnsetVar ()
   from /home/bll/local/lib/libtcl8.6.so
#6  0x00007ffff7b5bed9 in TclObjUnsetVar2 ()
   from /home/bll/local/lib/libtcl8.6.so
#7  0x00007ffff7b5c0c9 in Tcl_UnsetObjCmd ()
   from /home/bll/local/lib/libtcl8.6.so
#8  0x00007ffff7a6bbb7 in TclNRRunCallbacks ()
   from /home/bll/local/lib/libtcl8.6.so
#9  0x00007ffff7a6da55 in TclEvalEx () from /home/bll/local/lib/libtcl8.6.so
#10 0x00007ffff7a6e073 in Tcl_EvalEx () from /home/bll/local/lib/libtcl8.6.so
#11 0x00007ffff7b4ea4b in TraceVarProc () from /home/bll/local/lib/libtcl8.6.so
#12 0x00007ffff7b50bb1 in TclCallVarTraces ()
   from /home/bll/local/lib/libtcl8.6.so
#13 0x00007ffff7b57f2d in UnsetVarStruct ()
---Type <return> to continue, or q <return> to quit---
   from /home/bll/local/lib/libtcl8.6.so
#14 0x00007ffff7b5944d in TclPtrUnsetVar ()
   from /home/bll/local/lib/libtcl8.6.so
#15 0x00007ffff7b5bed9 in TclObjUnsetVar2 ()
   from /home/bll/local/lib/libtcl8.6.so
#16 0x00007ffff7afc065 in TEBCresume () from /home/bll/local/lib/libtcl8.6.so
#17 0x00007ffff7a6bbb7 in TclNRRunCallbacks ()
   from /home/bll/local/lib/libtcl8.6.so
#18 0x00007ffff3df5874 in Ttk_InvokeEnsemble ()
   from /home/bll/local/lib/libtk8.6.so
#19 0x00007ffff3dfb7fc in WidgetInstanceObjCmd ()
   from /home/bll/local/lib/libtk8.6.so
#20 0x00007ffff7a6bbb7 in TclNRRunCallbacks ()
   from /home/bll/local/lib/libtcl8.6.so
#21 0x00007ffff3dfc454 in TtkWidgetInstateCommand ()
   from /home/bll/local/lib/libtk8.6.so
#22 0x00007ffff3df5874 in Ttk_InvokeEnsemble ()
   from /home/bll/local/lib/libtk8.6.so
#23 0x00007ffff3dfb7fc in WidgetInstanceObjCmd ()
   from /home/bll/local/lib/libtk8.6.so
#24 0x00007ffff7a6bbb7 in TclNRRunCallbacks ()
   from /home/bll/local/lib/libtcl8.6.so
#25 0x00007ffff3dfc454 in TtkWidgetInstateCommand ()
   from /home/bll/local/lib/libtk8.6.so
---Type <return> to continue, or q <return> to quit---
#26 0x00007ffff3df5874 in Ttk_InvokeEnsemble ()
   from /home/bll/local/lib/libtk8.6.so
#27 0x00007ffff3dfb7fc in WidgetInstanceObjCmd ()
   from /home/bll/local/lib/libtk8.6.so
#28 0x00007ffff7a6bbb7 in TclNRRunCallbacks ()
   from /home/bll/local/lib/libtcl8.6.so
#29 0x00007ffff7a6da55 in TclEvalEx () from /home/bll/local/lib/libtcl8.6.so
#30 0x00007ffff7a6e073 in Tcl_EvalEx () from /home/bll/local/lib/libtcl8.6.so
#31 0x00007ffff3d4a594 in Tk_BindEvent () from /home/bll/local/lib/libtk8.6.so
#32 0x00007ffff3d4e0f9 in TkBindEventProc ()
   from /home/bll/local/lib/libtk8.6.so
#33 0x00007ffff3d55bfb in Tk_HandleEvent ()
   from /home/bll/local/lib/libtk8.6.so
#34 0x00007ffff3d55e04 in WindowEventProc ()
   from /home/bll/local/lib/libtk8.6.so
#35 0x00007ffff7b2efcf in Tcl_ServiceEvent ()
   from /home/bll/local/lib/libtcl8.6.so
#36 0x00007ffff7b2f205 in Tcl_DoOneEvent ()
   from /home/bll/local/lib/libtcl8.6.so
#37 0x00007ffff3d56442 in Tk_MainLoop () from /home/bll/local/lib/libtk8.6.so
#38 0x00007ffff7b29ab6 in Tcl_MainEx () from /home/bll/local/lib/libtcl8.6.so
#39 0x0000000000400834 in main ()
User Comments: dgp added on 2019-05-17 12:43:27:
Similar issues were confronted long ago in the ttk source code.
For background, see

https://core.tcl.tk/tcl/info/2629338
https://core.tcl.tk/tcl/info/3062331
https://core.tcl.tk/tk/info/3341056
https://core.tcl.tk/tk/info/3425285

dgp added on 2019-05-15 20:03:15:
Revised fix committed.

dgp added on 2019-05-11 16:40:50:
While working on TIP 543, I'm looking at callers of Tcl traces, and that
led me to this ticket.

While the patch committed stopped the particular demo scripts from
segfaulting, I'm sorry to say it is not a proper fix. This machinery is
very messy. Very difficult to use robustly.

I'll be looking to craft a correct fix. Re-opening ticket while I work.

fvogel added on 2018-02-04 17:35:55:
Merged to core-8-6-branch and trunk.

stu added on 2018-01-28 20:48:32:
FYI, my window manager (cwm) moves the mouse pointer to newly created windows and somehow because of this I don't get a segfault. If I move the mouse rapidly as the test program starts up I can sometimes cause a segfault. Because the mouse was never over the button? If I change the after time to 0 or replace the [after]  with [unset var] then I get a segfault instantly. 8.5.19, 8.6.8, 8.7a1.

bll added on 2018-01-28 20:19:05:
Confirmed something weird with my build, somehow wrong
source paths were used for compilation
(using a generic/ directory from a different build tree).
(methinks the makefile sucks).

Confirmed patch works for tcb3.tcl, tcb2.tcl.

fvogel added on 2018-01-28 19:51:33:

Patch committed in branch bug-5d991b822e for easy review and testing.

I confirm that for me it fixes the otherwise crashing test script tcb3.tcl


chw added on 2018-01-28 18:13:48:
bll, please check your build. I've just tried a build from 8.6.8 tarballs
where the tkMenu.c part of the patch failed and needed manual rework.
Running both tcb2.tcl and tcb3.tcl on my aarch64 test system gave no crashes,
thus in theory the patch should fix the issue.

bll added on 2018-01-28 16:05:26:
The attached patch does not fix the crash (ttk::label)
in my code, and the attached tcb3.tcl also crashes.

I applied the patch to tk 8.6.8.

chw added on 2018-01-27 09:37:50:
Not a quite polite Tcl trace procedure which steals a -textvariable but
still legit code. This pattern could be applied on some other widgets,
too, e.g. entry, scale, and most likely would set off similar crashes.
Please review the attached patch, which should catch all these cases.

bll added on 2018-01-26 18:26:18:
Not sure if it is useful, but attached the ::tcl_traceExec at level 3.

fvogel added on 2018-01-25 18:44:36:

Strange code perhaps, but there is an issue here. No segfault shall ever happen while executing any kind of Tcl code.

From the info you gave:

In ButtonTextVarProc:
at:
1713                Tcl_ObjSetVar2(interp, butPtr->textVarNamePtr, NULL,
1714                        butPtr->textPtr, TCL_GLOBAL_ONLY);
and from the output of 'p *butPtr' I understand that butPtr->textVarNamePtr is NULL. This is clearly a problem.

Can you narrow this further down?


bll added on 2018-01-22 14:17:45:
I suspect unsetting the variable again inside the trace procedure is a case 
of "don't do that".

You are right, the documentation says: "Any errors in unset traces are ignored."
But in the one situation it causes a segmentation violation.

This seems like a case of me writing strange code.

fvogel added on 2018-01-22 04:53:30:
> In the following code, within the trace procedures, the
> code following the 'unset ::x' is not executed.

Isn't this due to the fact ::x is already unset at this point (due to after 1000 [list unset ::x] in proc m), so the second unset ::x in the trace proc triggers an error that is silently catched?

bll added on 2018-01-16 19:05:10:
Gahh...seriously weird...

bll-tecra:bll$ tclsh tcb2.tcl 2>&1 | tee w
bll-tecra:bll$ tclsh tcb2.tcl
Segmentation fault

The code has a two traces active on unset of the variable,
and the trace procedure is unsetting the variable (crash).
Still cannot duplicate this in a minimal fashion.

Well, this is a bit strange, don't know if it is related.
In the following code, within the trace procedures, the
code following the 'unset ::x' is not executed.

package require Tk

proc tA { args } {
  puts "tA:$args"
  lassign $args v1 v2 t
  if { $t eq "unset" } {
    unset ::x
    puts "AA:x: [info exists ::x]"
  }
}

proc tB { args } {
  puts "tB:$args"
  lassign $args v1 v2 t
  if { $t eq "unset" } {
    unset ::x
    puts "BB:x: [info exists ::x]"
  }
}

proc m { } {
  set ::x 1

  ttk::label .l -textvariable ::x
  pack .l

  trace add variable ::x unset tA
  trace add variable ::x [list read write unset] tB

  after 1000 [list unset ::x]
}
m

fvogel added on 2018-01-13 10:07:06:

The following, based on your description, does not allow to reproduce, at least:

package require Tk
pack [checkbutton .cb -textvariable tv -variable tv -command {unset tv ; puts unset}]
set tv 1
pack .cb
unset tv

Will you work on the whittling?


bll added on 2018-01-12 22:15:34:
I have not worked on this.
Not even sure I can remember how to create it.

stu added on 2018-01-12 22:02:59:
How's that whittling coming along?

bll added on 2017-06-30 21:24:17:
In ButtonTextVarProc:
at:
1713                Tcl_ObjSetVar2(interp, butPtr->textVarNamePtr, NULL,
1714                        butPtr->textPtr, TCL_GLOBAL_ONLY);

(gdb) p *butPtr
$5 = {tkwin = 0x7a26d0, display = 0x6e1860, interp = 0x60e5b0, 
  widgetCmd = 0x960940, type = 0, optionTable = 0x905870, textPtr = 0x64f980, 
  underline = -1, textVarNamePtr = 0x0, bitmap = 0, imagePtr = 0x0, 
  image = 0x0, selectImagePtr = 0x0, selectImage = 0x0, 
  tristateImagePtr = 0x0, tristateImage = 0x0, state = STATE_NORMAL, 
  normalBorder = 0x736060, activeBorder = 0x774e50, borderWidthPtr = 0x9a7230, 
  borderWidth = 0, relief = 0, overRelief = 1751333434, 
  offRelief = 1114334053, highlightWidthPtr = 0x9a7050, highlightWidth = 0, 
  highlightBorder = 0x736060, highlightColorPtr = 0x6d5530, inset = 0, 
  tkfont = 0x795fc0, normalFg = 0x6d5530, activeFg = 0x6d5530, 
  disabledFg = 0x960a40, normalTextGC = 0x944a00, activeTextGC = 0x9298c0, 
  disabledGC = 0x92e850, stippleGC = 0x92e7a0, gray = 81788936, 
  copyGC = 0x92e900, widthPtr = 0x967fb0, width = 0, heightPtr = 0x967d10, 
  height = 0, wrapLengthPtr = 0x967fe0, wrapLength = 0, padXPtr = 0x967e30, 
  padX = 1, padYPtr = 0x967e60, padY = 1, anchor = TK_ANCHOR_CENTER, 
  justify = TK_JUSTIFY_LEFT, indicatorOn = 0, selectBorder = 0x0, 
  textWidth = 8, textHeight = 14, textLayout = 0x8b79e0, indicatorSpace = 0, 
  indicatorDiameter = 0, defaultState = DEFAULT_DISABLED, selVarNamePtr = 0x0, 
  onValuePtr = 0x0, offValuePtr = 0x0, tristateValuePtr = 0x0, cursor = 0x0, 
  takeFocusPtr = 0x9a6de0, commandPtr = 0x0, compound = 3, 
  repeatDelay = -520051266, repeatInterval = -520051266, flags = 1}

bll added on 2017-06-30 20:34:11:
Program received signal SIGSEGV, Segmentation fault.
TclObjLookupVarEx (interp=0x60e5b0, part1Ptr=0x0, part2Ptr=0x0, flags=1, 
    msg=0x7ffff7b8e97b "set", createPart1=1, createPart2=1, 
    arrayPtrPtr=0x7fffffffd148) at /home/bll/tcl/tcl/generic/tclVar.c:531
531         const Tcl_ObjType *typePtr = part1Ptr->typePtr;
(gdb) bt
#0  TclObjLookupVarEx (interp=0x60e5b0, part1Ptr=0x0, part2Ptr=0x0, flags=1, 
    msg=0x7ffff7b8e97b "set", createPart1=1, createPart2=1, 
    arrayPtrPtr=0x7fffffffd148) at /home/bll/tcl/tcl/generic/tclVar.c:531
#1  0x00007ffff7b5b7cf in Tcl_ObjSetVar2 (interp=0x60e5b0, part1Ptr=0x0, 
    part2Ptr=0x0, newValuePtr=0x64f980, flags=-138876549)
    at /home/bll/tcl/tcl/generic/tclVar.c:1718
#2  0x00007ffff3d74471 in ButtonTextVarProc ()
   from /home/bll/localdbg/lib/libtk8.6.so
#3  0x00007ffff7b50701 in TclCallVarTraces (iPtr=0x60e5b0, arrayPtr=0x0, 
    varPtr=<optimized out>, part1=0x9bf3c0 "::on", part2=<optimized out>, 
    flags=192, leaveErrMsg=0) at /home/bll/tcl/tcl/generic/tclTrace.c:2679
#4  0x00007ffff7b57b0d in UnsetVarStruct (varPtr=0x65a650, arrayPtr=0x0, 
    iPtr=0x60e5b0, part1Ptr=0x1, part1Ptr@entry=0x9694e0, 
    part2Ptr=0x7ffff7b8e97b, part2Ptr@entry=0x0, flags=512, index=-1)
    at /home/bll/tcl/tcl/generic/tclVar.c:2571
#5  0x00007ffff7b590fd in TclPtrUnsetVarIdx (interp=0x60e5b0, varPtr=0x65a650, 
    arrayPtr=0x0, part1Ptr=0x9694e0, part2Ptr=0x0, flags=512, index=-1)
    at /home/bll/tcl/tcl/generic/tclVar.c:2453

Attachments: