From 78ae0886bd1a3e42c53c9ba65764b6e6357640b5 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 29 Apr 2023 21:07:50 +0200 Subject: [PATCH 05/11] Fix GH-11160: Few tests failed building with new libxml 2.11.0 It's possible to categorise the failures into 2 categories: - Changed error message. In this case we either duplicate the test and modify the error message. Or if the change in error message is small, we use the EXPECTF matchers to make the test compatible with both old and new versions of libxml2. - Missing warnings. This is caused by a change in libxml2 where the parser started using SAX APIs internally [1]. In this case the error_type passed to php_libxml_internal_error_handler() changed from PHP_LIBXML_ERROR to PHP_LIBXML_CTX_WARNING because it internally started to use the SAX handlers instead of the generic handlers. However, for the SAX handlers the current input stack is empty, so nothing is actually printed. I fixed this by falling back to a regular warning without a filename & line number reference, which mimicks the old behaviour. Furthermore, this change now also shows an additional warning in a test which was previously hidden. [1] https://gitlab.gnome.org/GNOME/libxml2/-/commit/9a82b94a94bd310db426edd453b0f38c6c8f69f5 Closes GH-11162. (cherry picked from commit 7c0dfc5cf58d3c445b935fa14ea8f5f13568c419) --- .../DOMDocument_loadXML_error2_gte2_11.phpt | 34 +++ ...> DOMDocument_loadXML_error2_pre2_11.phpt} | 4 + .../DOMDocument_load_error2_gte2_11.phpt | 34 +++ ...t => DOMDocument_load_error2_pre2_11.phpt} | 4 + ext/libxml/libxml.c | 2 + ext/libxml/tests/bug61367-read_2.phpt | 2 +- .../tests/libxml_disable_entity_loader_2.phpt | 2 +- ...set_external_entity_loader_variation2.phpt | 2 + ext/openssl/tests/ServerClientTestCase.inc | 65 ++---- .../tests/http/ServerClientTestCase.inc | 199 ++++++++++++++++++ .../tests/http/ghsa-52jp-hrpf-2jff-001.phpt | 2 +- .../tests/http/ghsa-52jp-hrpf-2jff-002.phpt | 2 +- .../tests/http/ghsa-hgf5-96fm-v528-001.phpt | 2 +- .../tests/http/ghsa-hgf5-96fm-v528-002.phpt | 2 +- .../tests/http/ghsa-hgf5-96fm-v528-003.phpt | 2 +- .../tests/http/ghsa-pcmh-g36c-qc44-001.phpt | 2 +- .../tests/http/ghsa-pcmh-g36c-qc44-002.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt | 2 +- ext/xml/tests/bug26614_libxml_gte2_11.phpt | 95 +++++++++ ...bxml.phpt => bug26614_libxml_pre2_11.phpt} | 1 + 24 files changed, 404 insertions(+), 64 deletions(-) create mode 100644 ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt rename ext/dom/tests/{DOMDocument_loadXML_error2.phpt => DOMDocument_loadXML_error2_pre2_11.phpt} (90%) create mode 100644 ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt rename ext/dom/tests/{DOMDocument_load_error2.phpt => DOMDocument_load_error2_pre2_11.phpt} (90%) create mode 100644 ext/standard/tests/http/ServerClientTestCase.inc create mode 100644 ext/xml/tests/bug26614_libxml_gte2_11.phpt rename ext/xml/tests/{bug26614_libxml.phpt => bug26614_libxml_pre2_11.phpt} (96%) diff --git a/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt new file mode 100644 index 00000000000..ff5ceb3fbed --- /dev/null +++ b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test DOMDocument::loadXML() detects not-well formed XML +--SKIPIF-- += 2.11'); +?> +--DESCRIPTION-- +This test verifies the method detects attributes values not closed between " or ' +Environment variables used in the test: +- XML_FILE: the xml file to load +- LOAD_OPTIONS: the second parameter to pass to the method +- EXPECTED_RESULT: the expected result +--CREDITS-- +Antonio Diaz Ruiz +--INI-- +assert.bail=true +--EXTENSIONS-- +dom +--ENV-- +XML_FILE=/not_well_formed2.xml +LOAD_OPTIONS=0 +EXPECTED_RESULT=0 +--FILE_EXTERNAL-- +domdocumentloadxml_test_method.inc +--EXPECTF-- +Warning: DOMDocument::loadXML(): AttValue: " or ' expected in Entity, line: 4 in %s on line %d + +Warning: DOMDocument::loadXML(): internal error: xmlParseStartTag: problem parsing attributes in Entity, line: 4 in %s on line %d + +Warning: DOMDocument::loadXML(): Couldn't find end of Start Tag book line 4 in Entity, line: 4 in %s on line %d + +Warning: DOMDocument::loadXML(): Opening and ending tag mismatch: books line 3 and book in Entity, line: 7 in %s on line %d + +Warning: DOMDocument::loadXML(): Extra content at the end of the document in Entity, line: 8 in %s on line %d diff --git a/ext/dom/tests/DOMDocument_loadXML_error2.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt similarity index 90% rename from ext/dom/tests/DOMDocument_loadXML_error2.phpt rename to ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt index 6d56a317ed7..0e36d209058 100644 --- a/ext/dom/tests/DOMDocument_loadXML_error2.phpt +++ b/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt @@ -1,5 +1,9 @@ --TEST-- Test DOMDocument::loadXML() detects not-well formed XML +--SKIPIF-- += 21100) die('skip libxml2 test variant for version < 2.11'); +?> --DESCRIPTION-- This test verifies the method detects attributes values not closed between " or ' Environment variables used in the test: diff --git a/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt b/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt new file mode 100644 index 00000000000..32b6bf16114 --- /dev/null +++ b/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test DOMDocument::load() detects not-well formed +--SKIPIF-- += 2.11'); +?> +--DESCRIPTION-- +This test verifies the method detects attributes values not closed between " or ' +Environment variables used in the test: +- XML_FILE: the xml file to load +- LOAD_OPTIONS: the second parameter to pass to the method +- EXPECTED_RESULT: the expected result +--CREDITS-- +Antonio Diaz Ruiz +--INI-- +assert.bail=true +--EXTENSIONS-- +dom +--ENV-- +XML_FILE=/not_well_formed2.xml +LOAD_OPTIONS=0 +EXPECTED_RESULT=0 +--FILE_EXTERNAL-- +domdocumentload_test_method.inc +--EXPECTF-- +Warning: DOMDocument::load(): AttValue: " or ' expected in %s on line %d + +Warning: DOMDocument::load(): internal error: xmlParseStartTag: problem parsing attributes in %s on line %d + +Warning: DOMDocument::load(): Couldn't find end of Start Tag book line 4 in %s on line %d + +Warning: DOMDocument::load(): Opening and ending tag mismatch: books line 3 and book in %s on line %d + +Warning: DOMDocument::load(): Extra content at the end of the document in %s on line %d diff --git a/ext/dom/tests/DOMDocument_load_error2.phpt b/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt similarity index 90% rename from ext/dom/tests/DOMDocument_load_error2.phpt rename to ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt index f450cf16545..b97fff9d2f1 100644 --- a/ext/dom/tests/DOMDocument_load_error2.phpt +++ b/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt @@ -1,5 +1,9 @@ --TEST-- Test DOMDocument::load() detects not-well formed XML +--SKIPIF-- += 21100) die('skip libxml2 test variant for version < 2.11'); +?> --DESCRIPTION-- This test verifies the method detects attributes values not closed between " or ' Environment variables used in the test: diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 73486ae253f..c8bd1be60a4 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -525,6 +525,8 @@ static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg) } else { php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line); } + } else { + php_error_docref(NULL, E_WARNING, "%s", msg); } } diff --git a/ext/libxml/tests/bug61367-read_2.phpt b/ext/libxml/tests/bug61367-read_2.phpt index ed6576aa752..b935261cb2e 100644 --- a/ext/libxml/tests/bug61367-read_2.phpt +++ b/ext/libxml/tests/bug61367-read_2.phpt @@ -55,6 +55,6 @@ bool(true) int(4) bool(true) -Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "file:///%s/test_bug_61367-read/bad" in %s on line %d +Warning: DOMDocument::loadXML(): %Sfailed to load external entity "file:///%s/test_bug_61367-read/bad" in %s on line %d Warning: Attempt to read property "nodeValue" on null in %s on line %d diff --git a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt index caa7356ad30..d90f909ac2b 100644 --- a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt +++ b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt @@ -38,6 +38,6 @@ bool(true) Deprecated: Function libxml_disable_entity_loader() is deprecated in %s on line %d bool(false) -Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "%s" in %s on line %d +Warning: DOMDocument::loadXML(): %Sfailed to load external entity "%s" in %s on line %d bool(true) Done diff --git a/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt b/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt index 87894bcb91a..ddaf9bfa50e 100644 --- a/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt +++ b/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt @@ -39,6 +39,8 @@ echo "Done.\n"; string(10) "-//FOO/BAR" string(%d) "%sfoobar.dtd" +Warning: DOMDocument::validate(): Failed to load external entity "-//FOO/BAR" in %s on line %d + Warning: DOMDocument::validate(): Could not load the external subset "foobar.dtd" in %s on line %d bool(false) bool(true) diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc index 61d45385b62..753366df6f4 100644 --- a/ext/openssl/tests/ServerClientTestCase.inc +++ b/ext/openssl/tests/ServerClientTestCase.inc @@ -4,19 +4,14 @@ const WORKER_ARGV_VALUE = 'RUN_WORKER'; const WORKER_DEFAULT_NAME = 'server'; -function phpt_notify(string $worker = WORKER_DEFAULT_NAME, string $message = ""): void +function phpt_notify($worker = WORKER_DEFAULT_NAME) { - ServerClientTestCase::getInstance()->notify($worker, $message); + ServerClientTestCase::getInstance()->notify($worker); } -function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null): ?string +function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null) { - return ServerClientTestCase::getInstance()->wait($worker, $timeout); -} - -function phpt_notify_server_start($server): void -{ - ServerClientTestCase::getInstance()->notify_server_start($server); + ServerClientTestCase::getInstance()->wait($worker, $timeout); } function phpt_has_sslv3() { @@ -124,73 +119,43 @@ class ServerClientTestCase eval($code); } - /** - * Run client and all workers - * - * @param string $clientCode The client PHP code - * @param string|array $workerCode - * @param bool $ephemeral Select whether automatic port selection and automatic awaiting is used - * @return void - * @throws Exception - */ - public function run(string $clientCode, string|array $workerCode, bool $ephemeral = true): void + public function run($masterCode, $workerCode) { if (!is_array($workerCode)) { $workerCode = [WORKER_DEFAULT_NAME => $workerCode]; } - reset($workerCode); - $code = current($workerCode); - $worker = key($workerCode); - while ($worker != null) { + foreach ($workerCode as $worker => $code) { $this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code)); - $code = next($workerCode); - if ($ephemeral) { - $addr = trim($this->wait($worker)); - if (empty($addr)) { - throw new \Exception("Failed server start"); - } - if ($code === false) { - $clientCode = preg_replace('/{{\s*ADDR\s*}}/', $addr, $clientCode); - } else { - $code = preg_replace('/{{\s*ADDR\s*}}/', $addr, $code); - } - } - $worker = key($workerCode); } - - eval($this->stripPhpTagsFromCode($clientCode)); + eval($this->stripPhpTagsFromCode($masterCode)); foreach ($workerCode as $worker => $code) { $this->cleanupWorkerProcess($worker); } } - public function wait($worker, $timeout = null): ?string + public function wait($worker, $timeout = null) { $handle = $this->isWorker ? STDIN : $this->workerStdOut[$worker]; if ($timeout === null) { - return fgets($handle); + fgets($handle); + return true; } stream_set_blocking($handle, false); $read = [$handle]; $result = stream_select($read, $write, $except, $timeout); if (!$result) { - return null; + return false; } - $result = fgets($handle); + fgets($handle); stream_set_blocking($handle, true); - return $result; - } - - public function notify(string $worker, string $message = ""): void - { - fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "$message\n"); + return true; } - public function notify_server_start($server): void + public function notify($worker) { - echo stream_socket_get_name($server, false) . "\n"; + fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n"); } } diff --git a/ext/standard/tests/http/ServerClientTestCase.inc b/ext/standard/tests/http/ServerClientTestCase.inc new file mode 100644 index 00000000000..61d45385b62 --- /dev/null +++ b/ext/standard/tests/http/ServerClientTestCase.inc @@ -0,0 +1,199 @@ +notify($worker, $message); +} + +function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null): ?string +{ + return ServerClientTestCase::getInstance()->wait($worker, $timeout); +} + +function phpt_notify_server_start($server): void +{ + ServerClientTestCase::getInstance()->notify_server_start($server); +} + +function phpt_has_sslv3() { + static $result = null; + if (!is_null($result)) { + return $result; + } + $server = @stream_socket_server('sslv3://127.0.0.1:10013'); + if ($result = !!$server) { + fclose($server); + } + return $result; +} + +/** + * This is a singleton to let the wait/notify functions work + * I know it's horrible, but it's a means to an end + */ +class ServerClientTestCase +{ + private $isWorker = false; + + private $workerHandle = []; + + private $workerStdIn = []; + + private $workerStdOut = []; + + private static $instance; + + public static function getInstance($isWorker = false) + { + if (!isset(self::$instance)) { + self::$instance = new self($isWorker); + } + + return self::$instance; + } + + public function __construct($isWorker = false) + { + if (!isset(self::$instance)) { + self::$instance = $this; + } + + $this->isWorker = $isWorker; + } + + private function spawnWorkerProcess($worker, $code) + { + if (defined("PHP_WINDOWS_VERSION_MAJOR")) { + $ini = php_ini_loaded_file(); + $cmd = sprintf( + '%s %s "%s" %s', + PHP_BINARY, $ini ? "-n -c $ini" : "", + __FILE__, + WORKER_ARGV_VALUE + ); + } else { + $cmd = sprintf( + '%s "%s" %s %s', + PHP_BINARY, + __FILE__, + WORKER_ARGV_VALUE, + $worker + ); + } + $this->workerHandle[$worker] = proc_open( + $cmd, + [['pipe', 'r'], ['pipe', 'w'], STDERR], + $pipes + ); + $this->workerStdIn[$worker] = $pipes[0]; + $this->workerStdOut[$worker] = $pipes[1]; + + fwrite($this->workerStdIn[$worker], $code . "\n---\n"); + } + + private function cleanupWorkerProcess($worker) + { + fclose($this->workerStdIn[$worker]); + fclose($this->workerStdOut[$worker]); + proc_close($this->workerHandle[$worker]); + } + + private function stripPhpTagsFromCode($code) + { + return preg_replace('/^\s*<\?(?:php)?|\?>\s*$/i', '', $code); + } + + public function runWorker() + { + $code = ''; + + while (1) { + $line = fgets(STDIN); + + if (trim($line) === "---") { + break; + } + + $code .= $line; + } + + eval($code); + } + + /** + * Run client and all workers + * + * @param string $clientCode The client PHP code + * @param string|array $workerCode + * @param bool $ephemeral Select whether automatic port selection and automatic awaiting is used + * @return void + * @throws Exception + */ + public function run(string $clientCode, string|array $workerCode, bool $ephemeral = true): void + { + if (!is_array($workerCode)) { + $workerCode = [WORKER_DEFAULT_NAME => $workerCode]; + } + reset($workerCode); + $code = current($workerCode); + $worker = key($workerCode); + while ($worker != null) { + $this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code)); + $code = next($workerCode); + if ($ephemeral) { + $addr = trim($this->wait($worker)); + if (empty($addr)) { + throw new \Exception("Failed server start"); + } + if ($code === false) { + $clientCode = preg_replace('/{{\s*ADDR\s*}}/', $addr, $clientCode); + } else { + $code = preg_replace('/{{\s*ADDR\s*}}/', $addr, $code); + } + } + $worker = key($workerCode); + } + + eval($this->stripPhpTagsFromCode($clientCode)); + foreach ($workerCode as $worker => $code) { + $this->cleanupWorkerProcess($worker); + } + } + + public function wait($worker, $timeout = null): ?string + { + $handle = $this->isWorker ? STDIN : $this->workerStdOut[$worker]; + if ($timeout === null) { + return fgets($handle); + } + + stream_set_blocking($handle, false); + $read = [$handle]; + $result = stream_select($read, $write, $except, $timeout); + if (!$result) { + return null; + } + + $result = fgets($handle); + stream_set_blocking($handle, true); + return $result; + } + + public function notify(string $worker, string $message = ""): void + { + fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "$message\n"); + } + + public function notify_server_start($server): void + { + echo stream_socket_get_name($server, false) . "\n"; + } +} + +if (isset($argv[1]) && $argv[1] === WORKER_ARGV_VALUE) { + ServerClientTestCase::getInstance(true)->runWorker(); +} diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt index 744cff9cc72..461a649b147 100644 --- a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt +++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt @@ -39,7 +39,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt index bc71fd4e411..126b77bae62 100644 --- a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt +++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt @@ -39,7 +39,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt index c40123560ef..0f04f565d6b 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt @@ -36,7 +36,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt index 37a47df060a..aa23a96aedc 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt @@ -36,7 +36,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt index 6c84679ff63..8ef42b5700f 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt @@ -36,7 +36,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt index bb7945ce62d..595f0fd9272 100644 --- a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt +++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt @@ -35,7 +35,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt index 1d0e4fa70a2..99c8e025f93 100644 --- a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt +++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt @@ -35,7 +35,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt index f935b5a02ca..945225d9e06 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt @@ -35,7 +35,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt index 078d605b671..6619db3a5dd 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt @@ -35,7 +35,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt index ad5ddc879ce..7eb9015d963 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt @@ -35,7 +35,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt index d0396e819fb..f8f67886634 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt @@ -35,7 +35,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt index 037d2002cc5..671c82e8ee0 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt @@ -35,7 +35,7 @@ $clientCode = <<<'CODE' var_dump($http_response_header); CODE; -include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); +include sprintf("%s/ServerClientTestCase.inc", __DIR__); ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ?> --EXPECTF-- diff --git a/ext/xml/tests/bug26614_libxml_gte2_11.phpt b/ext/xml/tests/bug26614_libxml_gte2_11.phpt new file mode 100644 index 00000000000..9a81b67686d --- /dev/null +++ b/ext/xml/tests/bug26614_libxml_gte2_11.phpt @@ -0,0 +1,95 @@ +--TEST-- +Bug #26614 (CDATA sections skipped on line count) +--EXTENSIONS-- +xml +--SKIPIF-- += 2.11'); +?> +--FILE-- + + + +'; + +// Case 2: replace some characters so that we get comments instead +$xmls["Comment"] =' + + +'; + +// Case 3: replace even more characters so that only textual data is left +$xmls["Text"] =' + +-!-- ATA[ +multi +line +CDATA +block +--- +'; + +function startElement($parser, $name, $attrs) { + printf("<$name> at line %d, col %d (byte %d)\n", + xml_get_current_line_number($parser), + xml_get_current_column_number($parser), + xml_get_current_byte_index($parser)); +} + +function endElement($parser, $name) { + printf(" at line %d, col %d (byte %d)\n", + xml_get_current_line_number($parser), + xml_get_current_column_number($parser), + xml_get_current_byte_index($parser)); +} + +function characterData($parser, $data) { + // dummy +} + +foreach ($xmls as $desc => $xml) { + echo "$desc\n"; + $xml_parser = xml_parser_create(); + xml_set_element_handler($xml_parser, "startElement", "endElement"); + xml_set_character_data_handler($xml_parser, "characterData"); + if (!xml_parse($xml_parser, $xml, true)) + echo "Error: ".xml_error_string(xml_get_error_code($xml_parser))."\n"; + xml_parser_free($xml_parser); +} +?> +--EXPECTF-- +CDATA + at line 2, col %d (byte 50) + at line 9, col %d (byte 96) +Comment + at line 2, col %d (byte 50) + at line 9, col %d (byte 96) +Text + at line 2, col %d (byte 50) + at line 9, col %d (byte 96) diff --git a/ext/xml/tests/bug26614_libxml.phpt b/ext/xml/tests/bug26614_libxml_pre2_11.phpt similarity index 96% rename from ext/xml/tests/bug26614_libxml.phpt rename to ext/xml/tests/bug26614_libxml_pre2_11.phpt index b6c0b875818..90283850d24 100644 --- a/ext/xml/tests/bug26614_libxml.phpt +++ b/ext/xml/tests/bug26614_libxml_pre2_11.phpt @@ -4,6 +4,7 @@ Bug #26614 (CDATA sections skipped on line count) = 21100) die('skip libxml2 test variant for version < 2.11'); ?> --FILE-- Date: Fri, 1 Dec 2023 18:03:35 +0100 Subject: [PATCH 06/11] Backport 0a39890c: Fix libxml2 2.12 build due to API breaks See https://github.com/php/php-src/actions/runs/7062192818/job/19225478601 (cherry picked from commit fa6a0f80f644932506666beb7c85e4041c4a4646) --- ext/libxml/libxml.c | 14 ++++++++++---- ext/soap/php_sdl.c | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index c8bd1be60a4..554fcc34ff2 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -481,7 +481,11 @@ static void _php_libxml_free_error(void *ptr) xmlResetError((xmlErrorPtr) ptr); } -static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg) +#if LIBXML_VERSION >= 21200 +static void _php_list_set_error_structure(const xmlError *error, const char *msg) +#else +static void _php_list_set_error_structure(xmlError *error, const char *msg) +#endif { xmlError error_copy; int ret; @@ -734,7 +738,11 @@ PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...) va_end(args); } +#if LIBXML_VERSION >= 21200 +PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, const xmlError *error) +#else PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error) +#endif { _php_list_set_error_structure(error, NULL); @@ -1037,11 +1045,9 @@ PHP_FUNCTION(libxml_use_internal_errors) /* {{{ Retrieve last error from libxml */ PHP_FUNCTION(libxml_get_last_error) { - xmlErrorPtr error; - ZEND_PARSE_PARAMETERS_NONE(); - error = xmlGetLastError(); + const xmlError *error = xmlGetLastError(); if (error) { object_init_ex(return_value, libxmlerror_class_entry); diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index e5e7f2f9554..6060f634508 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -331,7 +331,7 @@ static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, int include) sdl_restore_uri_credentials(ctx); if (!wsdl) { - xmlErrorPtr xmlErrorPtr = xmlGetLastError(); + const xmlError *xmlErrorPtr = xmlGetLastError(); if (xmlErrorPtr) { soap_error2(E_ERROR, "Parsing WSDL: Couldn't load from '%s' : %s", struri, xmlErrorPtr->message); -- 2.48.1 From 6cb68826aaf68ffe8c70c8782450c38970236040 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 4 Jul 2024 06:29:50 -0700 Subject: [PATCH 07/11] Backport 4fe82131: Backport libxml2 2.13.2 fixes (#14816) Backproted from https://github.com/php/php-src/pull/14789 (cherry picked from commit bb46b4b799b583528025a775af45308133bfd4c1) --- ext/dom/document.c | 6 ++-- .../DOMDocument_loadHTMLfile_error1.phpt | 2 +- .../DOMDocument_loadXML_error2_pre2_11.phpt | 3 +- .../DOMDocument_load_error2_pre2_11.phpt | 3 +- .../DOMDocument_relaxNGValidate_error2.phpt | 2 +- .../tests/DOMDocument_saveHTMLFile_basic.phpt | 1 + ...DOMDocument_saveHTMLFile_formatOutput.phpt | 1 + ...nt_saveHTMLFile_formatOutput_gte_2_13.phpt | 32 +++++++++++++++++++ .../DOMDocument_saveHTML_basic_gte_2_13.phpt | 31 ++++++++++++++++++ .../DOMDocument_schemaValidate_error5.phpt | 2 +- ext/dom/tests/dom_create_element.phpt | 14 +++----- ext/libxml/libxml.c | 4 ++- ext/simplexml/tests/bug79971_1.phpt | 2 +- ext/soap/php_encoding.c | 9 ++++-- ext/soap/php_xml.c | 4 ++- ext/soap/tests/bugs/bug42151.phpt | 4 +-- ext/xml/compat.c | 3 +- ext/xmlwriter/php_xmlwriter.c | 3 +- 18 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput_gte_2_13.phpt create mode 100644 ext/dom/tests/DOMDocument_saveHTML_basic_gte_2_13.phpt diff --git a/ext/dom/document.c b/ext/dom/document.c index 02522b5014f..7735e5d5dc3 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -1253,11 +1253,13 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) { options |= XML_PARSE_NOBLANKS; } + if (recover) { + options |= XML_PARSE_RECOVER; + } php_libxml_sanitize_parse_ctxt_options(ctxt); xmlCtxtUseOptions(ctxt, options); - ctxt->recovery = recover; if (recover) { old_error_reporting = EG(error_reporting); EG(error_reporting) = old_error_reporting | E_WARNING; @@ -1267,7 +1269,7 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so if (ctxt->wellFormed || recover) { ret = ctxt->myDoc; - if (ctxt->recovery) { + if (recover) { EG(error_reporting) = old_error_reporting; } /* If loading from memory, set the base reference uri for the document */ diff --git a/ext/dom/tests/DOMDocument_loadHTMLfile_error1.phpt b/ext/dom/tests/DOMDocument_loadHTMLfile_error1.phpt index cfb41686e87..fc78273c85f 100644 --- a/ext/dom/tests/DOMDocument_loadHTMLfile_error1.phpt +++ b/ext/dom/tests/DOMDocument_loadHTMLfile_error1.phpt @@ -15,4 +15,4 @@ $result = $doc->loadHTMLFile(__DIR__ . "/ffff/test.html"); assert($result === false); ?> --EXPECTF-- -%r(PHP ){0,1}%rWarning: DOMDocument::loadHTMLFile(): I/O warning : failed to load external entity %s +%r(PHP ){0,1}%rWarning: DOMDocument::loadHTMLFile(): I/O %s diff --git a/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt index 0e36d209058..7e10771fdb7 100644 --- a/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt +++ b/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt @@ -2,6 +2,7 @@ Test DOMDocument::loadXML() detects not-well formed XML --SKIPIF-- = 21100) die('skip libxml2 test variant for version < 2.11'); ?> --DESCRIPTION-- @@ -14,8 +15,6 @@ Environment variables used in the test: Antonio Diaz Ruiz --INI-- assert.bail=true ---SKIPIF-- - --ENV-- XML_FILE=/not_well_formed2.xml LOAD_OPTIONS=0 diff --git a/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt b/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt index b97fff9d2f1..74b20c171e0 100644 --- a/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt +++ b/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt @@ -2,6 +2,7 @@ Test DOMDocument::load() detects not-well formed XML --SKIPIF-- = 21100) die('skip libxml2 test variant for version < 2.11'); ?> --DESCRIPTION-- @@ -14,8 +15,6 @@ Environment variables used in the test: Antonio Diaz Ruiz --INI-- assert.bail=true ---SKIPIF-- - --ENV-- XML_FILE=/not_well_formed2.xml LOAD_OPTIONS=0 diff --git a/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt b/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt index 3aa6a3b3189..bf8d7befa53 100644 --- a/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt +++ b/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt @@ -22,7 +22,7 @@ $result = $doc->relaxNGValidate($rng); var_dump($result); ?> --EXPECTF-- -Warning: DOMDocument::relaxNGValidate(): I/O warning : failed to load external entity "%s/foo.rng" in %s on line %d +Warning: DOMDocument::relaxNGValidate(): I/O %s : failed to load %s Warning: DOMDocument::relaxNGValidate(): xmlRelaxNGParse: could not load %s/foo.rng in %s on line %d diff --git a/ext/dom/tests/DOMDocument_saveHTMLFile_basic.phpt b/ext/dom/tests/DOMDocument_saveHTMLFile_basic.phpt index f71db0c32a3..c51852e120c 100644 --- a/ext/dom/tests/DOMDocument_saveHTMLFile_basic.phpt +++ b/ext/dom/tests/DOMDocument_saveHTMLFile_basic.phpt @@ -6,6 +6,7 @@ Knut Urdalen --SKIPIF-- = 21300) die("skip see https://gitlab.gnome.org/GNOME/libxml2/-/issues/756"); ?> --FILE-- --SKIPIF-- = 21300) die("skip see https://gitlab.gnome.org/GNOME/libxml2/-/issues/756"); ?> --FILE-- +#PHPTestFest2009 Norway 2009-06-09 \o/ +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- +formatOutput = true; +$root = $doc->createElement('html'); +$root = $doc->appendChild($root); +$head = $doc->createElement('head'); +$head = $root->appendChild($head); +$title = $doc->createElement('title'); +$title = $head->appendChild($title); +$text = $doc->createTextNode('This is the title'); +$text = $title->appendChild($text); +$bytes = $doc->saveHTMLFile($filename); +var_dump($bytes); +echo file_get_contents($filename); +unlink($filename); +?> +--EXPECT-- +int(59) +This is the title diff --git a/ext/dom/tests/DOMDocument_saveHTML_basic_gte_2_13.phpt b/ext/dom/tests/DOMDocument_saveHTML_basic_gte_2_13.phpt new file mode 100644 index 00000000000..c0be105253d --- /dev/null +++ b/ext/dom/tests/DOMDocument_saveHTML_basic_gte_2_13.phpt @@ -0,0 +1,31 @@ +--TEST-- +DOMDocument::saveHTMLFile() should dump the internal document into a file using HTML formatting +--CREDITS-- +Knut Urdalen +#PHPTestFest2009 Norway 2009-06-09 \o/ +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- +createElement('html'); +$root = $doc->appendChild($root); +$head = $doc->createElement('head'); +$head = $root->appendChild($head); +$title = $doc->createElement('title'); +$title = $head->appendChild($title); +$text = $doc->createTextNode('This is the title'); +$text = $title->appendChild($text); +$bytes = $doc->saveHTMLFile($filename); +var_dump($bytes); +echo file_get_contents($filename); +unlink($filename); +?> +--EXPECT-- +int(59) +This is the title diff --git a/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt b/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt index cb57b55b41a..44ea52c2d06 100644 --- a/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt +++ b/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt @@ -17,7 +17,7 @@ var_dump($result); ?> --EXPECTF-- -Warning: DOMDocument::schemaValidate(): I/O warning : failed to load external entity "%snon-existent-file" in %s.php on line %d +Warning: DOMDocument::schemaValidate(): I/O %s : failed to load %s Warning: DOMDocument::schemaValidate(): Failed to locate the main schema resource at '%s/non-existent-file'. in %s.php on line %d diff --git a/ext/dom/tests/dom_create_element.phpt b/ext/dom/tests/dom_create_element.phpt index bd2c8f11dae..70ae54a11bb 100644 --- a/ext/dom/tests/dom_create_element.phpt +++ b/ext/dom/tests/dom_create_element.phpt @@ -251,14 +251,10 @@ try { print $e->getMessage() . "\n"; } -/* This isn't because the xml namespace isn't there and we can't create it */ -print "29 DOMElement::__construct('xml:valid', '', 'http://www.w3.org/XML/1998/namespace')\n"; -try { - $element = new DomElement('xml:valid', '', 'http://www.w3.org/XML/1998/namespace'); - print "valid\n"; -} catch (Exception $e) { - print $e->getMessage() . "\n"; -} +/* There used to be a 29 here that tested DOMElement::__construct('xml:valid', '', 'http://www.w3.org/XML/1998/namespace'). + * In libxml2 version 2.12 or prior this didn't work because the xml namespace isn't there and you can't create it without + * a document. Starting from libxml2 version 2.13 it does actually work because the XML namespace is statically defined. + * The behaviour from version 2.13 is actually the desired behaviour anyway. */ /* the qualifiedName or its prefix is "xmlns" and the namespaceURI is @@ -378,8 +374,6 @@ Namespace Error Namespace Error 28 DOMDocument::createElementNS('http://www.w3.org/XML/1998/namespace', 'xml:valid') valid -29 DOMElement::__construct('xml:valid', '', 'http://www.w3.org/XML/1998/namespace') -Namespace Error 30 DOMDocument::createElementNS('http://wrong.namespaceURI.com', 'xmlns:valid') Namespace Error 31 DOMElement::__construct('xmlns:valid', '', 'http://wrong.namespaceURI.com') diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 554fcc34ff2..28dd86a55c9 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -428,8 +428,10 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) static xmlOutputBufferPtr php_libxml_output_buffer_create_filename(const char *URI, xmlCharEncodingHandlerPtr encoder, - int compression ATTRIBUTE_UNUSED) + int compression) { + ZEND_IGNORE_VALUE(compression); + xmlOutputBufferPtr ret; xmlURIPtr puri; void *context = NULL; diff --git a/ext/simplexml/tests/bug79971_1.phpt b/ext/simplexml/tests/bug79971_1.phpt index 197776d82d3..2ee24e89f12 100644 --- a/ext/simplexml/tests/bug79971_1.phpt +++ b/ext/simplexml/tests/bug79971_1.phpt @@ -20,7 +20,7 @@ var_dump($sxe->asXML("$uri.out%00foo")); --EXPECTF-- Warning: simplexml_load_file(): URI must not contain percent-encoded NUL bytes in %s on line %d -Warning: simplexml_load_file(): I/O warning : failed to load external entity "%s/bug79971_1.xml%00foo" in %s on line %d +Warning: simplexml_load_file(): I/O warning : failed to load %s bool(false) Warning: SimpleXMLElement::asXML(): URI must not contain percent-encoded NUL bytes in %s on line %d diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index f5723e213a4..2634d2c7db4 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -3379,7 +3379,6 @@ xmlNsPtr encode_add_ns(xmlNodePtr node, const char* ns) } else { smart_str prefix = {0}; int num = ++SOAP_GLOBAL(cur_uniq_ns); - xmlChar *enc_ns; while (1) { smart_str_appendl(&prefix, "ns", 2); @@ -3393,9 +3392,15 @@ xmlNsPtr encode_add_ns(xmlNodePtr node, const char* ns) num = ++SOAP_GLOBAL(cur_uniq_ns); } - enc_ns = xmlEncodeSpecialChars(node->doc, BAD_CAST(ns)); + /* Starting with libxml 2.13, we don't have to do this workaround anymore, otherwise we get double-encoded + * entities. See libxml2 commit f506ec66547ef9bac97a2bf306d368ecea8c0c9e. */ +#if LIBXML_VERSION < 21300 + xmlChar *enc_ns = xmlEncodeSpecialChars(node->doc, BAD_CAST(ns)); xmlns = xmlNewNs(node->doc->children, enc_ns, BAD_CAST(prefix.s ? ZSTR_VAL(prefix.s) : "")); xmlFree(enc_ns); +#else + xmlns = xmlNewNs(node->doc->children, BAD_CAST(ns), BAD_CAST(prefix.s ? ZSTR_VAL(prefix.s) : "")); +#endif smart_str_free(&prefix); } } diff --git a/ext/soap/php_xml.c b/ext/soap/php_xml.c index ed3495c1266..58c176031f9 100644 --- a/ext/soap/php_xml.c +++ b/ext/soap/php_xml.c @@ -92,13 +92,14 @@ xmlDocPtr soap_xmlParseFile(const char *filename) zend_bool old; php_libxml_sanitize_parse_ctxt_options(ctxt); + /* TODO: In libxml2 2.14.0 change this to the new options API so we don't rely on deprecated APIs. */ ctxt->keepBlanks = 0; + ctxt->options |= XML_PARSE_HUGE; ctxt->sax->ignorableWhitespace = soap_ignorableWhitespace; ctxt->sax->comment = soap_Comment; ctxt->sax->warning = NULL; ctxt->sax->error = NULL; /*ctxt->sax->fatalError = NULL;*/ - ctxt->options |= XML_PARSE_HUGE; old = php_libxml_disable_entity_loader(1); xmlParseDocument(ctxt); php_libxml_disable_entity_loader(old); @@ -146,6 +147,7 @@ xmlDocPtr soap_xmlParseMemory(const void *buf, size_t buf_size) ctxt->sax->warning = NULL; ctxt->sax->error = NULL; /*ctxt->sax->fatalError = NULL;*/ + /* TODO: In libxml2 2.14.0 change this to the new options API so we don't rely on deprecated APIs. */ ctxt->options |= XML_PARSE_HUGE; old = php_libxml_disable_entity_loader(1); xmlParseDocument(ctxt); diff --git a/ext/soap/tests/bugs/bug42151.phpt b/ext/soap/tests/bugs/bug42151.phpt index f945a8753e5..dd14d1afb62 100644 --- a/ext/soap/tests/bugs/bug42151.phpt +++ b/ext/soap/tests/bugs/bug42151.phpt @@ -25,8 +25,8 @@ try { } echo "ok\n"; ?> ---EXPECT-- -SOAP-ERROR: Parsing WSDL: Couldn't load from 'httpx://' : failed to load external entity "httpx://" +--EXPECTF-- +SOAP-ERROR: Parsing WSDL: Couldn't load from 'httpx://' : failed to load %s ok I don't get executed either. diff --git a/ext/xml/compat.c b/ext/xml/compat.c index 3b2a0cdf7fb..4d1f506840a 100644 --- a/ext/xml/compat.c +++ b/ext/xml/compat.c @@ -714,8 +714,7 @@ XML_GetCurrentByteCount(XML_Parser parser) { /* WARNING: this is identical to ByteIndex; it should probably * be different */ - return parser->parser->input->consumed + - (parser->parser->input->cur - parser->parser->input->base); + return XML_GetCurrentByteIndex(parser); } PHP_XML_API const XML_Char *XML_ExpatVersion(void) diff --git a/ext/xmlwriter/php_xmlwriter.c b/ext/xmlwriter/php_xmlwriter.c index 61e4a3a7d95..8a3fa1cea67 100644 --- a/ext/xmlwriter/php_xmlwriter.c +++ b/ext/xmlwriter/php_xmlwriter.c @@ -1001,7 +1001,8 @@ static void php_xmlwriter_flush(INTERNAL_FUNCTION_PARAMETERS, int force_string) } output_bytes = xmlTextWriterFlush(ptr); if (buffer) { - RETVAL_STRING((char *) buffer->content); + const xmlChar *content = xmlBufferContent(buffer); + RETVAL_STRING((const char *) content); if (empty) { xmlBufferEmpty(buffer); } -- 2.48.1 From 1196e566681a34564c02173ba234b5a42587ff07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 20 Nov 2024 10:47:27 +0100 Subject: [PATCH 08/11] Fix GHSA-p3x9-6h7p-cgfc: libxml streams wrong `content-type` on redirect libxml streams use wrong content-type header when requesting a redirected resource. (cherry picked from commit b6004a043c16b211d462218fbb3f72db68ec2b18) --- ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt | 60 +++++++++++++++++ ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt | 60 +++++++++++++++++ ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt | 60 +++++++++++++++++ ext/libxml/libxml.c | 77 ++++++++++++---------- 4 files changed, 224 insertions(+), 33 deletions(-) create mode 100644 ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt create mode 100644 ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt create mode 100644 ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt diff --git a/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt new file mode 100644 index 00000000000..47212cb3410 --- /dev/null +++ b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt @@ -0,0 +1,60 @@ +--TEST-- +GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Basic) +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- + + + + GHSA-p3x9-6h7p-cgfc + + + + + + +

GHSA-p3x9-6h7p-cgfc

+ + + EOT; + // Intentionally using non-standard casing for content-type to verify it is matched not case sensitively. + yield "data://text/plain,HTTP/1.1 200 OK\r\nconteNt-tyPe: text/html; charset=utf-8\r\n\r\n{$xml}"; +} + +['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output); +$document = new \DOMDocument(); +$document->loadHTMLFile($uri); + +$h1 = $document->getElementsByTagName('h1'); +var_dump($h1->length); +var_dump($document->saveHTML()); +http_server_kill($pid); +?> +--EXPECT-- +int(1) +string(266) " + + + GHSA-p3x9-6h7p-cgfc + + + + + + +

GHSA-p3x9-6h7p-cgfc

+ + +" diff --git a/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt new file mode 100644 index 00000000000..a7eff3b9a8b --- /dev/null +++ b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt @@ -0,0 +1,60 @@ +--TEST-- +GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Missing content-type) +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- + + + + GHSA-p3x9-6h7p-cgfc + + + + + + +

GHSA-p3x9-6h7p-cgfc

+ + + EOT; + // Missing content-type in actual response. + yield "data://text/plain,HTTP/1.1 200 OK\r\n\r\n{$xml}"; +} + +['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output); +$document = new \DOMDocument(); +$document->loadHTMLFile($uri); + +$h1 = $document->getElementsByTagName('h1'); +var_dump($h1->length); +var_dump($document->saveHTML()); +http_server_kill($pid); +?> +--EXPECT-- +int(1) +string(266) " + + + GHSA-p3x9-6h7p-cgfc + + + + + + +

GHSA-p3x9-6h7p-cgfc

+ + +" diff --git a/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt new file mode 100644 index 00000000000..178b35f3525 --- /dev/null +++ b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt @@ -0,0 +1,60 @@ +--TEST-- +GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Reason with colon) +--EXTENSIONS-- +dom +--SKIPIF-- + +--FILE-- + + + + GHSA-p3x9-6h7p-cgfc + + + + + + +

GHSA-p3x9-6h7p-cgfc

+ + + EOT; + // Missing content-type in actual response. + yield "data://text/plain,HTTP/1.1 200 OK: This is fine\r\n\r\n{$xml}"; +} + +['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output); +$document = new \DOMDocument(); +$document->loadHTMLFile($uri); + +$h1 = $document->getElementsByTagName('h1'); +var_dump($h1->length); +var_dump($document->saveHTML()); +http_server_kill($pid); +?> +--EXPECT-- +int(1) +string(266) " + + + GHSA-p3x9-6h7p-cgfc + + + + + + +

GHSA-p3x9-6h7p-cgfc

+ + +" diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 28dd86a55c9..7886ca79ad9 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -372,42 +372,53 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) if (Z_TYPE(s->wrapperdata) == IS_ARRAY) { zval *header; - ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) { + /* Scan backwards: The header array might contain the headers for multiple responses, if + * a redirect was followed. + */ + ZEND_HASH_REVERSE_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) { const char buf[] = "Content-Type:"; - if (Z_TYPE_P(header) == IS_STRING && - !zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) { - char *needle = estrdup("charset="); - char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header)); - char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), sizeof("charset=")-1); - - if (encoding) { - char *end; - - encoding += sizeof("charset=")-1; - if (*encoding == '"') { - encoding++; - } - end = strchr(encoding, ';'); - if (end == NULL) { - end = encoding + strlen(encoding); - } - end--; /* end == encoding-1 isn't a buffer underrun */ - while (*end == ' ' || *end == '\t') { - end--; - } - if (*end == '"') { - end--; - } - if (encoding >= end) continue; - *(end+1) = '\0'; - enc = xmlParseCharEncoding(encoding); - if (enc <= XML_CHAR_ENCODING_NONE) { - enc = XML_CHAR_ENCODING_NONE; + if (Z_TYPE_P(header) == IS_STRING) { + /* If no colon is found in the header, we assume it's the HTTP status line and bail out. */ + char *colon = memchr(Z_STRVAL_P(header), ':', Z_STRLEN_P(header)); + char *space = memchr(Z_STRVAL_P(header), ' ', Z_STRLEN_P(header)); + if (colon == NULL || space < colon) { + break; + } + + if (!zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) { + char *needle = estrdup("charset="); + char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header)); + char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), sizeof("charset=")-1); + + if (encoding) { + char *end; + + encoding += sizeof("charset=")-1; + if (*encoding == '"') { + encoding++; + } + end = strchr(encoding, ';'); + if (end == NULL) { + end = encoding + strlen(encoding); + } + end--; /* end == encoding-1 isn't a buffer underrun */ + while (*end == ' ' || *end == '\t') { + end--; + } + if (*end == '"') { + end--; + } + if (encoding >= end) continue; + *(end+1) = '\0'; + enc = xmlParseCharEncoding(encoding); + if (enc <= XML_CHAR_ENCODING_NONE) { + enc = XML_CHAR_ENCODING_NONE; + } } + efree(haystack); + efree(needle); + break; /* found content-type */ } - efree(haystack); - efree(needle); - break; /* found content-type */ } } ZEND_HASH_FOREACH_END(); } -- 2.48.1 From 294140ee981fda6a38244215e4b16e53b7f5b2a6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:44:05 +0100 Subject: [PATCH 09/11] Fix GHSA-wg4p-4hqh-c3g9 (cherry picked from commit 0e715e71d945b68f8ccedd62c5960df747af6625) --- ext/xml/tests/toffset_bounds.phpt | 42 +++++++++++++++++++++++++++++++ ext/xml/xml.c | 12 ++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 ext/xml/tests/toffset_bounds.phpt diff --git a/ext/xml/tests/toffset_bounds.phpt b/ext/xml/tests/toffset_bounds.phpt new file mode 100644 index 00000000000..5a3fd22f86c --- /dev/null +++ b/ext/xml/tests/toffset_bounds.phpt @@ -0,0 +1,42 @@ +--TEST-- +XML_OPTION_SKIP_TAGSTART bounds +--EXTENSIONS-- +xml +--FILE-- +"; +$parser = xml_parser_create(); +xml_parser_set_option($parser, XML_OPTION_SKIP_TAGSTART, 100); +$res = xml_parse_into_struct($parser,$sample,$vals,$index); +var_dump($vals); +?> +--EXPECT-- +array(3) { + [0]=> + array(3) { + ["tag"]=> + string(0) "" + ["type"]=> + string(4) "open" + ["level"]=> + int(1) + } + [1]=> + array(3) { + ["tag"]=> + string(0) "" + ["type"]=> + string(8) "complete" + ["level"]=> + int(2) + } + [2]=> + array(3) { + ["tag"]=> + string(0) "" + ["type"]=> + string(5) "close" + ["level"]=> + int(1) + } +} diff --git a/ext/xml/xml.c b/ext/xml/xml.c index cc1457d9705..cac86ca7508 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -668,9 +668,11 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch array_init(&tag); array_init(&atr); - _xml_add_to_info(parser, ZSTR_VAL(tag_name) + parser->toffset); + char *skipped_tag_name = SKIP_TAGSTART(ZSTR_VAL(tag_name)); - add_assoc_string(&tag, "tag", SKIP_TAGSTART(ZSTR_VAL(tag_name))); /* cast to avoid gcc-warning */ + _xml_add_to_info(parser, skipped_tag_name); + + add_assoc_string(&tag, "tag", skipped_tag_name); add_assoc_string(&tag, "type", "open"); add_assoc_long(&tag, "level", parser->level); @@ -737,9 +739,11 @@ void _xml_endElementHandler(void *userData, const XML_Char *name) } else { array_init(&tag); - _xml_add_to_info(parser, ZSTR_VAL(tag_name) + parser->toffset); + char *skipped_tag_name = SKIP_TAGSTART(ZSTR_VAL(tag_name)); + + _xml_add_to_info(parser, skipped_tag_name); - add_assoc_string(&tag, "tag", SKIP_TAGSTART(ZSTR_VAL(tag_name))); /* cast to avoid gcc-warning */ + add_assoc_string(&tag, "tag", skipped_tag_name); add_assoc_string(&tag, "type", "close"); add_assoc_long(&tag, "level", parser->level); -- 2.48.1 From d7ab2bb9856d938fca7989575695c14c25892589 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 17 Nov 2023 19:45:40 +0100 Subject: [PATCH 10/11] Fix GH-12702: libxml2 2.12.0 issue building from src Fixes GH-12702. Co-authored-by: nono303 (cherry picked from commit 6a76e5d0a2dcf46b4ab74cc3ffcbfeb860c4fdb3) --- ext/dom/document.c | 1 + ext/libxml/php_libxml.h | 1 + 2 files changed, 2 insertions(+) diff --git a/ext/dom/document.c b/ext/dom/document.c index 7735e5d5dc3..5ef5dc479d6 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -23,6 +23,7 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" #include +#include #ifdef LIBXML_SCHEMAS_ENABLED #include #include diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index d0ce7cec714..02717417a71 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -35,6 +35,7 @@ extern zend_module_entry libxml_module_entry; #include "zend_smart_str.h" #include +#include #define LIBXML_SAVE_NOEMPTYTAG 1<<2 -- 2.48.1 From adae2b8de8963ac6f92103803bf91a5174172f88 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 13 Mar 2025 09:39:19 +0100 Subject: [PATCH 11/11] NEWS --- NEWS | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/NEWS b/NEWS index e043901ee96..7db6f2660d2 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,23 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +Backported from 8.1.32 + +- LibXML: + . Fixed GHSA-wg4p-4hqh-c3g9 (Reocurrence of #72714). (nielsdos) + . Fixed GHSA-p3x9-6h7p-cgfc (libxml streams use wrong `content-type` header + when requesting a redirected resource). (CVE-2025-1219) (timwolla) + +- Streams: + . Fixed GHSA-hgf54-96fm-v528 (Stream HTTP wrapper header check might omit + basic auth header). (CVE-2025-1736) (Jakub Zelenka) + . Fixed GHSA-52jp-hrpf-2jff (Stream HTTP wrapper truncate redirect location + to 1024 bytes). (CVE-2025-1861) (Jakub Zelenka) + . Fixed GHSA-pcmh-g36c-qc44 (Streams HTTP wrapper does not fail for headers + without colon). (CVE-2025-1734) (Jakub Zelenka) + . Fixed GHSA-v8xr-gpvj-cx9g (Header parser of `http` stream wrapper does not + handle folded headers). (CVE-2025-1217) (Jakub Zelenka) + Backported from 8.1.31 - CLI: -- 2.48.1