Skip to content

feat: Node::free_subtree — immediate discard with wrapper neutralization - #210

Merged
dginev merged 2 commits into
mainfrom
feat-free-subtree
Jul 30, 2026
Merged

feat: Node::free_subtree — immediate discard with wrapper neutralization#210
dginev merged 2 commits into
mainfrom
feat-free-subtree

Conversation

@dginev

@dginev dginev commented Jul 30, 2026

Copy link
Copy Markdown
Member

What. A discard operation that actually frees: Node::free_subtree detaches the node, walks its C subtree iteratively (elements, texts, attributes), nulls the shared node_ptr of every registered wrapper (stray clones anywhere become inert no-ops) and clears the bookkeeping entries, then calls xmlFreeNode once. Documents/fragments are refused.

Why. unlink_node alone never frees a doc-owned node, and set_rust_owned defers the free to the last wrapper drop — which a clone parked in a long-lived collection postpones past the owning document's xmlFreeDoc, turning the eventual free into a use-after-free (observed: SIGSEGV in xmlDictOwns). Driver: latexml-oxide's math parser discards thousands of replaced subtrees per document (~1.4 MB/formula leaked; 31.2M leaked allocations on a 2 MB slice, ~99% eliminated by this).

Validation. New tests/free_subtree_tests.rs (5 tests: held-wrapper neutralization incl. post-document-drop, detached and never-linked trees, double-free-via-clone no-op, document refusal); full fork suite green; release prep 0.3.17 included.

🤖 Generated with Claude Code

dginev and others added 2 commits July 29, 2026 19:00
unlink_node alone never frees a doc-owned node, and set_rust_owned defers
the free to the last wrapper drop — which a stray clone in a long-lived
collection postpones past the owning document's xmlFreeDoc, turning the
eventual xmlFreeNode into a use-after-free (observed: SIGSEGV in
xmlDictOwns when a consumer's bookkeeping Vec dropped after the Document).

free_subtree frees the C subtree NOW: it unlinks, walks the subtree
iteratively (elements, texts, attributes), nulls the shared node_ptr of
every registered wrapper (clones anywhere become inert no-ops) and clears
the bookkeeping entries, then calls xmlFreeNode once. Documents and
document fragments are refused.

Driver: latexml-oxide's math parser discards thousands of replaced
subtrees per document; without a true free they leak ~1.4 MB per formula.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dginev
dginev merged commit cca0c0f into main Jul 30, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant