From bce536067c803c47e33508b5c85798e0ba038d46 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Mon, 29 May 2023 16:16:01 +0200
Subject: [PATCH 1/4] Fix GH-11338: SplFileInfo empty getBasename with more
than one slash
Regressed in 13e4ce386bb7.
Closes GH-11340.
---
NEWS | 4 ++++
ext/spl/spl_directory.c | 4 +++-
ext/spl/tests/gh11338.phpt | 47 ++++++++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+), 1 deletion(-)
create mode 100644 ext/spl/tests/gh11338.phpt
diff --git a/NEWS b/NEWS
index 143418e2a30..e587b833339 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,10 @@ PHP NEWS
. Fix allocation loop in zend_shared_alloc_startup(). (nielsdos)
. Access violation on smm_shared_globals with ALLOC_FALLBACK. (KoudelkaB)
+- SPL:
+ . Fixed bug GH-11338 (SplFileInfo empty getBasename with more than one
+ slash). (nielsdos)
+
- Standard:
. Fix access on NULL pointer in array_merge_recursive(). (ilutov)
. Fix exception handling in array_multisort(). (ilutov)
diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c
index aefa2aa933e..b00a1e66568 100644
--- a/ext/spl/spl_directory.c
+++ b/ext/spl/spl_directory.c
@@ -432,7 +432,9 @@ static void spl_filesystem_info_set_filename(spl_filesystem_object *intern, zend
path_len = ZSTR_LEN(path);
if (path_len > 1 && IS_SLASH_AT(ZSTR_VAL(path), path_len-1)) {
- path_len--;
+ do {
+ path_len--;
+ } while (path_len > 1 && IS_SLASH_AT(ZSTR_VAL(path), path_len - 1));
intern->file_name = zend_string_init(ZSTR_VAL(path), path_len, 0);
} else {
intern->file_name = zend_string_copy(path);
diff --git a/ext/spl/tests/gh11338.phpt b/ext/spl/tests/gh11338.phpt
new file mode 100644
index 00000000000..0a59cea9e74
--- /dev/null
+++ b/ext/spl/tests/gh11338.phpt
@@ -0,0 +1,47 @@
+--TEST--
+GH-11338 (SplFileInfo empty getBasename with more than on slash)
+--FILE--
+getBasename());
+ var_dump($file->getFilename());
+}
+
+test('/dir/anotherdir/basedir//');
+test('/dir/anotherdir/basedir/');
+test('/dir/anotherdir/basedir');
+test('/dir/anotherdir//basedir');
+test('///');
+test('//');
+test('/');
+test('');
+
+?>
+--EXPECT--
+Testing: '/dir/anotherdir/basedir//'
+string(7) "basedir"
+string(7) "basedir"
+Testing: '/dir/anotherdir/basedir/'
+string(7) "basedir"
+string(7) "basedir"
+Testing: '/dir/anotherdir/basedir'
+string(7) "basedir"
+string(7) "basedir"
+Testing: '/dir/anotherdir//basedir'
+string(7) "basedir"
+string(7) "basedir"
+Testing: '///'
+string(0) ""
+string(1) "/"
+Testing: '//'
+string(0) ""
+string(1) "/"
+Testing: '/'
+string(0) ""
+string(1) "/"
+Testing: ''
+string(0) ""
+string(0) ""
From 9c59d22a7bb4dd0b4cd0b138e6cea12686c1868d Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Mon, 29 May 2023 16:39:58 +0200
Subject: [PATCH 2/4] Fix GH-11336: php still tries to unlock the shared memory
ZendSem with opcache.file_cache_only=1 but it was never locked
I chose to check for the value of lock_file instead of checking the
file_cache_only, because it is probably a little bit faster and we're
going to access the lock_file variable anyway. It's also more generic.
Closes GH-11341.
---
NEWS | 2 ++
ext/opcache/ZendAccelerator.c | 4 ++++
ext/opcache/zend_shared_alloc.c | 2 +-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/NEWS b/NEWS
index e587b833339..a0f4c4b10fe 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,8 @@ PHP NEWS
- Opcache:
. Fix allocation loop in zend_shared_alloc_startup(). (nielsdos)
. Access violation on smm_shared_globals with ALLOC_FALLBACK. (KoudelkaB)
+ . Fixed bug GH-11336 (php still tries to unlock the shared memory ZendSem
+ with opcache.file_cache_only=1 but it was never locked). (nielsdos)
- SPL:
. Fixed bug GH-11338 (SplFileInfo empty getBasename with more than one
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 97b82378780..ed0602394ce 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -354,6 +354,10 @@ static inline void accel_unlock_all(void)
#ifdef ZEND_WIN32
accel_deactivate_sub();
#else
+ if (lock_file == -1) {
+ return;
+ }
+
struct flock mem_usage_unlock_all;
mem_usage_unlock_all.l_type = F_UNLCK;
diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c
index afe539bf987..37f6fea9199 100644
--- a/ext/opcache/zend_shared_alloc.c
+++ b/ext/opcache/zend_shared_alloc.c
@@ -52,7 +52,7 @@ zend_smm_shared_globals *smm_shared_globals;
#ifdef ZTS
static MUTEX_T zts_lock;
#endif
-int lock_file;
+int lock_file = -1;
static char lockfile_name[MAXPATHLEN];
#endif
From 154c2510135bf7d4b96374fb34c7c0aa0410e143 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Mon, 29 May 2023 18:36:02 +0200
Subject: [PATCH 3/4] Fix spec compliance error for
DOMDocument::getElementsByTagNameNS
Spec link: https://dom.spec.whatwg.org/#concept-getelementsbytagnamens
Spec says we should match any namespace when '*' is provided. This was
however not the case: elements that didn't have a namespace were not
returned. This patch fixes the error by modifying the namespace check.
Closes GH-11343.
---
NEWS | 2 +
ext/dom/php_dom.c | 7 +-
...ementsByTagNameNS_match_any_namespace.phpt | 82 +++++++++++++++++++
3 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 ext/dom/tests/DOMDocument_getElementsByTagNameNS_match_any_namespace.phpt
diff --git a/NEWS b/NEWS
index a0f4c4b10fe..1b40b1bceb4 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,8 @@ PHP NEWS
. Fixed bug GH-10234 (Setting DOMAttr::textContent results in an empty
attribute value). (nielsdos)
. Fix return value in stub file for DOMNodeList::item. (divinity76)
+ . Fix spec compliance error with '*' namespace for
+ DOMDocument::getElementsByTagNameNS. (nielsdos)
- Opcache:
. Fix allocation loop in zend_shared_alloc_startup(). (nielsdos)
diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c
index 01a206c0985..1883767d2e4 100644
--- a/ext/dom/php_dom.c
+++ b/ext/dom/php_dom.c
@@ -1270,10 +1270,15 @@ xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr nodep, char *ns, char *l
{
xmlNodePtr ret = NULL;
+ /* Note: The spec says that ns == '' must be transformed to ns == NULL. In other words, they are equivalent.
+ * PHP however does not do this and internally uses the empty string everywhere when the user provides ns == NULL.
+ * This is because for PHP ns == NULL has another meaning: "match every namespace" instead of "match the empty namespace". */
+ bool ns_match_any = ns == NULL || (ns[0] == '*' && ns[1] == '\0');
+
while (nodep != NULL && (*cur <= index || index == -1)) {
if (nodep->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(nodep->name, (xmlChar *)local) || xmlStrEqual((xmlChar *)"*", (xmlChar *)local)) {
- if (ns == NULL || (!strcmp(ns, "") && nodep->ns == NULL) || (nodep->ns != NULL && (xmlStrEqual(nodep->ns->href, (xmlChar *)ns) || xmlStrEqual((xmlChar *)"*", (xmlChar *)ns)))) {
+ if (ns_match_any || (!strcmp(ns, "") && nodep->ns == NULL) || (nodep->ns != NULL && xmlStrEqual(nodep->ns->href, (xmlChar *)ns))) {
if (*cur == index) {
ret = nodep;
break;
diff --git a/ext/dom/tests/DOMDocument_getElementsByTagNameNS_match_any_namespace.phpt b/ext/dom/tests/DOMDocument_getElementsByTagNameNS_match_any_namespace.phpt
new file mode 100644
index 00000000000..888d1ef9b80
--- /dev/null
+++ b/ext/dom/tests/DOMDocument_getElementsByTagNameNS_match_any_namespace.phpt
@@ -0,0 +1,82 @@
+--TEST--
+DOMDocument::getElementsByTagNameNS() match any namespace
+--EXTENSIONS--
+dom
+--FILE--
+
+
helloworld
'); + +echo "-- Append hello with world --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_hello->append($b_world); +var_dump($dom->saveHTML()); + +echo "-- Append hello with world's child --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_hello->append($b_world->firstChild); +var_dump($dom->saveHTML()); + +echo "-- Append world's child with hello --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_world->firstChild->append($b_hello); +var_dump($dom->saveHTML()); + +echo "-- Append hello with itself --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +try { + $b_hello->append($b_hello); +} catch (\DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom->saveHTML()); + +echo "-- Append world's i tag with the parent --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +try { + $b_world->firstChild->append($b_world); +} catch (\DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom->saveHTML()); + +echo "-- Append from another document --\n"; +$dom = clone $dom_original; +$dom2 = new DOMDocument; +$dom2->loadXML('other
'); +try { + $dom->firstChild->firstChild->prepend($dom2->firstChild); +} catch (\DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom2->saveHTML()); +var_dump($dom->saveHTML()); + +?> +--EXPECT-- +-- Append hello with world -- +string(39) "helloworld
+" +-- Append hello with world's child -- +string(39) "helloworld
+" +-- Append world's child with hello -- +string(39) "worldhello
+" +-- Append hello with itself -- +Hierarchy Request Error +string(39) "helloworld
+" +-- Append world's i tag with the parent -- +Hierarchy Request Error +string(39) "helloworld
+" +-- Append from another document -- +Wrong Document Error +string(13) "other
+" +string(39) "helloworld
+" diff --git a/ext/dom/tests/DOMElement_prepend_hierarchy_test.phpt b/ext/dom/tests/DOMElement_prepend_hierarchy_test.phpt new file mode 100644 index 00000000000..4d9cf24a618 --- /dev/null +++ b/ext/dom/tests/DOMElement_prepend_hierarchy_test.phpt @@ -0,0 +1,89 @@ +--TEST-- +DOMElement::prepend() with hierarchy changes and errors +--EXTENSIONS-- +dom +--FILE-- +loadXML('helloworld
'); + +echo "-- Prepend hello with world --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_hello->prepend($b_world); +var_dump($dom->saveHTML()); + +echo "-- Prepend hello with world's child --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_hello->prepend($b_world->firstChild); +var_dump($dom->saveHTML()); + +echo "-- Prepend world's child with hello --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +$b_world->firstChild->prepend($b_hello); +var_dump($dom->saveHTML()); + +echo "-- Prepend hello with itself --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +try { + $b_hello->prepend($b_hello); +} catch (\DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom->saveHTML()); + +echo "-- Prepend world's i tag with the parent --\n"; +$dom = clone $dom_original; +$b_hello = $dom->firstChild->firstChild; +$b_world = $b_hello->nextSibling; +try { + $b_world->firstChild->prepend($b_world); +} catch (\DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom->saveHTML()); + +echo "-- Append from another document --\n"; +$dom = clone $dom_original; +$dom2 = new DOMDocument; +$dom2->loadXML('other
'); +try { + $dom->firstChild->firstChild->prepend($dom2->firstChild); +} catch (\DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($dom2->saveHTML()); +var_dump($dom->saveHTML()); + +?> +--EXPECT-- +-- Prepend hello with world -- +string(39) "worldhello
+" +-- Prepend hello with world's child -- +string(39) "worldhello
+" +-- Prepend world's child with hello -- +string(39) "helloworld
+" +-- Prepend hello with itself -- +Hierarchy Request Error +string(39) "helloworld
+" +-- Prepend world's i tag with the parent -- +Hierarchy Request Error +string(39) "helloworld
+" +-- Append from another document -- +Wrong Document Error +string(13) "other
+" +string(39) "helloworld
+"