Menu

#540 Tcl_Command validity management

open
5
2009-01-14
2009-01-14
Don Porter
No

Extensions have a number of ways
to get hold of a Tcl_Command value.
The routines Tcl_(NR)Create(Obj)Command,
Tcl_GetCommandFromObj, Tcl_FindCommand,
Tcl_CreateEnsemble, and Tcl_FindEnsemble
are sources of them.

Other Tcl routines take Tcl_Command
token values as arguments.

The potential for trouble is a
situation where the command is deleted
between the time Tcl gives the
Tcl_Command value to the extension
and the time the extension passes it
back. Since a (Tcl_Command) value
is really a (Command *), in these
situations the extension may be passing
back a pointer to memory that's freed,
or even reallocated to a new purpose,
setting the stage for crashes or memory
corruption.

The Command struct has a refCount field
to keep the struct from being freed while
various parts of the Tcl internals still
hold references to it. This is not exposed
to extensions, though. The Tcl_Command value
has also not been set up to enable Tcl_Preserve().

The callers of Tcl_*Create*Command at least
have control over the Tcl_CmdDeleteProc so
they can erect some sort of notification scheme
about when their save Tcl_Command token values
are no longer safe to use.

The Tcl_Find*, Tcl_GetCommandFromObj, and
Tcl_CreateEnsemble routines don't have even
that. Perhaps a delete trace can be used to
achieve the same purpose.

There seems to be a mismatch in the
responsibilities of an extension (don't pass
back an invalid Tcl_Command) and the power
tools at its disposal (routine to test validity,
or notification when validity is going away,
or documented examples of how to use what tools
do exist effectively) to live up to those responsibilities.

I put this in the [namespace] category because
I've encountered this problem with tokens
returned by Tcl_CreateEnsemble.

Discussion

  • Don Porter

    Don Porter - 2009-01-14

    One idea how callers might deal
    with the current situation. Rather
    than directly store a Tcl_Command,
    arrange to store an unshared Tcl_Obj
    and use Tcl_GetCommandFromObj() to
    manipulate it into the "cmdName"
    Tcl_ObjType. The intrep management
    builtin to that Tcl_ObjType will
    take care of the Command refCounts
    to keep the struct alive. Whenever
    the Tcl_Command value is needed,
    another call to Tcl_GetCommandFromObj()
    will fetch it. When really done with
    the value, just Tcl_DecrRefCount to make
    the Tcl_Obj and its intrep go away.

    There's probably still some ugliness
    on how to handle renames. Hmmm....

     
  • Don Porter

    Don Porter - 2009-01-14

    Looking from the other side of
    the fence, I wonder if the solution
    might be for the Tcl_Command token
    to no longer be a (Command *) value
    which can become invalid, but instead
    be a TclHandle of some suitable kind.

     
  • Don Porter

    Don Porter - 2009-01-16

    Seems the TclHandle idea still would
    burden the caller with the responsibility
    of TclHandlePreserve/Release calls.