diff --git a/.editorconfig b/.editorconfig index 7911bf8490b63..19be96087d908 100644 --- a/.editorconfig +++ b/.editorconfig @@ -32,3 +32,7 @@ max_line_length = 80 [*.patch] trim_trailing_whitespace = false + +[*.rst] +indent_style = space +max_line_length = 100 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2254b8f037e54..6ae972d92e49c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,9 +19,9 @@ jobs: - name: git checkout uses: actions/checkout@v4 - name: Install dependencies - run: pip install sphinx-design sphinxawesome-theme rstfmt + run: pip install -r docs/requirements.txt - name: Check formatting - run: rstfmt --check -w 100 docs/source + run: make -C docs check-formatting - name: Publish if: github.event_name == 'push' uses: sphinx-notes/pages@v3 diff --git a/NEWS b/NEWS index 2bc7d6131aa81..1d9eaa59081fa 100644 --- a/NEWS +++ b/NEWS @@ -3,7 +3,8 @@ PHP NEWS ?? ??? ????, PHP 8.5.0alpha1 - COM: - . Fix property access of PHP objects wrapped in variant. (cmb) + . Fixed property access of PHP objects wrapped in variant. (cmb) + . Fixed method calls for PHP objects wrapped in variant. (cmb) - Core: . Fixed bug GH-16665 (\array and \callable should not be usable in @@ -20,6 +21,10 @@ PHP NEWS - Intl: . Bumped ICU requirement to ICU >= 57.1. (cmb) +- MySQLnd: + . Added mysqlnd.collect_memory_statistics to ini quick reference. + (hauk92) + - OPcache: . Fixed ZTS OPcache build on Cygwin. (cmb) . Added opcache.file_cache_read_only. (Samuel Melrose) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7e5351ddfafdb..89d104686a036 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2196,7 +2196,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) } /* Strip trailing slashes */ - while (end >= path && IS_SLASH_P(end)) { + while (end >= path && IS_SLASH_P_EX(end, end == path)) { end--; } if (end < path) { @@ -2207,7 +2207,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) } /* Strip filename */ - while (end >= path && !IS_SLASH_P(end)) { + while (end >= path && !IS_SLASH_P_EX(end, end == path)) { end--; } if (end < path) { @@ -2218,7 +2218,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) } /* Strip slashes which came before the file name */ - while (end >= path && IS_SLASH_P(end)) { + while (end >= path && IS_SLASH_P_EX(end, end == path)) { end--; } if (end < path) { diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h index 0fc2799118692..21735f6dfae57 100644 --- a/Zend/zend_virtual_cwd.h +++ b/Zend/zend_virtual_cwd.h @@ -75,8 +75,11 @@ typedef unsigned short mode_t; #define DEFAULT_SLASH '\\' #define DEFAULT_DIR_SEPARATOR ';' #define IS_SLASH(c) ((c) == '/' || (c) == '\\') +// IS_SLASH_P() may read the previous char on Windows, which may be OOB; use IS_SLASH_P_EX() instead #define IS_SLASH_P(c) (*(c) == '/' || \ (*(c) == '\\' && !IsDBCSLeadByte(*(c-1)))) +#define IS_SLASH_P_EX(c, first_byte) (*(c) == '/' || \ + (*(c) == '\\' && ((first_byte) || !IsDBCSLeadByte(*(c-1))))) /* COPY_WHEN_ABSOLUTE is 2 under Win32 because by chance both regular absolute paths in the file system and UNC paths need copying of two characters */ @@ -110,7 +113,9 @@ typedef unsigned short mode_t; #endif #define IS_SLASH(c) ((c) == '/') +// IS_SLASH_P() may read the previous char on Windows, which may be OOB; use IS_SLASH_P_EX() instead #define IS_SLASH_P(c) (*(c) == '/') +#define IS_SLASH_P_EX(c, first_byte) IS_SLASH_P(c) #endif diff --git a/build/gen_stub.php b/build/gen_stub.php index 1468a0b68b164..b105ed4efd8db 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -1159,8 +1159,7 @@ class ReturnInfo { const REFCOUNT_1 = "1"; const REFCOUNT_N = "N"; - const REFCOUNTS = [ - self::REFCOUNT_0, + const REFCOUNTS_NONSCALAR = [ self::REFCOUNT_1, self::REFCOUNT_N, ]; @@ -1204,16 +1203,14 @@ private function setRefcount(?string $refcount): void return; } - if (!in_array($refcount, ReturnInfo::REFCOUNTS, true)) { - throw new Exception("@refcount must have one of the following values: \"0\", \"1\", \"N\", $refcount given"); - } - - if ($isScalarType && $refcount !== self::REFCOUNT_0) { - throw new Exception('A scalar return type of "' . $type->__toString() . '" must have a refcount of "' . self::REFCOUNT_0 . '"'); + if ($isScalarType) { + throw new Exception( + "@refcount on functions returning scalar values is redundant and not permitted" + ); } - if (!$isScalarType && $refcount === self::REFCOUNT_0) { - throw new Exception('A non-scalar return type of "' . $type->__toString() . '" cannot have a refcount of "' . self::REFCOUNT_0 . '"'); + if (!in_array($refcount, ReturnInfo::REFCOUNTS_NONSCALAR, true)) { + throw new Exception("@refcount must have one of the following values: \"1\", \"N\", $refcount given"); } $this->refcount = $refcount; @@ -2613,7 +2610,7 @@ protected function getFieldSynopsisDefaultLinkend(): string { $className = str_replace(["\\", "_"], ["-", "-"], $this->name->class->toLowerString()); - return "$className.constants." . strtolower(str_replace(["__", "_"], ["", "-"], $this->name->getDeclarationName())); + return "$className.constants." . strtolower(str_replace("_", "-", trim($this->name->getDeclarationName(), "_"))); } protected function getFieldSynopsisName(): string @@ -3055,7 +3052,7 @@ protected function getFieldSynopsisDefaultLinkend(): string { $className = str_replace(["\\", "_"], ["-", "-"], $this->name->class->toLowerString()); - return "$className.props." . strtolower(str_replace(["__", "_"], ["", "-"], $this->name->getDeclarationName())); + return "$className.props." . strtolower(str_replace("_", "-", trim($this->name->getDeclarationName(), "_"))); } protected function getFieldSynopsisName(): string @@ -6427,8 +6424,14 @@ function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc } foreach ($methodSynopses as $filename => $content) { - if (!file_exists("$manualTarget/$filename")) { - if (file_put_contents("$manualTarget/$filename", $content)) { + $path = "$manualTarget/$filename"; + + if (!file_exists($path)) { + if (!file_exists(dirname($path))) { + mkdir(dirname($path)); + } + + if (file_put_contents($path, $content)) { echo "Saved $filename\n"; } } diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000000..12f3a0600a949 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,36 @@ +# Makefile for php-src/docs +# Copyright (c) The PHP Group + +# If people set these on the make command line, use 'em + +SPHINXBUILD ?= sphinx-build + +SOURCEDIR = source +BUILDDIR = build +RSTFMT = rstfmt +RSTFMTFLAGS = -w 100 + +rwildcard = $(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) +FILES = $(call rwildcard,$(SOURCEDIR),*.rst) + +all : html + +.PHONY : check-formatting clean html preflight +.SUFFIXES : # Disable legacy behavior + +check-formatting : + $(RSTFMT) $(RSTFMTFLAGS) --check $(SOURCEDIR) + +clean : + rm -rf -- $(wildcard $(SOURCEDIR)/.~ $(BUILDDIR)) + +html : preflight + $(SPHINXBUILD) -M $@ $(SOURCEDIR) $(BUILDDIR) + @printf 'Browse the \e]8;;%s\e\\%s\e]8;;\e\\.\n' \ + "file://$(abspath $(BUILDDIR))/$@/index.$@" "php-src html docs locally" + +preflight : $(SOURCEDIR)/.~ + +$(SOURCEDIR)/.~ : $(FILES) + $(RSTFMT) $(RSTFMTFLAGS) $? + touch $@ diff --git a/docs/README.md b/docs/README.md index fbd58155a5141..3c0d7ddd2bcc3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,9 +1,10 @@ # php-src docs This is the home of the php-src internal documentation, hosted at -[php.github.io/php-src/](https://php.github.io/php-src/). It is in very early stages, but is -intended to become the primary place where new information about php-src is documented. Over time, -it is expected to replace various mediums like: +[php.github.io/php-src/](https://php.github.io/php-src/). It is in very early +stages, but is intended to become the primary place where new information about +php-src is documented. Over time, it is expected to replace various mediums +like: * https://www.phpinternalsbook.com/ * https://wiki.php.net/internals @@ -14,11 +15,15 @@ it is expected to replace various mediums like: `python` 3 and `pip` are required. ```bash -pip install sphinx sphinx-design sphinxawesome-theme +cd docs +# Recommended: Initialize and activate a Python virtual environment +pip install --upgrade pip +pip install -r requirements.txt make html ``` -That's it! You can view the documentation under `./build/html/index.html` in your browser. +That's it! You can view the documentation under `./build/html/index.html` in +your browser. ## Formatting @@ -29,5 +34,5 @@ The files in this documentation are formatted using the rstfmt -w 100 source ``` -This tool is not perfect. It breaks on custom directives, so we might switch to either a fork or -something else in the future. +This tool is not perfect. It breaks on custom directives, so we might switch to +either a fork or something else in the future. diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 747ffb7b30336..0000000000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000000000..ca19fe15c2e05 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +Sphinx +sphinx-design +sphinxawesome-theme +rstfmt diff --git a/docs/source/conf.py b/docs/source/conf.py index 2eb75d509cc07..1129eb08a8216 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,7 +16,6 @@ author = 'The PHP Group' extensions = [ 'sphinx_design', - 'sphinxawesome_theme.highlighting', ] templates_path = ['_templates'] html_theme = 'sphinxawesome_theme' diff --git a/ext/com_dotnet/com_wrapper.c b/ext/com_dotnet/com_wrapper.c index 42698a2e651f5..6e885fa802e9f 100644 --- a/ext/com_dotnet/com_wrapper.c +++ b/ext/com_dotnet/com_wrapper.c @@ -256,39 +256,34 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex( /* TODO: if PHP raises an exception here, we should catch it * and expose it as a COM exception */ - - if (wFlags & DISPATCH_PROPERTYGET) { - retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), 1, &rv); - ret = S_OK; - } else if (wFlags & DISPATCH_PROPERTYPUT) { + zend_fcall_info_cache fcc; + if (wFlags & DISPATCH_PROPERTYPUT) { zend_update_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), ¶ms[0]); ret = S_OK; - } else if (wFlags & DISPATCH_METHOD) { + } else if (wFlags & DISPATCH_METHOD && zend_is_callable_ex(name, Z_OBJ(disp->object), 0, NULL, &fcc, NULL)) { zend_try { retval = &rv; - if (SUCCESS == call_user_function(NULL, &disp->object, name, - retval, pdp->cArgs, params)) { - ret = S_OK; - trace("function called ok\n"); - - /* Copy any modified values to callers copy of variant*/ - for (i = 0; i < pdp->cArgs; i++) { - php_com_dotnet_object *obj = CDNO_FETCH(¶ms[i]); - VARIANT *srcvar = &obj->v; - VARIANT *dstvar = &pdp->rgvarg[ pdp->cArgs - 1 - i]; - if ((V_VT(dstvar) & VT_BYREF) && obj->modified ) { - trace("percolate modified value for arg %u VT=%08x\n", i, V_VT(dstvar)); - php_com_copy_variant(dstvar, srcvar); - } + zend_call_known_fcc(&fcc, retval, pdp->cArgs, params, NULL); + ret = S_OK; + trace("function called ok\n"); + + /* Copy any modified values to callers copy of variant*/ + for (i = 0; i < pdp->cArgs; i++) { + php_com_dotnet_object *obj = CDNO_FETCH(¶ms[i]); + VARIANT *srcvar = &obj->v; + VARIANT *dstvar = &pdp->rgvarg[ pdp->cArgs - 1 - i]; + if ((V_VT(dstvar) & VT_BYREF) && obj->modified ) { + trace("percolate modified value for arg %u VT=%08x\n", i, V_VT(dstvar)); + php_com_copy_variant(dstvar, srcvar); } - } else { - trace("failed to call func\n"); - ret = DISP_E_EXCEPTION; } } zend_catch { trace("something blew up\n"); ret = DISP_E_EXCEPTION; } zend_end_try(); + } else if (wFlags & DISPATCH_PROPERTYGET) { + retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), 1, &rv); + ret = S_OK; } else { trace("Don't know how to handle this invocation %08x\n", wFlags); } @@ -307,7 +302,9 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex( VariantInit(pvarRes); php_com_variant_from_zval(pvarRes, retval, COMG(code_page)); } - // zval_ptr_dtor(retval); // TODO needed for function calls? + if (retval == &rv) { + zval_ptr_dtor(retval); + } } else if (pvarRes) { VariantInit(pvarRes); } diff --git a/ext/com_dotnet/tests/variant_variation3.phpt b/ext/com_dotnet/tests/variant_variation3.phpt new file mode 100644 index 0000000000000..cb77f6982c871 --- /dev/null +++ b/ext/com_dotnet/tests/variant_variation3.phpt @@ -0,0 +1,41 @@ +--TEST-- +Testing reading properties and calling functions +--EXTENSIONS-- +com_dotnet +--FILE-- +foo); +var_dump($v->foo()); +var_dump($v->bar); +var_dump($v->bar()); +var_dump($v->stdclass); +var_dump($v->stdclass()); +try { + var_dump($v->qux); +} catch (com_exception $ex) { + echo $ex->getMessage(), "\n"; +} +?> +--EXPECTF-- +string(6) "method" +string(6) "method" +string(3) "bar" +string(3) "bar" +object(variant)#%d (0) { +} +object(variant)#%d (0) { +} +Unable to lookup `qux': %s diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index ac11623471f19..43d26ec7a3c7d 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -2100,7 +2100,7 @@ public function saveXmlFile(string $filename, int $options = 0): int|false {} * @not-serializable * @strict-properties */ - final class TokenList implements IteratorAggregate, Countable + final class TokenList implements \IteratorAggregate, \Countable { /** @implementation-alias Dom\Node::__construct */ private function __construct() {} diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index 08dbf22a9b91c..5c21b909b0e18 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 860bf40a97ec6570e9f5b0616407ba55d7c8d6f8 */ + * Stub hash: 0fcee2fa666dc88faf084578dde157409a6f5594 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_dom_import_simplexml, 0, 1, DOMAttr|DOMElement, 0) ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0) @@ -3524,13 +3524,13 @@ static zend_class_entry *register_class_Dom_XMLDocument(zend_class_entry *class_ return class_entry; } -static zend_class_entry *register_class_Dom_TokenList(zend_class_entry *class_entry_Dom_IteratorAggregate, zend_class_entry *class_entry_Dom_Countable) +static zend_class_entry *register_class_Dom_TokenList(zend_class_entry *class_entry_IteratorAggregate, zend_class_entry *class_entry_Countable) { zend_class_entry ce, *class_entry; INIT_NS_CLASS_ENTRY(ce, "Dom", "TokenList", class_Dom_TokenList_methods); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); - zend_class_implements(class_entry, 2, class_entry_Dom_IteratorAggregate, class_entry_Dom_Countable); + zend_class_implements(class_entry, 2, class_entry_IteratorAggregate, class_entry_Countable); zval property_length_default_value; ZVAL_UNDEF(&property_length_default_value); diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index f1b02ecbc4e22..55da3ccdc09d3 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -7208,9 +7208,9 @@ static int zend_jit_cmp(zend_jit_ctx *jit, while (n) { n--; - ir_IF_TRUE(end_inputs->refs[n]); + jit_IF_TRUE_FALSE_ex(jit, end_inputs->refs[n], label); ir_END_list(true_inputs); - ir_IF_FALSE(end_inputs->refs[n]); + jit_IF_TRUE_FALSE_ex(jit, end_inputs->refs[n], label2); ir_END_list(false_inputs); } ir_MERGE_list(true_inputs); diff --git a/ext/opcache/tests/jit/gh16984.phpt b/ext/opcache/tests/jit/gh16984.phpt new file mode 100644 index 0000000000000..8432959c41027 --- /dev/null +++ b/ext/opcache/tests/jit/gh16984.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-16984 (function JIT overflow bug) +--EXTENSIONS-- +opcache +--SKIPIF-- + +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=32M +opcache.jit=function +--FILE-- +foo($value); + if ($val <= PHP_INT_MAX) { + $test->integer = $val; + } +} + +function main() { + $test = new Test; + foo($test, 9223372036854775806); + foo($test, 9223372036854775807); // Also reproduces without this call, but this imitates the psalm code + var_dump($test->integer); +} + +main(); +?> +--EXPECT-- +int(9223372036854775807) diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 1425cf12752a7..ec62dd91c72fb 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -629,6 +629,31 @@ static void php_snmp_internal(INTERNAL_FUNCTION_PARAMETERS, int st, } /* }}} */ +static void php_snmp_zend_string_release_from_char_pointer(char *ptr) { + if (ptr) { + zend_string *pptr = (zend_string *)(ptr - XtOffsetOf(zend_string, val)); + zend_string_release(pptr); + } +} + +static void php_free_objid_query(struct objid_query *objid_query, HashTable* oid_ht, const HashTable *value_ht, int st) { + if (oid_ht) { + uint32_t count = zend_hash_num_elements(oid_ht); + + for (uint32_t i = 0; i < count; i ++) { + snmpobjarg *arg = &objid_query->vars[i]; + if (!arg->oid) { + break; + } + if (value_ht) { + php_snmp_zend_string_release_from_char_pointer(arg->value); + } + php_snmp_zend_string_release_from_char_pointer(arg->oid); + } + } + efree(objid_query->vars); +} + /* {{{ php_snmp_parse_oid * * OID parser (and type, value for SNMP_SET command) @@ -677,10 +702,15 @@ static bool php_snmp_parse_oid( return false; } objid_query->vars = (snmpobjarg *)safe_emalloc(sizeof(snmpobjarg), zend_hash_num_elements(oid_ht), 0); + memset(objid_query->vars, 0, sizeof(snmpobjarg) * zend_hash_num_elements(oid_ht)); objid_query->array_output = (st & SNMP_CMD_SET) == 0; ZEND_HASH_FOREACH_VAL(oid_ht, tmp_oid) { - convert_to_string(tmp_oid); - objid_query->vars[objid_query->count].oid = Z_STRVAL_P(tmp_oid); + zend_string *tmp = zval_try_get_string(tmp_oid); + if (!tmp) { + php_free_objid_query(objid_query, oid_ht, value_ht, st); + return false; + } + objid_query->vars[objid_query->count].oid = ZSTR_VAL(tmp); if (st & SNMP_CMD_SET) { if (type_str) { pptr = ZSTR_VAL(type_str); @@ -704,18 +734,24 @@ static bool php_snmp_parse_oid( } } if (idx_type < type_ht->nNumUsed) { - convert_to_string(tmp_type); - if (Z_STRLEN_P(tmp_type) != 1) { + zend_string *type = zval_try_get_string(tmp_type); + if (!type) { + php_free_objid_query(objid_query, oid_ht, value_ht, st); + return false; + } + size_t len = ZSTR_LEN(type); + char ptype = *ZSTR_VAL(type); + zend_string_release(type); + if (len != 1) { zend_value_error("Type must be a single character"); - efree(objid_query->vars); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } - pptr = Z_STRVAL_P(tmp_type); - objid_query->vars[objid_query->count].type = *pptr; + objid_query->vars[objid_query->count].type = ptype; idx_type++; } else { - php_error_docref(NULL, E_WARNING, "'%s': no type set", Z_STRVAL_P(tmp_oid)); - efree(objid_query->vars); + php_error_docref(NULL, E_WARNING, "'%s': no type set", ZSTR_VAL(tmp)); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } } @@ -741,12 +777,16 @@ static bool php_snmp_parse_oid( } } if (idx_value < value_ht->nNumUsed) { - convert_to_string(tmp_value); - objid_query->vars[objid_query->count].value = Z_STRVAL_P(tmp_value); + zend_string *tmp = zval_try_get_string(tmp_value); + if (!tmp) { + php_free_objid_query(objid_query, oid_ht, value_ht, st); + return false; + } + objid_query->vars[objid_query->count].value = ZSTR_VAL(tmp); idx_value++; } else { - php_error_docref(NULL, E_WARNING, "'%s': no value set", Z_STRVAL_P(tmp_oid)); - efree(objid_query->vars); + php_error_docref(NULL, E_WARNING, "'%s': no value set", ZSTR_VAL(tmp)); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } } @@ -759,14 +799,14 @@ static bool php_snmp_parse_oid( if (st & SNMP_CMD_WALK) { if (objid_query->count > 1) { php_snmp_error(object, PHP_SNMP_ERRNO_OID_PARSING_ERROR, "Multi OID walks are not supported!"); - efree(objid_query->vars); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } objid_query->vars[0].name_length = MAX_NAME_LEN; if (strlen(objid_query->vars[0].oid)) { /* on a walk, an empty string means top of tree - no error */ if (!snmp_parse_oid(objid_query->vars[0].oid, objid_query->vars[0].name, &(objid_query->vars[0].name_length))) { php_snmp_error(object, PHP_SNMP_ERRNO_OID_PARSING_ERROR, "Invalid object identifier: %s", objid_query->vars[0].oid); - efree(objid_query->vars); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } } else { @@ -778,7 +818,7 @@ static bool php_snmp_parse_oid( objid_query->vars[objid_query->offset].name_length = MAX_OID_LEN; if (!snmp_parse_oid(objid_query->vars[objid_query->offset].oid, objid_query->vars[objid_query->offset].name, &(objid_query->vars[objid_query->offset].name_length))) { php_snmp_error(object, PHP_SNMP_ERRNO_OID_PARSING_ERROR, "Invalid object identifier: %s", objid_query->vars[objid_query->offset].oid); - efree(objid_query->vars); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } } @@ -1250,12 +1290,12 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) if (session_less_mode) { if (!netsnmp_session_init(&session, version, a1, a2, timeout, retries)) { - efree(objid_query.vars); + php_free_objid_query(&objid_query, oid_ht, value_ht, st); netsnmp_session_free(&session); RETURN_FALSE; } if (version == SNMP_VERSION_3 && !netsnmp_session_set_security(session, a3, a4, a5, a6, a7, NULL, NULL)) { - efree(objid_query.vars); + php_free_objid_query(&objid_query, oid_ht, value_ht, st); netsnmp_session_free(&session); /* Warning message sent already, just bail out */ RETURN_FALSE; @@ -1266,7 +1306,7 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) session = snmp_object->session; if (!session) { zend_throw_error(NULL, "Invalid or uninitialized SNMP object"); - efree(objid_query.vars); + php_free_objid_query(&objid_query, oid_ht, value_ht, st); RETURN_THROWS(); } @@ -1292,7 +1332,7 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) php_snmp_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, st, session, &objid_query); - efree(objid_query.vars); + php_free_objid_query(&objid_query, oid_ht, value_ht, st); if (session_less_mode) { netsnmp_session_free(&session); diff --git a/ext/snmp/tests/gh16959.phpt b/ext/snmp/tests/gh16959.phpt new file mode 100644 index 0000000000000..ce647b15b9dac --- /dev/null +++ b/ext/snmp/tests/gh16959.phpt @@ -0,0 +1,69 @@ +--TEST-- +snmpget() modifies object_id array source +--EXTENSIONS-- +snmp +--SKIPIF-- + +--FILE-- + 077, -066 => -066, -0345 => -0345, 0 => 0 +); +var_dump($bad_object_ids); +var_dump(snmpget($hostname, "", $bad_object_ids) === false); +// The array should remain unmodified +var_dump($bad_object_ids); +try { + snmpget($hostname, "", [0 => new stdClass()]); +} catch (Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + snmp2_set($hostname, $communityWrite, $bad_object_ids, array(new stdClass()), array(null)); +} catch (Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + snmp2_set($hostname, $communityWrite, $bad_object_ids, array("toolongtype"), array(null)); +} catch (Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + snmp2_set($hostname, $communityWrite, $bad_object_ids, array(str_repeat("onetoomuch", random_int(1, 1))), array(null)); +} catch (Throwable $e) { + echo $e->getMessage(); +} +?> +--EXPECTF-- +array(4) { + [63]=> + int(63) + [-54]=> + int(-54) + [-229]=> + int(-229) + [0]=> + int(0) +} + +Warning: snmpget(): Invalid object identifier: -54 in %s on line %d +bool(true) +array(4) { + [63]=> + int(63) + [-54]=> + int(-54) + [-229]=> + int(-229) + [0]=> + int(0) +} +Object of class stdClass could not be converted to string +Object of class stdClass could not be converted to string +Type must be a single character +Type must be a single character diff --git a/ext/standard/array.c b/ext/standard/array.c index 47ea40325b515..d95ea012ad6bc 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3632,7 +3632,8 @@ PHP_FUNCTION(array_shift) } idx++; } - RETVAL_COPY_DEREF(val); + RETVAL_COPY_VALUE(val); + ZVAL_UNDEF(val); /* Delete the first value */ zend_hash_packed_del_val(Z_ARRVAL_P(stack), val); @@ -3686,7 +3687,8 @@ PHP_FUNCTION(array_shift) } idx++; } - RETVAL_COPY_DEREF(val); + RETVAL_COPY_VALUE(val); + ZVAL_UNDEF(val); /* Delete the first value */ zend_hash_del_bucket(Z_ARRVAL_P(stack), p); @@ -3710,6 +3712,10 @@ PHP_FUNCTION(array_shift) } zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); + + if (Z_ISREF_P(return_value)) { + zend_unwrap_reference(return_value); + } } /* }}} */ diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index ba0f73d9a9c22..8d8c09f443c04 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -246,9 +246,10 @@ php_sprintf_appenddouble(zend_string **buffer, size_t *pos, } if (zend_isinf(number)) { - is_negative = (number<0); - php_sprintf_appendstring(buffer, pos, "INF", 3, 0, padding, - alignment, 3, is_negative, 0, always_sign); + is_negative = (number<0); + char *str = is_negative ? "-INF" : "INF"; + php_sprintf_appendstring(buffer, pos, str, strlen(str), 0, padding, + alignment, strlen(str), is_negative, 0, always_sign); return; } diff --git a/ext/standard/tests/array/gh16957.phpt b/ext/standard/tests/array/gh16957.phpt new file mode 100644 index 0000000000000..a716228249e7d --- /dev/null +++ b/ext/standard/tests/array/gh16957.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-16957 (Assertion failure in array_shift with self-referencing array) +--FILE-- + 1, 300 => 'two'); +var_dump($shifted = array_shift($new_array2)); +var_dump($new_array2); +var_dump($new_array2 === $shifted); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + string(3) "two" +} +array(2) { + [0]=> + int(1) + [1]=> + string(3) "two" +} +bool(true) +array(2) { + [0]=> + int(1) + [1]=> + string(3) "two" +} +array(2) { + [0]=> + int(1) + [1]=> + string(3) "two" +} +bool(true) diff --git a/main/rfc1867.c b/main/rfc1867.c index 8a5cd5a5e36c5..84b8788bbf7c3 100644 --- a/main/rfc1867.c +++ b/main/rfc1867.c @@ -319,8 +319,8 @@ static char *next_line(multipart_buffer *self) } /* return entire buffer as a partial line */ line[self->bufsize] = 0; - self->buf_begin = ptr; self->bytes_in_buffer = 0; + /* Let fill_buffer() handle the reset of self->buf_begin */ } return line; diff --git a/php.ini-development b/php.ini-development index 31353facdb394..d739521a373d7 100644 --- a/php.ini-development +++ b/php.ini-development @@ -119,6 +119,11 @@ ; Development Value: 60 (60 seconds) ; Production Value: 60 (60 seconds) +; mysqlnd.collect_memory_statistics +; Default Value: Off +; Development Value: On +; Production Value: Off + ; output_buffering ; Default Value: Off ; Development Value: 4096 @@ -1193,6 +1198,9 @@ mysqlnd.collect_statistics = On ; Enable / Disable collection of memory usage statistics by mysqlnd which can be ; used to tune and monitor MySQL operations. +; Default Value: Off +; Development Value: On +; Production Value: Off mysqlnd.collect_memory_statistics = On ; Records communication from all extensions using mysqlnd to the specified log diff --git a/php.ini-production b/php.ini-production index e4fbd6e785f86..8e4afb683f9c1 100644 --- a/php.ini-production +++ b/php.ini-production @@ -119,6 +119,11 @@ ; Development Value: 60 (60 seconds) ; Production Value: 60 (60 seconds) +; mysqlnd.collect_memory_statistics +; Default Value: Off +; Development Value: On +; Production Value: Off + ; output_buffering ; Default Value: Off ; Development Value: 4096 @@ -1195,6 +1200,9 @@ mysqlnd.collect_statistics = On ; Enable / Disable collection of memory usage statistics by mysqlnd which can be ; used to tune and monitor MySQL operations. +; Default Value: Off +; Development Value: On +; Production Value: Off mysqlnd.collect_memory_statistics = Off ; Records communication from all extensions using mysqlnd to the specified log diff --git a/tests/basic/gh16998.phpt b/tests/basic/gh16998.phpt new file mode 100644 index 0000000000000..8bfcbbda99dd0 --- /dev/null +++ b/tests/basic/gh16998.phpt @@ -0,0 +1,49 @@ +--TEST-- +GH-16998 (UBSAN warning in rfc1867) +--SKIPIF-- + +--FILE-- + '1', + 'CONTENT_TYPE' => "multipart/form-data; boundary=", + 'CONTENT_LENGTH' => strlen($body), + 'REQUEST_METHOD' => 'POST', + 'SCRIPT_FILENAME' => __DIR__ . '/GHSA-9pqp-7h25-4f32.inc', +]); +$spec = [ + 0 => ['pipe', 'r'], + 1 => STDOUT, + 2 => STDOUT, +]; +$pipes = []; +$handle = proc_open($cmd, $spec, $pipes, getcwd(), $env); +fwrite($pipes[0], $body); +proc_close($handle); +?> +--EXPECTF-- +X-Powered-By: PHP/%s +Content-type: text/html; charset=UTF-8 + +Hello world +array(0) { +} diff --git a/tests/strings/002.phpt b/tests/strings/002.phpt index 54630836b1632..6284e9bf5d339 100644 --- a/tests/strings/002.phpt +++ b/tests/strings/002.phpt @@ -44,6 +44,8 @@ try { } catch(\ValueError $e) { print('Error found: '.$e->getMessage()."\n"); } +printf("printf test 31:%.17g\n", INF); +printf("printf test 32:%.17g\n", -INF); vprintf("vprintf test 1:%2\$-2d %1\$2d\n", array(1, 2)); @@ -83,4 +85,6 @@ printf test 27:3 1 2 printf test 28:02 1 printf test 29:2 1 printf test 30:Error found: Argument number specifier must be greater than zero and less than 2147483647 +printf test 31:INF +printf test 32:-INF vprintf test 1:2 1