diff --git a/ChangeLog b/ChangeLog index 0604f3de..2b575f18 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2026-06-28 Todd White + * Source/CFTree.c (CFTreePrependChild): Set _lastChild to the new + child when the tree was empty, instead of NULL. + (CFTreeGetChildAtIndex): Stop walking at the end of the child list + so an out-of-range index returns NULL instead of dereferencing it. + * Tests/CFTree/basic.m: Regression tests. + 2021-09-30 Frederik Seiffert * Source/CFDate.c: Fix logic in CFGregorianDateIsValid() diff --git a/Source/CFTree.c b/Source/CFTree.c index f5c5306e..96e93c79 100644 --- a/Source/CFTree.c +++ b/Source/CFTree.c @@ -189,7 +189,7 @@ CFTreePrependChild (CFTreeRef tree, CFTreeRef newChild) newChild->_nextSibling = tree->_firstChild; tree->_firstChild = newChild; if (tree->_lastChild == NULL) - tree->_lastChild = NULL; + tree->_lastChild = newChild; } void @@ -240,9 +240,9 @@ CFTreeGetChildAtIndex (CFTreeRef tree, CFIndex idx) j = 0; child = tree->_firstChild; - while (j++ < idx) + while (child != NULL && j++ < idx) child = child->_nextSibling; - + return child; } diff --git a/Tests/CFTree/basic.m b/Tests/CFTree/basic.m index 4ed93648..7e655ecb 100644 --- a/Tests/CFTree/basic.m +++ b/Tests/CFTree/basic.m @@ -30,7 +30,28 @@ int main (void) PASS_CF(CFTreeGetNextSibling (child1) == child2, "Next sibling for child1 is child2."); PASS_CF(CFTreeGetChildAtIndex (tree, 2) == child3, "Child3 is at index 2"); - + + { + /* Prepending to an empty tree must set _lastChild, otherwise the next + append dereferences NULL. An out-of-range index must return NULL + rather than walking off the end of the child list. */ + CFTreeRef t2 = CFTreeCreate (NULL, &ctxt); + CFTreeRef a = CFTreeCreate (NULL, &ctxt); + CFTreeRef b = CFTreeCreate (NULL, &ctxt); + + CFTreePrependChild (t2, a); + CFTreeAppendChild (t2, b); + PASS_CF(CFTreeGetChildCount (t2) == 2 + && CFTreeGetChildAtIndex (t2, 1) == b, + "Append after prepend-to-empty works."); + PASS_CF(CFTreeGetChildAtIndex (t2, 5) == NULL, + "Out-of-range child index returns NULL."); + + CFRelease (a); + CFRelease (b); + CFRelease (t2); + } + CFRelease (child1); CFRelease (child2); CFRelease (child3);