Skip to content

Commit d70f3ba

Browse files
committed
Fix GH-16465: Heap buffer overflow in DOMNode->getElementByTagName
If the input contains NUL bytes then the length doesn't match the actual duplicated string's length. Note that libxml can't handle this properly anyway so we just reject NUL bytes and too long strings. Closes GH-16467.
1 parent ef1c3b8 commit d70f3ba

File tree

4 files changed

+51
-9
lines changed

4 files changed

+51
-9
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ PHP NEWS
2626
. Fixed bug GH-16316 (DOMXPath breaks when not initialized properly).
2727
(nielsdos)
2828
. Add missing hierarchy checks to replaceChild. (nielsdos)
29+
. Fixed bug GH-16465 (Heap buffer overflow in DOMNode->getElementByTagName).
30+
(nielsdos)
2931

3032
- EXIF:
3133
. Fixed bug GH-16409 (Segfault in exif_thumbnail when not dealing with a

ext/dom/element.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,12 @@ static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, b
816816
dom_object *intern, *namednode;
817817
char *name;
818818

819-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
819+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &name, &name_len) == FAILURE) {
820+
RETURN_THROWS();
821+
}
822+
823+
if (name_len > INT_MAX) {
824+
zend_argument_value_error(1, "is too long");
820825
RETURN_THROWS();
821826
}
822827

@@ -1239,7 +1244,17 @@ static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS
12391244
dom_object *intern, *namednode;
12401245
char *uri, *name;
12411246

1242-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1247+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!p", &uri, &uri_len, &name, &name_len) == FAILURE) {
1248+
RETURN_THROWS();
1249+
}
1250+
1251+
if (uri_len > INT_MAX) {
1252+
zend_argument_value_error(1, "is too long");
1253+
RETURN_THROWS();
1254+
}
1255+
1256+
if (name_len > INT_MAX) {
1257+
zend_argument_value_error(2, "is too long");
12431258
RETURN_THROWS();
12441259
}
12451260

ext/dom/php_dom.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,23 +1473,19 @@ void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xml
14731473
const xmlChar* tmp;
14741474

14751475
if (local) {
1476-
int len = local_len > INT_MAX ? -1 : (int) local_len;
1476+
int len = (int) local_len;
14771477
if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)local, len)) != NULL) {
14781478
mapptr->local = BAD_CAST tmp;
14791479
} else {
14801480
mapptr->local = xmlCharStrndup(local, len);
14811481
mapptr->free_local = true;
14821482
}
14831483
mapptr->local_lower = BAD_CAST estrdup(local);
1484-
if (len < 0) {
1485-
zend_str_tolower((char *) mapptr->local_lower, strlen((const char *) mapptr->local_lower));
1486-
} else {
1487-
zend_str_tolower((char *) mapptr->local_lower, len);
1488-
}
1484+
zend_str_tolower((char *) mapptr->local_lower, len);
14891485
}
14901486

14911487
if (ns) {
1492-
int len = ns_len > INT_MAX ? -1 : (int) ns_len;
1488+
int len = (int) ns_len;
14931489
if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ns, len)) != NULL) {
14941490
mapptr->ns = BAD_CAST tmp;
14951491
} else {

ext/dom/tests/gh16465.phpt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
GH-16465 (Heap buffer overflow in DOMNode->getElementByTagName)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$v10 = new DOMElement("a");
9+
try {
10+
$v10->getElementsByTagName("text\0something");
11+
} catch (ValueError $e) {
12+
echo $e->getMessage(), "\n";
13+
}
14+
try {
15+
$v10->getElementsByTagNameNS("", "text\0something");
16+
} catch (ValueError $e) {
17+
echo $e->getMessage(), "\n";
18+
}
19+
try {
20+
$v10->getElementsByTagNameNS("text\0something", "");
21+
} catch (ValueError $e) {
22+
echo $e->getMessage(), "\n";
23+
}
24+
25+
?>
26+
--EXPECT--
27+
DOMElement::getElementsByTagName(): Argument #1 ($qualifiedName) must not contain any null bytes
28+
DOMElement::getElementsByTagNameNS(): Argument #2 ($localName) must not contain any null bytes
29+
DOMElement::getElementsByTagNameNS(): Argument #1 ($namespace) must not contain any null bytes

0 commit comments

Comments
 (0)