Tcl Source Code

View Ticket
Login
Ticket UUID: 405998
Title: Tcl_SetListObj fails reset obj properly
Type: Bug Version: obsolete: 8.3.2
Submitter: nobody Created on: 2001-03-05 11:39:22
Subsystem: 14. List Object Assigned To: hobbs
Priority: 5 Medium Severity:
Status: Closed Last Modified: 2001-04-04 14:17:49
Resolution: Fixed Closed By: hobbs
    Closed on: 2001-04-04 07:17:49
Description:
The case of no elements in Tcl_SetListObj when setting 
the string representation to tclEmptyStringRep fails 
to reset the length to zero.

diff -rc tcl8.3.2/generic/tclListObj.c 
tcl8.3.2.new/generic/tclListObj.c
*** tcl8.3.2/generic/tclListObj.c       Tue Aug 10 
22:45:11 1999
--- tcl8.3.2.new/generic/tclListObj.c   Tue Feb 20 
10:34:08 2001
***************
*** 265,270 ****
--- 265,271 ----
        objPtr->typePtr = &tclListType;
      } else {
        objPtr->bytes = tclEmptyStringRep;
+       objPtr->length = 0;
      }
  }
User Comments: hobbs added on 2001-04-04 14:17:49:
Logged In: YES 
user_id=72656

Don's good read of the docs encourages me to just take the 
patch as is.  Improving the 'empty' object in the future is 
still a possibility. In 8.4a3/8.3.3.

dgp added on 2001-04-04 11:21:44:
Logged In: YES 
user_id=80530

According to Tcl_Obj(3) (doc.Object.3):

 The bytes and the length members together hold an object's
 string representation, which is a counted or binary string
 that  may  contain  binary  data with embedded null bytes.
 bytes points to the first byte of the  string  representa-
 tion.   The  length member gives the number of bytes.  The
 byte array must always have a null after the last byte, at
 offset  length;

There is similar language in ObjectType.3.  Manipulators of
Tcl_Obj's must preserve the invariant:

(objPtr->bytes == NULL) ||
(objPtr->bytes[objPtr->length] == '\0')

Tcl_SetListObj() fails to do that.  The contributed
patch corrects the failure.

I'm punting back to the List Object maintainers to 
determine whether the best fix is the contributed
patch, or having Tcl_SetListObj() return a Tcl_Obj
that actually has (objPtr->typePtr == &tclListType).

hobbs added on 2001-03-30 01:58:05:
Logged In: YES 
user_id=72656

OK, it looks like there are a few things here that could be 
fixed.  There is a bug, but I think that setting length to 
0 is just a band-aid.  In SetStringFromAny, the reason it 
uses the objPtr->length is that bytes != NULL, WITHOUT 
checking for bytes == tclEmptyStringRep (a pseudo-
equivalent) which other procs in tclStringObj.c make sure 
to do (also in tclObj.c).  So, you could fix 
SetStringFromAny to account for this just as 
Tcl_SetObjLength and others do.

However, what might be more interesting, and has come up in 
discussion before, is why an empty list isn't a Tcl list 
obj.  The following is not true:

   Tcl_SetListObj(objPtr, objc, objv);
   /* This fails for objc == 0 */
   assert(objPtr->type == &tclListType);

From basic tests, you wouldn't save much time trying to 
avoid that, because any tclListType must have a valid list 
rep (which means an alloc for an empty list).

It seems odd in any case that Tcl_SetListObj would just set 
the bytes to tclEmptyStringRep when that means anything 
done to the object will cause it to become a string.

All in all, it might be easiest to set the length to 0, and 
note that that is equivalent to TclInitStringRep.  It looks 
to be intentional (and in the spirit of Tcl) that empty 
objects have no particular type to start, and don't get one 
until you do something with it.

dgp added on 2001-03-29 10:09:12:

File Added - 4710: segfault.c

dgp added on 2001-03-29 10:09:11:
Logged In: YES 
user_id=80530

I'm re-opening this and attaching a small C program that
demonstrates that this bug can cause segfaults.  I think
the contributed fix should be accepted.

In segfault.c, after Tcl_SetListObj() returns, 
objPtr->bytes == tclEmptyStringRep, but objPtr->length != 0.
Then Tcl_AppendToObj() calls SetStringFromAny() which writes
to objPtr->bytes[objPtr->length], assuming that length has
a value that makes this safe.  Instead memory is corrupted.

segfault.c has been written so the corrupted memory changes
the value of refArray in tclPreserve.c so the next time
Tcl_Preserve() is called => SIGSEGV!

This *could* be fixed with a rather complicated fix to
SetStringFromAny(), but the simple fix contributed here
is the better choice.  Also, everywhere else in the core
when ->bytes is assigned to tclEmptyStringRep, ->length is
also assigned to 0, even if ->typePtr == NULL, notably in
Tcl_NewObj().  I don't see any reason why Tcl_SetListObj()
should be exceptional.

hobbs added on 2001-03-28 08:19:51:
Logged In: YES 
user_id=72656

This presumes that someone is accessing the length value 
without taking into account that typePtr is NULL.  That 
would be a no-no.  If that occurs somewhere, I'd fix that, 
not add this unnecessary reset of length.

Attachments: