代码之家  ›  专栏  ›  技术社区  ›  my_question

TclInvalidateStringRep()不应该重置长度吗?

tcl
  •  3
  • my_question  · 技术社区  · 6 年前

    我对TCL 8.6.8源代码tclInt.h中的以下代码有疑问:

    4277 #define TclInvalidateStringRep(objPtr) \
    4278     if (objPtr->bytes != NULL) { \
    4279         if (objPtr->bytes != tclEmptyStringRep) { \
    4280             ckfree((char *) objPtr->bytes); \
    4281         } \
    4282         objPtr->bytes = NULL; \
    4283     }
    

    我的疑问是,为什么tclObj的长度不能重置为零?

    以下是Tcl\u Obj的定义:

     808 typedef struct Tcl_Obj {
     809     int refCount;               /* When 0 the object will be freed. */
     810     char *bytes;                /* This points to the first byte of the
     811                                  * object's string representation. The array
     812                                  * must be followed by a null byte (i.e., at
     813                                  * offset length) but may also contain
     814                                  * embedded null characters. The array's
     815                                  * storage is allocated by ckalloc. NULL means
     816                                  * the string rep is invalid and must be
     817                                  * regenerated from the internal rep.  Clients
     818                                  * should use Tcl_GetStringFromObj or
     819                                  * Tcl_GetString to get a pointer to the byte
     820                                  * array as a readonly value. */
     821     int length;                 /* The number of bytes at *bytes, not
     822                                  * including the terminating null. */
    

    所以你可以看到长度和字节是紧密耦合的,当字节被清除时,我们不应该重置长度吗?

    我的疑问来自以下代码,tclLiteral.c中的TclCreateLiteral():

     200     for (globalPtr=globalTablePtr->buckets[globalHash] ; globalPtr!=NULL;
     201             globalPtr = globalPtr->nextPtr) {
     202         objPtr = globalPtr->objPtr;
     203         if ((globalPtr->nsPtr == nsPtr)
     204                 && (objPtr->length == length) && ((length == 0)
     205                 || ((objPtr->bytes[0] == bytes[0])
     206                 && (memcmp(objPtr->bytes, bytes, (unsigned) length) == 0)))) {
    

    我的产品包括TCL源代码,我在跟踪程序崩溃时发现了上述问题。我在我们的代码中加入了解决方法,但希望与社区确认它是否确实是一个漏洞。

    2 回复  |  直到 6 年前
        1
  •  0
  •   sebres    6 年前

    你的建议好像有点不对劲。

    TclInvalidateStringRep 对于没有引用的对象基本上是允许的( refCount == 0 )或者只有一个参考(所以 refCount <= 1 )只有当你确定,这1个参考是你自己的参考。

    最简单的例子可以解释这一点:

    set k 0x7f
    dict set d $k test
    expr {$k};             # ==> 127 (obj is integer now, but...)
    puts $k;               # ==> 0x7f (... still remains the string-representation)
    puts [dict get $d $k]; # ==> test
    
    # some code that fouls it up (despite of two references var `k` and key in dict `d`):
    magic_happens_here $k; # string representation gets lost.
    
    # and hereafter:
    puts $k;               # ==> 127 (representation is now 127, so...)
    puts [dict get $d $k]; # ==> ERROR: key "127" not known in dictionary
    
    

    如你所见, 重置响应。改变共享对象的字符串表示形式
    请避免在Tcl中出现这种情况。

        2
  •  0
  •   Donal Fellows    6 年前

    我已经考虑过这一点,虽然我认为清除表示的代码这样做是错误的(因为原则上应该共享对象,所以不应该被观察到更改),但我肯定认为要真正证明这不可能发生是极其困难的。当然, TclCreateLiteral tclLiteral.c

    The fix Tcl创建文字 使用 TclGetStringFromObj Tcl_GetStringFromObj )为了得到 bytes length 字段,而不是直接使用它们,以便保留正确的约束。 如果字符串表示被删除,这将使它再次存在。 如果代码继续崩溃,问题是调用的代码 TclInvalidateStringRep 在一个文本上(设置一个不能为其生成字符串的类型;Tcl有一些这样的类型,但这是因为它从不清除原始字符串)。

    Tcl_Obj 只应在其变为 不仅仅是当它获得非字符串表示时。一个值被解释为整数并不意味着它不应该被解释为一个列表(恰恰相反!)如果内部表示从未更新为不同的值(就地修改应该只发生在非共享对象上),那么它应该根本不需要丢失该字符串表示。