我对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.c中的Tcl_InvalidateStringRep()调用。
我的疑问是,为什么不将tclObj的长度重置为零?
这来自Tcl_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)))) {
所以在第204行,当长度不为零而字节为NULL时,程序崩溃。
我的产品包括TCL源,当我跟踪程序崩溃时发现上述问题。我将变通方法放入了我们的代码中,但想与社区确认是否确实存在漏洞。
答案 0 :(得分:0)
我已经考虑过这一点,尽管我认为清除表示的代码这样做是错误的(因为原则上应该共享对象,因此不应观察到更改),但我当然认为很难证明这是不可能的。当然,TclCreateLiteral
中的tclLiteral.c
不会爆炸!
The fix我正在使用的是使TclCreateLiteral
使用TclGetStringFromObj
(Tcl_GetStringFromObj
的Tcl内部宏版本)来获取bytes
和length
字段,而不是直接使用它们,以便保留正确的约束。 这将使字符串表示形式再次存在(如果将其删除)。如果代码继续崩溃,则问题在于您的代码正在文字上调用TclInvalidateStringRep
(并设置了不能为此生成一个字符串; Tcl包含其中的一些字符串,但这是因为它从未从中清除原始字符串)。
请记住,Tcl_Obj
仅在错误时才应清除其字符串rep,而不仅仅是在获得非字符串表示形式时。值已被解释为整数的事实并不意味着不应将其解释为列表(正好相反!),并且如果内部表示形式从未更新为其他值(就地修改仅应碰巧出现在未共享的对象上),它根本不需要丢失该字符串表示形式。
答案 1 :(得分:0)
您的方法在某处似乎是错误的。
对于没有引用(TclInvalidateStringRep
)或只有一个引用(so refCount == 0
)的对象,基本上允许refCount <= 1
的调用,然后只有在您确定此1的情况下,才可以调用参考仅供您参考。
Tcl的共享对象可以切换其内部表示,但是字符串表示保持不变。否则,您将破坏Tcl的基本原理(例如EIAS等)。
最简单的示例可以解释这一点:
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中避免这种情况。