Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix bug 3293874 |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
09c2da3a2a466b0bdb20a73a13f2e378 |
User & Date: | dgp 2011-05-31 20:36:31 |
Context
2011-06-01
| ||
12:09 | fix for [Bug 3309871]: Valgrind finds: invalid read in TclMaxListLength() check-in: f3a017078d user: jan.nijtmans tags: trunk | |
2011-05-31
| ||
20:36 | Fix bug 3293874 check-in: 09c2da3a2a user: dgp tags: trunk | |
19:58 | Rewind from a refactoring that veered into the weeds. Closed-Leaf check-in: 1d247886db user: dgp tags: bug-3293874 | |
2011-05-27
| ||
17:50 | fix a timing issue in socket-12.3 check-in: 188a795873 user: max tags: trunk | |
Changes
Changes to ChangeLog.
1 2 3 4 5 6 7 | 2011-05-25 Don Porter <[email protected]> * library/msgcat/msgcat.tcl: Bump to msgcat 1.4.4. * library/msgcat/pkgIndex.tcl: * unix/Makefile.in * win/Makefile.in | > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 2011-05-31 Don Porter <[email protected]> * generic/tclInt.h: Use a complete growth algorithm for lists * generic/tclListObj.c: so that length limits do not overconstrain * generic/tclStringObj.c: by a factor of 2. [Bug 3293874] * generic/tclUtil.c: Fix includes rooting all growth routines by default on a commone tunable parameter TCL_MIN_GROWTH. 2011-05-25 Don Porter <[email protected]> * library/msgcat/msgcat.tcl: Bump to msgcat 1.4.4. * library/msgcat/pkgIndex.tcl: * unix/Makefile.in * win/Makefile.in |
︙ | ︙ |
Changes to generic/tclInt.h.
︙ | ︙ | |||
2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 | * all.*/ Tcl_Obj *elements; /* First list element; the struct is grown to * accomodate all elements. */ } List; #define LIST_MAX \ (1 + (int)(((size_t)UINT_MAX - sizeof(List))/sizeof(Tcl_Obj *))) /* * Macro used to get the elements of a list object. */ #define ListRepPtr(listPtr) \ ((List *) (listPtr)->internalRep.twoPtrValue.ptr1) | > > | 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 | * all.*/ Tcl_Obj *elements; /* First list element; the struct is grown to * accomodate all elements. */ } List; #define LIST_MAX \ (1 + (int)(((size_t)UINT_MAX - sizeof(List))/sizeof(Tcl_Obj *))) #define LIST_SIZE(numElems) \ (unsigned)(sizeof(List) + (((numElems) - 1) * sizeof(Tcl_Obj *))) /* * Macro used to get the elements of a list object. */ #define ListRepPtr(listPtr) \ ((List *) (listPtr)->internalRep.twoPtrValue.ptr1) |
︙ | ︙ | |||
4092 4093 4094 4095 4096 4097 4098 4099 | * MODULE_SCOPE void TclGrowTokenArray(Tcl_Token *tokenPtr, int used, * int available, int append, * Tcl_Token *staticPtr); * MODULE_SCOPE void TclGrowParseTokenArray(Tcl_Parse *parsePtr, * int append); *---------------------------------------------------------------- */ | > > > > > > > > > | > > | > > > | 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 | * MODULE_SCOPE void TclGrowTokenArray(Tcl_Token *tokenPtr, int used, * int available, int append, * Tcl_Token *staticPtr); * MODULE_SCOPE void TclGrowParseTokenArray(Tcl_Parse *parsePtr, * int append); *---------------------------------------------------------------- */ /* General tuning for minimum growth in Tcl growth algorithms */ #ifndef TCL_MIN_GROWTH # ifdef TCL_GROWTH_MIN_ALLOC /* Support for any legacy tuners */ # define TCL_MIN_GROWTH TCL_GROWTH_MIN_ALLOC # else # define TCL_MIN_GROWTH 1024 # endif #endif /* Token growth tuning, default to the general value. */ #ifndef TCL_MIN_TOKEN_GROWTH #define TCL_MIN_TOKEN_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_Token) #endif #define TCL_MAX_TOKENS (int)(UINT_MAX / sizeof(Tcl_Token)) #define TclGrowTokenArray(tokenPtr, used, available, append, staticPtr) \ do { \ int needed = (used) + (append); \ if (needed > TCL_MAX_TOKENS) { \ Tcl_Panic("max # of tokens for a Tcl parse (%d) exceeded", \ TCL_MAX_TOKENS); \ } \ |
︙ | ︙ |
Changes to generic/tclListObj.c.
︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 50 51 52 53 54 | const Tcl_ObjType tclListType = { "list", /* name */ FreeListInternalRep, /* freeIntRepProc */ DupListInternalRep, /* dupIntRepProc */ UpdateStringOfList, /* updateStringProc */ SetListFromAny /* setFromAnyProc */ }; /* *---------------------------------------------------------------------- * * NewListIntRep -- * * Creates a list internal rep with space for objc elements. objc | > > > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | const Tcl_ObjType tclListType = { "list", /* name */ FreeListInternalRep, /* freeIntRepProc */ DupListInternalRep, /* dupIntRepProc */ UpdateStringOfList, /* updateStringProc */ SetListFromAny /* setFromAnyProc */ }; #ifndef TCL_MIN_ELEMENT_GROWTH #define TCL_MIN_ELEMENT_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_Obj *) #endif /* *---------------------------------------------------------------------- * * NewListIntRep -- * * Creates a list internal rep with space for objc elements. objc |
︙ | ︙ | |||
92 93 94 95 96 97 98 | if (p) { Tcl_Panic("max length of a Tcl list (%d elements) exceeded", LIST_MAX); } return NULL; } | | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | if (p) { Tcl_Panic("max length of a Tcl list (%d elements) exceeded", LIST_MAX); } return NULL; } listRepPtr = attemptckalloc(LIST_SIZE(objc)); if (listRepPtr == NULL) { if (p) { Tcl_Panic("list creation failed: unable to alloc %u bytes", LIST_SIZE(objc)); } return NULL; } listRepPtr->canonicalFlag = 0; listRepPtr->refCount = 0; listRepPtr->maxElemCount = objc; |
︙ | ︙ | |||
159 160 161 162 163 164 165 | if (objc > LIST_MAX) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "max length of a Tcl list (%d elements) exceeded", LIST_MAX)); } else { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "list creation failed: unable to alloc %u bytes", | | | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | if (objc > LIST_MAX) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "max length of a Tcl list (%d elements) exceeded", LIST_MAX)); } else { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "list creation failed: unable to alloc %u bytes", LIST_SIZE(objc))); } Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); } return listRepPtr; } /* |
︙ | ︙ | |||
478 479 480 481 482 483 484 | } /* *---------------------------------------------------------------------- * * Tcl_ListObjAppendList -- * | | | < < | | < | < < < < | | < | | | | 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 | } /* *---------------------------------------------------------------------- * * Tcl_ListObjAppendList -- * * This function appends the elements in the list value referenced by * elemListPtr to the list value referenced by listPtr. * * Results: * The return value is normally TCL_OK. If listPtr or elemListPtr do not * refer to list values, TCL_ERROR is returned and an error message is * left in the interpreter's result if interp is not NULL. * * Side effects: * The reference counts of the elements in elemListPtr are incremented * since the list now refers to them. listPtr and elemListPtr are * converted, if necessary, to list objects. Also, appending the new * elements may cause listObj's array of element pointers to grow. * listPtr's old string representation, if any, is invalidated. * *---------------------------------------------------------------------- */ int Tcl_ListObjAppendList( Tcl_Interp *interp, /* Used to report errors if not NULL. */ register Tcl_Obj *listPtr, /* List object to append elements to. */ Tcl_Obj *elemListPtr) /* List obj with elements to append. */ { int objc; Tcl_Obj **objv; if (Tcl_IsShared(listPtr)) { Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendList"); } /* Pull the elements to append from elemListPtr */ if (TCL_OK != TclListObjGetElements(interp, elemListPtr, &objc, &objv)) { return TCL_ERROR; } /* * Insert the new elements starting after the lists's last element. * Delete zero existing elements. */ return Tcl_ListObjReplace(interp, listPtr, LIST_MAX, 0, objc, objv); } /* *---------------------------------------------------------------------- * * Tcl_ListObjAppendElement -- * |
︙ | ︙ | |||
563 564 565 566 567 568 569 | int Tcl_ListObjAppendElement( Tcl_Interp *interp, /* Used to report errors if not NULL. */ Tcl_Obj *listPtr, /* List object to append objPtr to. */ Tcl_Obj *objPtr) /* Object to append to listPtr's list. */ { | | < | > > < < | | < > > > > | > > | > > | > > > | < > > > > | > | > > | > > > > | > > | | > > > | > > > > > > > | > > > > | > | > > | > > | | > > > > > | > | | < < | | > > > | > | | < | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 | int Tcl_ListObjAppendElement( Tcl_Interp *interp, /* Used to report errors if not NULL. */ Tcl_Obj *listPtr, /* List object to append objPtr to. */ Tcl_Obj *objPtr) /* Object to append to listPtr's list. */ { register List *listRepPtr, *newPtr = NULL; int numElems, numRequired, needGrow, isShared, attempt; if (Tcl_IsShared(listPtr)) { Tcl_Panic("%s called with shared object", "Tcl_ListObjAppendElement"); } if (listPtr->typePtr != &tclListType) { int result; if (listPtr->bytes == tclEmptyStringRep) { Tcl_SetListObj(listPtr, 1, &objPtr); return TCL_OK; } result = SetListFromAny(interp, listPtr); if (result != TCL_OK) { return result; } } listRepPtr = ListRepPtr(listPtr); numElems = listRepPtr->elemCount; numRequired = numElems + 1 ; needGrow = (numRequired > listRepPtr->maxElemCount); isShared = (listRepPtr->refCount > 1); if (numRequired > LIST_MAX) { if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "max length of a Tcl list (%d elements) exceeded", LIST_MAX)); Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); } return TCL_ERROR; } if (needGrow && !isShared) { /* Need to grow + unshared intrep => try to realloc */ attempt = 2 * numRequired; if (attempt <= LIST_MAX) { newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); } if (newPtr == NULL) { attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH; if (attempt > LIST_MAX) { attempt = LIST_MAX; } newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); } if (newPtr == NULL) { attempt = numRequired; newPtr = attemptckrealloc(listRepPtr, LIST_SIZE(attempt)); } if (newPtr) { listRepPtr = newPtr; listRepPtr->maxElemCount = attempt; needGrow = 0; } } if (isShared || needGrow) { Tcl_Obj **dst, **src = &listRepPtr->elements; /* * Either we have a shared intrep and we must copy to write, * or we need to grow and realloc attempts failed. * Attempt intrep copy. */ attempt = 2 * numRequired; newPtr = AttemptNewList(NULL, attempt, NULL); if (newPtr == NULL) { attempt = numRequired + 1 + TCL_MIN_ELEMENT_GROWTH; if (attempt > LIST_MAX) { attempt = LIST_MAX; } newPtr = AttemptNewList(NULL, attempt, NULL); } if (newPtr == NULL) { attempt = numRequired; newPtr = AttemptNewList(interp, attempt, NULL); } if (newPtr == NULL) { /* All growth attempts failed; throw the error */ return TCL_ERROR; } dst = &newPtr->elements; newPtr->refCount++; newPtr->canonicalFlag = listRepPtr->canonicalFlag; newPtr->elemCount = listRepPtr->elemCount; if (isShared) { /* * The original intrep must remain undisturbed. * Copy into the new one and bump refcounts */ while (numElems--) { *dst = *src++; Tcl_IncrRefCount(*dst++); } listRepPtr->refCount--; } else { /* Old intrep to be freed, re-use refCounts */ memcpy(dst, src, (size_t) numElems * sizeof(Tcl_Obj *)); ckfree(listRepPtr); } listRepPtr = newPtr; } listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr; /* * Add objPtr to the end of listPtr's array of element pointers. Increment * the ref count for the (now shared) objPtr. */ *(&listRepPtr->elements + listRepPtr->elemCount) = objPtr; Tcl_IncrRefCount(objPtr); listRepPtr->elemCount++; /* * Invalidate any old string representation since the list's internal * representation has changed. */ |
︙ | ︙ | |||
894 895 896 897 898 899 900 | if (numRequired > listRepPtr->maxElemCount){ newMax = 2 * numRequired; } else { newMax = listRepPtr->maxElemCount; } | > > > > > > > > > | | | > > | 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 | if (numRequired > listRepPtr->maxElemCount){ newMax = 2 * numRequired; } else { newMax = listRepPtr->maxElemCount; } listRepPtr = AttemptNewList(NULL, newMax, NULL); if (listRepPtr == NULL) { unsigned int limit = LIST_MAX - numRequired; unsigned int extra = numRequired - numElems + TCL_MIN_ELEMENT_GROWTH; int growth = (int) ((extra > limit) ? limit : extra); listRepPtr = AttemptNewList(NULL, numRequired + growth, NULL); if (listRepPtr == NULL) { listRepPtr = AttemptNewList(interp, numRequired, NULL); if (listRepPtr == NULL) { return TCL_ERROR; } } } listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr; listRepPtr->refCount++; elemPtrs = &listRepPtr->elements; |
︙ | ︙ | |||
1540 1541 1542 1543 1544 1545 1546 | if (result != TCL_OK) { return result; } } listRepPtr = ListRepPtr(listPtr); elemCount = listRepPtr->elemCount; | < | | < > | | | | > > > | > | | | | > | | | < > | 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 | if (result != TCL_OK) { return result; } } listRepPtr = ListRepPtr(listPtr); elemCount = listRepPtr->elemCount; /* * Ensure that the index is in bounds. */ if (index<0 || index>=elemCount) { if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj("list index out of range", -1)); Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSET", "BADINDEX", NULL); } return TCL_ERROR; } /* * If the internal rep is shared, replace it with an unshared copy. */ if (listRepPtr->refCount > 1) { Tcl_Obj **dst, **src = &listRepPtr->elements; List *newPtr = AttemptNewList(NULL, listRepPtr->maxElemCount, NULL); if (newPtr == NULL) { newPtr = AttemptNewList(interp, elemCount, NULL); if (newPtr == NULL) { return TCL_ERROR; } } newPtr->refCount++; newPtr->elemCount = elemCount; newPtr->canonicalFlag = listRepPtr->canonicalFlag; dst = &newPtr->elements; while (elemCount--) { *dst = *src++; Tcl_IncrRefCount(*dst++); } listRepPtr->refCount--; listPtr->internalRep.twoPtrValue.ptr1 = listRepPtr = newPtr; } elemPtrs = &listRepPtr->elements; /* * Add a reference to the new list element. */ Tcl_IncrRefCount(valuePtr); |
︙ | ︙ |
Changes to generic/tclStringObj.c.
︙ | ︙ | |||
148 149 150 151 152 153 154 | * TCL STRING GROWTH ALGORITHM * * When growing strings (during an append, for example), the following growth * algorithm is used: * * Attempt to allocate 2 * (originalLength + appendLength) * On failure: | | < | | | | | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | * TCL STRING GROWTH ALGORITHM * * When growing strings (during an append, for example), the following growth * algorithm is used: * * Attempt to allocate 2 * (originalLength + appendLength) * On failure: * attempt to allocate originalLength + 2*appendLength + TCL_MIN_GROWTH * * This algorithm allows very good performance, as it rapidly increases the * memory allocated for a given string, which minimizes the number of * reallocations that must be performed. However, using only the doubling * algorithm can lead to a significant waste of memory. In particular, it may * fail even when there is sufficient memory available to complete the append * request (but there is not 2*totalLength memory available). So when the * doubling fails (because there is not enough memory available), the * algorithm requests a smaller amount of memory, which is still enough to * cover the request, but which hopefully will be less than the total * available memory. * * The addition of TCL_MIN_GROWTH allows for efficient handling of very * small appends. Without this extra slush factor, a sequence of several small * appends would cause several memory allocations. As long as * TCL_MIN_GROWTH is a reasonable size, we can avoid that behavior. * * The growth algorithm can be tuned by adjusting the following parameters: * * TCL_MIN_GROWTH Additional space, in bytes, to allocate when * the double allocation has failed. Default is * 1024 (1 kilobyte). See tclInt.h. */ #ifndef TCL_MIN_UNICHAR_GROWTH #define TCL_MIN_UNICHAR_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_UniChar) #endif static void GrowStringBuffer( Tcl_Obj *objPtr, int needed, int flag) |
︙ | ︙ | |||
210 211 212 213 214 215 216 | if (ptr == NULL) { /* * Take care computing the amount of modest growth to avoid * overflow into invalid argument values for attempt. */ unsigned int limit = INT_MAX - needed; | | | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | if (ptr == NULL) { /* * Take care computing the amount of modest growth to avoid * overflow into invalid argument values for attempt. */ unsigned int limit = INT_MAX - needed; unsigned int extra = needed - objPtr->length + TCL_MIN_GROWTH; int growth = (int) ((extra > limit) ? limit : extra); attempt = needed + growth; ptr = attemptckrealloc(objPtr->bytes, attempt + 1); } } if (ptr == NULL) { |
︙ | ︙ | |||
261 262 263 264 265 266 267 | /* * Take care computing the amount of modest growth to avoid * overflow into invalid argument values for attempt. */ unsigned int limit = STRING_MAXCHARS - needed; unsigned int extra = needed - stringPtr->numChars | | | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | /* * Take care computing the amount of modest growth to avoid * overflow into invalid argument values for attempt. */ unsigned int limit = STRING_MAXCHARS - needed; unsigned int extra = needed - stringPtr->numChars + TCL_MIN_UNICHAR_GROWTH; int growth = (int) ((extra > limit) ? limit : extra); attempt = needed + growth; ptr = stringAttemptRealloc(stringPtr, attempt); } } if (ptr == NULL) { |
︙ | ︙ |
Changes to generic/tclUtil.c.
︙ | ︙ | |||
1774 1775 1776 1777 1778 1779 1780 | } Tcl_GetStringFromObj(objPtr, &length); if (length > 0) { break; } } if (i == objc) { | < < < < < < < < < < < < < < | | | | < | 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 | } Tcl_GetStringFromObj(objPtr, &length); if (length > 0) { break; } } if (i == objc) { resPtr = NULL; for (i = 0; i < objc; i++) { objPtr = objv[i]; if (objPtr->bytes && objPtr->length == 0) { continue; } if (resPtr) { Tcl_ListObjAppendList(NULL, resPtr, objPtr); } else { resPtr = TclListObjCopy(NULL, objPtr); } } if (!resPtr) { resPtr = Tcl_NewObj(); } return resPtr; } |
︙ | ︙ |