diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 82b67dcd5bcc6..149af66e3f269 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -337,6 +337,7 @@ jobs: $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) \ > $GITHUB_STEP_SUMMARY FREEBSD: + if: github.repository == 'php/php-src' || github.event_name == 'pull_request' name: FREEBSD runs-on: ubuntu-latest steps: diff --git a/NEWS b/NEWS index caad04f9d0895..27bfefbfac4ab 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,59 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.20 +08 May 2025, PHP 8.3.21 + +- Core: + . Fixed bug GH-18304 (Changing the properties of a DateInterval through + dynamic properties triggers a SegFault). (nielsdos) + . Fix some leaks in php_scandir. (nielsdos) + +- Filter: + . Fixed bug GH-18309 (ipv6 filter integer overflow). (nielsdos) + +- GD: + . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage + in gdImageCrop(). (David Carlier) + . Fixed GH-18243 imagettftext() overflow/underflow on font size value. + (David Carlier) + +- Intl: + . Fix reference support for intltz_get_offset(). (nielsdos) + +- LDAP: + . Fixed bug GH-17776 (LDAP_OPT_X_TLS_* options can't be overridden). (Remi) + . Fix NULL deref on high modification key. (nielsdos) + +- libxml: + . Fixed custom external entity loader returning an invalid resource leading + to a confusing TypeError message. (Girgias) + +- OpenSSL: + . Fix memory leak in openssl_sign() when passing invalid algorithm. + (nielsdos) + . Fix potential leaks when writing to BIO fails. (nielsdos) + +- PDO Firebird: + . Fixed GH-18276 - persistent connection - "zend_mm_heap corrupted" + with setAttribute() (SakiTakamachi). + +- SPL: + . Fixed bug GH-18322 (SplObjectStorage debug handler mismanages memory). + (nielsdos) + +- Standard: + . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). + (Jakub Zelenka) + . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov) + . Fixed bug GH-18212 (fseek with SEEK_CUR whence value and negative offset + leads to negative stream position). (David Carlier) + . Fix resource leak in iptcembed() on error. (nielsdos) + +- Zip: + . Fix uouv when handling empty options in ZipArchive::addGlob(). (nielsdos) + . Fix memory leak when handling a too long path in ZipArchive::addGlob(). + (nielsdos) + +10 Apr 2025, PHP 8.3.20 - Core: . Fixed bug GH-17961 (use-after-free during dl()'ed module class destruction). diff --git a/Zend/zend.h b/Zend/zend.h index 927088ea18706..f137a3bb4d765 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.20-dev" +#define ZEND_VERSION "4.3.21" #define ZEND_ENGINE_3 diff --git a/benchmark/generate_diff.php b/benchmark/generate_diff.php index b62f55c06d9e4..94c020df4b998 100644 --- a/benchmark/generate_diff.php +++ b/benchmark/generate_diff.php @@ -10,6 +10,7 @@ function main(?string $headCommitHash, ?string $baseCommitHash) { $repo = __DIR__ . '/repos/data'; cloneRepo($repo, 'git@github.com:php/benchmarking-data.git'); + $baseCommitHash = find_benchmarked_commit_hash($repo, $baseCommitHash); $headSummaryFile = $repo . '/' . substr($headCommitHash, 0, 2) . '/' . $headCommitHash . '/summary.json'; $baseSummaryFile = $repo . '/' . substr($baseCommitHash, 0, 2) . '/' . $baseCommitHash . '/summary.json'; if (!file_exists($headSummaryFile)) { @@ -60,6 +61,28 @@ function formatDiff(?int $baseInstructions, int $headInstructions): string { return sprintf('%.2f%%', $instructionDiff / $baseInstructions * 100); } +function find_benchmarked_commit_hash(string $repo, string $commitHash): ?string { + $repeat = 10; + + while (true) { + if ($repeat-- <= 0) { + fwrite(STDERR, "Count not find benchmarked commit hash\n"); + exit(1); + } + $summaryFile = $repo . '/' . substr($commitHash, 0, 2) . '/' . $commitHash . '/summary.json'; + if (file_exists($summaryFile)) { + break; + } + $commitHash = trim(runCommand( + ['git', 'rev-parse', $commitHash . '^'], + dirname(__DIR__), + printCommand: false, + )->stdout); + } + + return $commitHash; +} + $headCommitHash = $argv[1] ?? null; $baseCommitHash = $argv[2] ?? null; $output = main($headCommitHash, $baseCommitHash); diff --git a/benchmark/shared.php b/benchmark/shared.php index 450101770b28b..0f58a2a1bf870 100644 --- a/benchmark/shared.php +++ b/benchmark/shared.php @@ -5,12 +5,14 @@ class ProcessResult { public $stderr; } -function runCommand(array $args, ?string $cwd = null): ProcessResult { +function runCommand(array $args, ?string $cwd = null, bool $printCommand = true): ProcessResult { $cmd = implode(' ', array_map('escapeshellarg', $args)); $pipes = null; $result = new ProcessResult(); $descriptorSpec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; - fwrite(STDOUT, "> $cmd\n"); + if ($printCommand) { + fwrite(STDOUT, "> $cmd\n"); + } $processHandle = proc_open($cmd, $descriptorSpec, $pipes, $cwd ?? getcwd(), null); $stdin = $pipes[0]; diff --git a/configure.ac b/configure.ac index a94d4958492fd..d2b4f9422068d 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.3.20-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.21],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 6f7796eec1250..c1faa842f8a12 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -4399,7 +4399,9 @@ static zval *date_interval_get_property_ptr_ptr(zend_object *object, zend_string zend_string_equals_literal(name, "days") || zend_string_equals_literal(name, "invert") ) { /* Fallback to read_property. */ - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } ret = NULL; } else { ret = zend_std_get_property_ptr_ptr(object, name, type, cache_slot); diff --git a/ext/date/tests/gh18304.phpt b/ext/date/tests/gh18304.phpt new file mode 100644 index 0000000000000..4bab058a7ec4a --- /dev/null +++ b/ext/date/tests/gh18304.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--FILE-- +$field += $i; +var_dump($di); +?> +--EXPECT-- +object(DateInterval)#1 (10) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(1) + ["h"]=> + int(0) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["f"]=> + float(0) + ["invert"]=> + int(0) + ["days"]=> + bool(false) + ["from_string"]=> + bool(false) +} diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 1da53bae64b51..841dcd66186f8 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -303,7 +303,9 @@ static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, in return zend_std_get_property_ptr_ptr(object, name, type, cache_slot); } - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } return NULL; } diff --git a/ext/dom/tests/gh18304.phpt b/ext/dom/tests/gh18304.phpt new file mode 100644 index 0000000000000..ead44306ff801 --- /dev/null +++ b/ext/dom/tests/gh18304.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--EXTENSIONS-- +dom +--FILE-- +$field .= 'hello'; +var_dump($text->$field); +?> +--EXPECT-- +string(5) "hello" diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 1bd9bad5afbe1..76656a218d281 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -762,7 +762,8 @@ static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) { int compressed_pos = -1; int blocks = 0; - int num, n, i; + unsigned int num, n; + int i; char *ipv4; const char *end; int ip4elm[4]; diff --git a/ext/filter/tests/gh18309.phpt b/ext/filter/tests/gh18309.phpt new file mode 100644 index 0000000000000..b541f10883fe6 --- /dev/null +++ b/ext/filter/tests/gh18309.phpt @@ -0,0 +1,10 @@ +--TEST-- +GH-18309 (ipv6 filter integer overflow) +--EXTENSIONS-- +filter +--FILE-- + +--EXPECT-- +bool(false) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 2585923edcc22..6b727a211189a 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -3082,6 +3082,17 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode) im = php_gd_libgdimageptr_from_zval_p(IM); } + // FT_F26Dot6 is a signed long alias + if (ptsize < (double)LONG_MIN / 64 || ptsize > (double)LONG_MAX / 64) { + zend_argument_value_error(2, "must be between " ZEND_LONG_FMT " and " ZEND_LONG_FMT, (zend_long)((double)LONG_MIN / 64), (zend_long)((double)LONG_MAX / 64)); + RETURN_THROWS(); + } + + if (UNEXPECTED(!zend_finite(ptsize))) { + zend_argument_value_error(2, "must be finite"); + RETURN_THROWS(); + } + /* convert angle to radians */ angle = angle * (M_PI/180); @@ -3560,6 +3571,26 @@ PHP_FUNCTION(imagecrop) RETURN_THROWS(); } + if ((rect.width > 0 && rect.x > INT_MAX - rect.width)) { + zend_argument_value_error(2, "overflow with \"x\" and \"width\" keys"); + RETURN_THROWS(); + } + + if ((rect.width < 0 && rect.x < INT_MIN - rect.width)) { + zend_argument_value_error(2, "underflow with \"x\" and \"width\" keys"); + RETURN_THROWS(); + } + + if ((rect.height > 0 && rect.y > INT_MAX - rect.height)) { + zend_argument_value_error(2, "overflow with \"y\" and \"height\" keys"); + RETURN_THROWS(); + } + + if ((rect.height < 0 && rect.y < INT_MIN - rect.height)) { + zend_argument_value_error(2, "underflow with \"y\" and \"height\" keys"); + RETURN_THROWS(); + } + im_crop = gdImageCrop(im, &rect); if (im_crop == NULL) { diff --git a/ext/gd/tests/gh18243.phpt b/ext/gd/tests/gh18243.phpt new file mode 100644 index 0000000000000..3235098a3dcc2 --- /dev/null +++ b/ext/gd/tests/gh18243.phpt @@ -0,0 +1,42 @@ +--TEST-- +GH-18243: imagefttext underflow/overflow on $size +--EXTENSIONS-- +gd +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + imagettftext($im, PHP_INT_MIN, 0, 15, 60, 0, $font, ""); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + imagettftext($im, NAN, 0, 15, 60, 0, $font, ""); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + imagettftext($im, INF, 0, 15, 60, 0, $font, ""); +} catch (\ValueError $e) { + echo $e->getMessage(); +} +?> +--EXPECTF-- +imagettftext(): Argument #2 ($size) must be between %i and %d +imagettftext(): Argument #2 ($size) must be between %i and %d +imagettftext(): Argument #2 ($size) must be finite +imagettftext(): Argument #2 ($size) must be between %i and %d diff --git a/ext/gd/tests/imagecrop_overflow.phpt b/ext/gd/tests/imagecrop_overflow.phpt new file mode 100644 index 0000000000000..3331a6267168a --- /dev/null +++ b/ext/gd/tests/imagecrop_overflow.phpt @@ -0,0 +1,45 @@ +--TEST-- +imagecrop() overflows when the combo x/width or y/height is over INT_MAX or under INT_MIN. +--EXTENSIONS-- +gd +--FILE-- + 2147483647, "y" => 2147483647, "width" => 10, "height" => 10]; + +try { + imagecrop($img, $arr); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + +$arr = ["x" => -2147483648, "y" => 0, "width" => -10, "height" => 10]; + +try { + imagecrop($img, $arr); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + +$arr = ["x" => 1, "y" => 2147483647, "width" => 10, "height" => 10]; + +try { + imagecrop($img, $arr); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + +$arr = ["x" => 1, "y" => -2147483648, "width" => 10, "height" => -10]; + +try { + imagecrop($img, $arr); +} catch (\ValueError $e) { + echo $e->getMessage(); +} +?> +--EXPECT-- +imagecrop(): Argument #2 ($rectangle) overflow with "x" and "width" keys +imagecrop(): Argument #2 ($rectangle) underflow with "x" and "width" keys +imagecrop(): Argument #2 ($rectangle) overflow with "y" and "height" keys +imagecrop(): Argument #2 ($rectangle) underflow with "y" and "height" keys diff --git a/ext/intl/tests/dateformat_format_references.phpt b/ext/intl/tests/dateformat_format_references.phpt index da1a52955f121..576d901edeb82 100644 --- a/ext/intl/tests/dateformat_format_references.phpt +++ b/ext/intl/tests/dateformat_format_references.phpt @@ -1,5 +1,7 @@ --TEST-- Fix dateformat_format() with array argument with values as references. +--EXTENSIONS-- +intl --SKIPIF-- a = $test->b = "hello"; + +$rawOffset =& $test->a; +$dstOffset =& $test->b; +intltz_get_offset($tz, 0.0, true, $rawOffset, $dstOffset); +var_dump($test); +?> +--EXPECT-- +object(Test)#2 (2) { + ["a"]=> + &string(7) "3600000" + ["b"]=> + &string(1) "0" +} diff --git a/ext/intl/timezone/timezone_methods.cpp b/ext/intl/timezone/timezone_methods.cpp index cae64026fa5db..580a721e79ec1 100644 --- a/ext/intl/timezone/timezone_methods.cpp +++ b/ext/intl/timezone/timezone_methods.cpp @@ -419,7 +419,7 @@ U_CFUNC PHP_FUNCTION(intltz_get_offset) TIMEZONE_METHOD_INIT_VARS; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Odbz/z/", &object, TimeZone_ce_ptr, &date, &local, &rawOffsetArg, + "Odbzz", &object, TimeZone_ce_ptr, &date, &local, &rawOffsetArg, &dstOffsetArg) == FAILURE) { RETURN_THROWS(); } @@ -431,10 +431,8 @@ U_CFUNC PHP_FUNCTION(intltz_get_offset) INTL_METHOD_CHECK_STATUS(to, "intltz_get_offset: error obtaining offset"); - zval_ptr_dtor(rawOffsetArg); - ZVAL_LONG(rawOffsetArg, rawOffset); - zval_ptr_dtor(dstOffsetArg); - ZVAL_LONG(dstOffsetArg, dstOffset); + ZEND_TRY_ASSIGN_REF_LONG(rawOffsetArg, rawOffset); + ZEND_TRY_ASSIGN_REF_LONG(dstOffsetArg, dstOffset); RETURN_TRUE; } diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index db4c5ab231042..6c005337346b5 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -835,6 +835,21 @@ static PHP_GINIT_FUNCTION(ldap) } /* }}} */ +/* {{{ PHP_RINIT_FUNCTION */ +static PHP_RINIT_FUNCTION(ldap) +{ +#if defined(COMPILE_DL_LDAP) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + + /* needed before first connect and after TLS option changes */ + LDAPG(tls_newctx) = true; + + return SUCCESS; +} +/* }}} */ + + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(ldap) { @@ -987,6 +1002,20 @@ PHP_FUNCTION(ldap_connect) snprintf( url, urllen, "ldap://%s:" ZEND_LONG_FMT, host, port ); } +#ifdef LDAP_OPT_X_TLS_NEWCTX + if (LDAPG(tls_newctx) && url && !strncmp(url, "ldaps:", 6)) { + int val = 0; + + /* ensure all pending TLS options are applied in a new context */ + if (ldap_set_option(NULL, LDAP_OPT_X_TLS_NEWCTX, &val) != LDAP_OPT_SUCCESS) { + zval_ptr_dtor(return_value); + php_error_docref(NULL, E_WARNING, "Could not create new security context"); + RETURN_FALSE; + } + LDAPG(tls_newctx) = false; + } +#endif + #ifdef LDAP_API_FEATURE_X_OPENLDAP /* ldap_init() is deprecated, use ldap_initialize() instead. */ @@ -2756,12 +2785,12 @@ PHP_FUNCTION(ldap_modify_batch) ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0); /* for each modification */ - for (i = 0; i < num_mods; i++) { + i = 0; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(mods), fetched) { /* allocate the modification struct */ ldap_mods[i] = safe_emalloc(1, sizeof(LDAPMod), 0); /* fetch the relevant data */ - fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i); mod = fetched; _ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_ATTRIB, &attrib); @@ -2826,7 +2855,9 @@ PHP_FUNCTION(ldap_modify_batch) /* NULL-terminate values */ ldap_mods[i]->mod_bvalues[num_modvals] = NULL; } - } + + i++; + } ZEND_HASH_FOREACH_END(); /* NULL-terminate modifications */ ldap_mods[num_mods] = NULL; @@ -3172,15 +3203,7 @@ PHP_FUNCTION(ldap_set_option) } switch (option) { - /* options with int value */ - case LDAP_OPT_DEREF: - case LDAP_OPT_SIZELIMIT: - case LDAP_OPT_TIMELIMIT: - case LDAP_OPT_PROTOCOL_VERSION: - case LDAP_OPT_ERROR_NUMBER: -#ifdef LDAP_OPT_DEBUG_LEVEL - case LDAP_OPT_DEBUG_LEVEL: -#endif + /* TLS options with int value */ #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT case LDAP_OPT_X_TLS_REQUIRE_CERT: #endif @@ -3189,6 +3212,18 @@ PHP_FUNCTION(ldap_set_option) #endif #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN case LDAP_OPT_X_TLS_PROTOCOL_MIN: +#endif + /* TLS option change requires resetting TLS context */ + LDAPG(tls_newctx) = true; + ZEND_FALLTHROUGH; + /* other options with int value */ + case LDAP_OPT_DEREF: + case LDAP_OPT_SIZELIMIT: + case LDAP_OPT_TIMELIMIT: + case LDAP_OPT_PROTOCOL_VERSION: + case LDAP_OPT_ERROR_NUMBER: +#ifdef LDAP_OPT_DEBUG_LEVEL + case LDAP_OPT_DEBUG_LEVEL: #endif #ifdef LDAP_OPT_X_KEEPALIVE_IDLE case LDAP_OPT_X_KEEPALIVE_IDLE: @@ -3245,17 +3280,7 @@ PHP_FUNCTION(ldap_set_option) } } break; #endif - /* options with string value */ - case LDAP_OPT_ERROR_STRING: -#ifdef LDAP_OPT_HOST_NAME - case LDAP_OPT_HOST_NAME: -#endif -#ifdef HAVE_LDAP_SASL - case LDAP_OPT_X_SASL_MECH: - case LDAP_OPT_X_SASL_REALM: - case LDAP_OPT_X_SASL_AUTHCID: - case LDAP_OPT_X_SASL_AUTHZID: -#endif + /* TLS options with string value */ #if (LDAP_API_VERSION > 2000) case LDAP_OPT_X_TLS_CACERTDIR: case LDAP_OPT_X_TLS_CACERTFILE: @@ -3269,6 +3294,20 @@ PHP_FUNCTION(ldap_set_option) #endif #ifdef LDAP_OPT_X_TLS_DHFILE case LDAP_OPT_X_TLS_DHFILE: +#endif + /* TLS option change requires resetting TLS context */ + LDAPG(tls_newctx) = true; + ZEND_FALLTHROUGH; + /* other options with string value */ + case LDAP_OPT_ERROR_STRING: +#ifdef LDAP_OPT_HOST_NAME + case LDAP_OPT_HOST_NAME: +#endif +#ifdef HAVE_LDAP_SASL + case LDAP_OPT_X_SASL_MECH: + case LDAP_OPT_X_SASL_REALM: + case LDAP_OPT_X_SASL_AUTHCID: + case LDAP_OPT_X_SASL_AUTHZID: #endif #ifdef LDAP_OPT_MATCHED_DN case LDAP_OPT_MATCHED_DN: @@ -3688,6 +3727,9 @@ PHP_FUNCTION(ldap_start_tls) zval *link; ldap_linkdata *ld; int rc, protocol = LDAP_VERSION3; +#ifdef LDAP_OPT_X_TLS_NEWCTX + int val = 0; +#endif if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &link, ldap_link_ce) != SUCCESS) { RETURN_THROWS(); @@ -3697,13 +3739,16 @@ PHP_FUNCTION(ldap_start_tls) VERIFY_LDAP_LINK_CONNECTED(ld); if (((rc = ldap_set_option(ld->link, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) || +#ifdef LDAP_OPT_X_TLS_NEWCTX + (LDAPG(tls_newctx) && (rc = ldap_set_option(ld->link, LDAP_OPT_X_TLS_NEWCTX, &val)) != LDAP_OPT_SUCCESS) || +#endif ((rc = ldap_start_tls_s(ld->link, NULL, NULL)) != LDAP_SUCCESS) ) { php_error_docref(NULL, E_WARNING,"Unable to start TLS: %s", ldap_err2string(rc)); RETURN_FALSE; - } else { - RETURN_TRUE; } + LDAPG(tls_newctx) = false; + RETURN_TRUE; } /* }}} */ #endif @@ -4218,7 +4263,7 @@ zend_module_entry ldap_module_entry = { /* {{{ */ ext_functions, PHP_MINIT(ldap), PHP_MSHUTDOWN(ldap), - NULL, + PHP_RINIT(ldap), NULL, PHP_MINFO(ldap), PHP_LDAP_VERSION, diff --git a/ext/ldap/php_ldap.h b/ext/ldap/php_ldap.h index 5581f5b7671a8..a8c4a77af8010 100644 --- a/ext/ldap/php_ldap.h +++ b/ext/ldap/php_ldap.h @@ -39,6 +39,7 @@ PHP_MINFO_FUNCTION(ldap); ZEND_BEGIN_MODULE_GLOBALS(ldap) zend_long num_links; zend_long max_links; + bool tls_newctx; /* create new TLS context before connect */ ZEND_END_MODULE_GLOBALS(ldap) #if defined(ZTS) && defined(COMPILE_DL_LDAP) diff --git a/ext/ldap/tests/ldap_modify_batch_error.phpt b/ext/ldap/tests/ldap_modify_batch_error.phpt index bce62cafb2791..0ac093b4a0341 100644 --- a/ext/ldap/tests/ldap_modify_batch_error.phpt +++ b/ext/ldap/tests/ldap_modify_batch_error.phpt @@ -59,6 +59,16 @@ $mods = array( ) ); +var_dump(ldap_modify_batch($link, "dc=my-domain,$base", $mods)); + +// high key with invalid attribute type +$mods = [ + 99999 => [ + "attrib" => "weirdAttribute", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], +]; var_dump(ldap_modify_batch($link, "dc=my-domain,$base", $mods)); ?> --CLEAN-- @@ -81,3 +91,6 @@ bool(false) Warning: ldap_modify_batch(): Batch Modify: Undefined attribute type in %s on line %d bool(false) + +Warning: ldap_modify_batch(): Batch Modify: Undefined attribute type in %s on line %d +bool(false) diff --git a/ext/ldap/tests/ldap_start_tls_basic.phpt b/ext/ldap/tests/ldap_start_tls_basic.phpt index 4dbdce034369f..b8816de9ac4f5 100644 --- a/ext/ldap/tests/ldap_start_tls_basic.phpt +++ b/ext/ldap/tests/ldap_start_tls_basic.phpt @@ -9,11 +9,28 @@ ldap --FILE-- --EXPECT-- +bool(false) bool(true) +bool(false) diff --git a/ext/ldap/tests/ldaps_basic.phpt b/ext/ldap/tests/ldaps_basic.phpt new file mode 100644 index 0000000000000..7a1a1383436d7 --- /dev/null +++ b/ext/ldap/tests/ldaps_basic.phpt @@ -0,0 +1,55 @@ +--TEST-- +ldap_connect() - Basic ldaps test +--EXTENSIONS-- +ldap +--XFAIL-- +Passes locally but fails on CI - need investigation (configuration ?) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 18ca51e36a052..5c903d2c9a228 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -793,13 +793,18 @@ static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL, is_string: resource = Z_STRVAL(retval); } else if (Z_TYPE(retval) == IS_RESOURCE) { - php_stream *stream; - php_stream_from_zval_no_verify(stream, &retval); - if (stream == NULL) { - php_libxml_ctx_error(context, - "The user entity loader callback '%s' has returned a " - "resource, but it is not a stream", - ZSTR_VAL(LIBXML(entity_loader_callback).function_handler->common.function_name)); + php_stream *stream = (php_stream*)zend_fetch_resource2_ex(&retval, NULL, php_file_le_stream(), php_file_le_pstream()); + if (UNEXPECTED(stream == NULL)) { + zval callable; + zend_get_callable_zval_from_fcc(&LIBXML(entity_loader_callback), &callable); + zend_string *callable_name = zend_get_callable_name(&callable); + zend_string *func_name = get_active_function_or_method_name(); + zend_type_error( + "%s(): The user entity loader callback \"%s\" has returned a resource, but it is not a stream", + ZSTR_VAL(func_name), ZSTR_VAL(callable_name)); + zend_string_release(func_name); + zend_string_release(callable_name); + zval_ptr_dtor(&callable); } else { /* TODO: allow storing the encoding in the stream context? */ xmlCharEncoding enc = XML_CHAR_ENCODING_NONE; diff --git a/ext/libxml/tests/libxml_get_external_entity_loader_error_callback_name.phpt b/ext/libxml/tests/libxml_get_external_entity_loader_error_callback_name.phpt index 1bdbbfb5b817e..2122785ef5b9b 100644 --- a/ext/libxml/tests/libxml_get_external_entity_loader_error_callback_name.phpt +++ b/ext/libxml/tests/libxml_get_external_entity_loader_error_callback_name.phpt @@ -40,4 +40,4 @@ $file = __DIR__ . '/db.dba'; unlink($file); ?> --EXPECT-- -string(73) "DOMDocument::validate(): supplied resource is not a valid stream resource" +string(122) "DOMDocument::validate(): The user entity loader callback "Handler::handle" has returned a resource, but it is not a stream" diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index c5720ee97e38c..c978859b7ec00 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -5742,8 +5742,8 @@ PHP_FUNCTION(openssl_pkcs7_read) BIO_get_mem_ptr(bio_out, &bio_buf); ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length); add_index_zval(zout, i, &zcert); - BIO_free(bio_out); } + BIO_free(bio_out); } } @@ -5757,8 +5757,8 @@ PHP_FUNCTION(openssl_pkcs7_read) BIO_get_mem_ptr(bio_out, &bio_buf); ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length); add_index_zval(zout, i, &zcert); - BIO_free(bio_out); } + BIO_free(bio_out); } } @@ -6383,8 +6383,8 @@ PHP_FUNCTION(openssl_cms_read) BIO_get_mem_ptr(bio_out, &bio_buf); ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length); add_index_zval(zout, i, &zcert); - BIO_free(bio_out); } + BIO_free(bio_out); } } @@ -6398,8 +6398,8 @@ PHP_FUNCTION(openssl_cms_read) BIO_get_mem_ptr(bio_out, &bio_buf); ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length); add_index_zval(zout, i, &zcert); - BIO_free(bio_out); } + BIO_free(bio_out); } } @@ -6959,6 +6959,7 @@ PHP_FUNCTION(openssl_sign) mdtype = php_openssl_get_evp_md_from_algo(method_long); } if (!mdtype) { + EVP_PKEY_free(pkey); php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; } @@ -7939,11 +7940,10 @@ PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_le PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(buffer_length, length); PHP_OPENSSL_RAND_ADD_TIME(); if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), (int)buffer_length) <= 0) { + php_openssl_store_errors(); zend_string_release_ex(buffer, 0); zend_throw_exception(zend_ce_exception, "Error reading from source device", 0); return NULL; - } else { - php_openssl_store_errors(); } return buffer; @@ -7960,17 +7960,15 @@ PHP_FUNCTION(openssl_random_pseudo_bytes) RETURN_THROWS(); } - if (zstrong_result_returned) { - ZEND_TRY_ASSIGN_REF_FALSE(zstrong_result_returned); - } - if ((buffer = php_openssl_random_pseudo_bytes(buffer_length))) { ZSTR_VAL(buffer)[buffer_length] = 0; RETVAL_NEW_STR(buffer); - } - if (zstrong_result_returned) { - ZEND_TRY_ASSIGN_REF_TRUE(zstrong_result_returned); + if (zstrong_result_returned) { + ZEND_TRY_ASSIGN_REF_TRUE(zstrong_result_returned); + } + } else if (zstrong_result_returned) { + ZEND_TRY_ASSIGN_REF_FALSE(zstrong_result_returned); } } /* }}} */ diff --git a/ext/openssl/tests/openssl_sign_invalid_algorithm.phpt b/ext/openssl/tests/openssl_sign_invalid_algorithm.phpt new file mode 100644 index 0000000000000..c669a373a1079 --- /dev/null +++ b/ext/openssl/tests/openssl_sign_invalid_algorithm.phpt @@ -0,0 +1,18 @@ +--TEST-- +openssl_sign: invalid algorithm +--EXTENSIONS-- +openssl +--FILE-- + +--EXPECTF-- +Warning: openssl_sign(): Unknown digest algorithm in %s on line %d diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index efbf519e5411e..8454b4875374e 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2493,9 +2493,10 @@ static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name ZEND_IGNORE_VALUE(object); ZEND_IGNORE_VALUE(name); ZEND_IGNORE_VALUE(type); - ZEND_IGNORE_VALUE(cache_slot); - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } return NULL; } diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 4f8cd83d7b8ab..504e739a974d9 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -492,13 +492,13 @@ static void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */ } if (H->date_format) { - efree(H->date_format); + pefree(H->date_format, dbh->is_persistent); } if (H->time_format) { - efree(H->time_format); + pefree(H->time_format, dbh->is_persistent); } if (H->timestamp_format) { - efree(H->timestamp_format); + pefree(H->timestamp_format, dbh->is_persistent); } pefree(H, dbh->is_persistent); @@ -881,9 +881,10 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval * return false; } if (H->date_format) { - efree(H->date_format); + pefree(H->date_format, dbh->is_persistent); + H->date_format = NULL; } - spprintf(&H->date_format, 0, "%s", ZSTR_VAL(str)); + H->date_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent); zend_string_release_ex(str, 0); } return true; @@ -895,9 +896,10 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval * return false; } if (H->time_format) { - efree(H->time_format); + pefree(H->time_format, dbh->is_persistent); + H->time_format = NULL; } - spprintf(&H->time_format, 0, "%s", ZSTR_VAL(str)); + H->time_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent); zend_string_release_ex(str, 0); } return true; @@ -909,9 +911,10 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval * return false; } if (H->timestamp_format) { - efree(H->timestamp_format); + pefree(H->timestamp_format, dbh->is_persistent); + H->timestamp_format = NULL; } - spprintf(&H->timestamp_format, 0, "%s", ZSTR_VAL(str)); + H->timestamp_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent); zend_string_release_ex(str, 0); } return true; diff --git a/ext/pdo_firebird/tests/gh18276.phpt b/ext/pdo_firebird/tests/gh18276.phpt new file mode 100644 index 0000000000000..610876166ccf7 --- /dev/null +++ b/ext/pdo_firebird/tests/gh18276.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-18276 (persistent connection - setAttribute(Pdo\Firebird::ATTR_DATE_FORMAT, ..) results in "zend_mm_heap corrupted") +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--XLEAK-- +A bug in firebird causes a memory leak when calling `isc_attach_database()`. +See https://github.com/FirebirdSQL/firebird/issues/7849 +--FILE-- + true, + ], + ); + // Avoid interned + $dbh->setAttribute(PDO::FB_ATTR_DATE_FORMAT, str_repeat('Y----m----d', random_int(1, 1))); + $dbh->setAttribute(PDO::FB_ATTR_TIME_FORMAT, str_repeat('H::::i::::s', random_int(1, 1))); + $dbh->setAttribute(PDO::FB_ATTR_TIMESTAMP_FORMAT, str_repeat('Y----m----d....H::::i::::s', random_int(1, 1))); + unset($dbh); +} + +echo 'done!'; +?> +--EXPECT-- +done! diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 46ec3a2a788ab..11f497a6673ea 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -635,7 +635,9 @@ static zval *sxe_property_get_adr(zend_object *object, zend_string *zname, int f SXE_ITER type; zval member; - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } sxe = php_sxe_fetch_object(object); GET_NODE(sxe, node); diff --git a/ext/simplexml/tests/gh18304.phpt b/ext/simplexml/tests/gh18304.phpt new file mode 100644 index 0000000000000..b92690856e231 --- /dev/null +++ b/ext/simplexml/tests/gh18304.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--EXTENSIONS-- +simplexml +--FILE-- +'); +$field = 'abc'; +$sxe->$field .= 'hello'; +var_dump($sxe->$field); +?> +--EXPECT-- +object(SimpleXMLElement)#3 (1) { + [0]=> + string(5) "hello" +} diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 02348a5fbca00..0ba39ed87c241 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -1861,7 +1861,9 @@ static zval *php_snmp_get_property_ptr_ptr(zend_object *object, zend_string *nam return zend_std_get_property_ptr_ptr(object, name, type, cache_slot); } - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } return NULL; } diff --git a/ext/snmp/tests/gh18304.phpt b/ext/snmp/tests/gh18304.phpt new file mode 100644 index 0000000000000..2faf503ac1a58 --- /dev/null +++ b/ext/snmp/tests/gh18304.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--EXTENSIONS-- +snmp +--FILE-- +$field++; +var_dump($snmp->$field); +?> +--EXPECT-- +int(1) diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 8d4541797a1c5..0abaf743ac9d1 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -846,7 +846,9 @@ static zval *spl_array_get_property_ptr_ptr(zend_object *object, zend_string *na if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0 && !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) { - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } /* If object has offsetGet() overridden, then fallback to read_property, * which will call offsetGet(). */ diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index b3a27cb10292c..83889c430b7a2 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -340,12 +340,10 @@ static inline HashTable* spl_object_storage_debug_info(zend_object *obj) /* {{{ ZEND_HASH_FOREACH_PTR(&intern->storage, element) { array_init(&tmp); - /* Incrementing the refcount of obj and inf would confuse the garbage collector. - * Prefer to null the destructor */ - Z_ARRVAL_P(&tmp)->pDestructor = NULL; zval obj; - ZVAL_OBJ(&obj, element->obj); + ZVAL_OBJ_COPY(&obj, element->obj); add_assoc_zval_ex(&tmp, "obj", sizeof("obj") - 1, &obj); + Z_TRY_ADDREF(element->inf); add_assoc_zval_ex(&tmp, "inf", sizeof("inf") - 1, &element->inf); zend_hash_next_index_insert(Z_ARRVAL(storage), &tmp); } ZEND_HASH_FOREACH_END(); diff --git a/ext/spl/tests/gh18304.phpt b/ext/spl/tests/gh18304.phpt new file mode 100644 index 0000000000000..d93ee3534d0d4 --- /dev/null +++ b/ext/spl/tests/gh18304.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--FILE-- + 1]); +$ao->setFlags(ArrayObject::ARRAY_AS_PROPS); +$field = 'abc'; +$ao->$field++; +var_dump($ao->$field); +?> +--EXPECT-- +int(2) diff --git a/ext/spl/tests/gh18322.phpt b/ext/spl/tests/gh18322.phpt new file mode 100644 index 0000000000000..e70cbd0d37dd5 --- /dev/null +++ b/ext/spl/tests/gh18322.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-18322 (SplObjectStorage debug handler mismanages memory) +--FILE-- +__debugInfo(); +$tmp2 = $tmp[array_key_first($tmp)]; +unset($tmp); // Drop $tmp2 RC to 1 +$tmp2[0]['obj'] = new stdClass; +var_dump($tmp2); + +?> +--EXPECT-- +array(1) { + [0]=> + array(2) { + ["obj"]=> + object(stdClass)#3 (0) { + } + ["inf"]=> + int(1) + } +} diff --git a/ext/standard/array.c b/ext/standard/array.c index 7382e1e9f8bd9..b89deb241f046 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1863,8 +1863,10 @@ static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_t } else { ZVAL_MAKE_REF_EX(entry, 2); } - zval_ptr_dtor(orig_var); + zval garbage; + ZVAL_COPY_VALUE(&garbage, orig_var); ZVAL_REF(orig_var, Z_REF_P(entry)); + zval_ptr_dtor(&garbage); } else { if (Z_ISREF_P(entry)) { Z_ADDREF_P(entry); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 36cf727b82a24..99d92af167f0e 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -215,31 +215,16 @@ static void php_putenv_destructor(zval *zv) /* {{{ */ static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */ { - BG(umask) = -1; - BG(user_tick_functions) = NULL; - BG(user_filter_map) = NULL; - BG(serialize_lock) = 0; - - memset(&BG(serialize), 0, sizeof(BG(serialize))); - memset(&BG(unserialize), 0, sizeof(BG(unserialize))); - - memset(&BG(url_adapt_session_ex), 0, sizeof(BG(url_adapt_session_ex))); - memset(&BG(url_adapt_output_ex), 0, sizeof(BG(url_adapt_output_ex))); + memset(basic_globals_p, 0, sizeof(php_basic_globals)); - BG(url_adapt_session_ex).type = 1; - BG(url_adapt_output_ex).type = 0; + basic_globals_p->umask = -1; + basic_globals_p->url_adapt_session_ex.type = 1; - zend_hash_init(&BG(url_adapt_session_hosts_ht), 0, NULL, NULL, 1); - zend_hash_init(&BG(url_adapt_output_hosts_ht), 0, NULL, NULL, 1); - -#if defined(_REENTRANT) - memset(&BG(mblen_state), 0, sizeof(BG(mblen_state))); -#endif - - BG(page_uid) = -1; - BG(page_gid) = -1; + zend_hash_init(&basic_globals_p->url_adapt_session_hosts_ht, 0, NULL, NULL, 1); + zend_hash_init(&basic_globals_p->url_adapt_output_hosts_ht, 0, NULL, NULL, 1); - BG(syslog_device) = NULL; + basic_globals_p->page_uid = -1; + basic_globals_p->page_gid = -1; } /* }}} */ diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index be9b024258ee8..3bdc3827b4d36 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1513,7 +1513,7 @@ function header_register_callback(callable $callback): bool {} /* main/output.c */ -/** @param callable $callback */ +/** @param callable|null $callback */ function ob_start($callback = null, int $chunk_size = 0, int $flags = PHP_OUTPUT_HANDLER_STDFLAGS): bool {} function ob_flush(): bool {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 99e08feddf590..1361cf6062797 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7389d094a842a2289cd32cb37386e5e40ea7e031 */ + * Stub hash: 2a3d8da0b92134dcca74f2ac70454bd27768f20e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) diff --git a/ext/standard/iptc.c b/ext/standard/iptc.c index e4dd38637570a..44dd33bab10ac 100644 --- a/ext/standard/iptc.c +++ b/ext/standard/iptc.c @@ -204,6 +204,7 @@ PHP_FUNCTION(iptcembed) if (spool < 2) { if (zend_fstat(fileno(fp), &sb) != 0) { + fclose(fp); RETURN_FALSE; } diff --git a/ext/standard/tests/file/gh18212.phpt b/ext/standard/tests/file/gh18212.phpt new file mode 100644 index 0000000000000..6e4d8ad9bd328 --- /dev/null +++ b/ext/standard/tests/file/gh18212.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-18212: fseek with SEEK_CUR and negative offset leads to negative file stream position. +--FILE-- + +--EXPECT-- +int(-1) +int(-1) + diff --git a/ext/standard/tests/file/stream_rfc2397_007.phpt b/ext/standard/tests/file/stream_rfc2397_007.phpt index dcbe5beeb3dc9..49a037e855fbd 100644 --- a/ext/standard/tests/file/stream_rfc2397_007.phpt +++ b/ext/standard/tests/file/stream_rfc2397_007.phpt @@ -118,7 +118,7 @@ int(2) bool(false) ===S:-10,C=== int(-1) -bool(false) +int(2) bool(false) ===S:3,S=== int(0) diff --git a/ext/standard/tests/gh18209.phpt b/ext/standard/tests/gh18209.phpt new file mode 100644 index 0000000000000..6a759639f7dcb --- /dev/null +++ b/ext/standard/tests/gh18209.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-18209: Use-after-free in extract() with EXTR_REFS +--CREDITS-- +Noam Rathaus (nrathaus) +--FILE-- + 42]; +extract($array, EXTR_REFS); +var_dump($b); + +?> +--EXPECT-- +int(42) +int(43) diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index 569058e91a454..dca1898f1c044 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -121,8 +121,6 @@ zval *xmlreader_get_property_ptr_ptr(zend_object *object, zend_string *name, int zval *retval = NULL; xmlreader_prop_handler *hnd = NULL; - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; - obj = php_xmlreader_fetch_object(object); if (obj->prop_handler != NULL) { @@ -131,6 +129,8 @@ zval *xmlreader_get_property_ptr_ptr(zend_object *object, zend_string *name, int if (hnd == NULL) { retval = zend_std_get_property_ptr_ptr(object, name, type, cache_slot); + } else if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; } return retval; diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index e28cf688dcff8..45b83aeae63bc 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -354,13 +354,13 @@ typedef struct { #endif } zip_options; +/* Expects opts to be zero-initialized. */ static int php_zip_parse_options(HashTable *options, zip_options *opts) /* {{{ */ { zval *option; /* default values */ - memset(opts, 0, sizeof(zip_options)); opts->flags = ZIP_FL_OVERWRITE; opts->comp_method = -1; /* -1 to not change default */ #ifdef HAVE_ENCRYPTION @@ -890,8 +890,6 @@ static zval *php_zip_get_property_ptr_ptr(zend_object *object, zend_string *name zval *retval = NULL; zip_prop_handler *hnd = NULL; - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; - obj = php_zip_fetch_object(object); if (obj->prop_handler != NULL) { @@ -900,6 +898,8 @@ static zval *php_zip_get_property_ptr_ptr(zend_object *object, zend_string *name if (hnd == NULL) { retval = zend_std_get_property_ptr_ptr(object, name, type, cache_slot); + } else if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; } return retval; @@ -1732,7 +1732,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* size_t path_len = 1; zend_long glob_flags = 0; HashTable *options = NULL; - zip_options opts; + zip_options opts = {0}; int found; zend_string *pattern; @@ -1796,6 +1796,9 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* if (opts.add_path) { if ((opts.add_path_len + file_stripped_len) > MAXPATHLEN) { + if (basename) { + zend_string_release_ex(basename, 0); + } php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %zd given)", MAXPATHLEN - 1, (opts.add_path_len + file_stripped_len)); zend_array_destroy(Z_ARR_P(return_value)); diff --git a/ext/zip/tests/addGlob_empty_options.phpt b/ext/zip/tests/addGlob_empty_options.phpt new file mode 100644 index 0000000000000..f4a4126059a7b --- /dev/null +++ b/ext/zip/tests/addGlob_empty_options.phpt @@ -0,0 +1,22 @@ +--TEST-- +addGlob with empty options +--EXTENSIONS-- +zip +--FILE-- +open($file, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->addGlob(__FILE__, 0, []); +var_dump($zip->statIndex(0)['name'] === __FILE__); +$zip->close(); + +?> +--CLEAN-- + +--EXPECT-- +bool(true) diff --git a/ext/zip/tests/addGlob_too_long_add_path_option.phpt b/ext/zip/tests/addGlob_too_long_add_path_option.phpt new file mode 100644 index 0000000000000..9598eeca40a89 --- /dev/null +++ b/ext/zip/tests/addGlob_too_long_add_path_option.phpt @@ -0,0 +1,21 @@ +--TEST-- +addGlob with too long add_path option +--EXTENSIONS-- +zip +--FILE-- +open($file, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->addGlob(__FILE__, 0, ['add_path' => str_repeat('A', PHP_MAXPATHLEN - 2)]); +$zip->close(); + +?> +--CLEAN-- + +--EXPECTF-- +Warning: ZipArchive::addGlob(): Entry name too long (max: %d, %d given) in %s on line %d diff --git a/main/php_scandir.c b/main/php_scandir.c index 7d1eb36023665..7bf91bdf7f33d 100644 --- a/main/php_scandir.c +++ b/main/php_scandir.c @@ -83,7 +83,7 @@ PHPAPI int php_scandir(const char *dirname, struct dirent **namelist[], int (*se newv = (struct dirent **) realloc (vector, vector_size * sizeof (struct dirent *)); if (!newv) { - return -1; + goto fail; } vector = newv; } @@ -113,6 +113,7 @@ PHPAPI int php_scandir(const char *dirname, struct dirent **namelist[], int (*se free(vector[nfiles]); } free(vector); + closedir(dirp); return -1; } #endif diff --git a/main/php_version.h b/main/php_version.h index 3dee6de933251..335e319de4fee 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 20 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.20-dev" -#define PHP_VERSION_ID 80320 +#define PHP_RELEASE_VERSION 21 +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.3.21" +#define PHP_VERSION_ID 80321 diff --git a/main/streams/streams.c b/main/streams/streams.c index 7a5dc2a58aaf1..4e0aaa53b443e 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1390,6 +1390,10 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) } whence = SEEK_SET; break; + case SEEK_SET: + if (offset < 0) { + return -1; + } } ret = stream->ops->seek(stream, offset, whence, &stream->position);