From: Daniel Lehman <dlehman25@gmail.com> Calling put_text on IXMLDOMElement calls xmlNodeParseContent which deletes the child Text node with xmlFreeNodeList An IXMLDOMNode created that references that Text node will then double-free it on Release, probably leading to a crash The fix here is to possibly pre-free the Text node child if there's no COM reference to it (xmlNodePtr->_private == 0) and set children = NULL so that libxml2 won't free it If there's a COM reference to that Text node, it'll free it on Release If there's none, IXMLDOMElement_put_text frees the memory The tests show that once IXMLDOMElement_put_text is called, any IXMLDOMText object is detached from the tree and doesn't need updated --- dlls/msxml3/element.c | 14 ++++++++++++++ dlls/msxml3/msxml_private.h | 1 + dlls/msxml3/node.c | 2 +- dlls/msxml3/tests/domdoc.c | 2 -- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/dlls/msxml3/element.c b/dlls/msxml3/element.c index fd9a2973cd8..e38aea723dd 100644 --- a/dlls/msxml3/element.c +++ b/dlls/msxml3/element.c @@ -405,6 +405,20 @@ static HRESULT WINAPI domelem_put_text( { domelem *This = impl_from_IXMLDOMElement( iface ); TRACE("(%p)->(%s)\n", This, debugstr_w(p)); + + /* libxml2 calls xmlNodeParseContent from node_put_text for Elements + xmlNodeParseContent calls xmlFreeNodeList on the child Text node + an IXMLDOMTextNode was created from that Text node will crash + on Release because it holds a pointer to that freed node + pre-free and NULL the pointer before-hand to avoid that crash + but only if there's no outstanding references to it */ + if (This->node.node && This->node.node->children && + This->node.node->children->type == XML_TEXT_NODE) + { + if (!node_get_inst_cnt(This->node.node->children)) + xmlFreeNodeList(This->node.node->children); + This->node.node->children = NULL; + } return node_put_text( &This->node, p ); } diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index 9709f8247ab..a6b645f5233 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -247,6 +247,7 @@ extern HRESULT node_transform_node(const xmlnode*,IXMLDOMNode*,BSTR*); extern HRESULT node_transform_node_params(const xmlnode*,IXMLDOMNode*,BSTR*,ISequentialStream*, const struct xslprocessor_params*); extern HRESULT node_create_supporterrorinfo(const tid_t*,void**); +extern int node_get_inst_cnt(xmlNodePtr); extern HRESULT get_domdoc_from_xmldoc(xmlDocPtr xmldoc, IXMLDOMDocument3 **document); diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index 349e154e05c..bc536c87adb 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -386,7 +386,7 @@ HRESULT node_get_next_sibling(xmlnode *This, IXMLDOMNode **ret) return get_node(This, "next", This->node->next, ret); } -static int node_get_inst_cnt(xmlNodePtr node) +int node_get_inst_cnt(xmlNodePtr node) { int ret = *(LONG *)&node->_private & NODE_PRIV_REFCOUNT_MASK; xmlNodePtr child; diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index 913571ad263..7c7f4287985 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -14517,13 +14517,11 @@ static void test_put_text(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); expected = L"<open what=\"window\">sesame</open>"; - todo_wine ok(!lstrcmpW(str, expected), "Incorrect element string, got '%s'\n", wine_dbgstr_w(str)); SysFreeString(str); IXMLDOMNode_Release(node2); IXMLDOMNamedNodeMap_Release(map); - if (0) /* crashes on wine with IXMLDOMElement_put_text */ IXMLDOMNode_Release(node); IXMLDOMElement_Release(element); IXMLDOMDocument2_Release(doc); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10202