Skip to content

Work around C14N duplicate xmlns bug on libxml 2.9.14#101

Merged
veewee merged 2 commits into4.xfrom
workarounds-c14n
Mar 27, 2026
Merged

Work around C14N duplicate xmlns bug on libxml 2.9.14#101
veewee merged 2 commits into4.xfrom
workarounds-c14n

Conversation

@veewee
Copy link
Copy Markdown
Owner

@veewee veewee commented Mar 27, 2026

Summary

  • canonicalize(): round-trip through saveXml() before calling C14N() to normalize namespace declarations that get corrupted in DOM-manipulated documents
  • rename_element_namespace(): remove stray Attr::rename() call that ran on a detached attribute node after removeAttributeNode()

Problem

C14N() on DOM-manipulated documents (e.g. after optimize_namespaces()) produces duplicate xmlns declarations when the element has a default xmlns and non-xmlns attributes. On libxml 2.9.14 (Ubuntu 24.04), parsing the invalid C14N output hangs or crashes with double free.

Minimal reproducer:

$doc = Dom\XMLDocument::createFromString('<root xmlns="urn:a" attr="val"/>');
$doc->documentElement->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ns1", "urn:a");
echo $doc->C14N();
// Output: <root xmlns="urn:a" xmlns:ns1="urn:a" attr="val" xmlns:ns1="urn:a"></root>
//         ^ xmlns:ns1 appears TWICE

This caused comparable() (which calls optimize_namespaces() then canonicalize()) to hang on CI with libxml 2.9.14.

Test plan

  • New test CanonicalizeTest::test_it_can_canonicalize_after_dom_manipulation passes
  • New ComparableTest data case default-xmlns-with-attributes passes
  • Full test suite passes on libxml 2.9.14 (tested via Docker docphpro/php84)
  • Full test suite passes on libxml 2.9.13 (tested locally on macOS)

C14N() on DOM-manipulated documents produces duplicate xmlns
declarations when the element has a default xmlns and non-xmlns
attributes (e.g. after optimize_namespaces). On libxml 2.9.14,
parsing the invalid output hangs or crashes with double free.

Fixes:
- canonicalize(): round-trip through saveXml before C14N to
  normalize namespace declarations
- rename_element_namespace(): remove stray Attr::rename() call
  that ran on a detached attribute node after removeAttributeNode()
@veewee veewee merged commit 5521863 into 4.x Mar 27, 2026
12 checks passed
@veewee veewee deleted the workarounds-c14n branch March 27, 2026 10:24
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