Skip to content

ext/dom: UAF node_list_unlink() skips attribute children that still have a live wrapper #23331

Closed

Description

@alexandre-daubois

Description

Originally reported by @ExPatch-LLC.

The following code:

<?php

$doc = new DOMDocument();
$doc->loadXML('<!DOCTYPE root [<!ENTITY e "X">]><root attr="a&e;b"/>');
$attr = $doc->documentElement->getAttributeNode('attr');
$child = $attr->lastChild;
$doc->documentElement->setAttribute('attr', 'updated');
var_dump($child->nodeType);

Produces an UAF.

node_list_unlink() is meant to detach an attribute's children that still have a live PHP wrapper before libxml2 frees them, but it has two exits that abandon the remaining siblings:

  • xmlUnlinkNode(node) zeroes node->next, so the loop advance node = node->next reads NULL and stops;
  • the break on XML_ENTITY_REF_NODE leaves the loop entirely rather than just skipping recursion into the entity reference.

Any child past either exit is then freed by xmlSetProp() / xmlSetNsProp() / xmlFreeProp() while its PHP wrapper stays alive, so the wrapper reads freed memory.

PHP Version

Happens on master

Operating System

No response

Activity

  1. self-assigned this
    on Aug 17, 2026
  2. added a commit that references this issue on Aug 17, 2026
    ec8556a
  3. added a commit that references this issue on Aug 17, 2026
    4b871e3
  4. added a commit that references this issue on Aug 18, 2026
    030de77
  5. added 2 commits that reference this issue on Aug 18, 2026
    d19f248
    e353b8b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions