diff --git a/NEWS b/NEWS index 8969824fe760..0dd04e091cda 100644 --- a/NEWS +++ b/NEWS @@ -15,11 +15,16 @@ PHP NEWS . Fixed cloning IntlDateFormatter and MessageFormatter losing PHP-side state such as dateType, timeType, calendar and the message pattern. (Ilia Alshanetsky) + . Fixed a crash when converting with a cloned UConverter that uses + toUCallback/fromUCallback. (Ilia Alshanetsky) - MBString: . Fixed bug GH-23106 (mb_strpos() reads past the end of a haystack ending in a truncated UTF-8 sequence). (Lazizbek Ergashev) +- PGSQL: + . Fixed pg_lo_write() rejecting data containing null bytes. (Ilia Alshanetsky) + - SPL: . Fixed bug GH-23385 (SplDoublyLinkedList::serialize() use-after-free when __serialize() removes an element). (David Carlier) diff --git a/UPGRADING b/UPGRADING index 7c7b727759b6..07223cd7a21f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -1098,6 +1098,7 @@ PHP 8.6 UPGRADE NOTES . Reduced temporary allocations when iterating Phar directories. - Standard: + . Improved performance of addcslashes() when generating octal escapes. . Improved performance of sorting single-element arrays. . Improved performance of array_fill_keys(). . Improved performance of array_intersect(). diff --git a/ext/dom/tests/DOMDocument_saveHTMLFile_failed_output_encoding.phpt b/ext/dom/tests/DOMDocument_saveHTMLFile_failed_output_encoding.phpt new file mode 100644 index 000000000000..05adefb973aa --- /dev/null +++ b/ext/dom/tests/DOMDocument_saveHTMLFile_failed_output_encoding.phpt @@ -0,0 +1,31 @@ +--TEST-- +DOMDocument::saveHTMLFile() does not close the encoder twice when opening the output fails +--EXTENSIONS-- +dom +--FILE-- +loadHTML('
value'); + $doc->getElementsByTagName('meta')->item(0)->setAttribute('content', 'text/html; charset=' . $encoding); + for ($i = 0; $i < 3; $i++) { + $result = @$doc->saveHTMLFile($filename); + var_dump($result === 0 || $result === false); + } + var_dump($doc->getElementsByTagName('body')->item(0)->textContent === 'value'); +} +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/intl/converter/converter.cpp b/ext/intl/converter/converter.cpp index c2f543f2c4ab..1369b88742f4 100644 --- a/ext/intl/converter/converter.cpp +++ b/ext/intl/converter/converter.cpp @@ -547,8 +547,6 @@ PHP_METHOD(UConverter, __construct) { ZEND_ASSERT(EG(exception)); goto cleanup; } - php_converter_resolve_callback(&objval->to_cache, Z_OBJ_P(ZEND_THIS), ZEND_STRL("toUCallback")); - php_converter_resolve_callback(&objval->from_cache, Z_OBJ_P(ZEND_THIS), ZEND_STRL("fromUCallback")); cleanup: INTL_G(use_exceptions) = old_use_exception; INTL_G(error_level) = old_error_level; @@ -927,6 +925,8 @@ static zend_object *php_converter_object_ctor(zend_class_entry *ce, php_converte zend_object_std_init(&objval->obj, ce); object_properties_init(&objval->obj, ce); intl_error_init(&(objval->error)); + php_converter_resolve_callback(&objval->to_cache, &objval->obj, ZEND_STRL("toUCallback")); + php_converter_resolve_callback(&objval->from_cache, &objval->obj, ZEND_STRL("fromUCallback")); *pobjval = objval; diff --git a/ext/intl/tests/uconverter_clone_callback.phpt b/ext/intl/tests/uconverter_clone_callback.phpt new file mode 100644 index 000000000000..140841b455e5 --- /dev/null +++ b/ext/intl/tests/uconverter_clone_callback.phpt @@ -0,0 +1,24 @@ +--TEST-- +Cloned UConverter resolves toUCallback/fromUCallback on the clone +--EXTENSIONS-- +intl +--FILE-- +hits++; + return parent::toUCallback($reason, $source, $codeUnits, $error); + } +} + +$orig = new MyConverter('ascii', 'utf-8'); +$clone = clone $orig; +$clone->convert("irregul\xC1\xA1r"); +echo $clone->hits > 0 ? "ok\n" : "no callback\n"; + +?> +--EXPECT-- +ok diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 42cd0ceb9711..361339a3466f 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -618,8 +618,10 @@ php_libxml_output_buffer_create_filename(const char *URI, return ret; err: - /* Similarly to __xmlOutputBufferCreateFilename we should also close the encoder on failure. */ +#if LIBXML_VERSION < 21404 + /* As of libxml 2.14.4, libxml closes the encoder after this callback fails. */ xmlCharEncCloseFunc(encoder); +#endif return NULL; } diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 351e1b5e3aa3..de50c6929c5c 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -2830,7 +2830,7 @@ PHP_FUNCTION(pg_lo_write) ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_OBJECT_OF_CLASS(pgsql_id, pgsql_lob_ce) - Z_PARAM_PATH_STR(str) + Z_PARAM_STR(str) Z_PARAM_OPTIONAL Z_PARAM_LONG_OR_NULL(z_len, z_len_is_null) ZEND_PARSE_PARAMETERS_END(); diff --git a/ext/pgsql/tests/05large_object.phpt b/ext/pgsql/tests/05large_object.phpt index 957f2dffa8ea..a785b76a572d 100644 --- a/ext/pgsql/tests/05large_object.phpt +++ b/ext/pgsql/tests/05large_object.phpt @@ -17,11 +17,6 @@ $oid = pg_lo_create ($db); if (!$oid) echo ("pg_lo_create() error\n"); $handle = pg_lo_open ($db, $oid, "w"); if (!$handle) echo ("pg_lo_open() error\n"); -try { - pg_lo_write ($handle, "large\0object data"); -} catch (\ValueError $e) { - echo $e->getMessage(), PHP_EOL; -} pg_lo_write ($handle, "large object data"); pg_lo_close ($handle); pg_exec ($db, "COMMIT"); @@ -110,7 +105,6 @@ echo "OK"; ?> --EXPECTF-- create/write/close LO -pg_lo_write(): Argument #2 ($data) must not contain any null bytes open/read/tell/seek/close LO string(5) "large" int(5) diff --git a/ext/pgsql/tests/pg_lo_write_null_bytes.phpt b/ext/pgsql/tests/pg_lo_write_null_bytes.phpt new file mode 100644 index 000000000000..44073692a182 --- /dev/null +++ b/ext/pgsql/tests/pg_lo_write_null_bytes.phpt @@ -0,0 +1,49 @@ +--TEST-- +pg_lo_write() must accept data containing null bytes +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} +try { + pg_lo_write($handle, "abc", 4); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +pg_lo_close($handle); +pg_exec($db, "ROLLBACK"); + +pg_exec($db, "BEGIN"); +$oid = pg_lo_create($db); +$handle = pg_lo_open($db, $oid, "w"); +pg_lo_write($handle, "x\0y"); +pg_lo_close($handle); +$handle = pg_lo_open($db, $oid, "r"); +var_dump(bin2hex(pg_lo_read($handle))); +pg_lo_close($handle); +pg_exec($db, "ROLLBACK"); +?> +--EXPECT-- +int(12) +int(4) +int(2) +int(0) +ValueError: pg_lo_write(): Argument #3 ($length) must be greater than or equal to 0 +ValueError: pg_lo_write(): Argument #3 ($length) must be less than or equal to the length of argument #2 ($buf) +string(6) "780079" diff --git a/ext/standard/string.c b/ext/standard/string.c index 577d4267ed2f..f7c2ed37cf64 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -3904,7 +3904,11 @@ PHPAPI zend_string *php_addcslashes_str(const char *str, size_t len, const char case '\v': *target++ = 'v'; break; case '\b': *target++ = 'b'; break; case '\f': *target++ = 'f'; break; - default: target += snprintf(target, 4, "%03o", (unsigned char) c); + default: + /* Write the byte as three octal digits, including leading zeros. */ + *target++ = ((unsigned char) c >> 6) + '0'; + *target++ = (((unsigned char) c >> 3) & 7) + '0'; + *target++ = ((unsigned char) c & 7) + '0'; } continue; }