Skip to content

Commit 1d4300d

Browse files
NathanFreemanGirgias
authored andcommitted
Fix bug #79451: Using DOMDocument->replaceChild on doctype causes double free
Closes GH-9201
1 parent 72da418 commit 1d4300d

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? 2022, PHP 8.0.24
44

5+
- DOM:
6+
. Fixed bug #79451 (Using DOMDocument->replaceChild on doctype causes
7+
double free) (NathanFreeman)
8+
59
- Streams:
610
. Fixed bug GH-9316 ($http_response_header is wrong for long status line).
711
(cmb, timwolla)

ext/dom/node.c

+15-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#endif
2121

2222
#include "php.h"
23+
2324
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
2425
#include "php_dom.h"
2526

@@ -1001,6 +1002,7 @@ PHP_METHOD(DOMNode, replaceChild)
10011002
xmlNodePtr children, newchild, oldchild, nodep;
10021003
dom_object *intern, *newchildobj, *oldchildobj;
10031004
int foundoldchild = 0, stricterror;
1005+
bool replacedoctype = false;
10041006

10051007
int ret;
10061008

@@ -1063,13 +1065,21 @@ PHP_METHOD(DOMNode, replaceChild)
10631065
dom_reconcile_ns(nodep->doc, newchild);
10641066
}
10651067
} else if (oldchild != newchild) {
1068+
xmlDtdPtr intSubset = xmlGetIntSubset(nodep->doc);
1069+
replacedoctype = (intSubset == (xmlDtd *) oldchild);
1070+
10661071
if (newchild->doc == NULL && nodep->doc != NULL) {
10671072
xmlSetTreeDoc(newchild, nodep->doc);
10681073
newchildobj->document = intern->document;
10691074
php_libxml_increment_doc_ref((php_libxml_node_object *)newchildobj, NULL);
10701075
}
1076+
10711077
xmlReplaceNode(oldchild, newchild);
10721078
dom_reconcile_ns(nodep->doc, newchild);
1079+
1080+
if (replacedoctype) {
1081+
nodep->doc->intSubset = (xmlDtd *) newchild;
1082+
}
10731083
}
10741084
DOM_RET_OBJ(oldchild, &ret, intern);
10751085
return;
@@ -1668,7 +1678,7 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
16681678
buf = xmlAllocOutputBuffer(NULL);
16691679
}
16701680

1671-
if (buf != NULL) {
1681+
if (buf != NULL) {
16721682
ret = xmlC14NDocSaveTo(docp, nodeset, exclusive, inclusive_ns_prefixes,
16731683
with_comments, buf);
16741684
}
@@ -1683,9 +1693,9 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
16831693
xmlXPathFreeContext(ctxp);
16841694
}
16851695

1686-
if (buf == NULL || ret < 0) {
1687-
RETVAL_FALSE;
1688-
} else {
1696+
if (buf == NULL || ret < 0) {
1697+
RETVAL_FALSE;
1698+
} else {
16891699
if (mode == 0) {
16901700
#ifdef LIBXML2_NEW_BUFFER
16911701
ret = xmlOutputBufferGetSize(buf);
@@ -1702,7 +1712,7 @@ static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{
17021712
RETVAL_EMPTY_STRING();
17031713
}
17041714
}
1705-
}
1715+
}
17061716

17071717
if (buf) {
17081718
int bytes;

ext/dom/tests/bug79451.phpt

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Bug #79451 (Using DOMDocument->replaceChild on doctype causes double free)
3+
--SKIPIF--
4+
<?php require_once('skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
$dom = new \DOMDocument();
8+
$dom->loadHTML("<!DOCTYPE html><p>hello</p>");
9+
$impl = new \DOMImplementation();
10+
$dt = $impl->createDocumentType("html_replace", "", "");
11+
$dom->replaceChild($dt, $dom->doctype);
12+
13+
var_dump($dom->doctype->name);
14+
echo $dom->saveXML();
15+
?>
16+
--EXPECTF--
17+
string(12) "html_replace"
18+
<?xml version="1.0" standalone="yes"?>
19+
<!DOCTYPE html_replace>
20+
<html><body><p>hello</p></body></html>

0 commit comments

Comments
 (0)