Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ PHP NEWS
- DOM:
. Fixed bug GH-22624 (use-after-free via DOMNameSpaceNode after
DOMDocument::xinclude()). (David Carlier)
. Fixed a use-after-free when cloning a DOMNameSpaceNode after
DOMDocument::xinclude(). (iliaal)
. Fixed a crash in DOMXPath when a php:function callback receives a nodeset
and a later callback returns a node from another document. (iliaal)

- PDO_PGSQL:
. Fixed several lazy fetch (PDO::ATTR_PREFETCH => 0) defects: an infinite
Expand All @@ -25,6 +29,12 @@ PHP NEWS
left busy for the next fetch, and rows delivered from a result another
statement took over. (KentarouTakeda)

- Intl:
. Fixed grapheme_strrev() treating UBRK_DONE as a byte index and leaving
the result without a terminating NUL. (iliaal)
. Fixed a double-free when IntlGregorianCalendar construction fails after
the ICU constructor adopts the TimeZone. (iliaal)

- Phar:
. Fixed Phar archives being automatically detected when ".phar" only occurs
in a directory name or is not a filename extension in an included file's
Expand Down
10 changes: 9 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1224,7 +1224,15 @@ AS_CASE([$host_cpu],

AS_CASE([$host_os],
[darwin*], [fiber_os="mac"],
[aix*|os400*], [fiber_os="aix"],
[aix*|os400*], [
fiber_os="aix"
dnl AIX triplets start w/ powerpc- even though it may be 64-bit (as AIX
dnl has a multiple bitness world). Check if the build is 64-bit, or we
dnl end up using the wrong fibre asm (w/o forcing a powerpc64 triplet)
AS_IF([test "$ac_cv_sizeof_long" -eq 8], [
fiber_cpu="ppc64"
], [])
],
[freebsd*], [fiber_os="freebsd"],
[midipix], [fiber_os="midipix"],
[fiber_os="other"]
Expand Down
14 changes: 8 additions & 6 deletions ext/dom/php_dom.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ static HashTable dom_xpath_prop_handlers;

static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type);
static void dom_object_namespace_node_free_storage(zend_object *object);
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original);
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original, xmlDocPtr fallback_doc);

typedef zend_result (*dom_read_t)(dom_object *obj, zval *retval);
typedef zend_result (*dom_write_t)(dom_object *obj, zval *newval);
Expand Down Expand Up @@ -725,7 +725,8 @@ static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject)
xmlNodePtr original_node = dom_object_get_node(&intern->dom);
if (original_node != NULL) {
ZEND_ASSERT(original_node->type == XML_NAMESPACE_DECL);
xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(original_node->parent, original_node->ns);
xmlNodePtr parent = intern->parent_intern ? dom_object_get_node(intern->parent_intern) : NULL;
xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(parent, original_node->ns, original_node->doc);
dom_update_refcount_after_clone(&intern->dom, original_node, &clone_intern->dom, cloned_node);
}

Expand Down Expand Up @@ -2262,15 +2263,16 @@ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
}
/* }}} end dom_get_nsdecl */

static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original)
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original, xmlDocPtr fallback_doc)
{
xmlNodePtr attrp;
xmlDocPtr doc = nodep ? nodep->doc : fallback_doc;
xmlNsPtr curns = xmlNewNs(NULL, original->href, NULL);
if (original->prefix) {
curns->prefix = xmlStrdup(original->prefix);
attrp = xmlNewDocNode(nodep->doc, NULL, BAD_CAST original->prefix, original->href);
attrp = xmlNewDocNode(doc, NULL, BAD_CAST original->prefix, original->href);
} else {
attrp = xmlNewDocNode(nodep->doc, NULL, BAD_CAST "xmlns", original->href);
attrp = xmlNewDocNode(doc, NULL, BAD_CAST "xmlns", original->href);
}
attrp->type = XML_NAMESPACE_DECL;
attrp->parent = nodep;
Expand All @@ -2281,7 +2283,7 @@ static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep,
/* Note: Assumes the additional lifetime was already added in the caller. */
xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern)
{
xmlNodePtr attrp = php_dom_create_fake_namespace_decl_node_ptr(nodep, original);
xmlNodePtr attrp = php_dom_create_fake_namespace_decl_node_ptr(nodep, original, NULL);
php_dom_create_object(attrp, return_value, parent_intern);
/* This object must exist, because we just created an object for it via php_dom_create_object(). */
php_dom_namespace_node_obj_from_obj(Z_OBJ_P(return_value))->parent_intern = parent_intern;
Expand Down
44 changes: 44 additions & 0 deletions ext/dom/tests/dom_namespacenode_clone_xinclude.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
--TEST--
DOMNameSpaceNode clone after xinclude does not use a dangling parent
--EXTENSIONS--
dom
--FILE--
<?php
$included = __DIR__ . '/dom_namespacenode_clone_xinclude_included.xml';
file_put_contents($included, '<?xml version="1.0"?><included/>');
$href = 'file:///' . ltrim(str_replace('\\', '/', $included), '/');

$doc = new DOMDocument();
$doc->loadXML('<?xml version="1.0"?>
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="' . $href . '" xmlns:local="urn:test"/>
</root>');

$xpath = new DOMXPath($doc);
$xpath->registerNamespace('xi', 'http://www.w3.org/2001/XInclude');
$xi = $xpath->query('//xi:include')->item(0);
$ns = $xpath->query('namespace::local', $xi)->item(0);

$live = clone $ns;
echo "live clone: ", $live->nodeName, "\n";
echo "live parent: ", $live->parentNode->nodeName, "\n";

$doc->xinclude();

$clone = clone $ns;
echo "after xinclude: ", $clone->nodeName, "\n";
var_dump($clone->parentNode);
var_dump($clone->parentElement);
var_dump($clone->isConnected);
?>
--CLEAN--
<?php
@unlink(__DIR__ . '/dom_namespacenode_clone_xinclude_included.xml');
?>
--EXPECT--
live clone: xmlns:local
live parent: xi:include
after xinclude: xmlns:local
NULL
NULL
bool(false)
40 changes: 40 additions & 0 deletions ext/dom/tests/xpath_php_function_foreign_doc.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
DOMXPath: php:function nodeset args plus a foreign-document return must not treat arrays as DOM objects
--EXTENSIONS--
dom
--FILE--
<?php
$doc1 = new DOMDocument();
$doc1->loadXML('<root><a>1</a></root>');
$doc2 = new DOMDocument();
$doc2->loadXML('<root><b>2</b></root>');

$xp = new DOMXPath($doc1);
$xp->registerNamespace('php', 'http://php.net/xpath');
$xp->registerPhpFunctions();

function uses_nodeset($nodes) {
return true;
}

function foreign() {
global $doc2;
return $doc2->documentElement;
}

$xp->query('//a[php:function("uses_nodeset", //a)]');
$res = $xp->query('php:function("foreign")');
echo "count: ";
var_dump($res->length);
$n = $res->item(0);
echo "name: ";
var_dump($n->nodeName);
echo "owner is doc2: ";
var_dump($n->ownerDocument === $doc2);
echo "done\n";
?>
--EXPECT--
count: int(1)
name: string(4) "root"
owner is doc2: bool(true)
done
23 changes: 21 additions & 2 deletions ext/dom/xpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@

#ifdef LIBXML_XPATH_ENABLED

static dom_object *dom_xpath_intern_from_entry(zval *entry, xmlDocPtr doc)
{
if (Z_TYPE_P(entry) == IS_OBJECT) {
dom_object *obj = Z_DOMOBJ_P(entry);
if (obj->document && obj->document->ptr == doc) {
return obj;
}
} else if (Z_TYPE_P(entry) == IS_ARRAY) {
zval *inner;
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), inner) {
dom_object *obj = dom_xpath_intern_from_entry(inner, doc);
if (obj) {
return obj;
}
} ZEND_HASH_FOREACH_END();
}
return NULL;
}

static dom_object *dom_xpath_intern_for_doc(dom_xpath_object *xpath_obj, xmlDocPtr doc)
{
if (xpath_obj->dom.document && xpath_obj->dom.document->ptr == doc) {
Expand All @@ -42,8 +61,8 @@ static dom_object *dom_xpath_intern_for_doc(dom_xpath_object *xpath_obj, xmlDocP
if (node_list) {
zval *entry;
ZEND_HASH_PACKED_FOREACH_VAL(node_list, entry) {
dom_object *obj = Z_DOMOBJ_P(entry);
if (obj->document && obj->document->ptr == doc) {
dom_object *obj = dom_xpath_intern_from_entry(entry, doc);
if (obj) {
return obj;
}
} ZEND_HASH_FOREACH_END();
Expand Down
1 change: 0 additions & 1 deletion ext/intl/calendar/gregoriancalendar_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ static void _php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAMETERS, bool
if (U_FAILURE(status)) {
intl_error_set(NULL, status, "error creating ICU "
"GregorianCalendar from time zone and locale");
delete tz;
if (!is_constructor) {
zval_ptr_dtor(return_value);
RETVAL_NULL();
Expand Down
4 changes: 4 additions & 0 deletions ext/intl/grapheme/grapheme_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1175,13 +1175,17 @@ U_CFUNC PHP_FUNCTION(grapheme_strrev)
current = ZSTR_LEN(string);
for (end = pstr; pos != UBRK_DONE; ) {
pos = ubrk_previous(bi);
if (pos == UBRK_DONE) {
break;
}
end_len = current - pos;
for (int32_t j = 0; j < end_len; j++) {
*p++ = *(pstr + pos + j);
}
current = pos;
}
ubrk_end:
ZSTR_VAL(ret)[ZSTR_LEN(ret)] = '\0';
RETVAL_NEW_STR(ret);
ubrk_close(bi);
close:
Expand Down
25 changes: 25 additions & 0 deletions ext/intl/tests/grapheme_strrev_ubrk_done.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
grapheme_strrev() stops at UBRK_DONE instead of using it as a byte index
--EXTENSIONS--
intl
--FILE--
<?php

$cases = [
'abc',
'a',
'土下座',
"null\x00byte",
];

foreach ($cases as $s) {
$rev = grapheme_strrev($s);
echo strlen($s), ' ', strlen($rev), ' ', bin2hex($rev), "\n";
}

?>
--EXPECT--
3 3 636261
1 1 61
9 9 e5baa7e4b88be59c9f
9 9 65747962006c6c756e
36 changes: 31 additions & 5 deletions ext/mysqli/tests/fake_server.inc
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,32 @@ class my_mysqli_fake_server_conn
}
}

private function read_bytes(int $length): string
{
$data = '';
while (strlen($data) < $length) {
$chunk = fread($this->conn, $length - strlen($data));
if ($chunk === false || $chunk === '') {
throw new RuntimeException("Failed to read $length bytes from the client");
}
$data .= $chunk;
}
return $data;
}

public function read_packets(int $packet_count): void
{
$data = '';
for ($i = 0; $i < $packet_count; $i++) {
$header = $this->read_bytes(4);
$packet_length = ord($header[0])
| (ord($header[1]) << 8)
| (ord($header[2]) << 16);
$data .= $header . $this->read_bytes($packet_length);
}
fprintf(STDERR, "[*] Received: %s\n", bin2hex($data));
}

public function close()
{
fclose($this->conn);
Expand Down Expand Up @@ -732,7 +758,7 @@ function my_mysqli_test_stmt_response_row_over_read_two_fields(
$conn->packets_to_bytes($rh),
"Malicious Stmt Response for data $field_name [Extract heap through buffer over-read]"
);
$conn->read(65536);
$conn->read_packets(2);
}

function my_mysqli_test_stmt_response_row_over_read_int(my_mysqli_fake_server_conn $conn): void
Expand Down Expand Up @@ -778,15 +804,15 @@ function my_mysqli_test_stmt_response_row_over_read_bit(my_mysqli_fake_server_co
function my_mysqli_test_stmt_response_row_read_two_fields(my_mysqli_fake_server_conn $conn): void
{
$conn->send_server_greetings();
$conn->read();
$conn->read_packets(1);
$conn->send_server_ok();
$conn->read();
$conn->read_packets(1);
$field_names = array_keys(my_mysqli_data_fields());
foreach ($field_names as $field_name) {
$conn->send_server_stmt_prepare_data_response($field_name);
$conn->read(65536);
$conn->read_packets(1);
$conn->send_server_stmt_execute_data_response($field_name);
$conn->read(65536);
$conn->read_packets(2);
}
}

Expand Down