Tcl Source Code

View Ticket
Bounty program for improvements to Tcl and certain Tcl packages.
Ticket UUID: 1334947
Title: refCounts on PtrSetVar failure
Type: Bug Version: obsolete: 8.5a4
Submitter: msofer Created on: 2005-10-22 15:25:12
Subsystem: 07. Variables Assigned To: msofer
Priority: 9 Immediate Severity:
Status: Closed Last Modified: 2013-03-12 00:40:37
Resolution: Fixed Closed By: dgp
    Closed on: 2013-03-11 17:40:37
The refCount management of TclPtrSetVar, and also its
different "front ends" in tclVar.c, presents one
particular problem which was revealed by [Bug 1191369]
(both 8.4 and 8.5 are concerned).

The problem arises when the newValuePtr obj has refCount 0:

(1) if setting the new value succeeds as requested,
newValuePtr gets its refCount incremented to reflect
the new reference

(2) if setting the new value fails early (eg, trying to
set an array to a scalar value), newValuePtr's refCount
is not touched 

(3) If there is a write trace that does not allow the
value to be set as requested, newValuePtr's refCount
will be incremented/decremented, resulting in it being

This renders the caller's refCount management rather
critical, as reflected in 1191369. There are other
spots in the core where a similar bug is present.
Example: Tcl_AddObjErrorInfo would leak an obj if
errorCode or errorInfo redefined to be arrays (I know,
well deserved); Tcl_Main would leak if "argc" was an
array. Worse, witness (in a MEM_DEBUG HEAD):

% trace add variable a write {return -code error "RO
% regexp qw qwerty -> a
couldn't set variable "a"
% set a
file = ../generic/tclExecute.c, line = 2099
Trying to increment refCount of previously disposed object.

There are two possible fixes:
(a) fix all callers so that they incr before calling,
decr on return. Add a warning in the docs.

(b) fix TclPtrSetVar so that it does incr/decr for all
paths where it fails to set the value as requested,
allowing the callers to delegate the refCount
management to TclPtrSetVar. The callers still have to
be fixed to not touch that refCount.

I have a preference for (b); assigning to dgp as a
request for opinion. Please comment and assign back.
User Comments: dgp added on 2013-03-12 00:40:37:
allow_comments - 1

...and trunk.

dgp added on 2013-03-12 00:37:27:
Calling TclFreeObj() on the same value
twice causes really severe damage.  At
least in TCL_MEM_DEBUG mode we 
should better detect and report this.  This
will help a great deal finding the next bug
of this same kind, if any remain.

Patch committed to 8.5 branch.

dgp added on 2013-03-12 00:03:42:
This patch changed the refcount demands of
several variable setting routines.  The new
interface contract is much nicer, but some existing
code was written to the old requirements, and
hasn't yet been updated to the new reality.  This
code now breaks in ugly ways.

dgp added on 2013-03-08 22:51:22:
This bug fix causes segfaults in Tk.
See Tk Bug 3607326.

msofer added on 2005-11-05 05:42:50:
File Added - 155064: 1334947-8-5.patch

msofer added on 2005-11-05 05:42:49:
Logged In: YES 

Fixed in HEAD by method (b).

msofer added on 2005-10-24 05:05:26:
File Added - 153548: 1334947-8-4.patch

msofer added on 2005-10-24 05:05:25:
Logged In: YES 

Fixed in 8.4 by method (a). Attempting (b) caused many
testsuite failures, a fix was deemed too risky as many
extensions may have copied the buggy core code. Patch
attached to this ticket.

HEAD still untouched.

jenglish added on 2005-10-23 06:52:11:
Logged In: YES 

FWIW, I agree with Miguel that option (b) is strongly
preferable.  Callers should be able to pass a fresh Tcl_Obj*
to Tcl_ObjSetVar() and relatives without having to increment
/ decrement the refcount before and after.

msofer added on 2005-10-23 05:42:37:
Logged In: YES 

Clarifying my thoughts: the real "bug" is that some failure
paths do not clear 0-ref objs, some do not. This makes it
imperative for callers to manage the refCount explicitly. 

If a new obj is created by the caller and used as new value
without incrementing its refCount, the handling of error
returns is impossible to get right:either you DecrRefCount
and crash on errors caused by traces, or you don't and leak
the obj on other errors.

My preference for alternative (b) is that it allows the
fire-and-forget technique: just call e.g. 
  Tcl_ObjSetVar2(interp, part1Ptr, part2Ptr, Tcl_NewObj(),
and forget all about that object's fate in the knowledge
that it will be disposed of proeprly (as is currently done
in  error by eg Tcl_AddObjErrorInfo).

Maybe a similar policy would make sense for other
interfaces, eg Tcl_ListObjAppendElement?

msofer added on 2005-10-22 22:42:13:
Logged In: YES 

Better crash example:
% trace add variable a write {set a 1; return -code error
"RO var";#}
% regexp qw qwerty -> a
file = ../generic/tclCmdMZ.c, line = 370
Trying to decrement refCount of previously disposed object.

msofer added on 2005-10-22 22:30:09:
Logged In: YES 

correction to (b): either not touch it, or else incr/decr on
all paths.