Skip to content

CFTree: fix CFTreeRemove so it actually unlinks the node#50

Open
DTW-Thalion wants to merge 1 commit into
gnustep:masterfrom
DTW-Thalion:fix/cftree-remove-uaf
Open

CFTree: fix CFTreeRemove so it actually unlinks the node#50
DTW-Thalion wants to merge 1 commit into
gnustep:masterfrom
DTW-Thalion:fix/cftree-remove-uaf

Conversation

@DTW-Thalion

Copy link
Copy Markdown
Contributor

CFTreeRemove() walked tree->_firstChild (the node's own children) instead
of the parent's child list, and never assigned previousSibling, so the
loop condition "child != previousSibling" simply ran to the end of the
list and nothing was ever unlinked. It then called CFTreeFinalize() on
the node while the parent still pointed at it. The node was therefore
left linked into its parent; once it was released the parent held a
dangling pointer (AddressSanitizer: heap-use-after-free in
CFTreeGetChildCount/CFTreeFinalize after removing a child and releasing
it), and the child count was never updated.

Rewrite it to unlink the node from its parent's list of children, fix up
the parent's _lastChild pointer when the last child is removed, and clear
the node's _parent and _nextSibling. A node with no parent is left
untouched. Adds regression tests covering removal of the first, middle,
last and only child.

The test also releases the tree before its children now: CFTreeAppendChild
does not retain the child, so a child freed before the tree that links it
would otherwise dangle.

CFTreeRemove() walked tree->_firstChild (the node's own children) instead
of the parent's child list, and never assigned previousSibling, so the
loop condition "child != previousSibling" simply ran to the end of the
list and nothing was ever unlinked.  It then called CFTreeFinalize() on
the node while the parent still pointed at it.  The node was therefore
left linked into its parent; once it was released the parent held a
dangling pointer (AddressSanitizer: heap-use-after-free in
CFTreeGetChildCount/CFTreeFinalize after removing a child and releasing
it), and the child count was never updated.

Rewrite it to unlink the node from its parent's list of children, fix up
the parent's _lastChild pointer when the last child is removed, and clear
the node's _parent and _nextSibling.  A node with no parent is left
untouched.  Adds regression tests covering removal of the first, middle,
last and only child.

The test also releases the tree before its children now: CFTreeAppendChild
does not retain the child, so a child freed before the tree that links it
would otherwise dangle.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant