From 556def741ccd4da973bf27e3eae5d8075386be68 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Mon, 27 Jan 2025 12:41:11 -0500 Subject: [PATCH 1/6] Fix crash in PDO_ODBC statement dtor (#17586) Port of 2ae897fff7af3a794a31a8aeeeeb4f21f6a41393 to PDO_ODBC. --- ext/pdo_odbc/odbc_stmt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 1df4e22571a76..4bf7162ea06e6 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -136,7 +136,11 @@ static int odbc_stmt_dtor(pdo_stmt_t *stmt) { pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; - if (S->stmt != SQL_NULL_HANDLE) { + // TODO: Factor this out; pg/mysql/firebird do the same thing + bool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle) + && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) + && !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED); + if (S->stmt != SQL_NULL_HANDLE && server_obj_usable) { if (stmt->executed) { SQLCloseCursor(S->stmt); } From f926c5ce81bb0c40929ab17acc53134d6fa75ad7 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 26 Jan 2025 18:30:46 +0100 Subject: [PATCH 2/6] Fix GH-16883: gzopen() does not use the default stream context when opening HTTP URLs Otherwise it's not possible to control the context; it's also consistent with how the standard open functions work. Closes GH-17589. --- NEWS | 2 ++ UPGRADING | 2 ++ ext/zlib/tests/gh16883.phpt | 46 +++++++++++++++++++++++++++++++++++++ ext/zlib/zlib.c | 7 +++--- 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 ext/zlib/tests/gh16883.phpt diff --git a/NEWS b/NEWS index 44780f84d7454..b6d27bb15aa0b 100644 --- a/NEWS +++ b/NEWS @@ -149,6 +149,8 @@ PHP NEWS - Zlib: . gzfile, gzopen and readgzfile, their "use_include_path" argument is now a boolean. (David Carlier) + . Fixed bug GH-16883 (gzopen() does not use the default stream context when + opening HTTP URLs). (nielsdos) <<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> diff --git a/UPGRADING b/UPGRADING index 805d2f62440b7..3d0c1101ed5ad 100644 --- a/UPGRADING +++ b/UPGRADING @@ -166,6 +166,8 @@ PHP 8.5 UPGRADE NOTES . The "use_include_path" argument for the gzfile, gzopen and readgzfile functions had been changed from int to boolean. + . gzfile, gzopen and readgzfile functions now respect the default + stream context. ======================================== 6. New Functions diff --git a/ext/zlib/tests/gh16883.phpt b/ext/zlib/tests/gh16883.phpt new file mode 100644 index 0000000000000..c3d81d4537938 --- /dev/null +++ b/ext/zlib/tests/gh16883.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-16883 (gzopen() does not use the default stream context when opening HTTP URLs) +--EXTENSIONS-- +zlib +--INI-- +allow_url_fopen=1 +--SKIPIF-- + +--FILE-- + array( + 'user_agent' => 'dummy', + ) +]); + +$f = gzopen('http://'.PHP_CLI_SERVER_HOSTNAME.':'.PHP_CLI_SERVER_PORT, 'r'); +var_dump(stream_get_contents($f)); + +var_dump(gzfile('http://'.PHP_CLI_SERVER_HOSTNAME.':'.PHP_CLI_SERVER_PORT, 'r')); + +var_dump(readgzfile('http://'.PHP_CLI_SERVER_HOSTNAME.':'.PHP_CLI_SERVER_PORT, 'r')); + +?> +--EXPECT-- +string(6) "dummy +" +array(1) { + [0]=> + string(6) "dummy +" +} +dummy +int(6) diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index df3d270217d45..3a72bd506340f 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -26,6 +26,7 @@ #include "SAPI.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/file.h" #include "php_zlib.h" #include "zlib_arginfo.h" @@ -621,7 +622,7 @@ PHP_FUNCTION(gzfile) } /* using a stream here is a bit more efficient (resource wise) than php_gzopen_wrapper */ - stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, NULL STREAMS_CC); + stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, php_stream_context_from_zval(NULL, false) STREAMS_CC); if (!stream) { /* Error reporting is already done by stream code */ @@ -659,7 +660,7 @@ PHP_FUNCTION(gzopen) flags |= USE_PATH; } - stream = php_stream_gzopen(NULL, filename, mode, flags, NULL, NULL STREAMS_CC); + stream = php_stream_gzopen(NULL, filename, mode, flags, NULL, php_stream_context_from_zval(NULL, false) STREAMS_CC); if (!stream) { RETURN_FALSE; @@ -686,7 +687,7 @@ PHP_FUNCTION(readgzfile) flags |= USE_PATH; } - stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, NULL STREAMS_CC); + stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, php_stream_context_from_zval(NULL, false) STREAMS_CC); if (!stream) { RETURN_FALSE; From 8ea9b04a2301cb3aaac7ac15612c99ac70352970 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sun, 26 Jan 2025 01:26:48 +0100 Subject: [PATCH 3/6] Fix inline zend_string using struct padding As explained by Snape3058: On 64-bit machines, we typically have 7 bytes of padding between the zend_string.val[0] char and the following char[]. This means that zend_string.val[1-7] write to and read from the struct padding, which is a bad idea. Allocate the given string separately instead. Fixes GH-17564 Closes GH-17576 --- NEWS | 3 ++- ext/opcache/ZendAccelerator.c | 35 ++++++++++++++++++++++------------- ext/opcache/ZendAccelerator.h | 3 +-- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/NEWS b/NEWS index 28b9116fc47a8..e484b900923ed 100644 --- a/NEWS +++ b/NEWS @@ -17,7 +17,6 @@ PHP NEWS . Fix may_have_extra_named_args flag for ZEND_AST_UNPACK. (nielsdos) . Fix NULL arithmetic in System V shared memory emulation for Windows. (cmb) - - DOM: . Fixed bug GH-17500 (Segfault with requesting nodeName on nameless doctype). (nielsdos) @@ -43,6 +42,8 @@ PHP NEWS - Opcache: . Fixed bug GH-17307 (Internal closure causes JIT failure). (nielsdos) + . Fixed bug GH-17564 (Potential UB when reading from / writing to struct + padding). (ilutov) - PDO: . Fixed a memory leak when the GC is used to free a PDOStatment. (Girgias) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 66c10021442ad..313f89206f11a 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -141,6 +141,8 @@ static void preload_restart(void); # define LOCKVAL(v) (ZCSG(v)) #endif +#define ZCG_KEY_LEN (MAXPATHLEN * 8) + /** * Clear AVX/SSE2-aligned memory. */ @@ -1190,7 +1192,8 @@ zend_string *accel_make_persistent_key(zend_string *str) char *key; int key_length; - ZSTR_LEN(&ZCG(key)) = 0; + ZEND_ASSERT(GC_REFCOUNT(ZCG(key)) == 1); + ZSTR_LEN(ZCG(key)) = 0; /* CWD and include_path don't matter for absolute file names and streams */ if (IS_ABSOLUTE_PATH(path, path_length)) { @@ -1300,7 +1303,7 @@ zend_string *accel_make_persistent_key(zend_string *str) } /* Calculate key length */ - if (UNEXPECTED((size_t)(cwd_len + path_length + include_path_len + 2) >= sizeof(ZCG(_key)))) { + if (UNEXPECTED((size_t)(cwd_len + path_length + include_path_len + 2) >= ZCG_KEY_LEN)) { return NULL; } @@ -1309,7 +1312,7 @@ zend_string *accel_make_persistent_key(zend_string *str) * since in itself, it may include colons (which we use to separate * different components of the key) */ - key = ZSTR_VAL(&ZCG(key)); + key = ZSTR_VAL(ZCG(key)); memcpy(key, path, path_length); key[path_length] = ':'; key_length = path_length + 1; @@ -1333,7 +1336,7 @@ zend_string *accel_make_persistent_key(zend_string *str) parent_script_len = ZSTR_LEN(parent_script); while ((--parent_script_len > 0) && !IS_SLASH(ZSTR_VAL(parent_script)[parent_script_len])); - if (UNEXPECTED((size_t)(key_length + parent_script_len + 1) >= sizeof(ZCG(_key)))) { + if (UNEXPECTED((size_t)(key_length + parent_script_len + 1) >= ZCG_KEY_LEN)) { return NULL; } key[key_length] = ':'; @@ -1342,11 +1345,9 @@ zend_string *accel_make_persistent_key(zend_string *str) key_length += parent_script_len; } key[key_length] = '\0'; - GC_SET_REFCOUNT(&ZCG(key), 1); - GC_TYPE_INFO(&ZCG(key)) = GC_STRING; - ZSTR_H(&ZCG(key)) = 0; - ZSTR_LEN(&ZCG(key)) = key_length; - return &ZCG(key); + ZSTR_H(ZCG(key)) = 0; + ZSTR_LEN(ZCG(key)) = key_length; + return ZCG(key); } /* not use_cwd */ @@ -2014,8 +2015,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) ZCG(cache_opline) == EG(current_execute_data)->opline))) { persistent_script = ZCG(cache_persistent_script); - if (ZSTR_LEN(&ZCG(key))) { - key = &ZCG(key); + if (ZSTR_LEN(ZCG(key))) { + key = ZCG(key); } } else { @@ -2555,7 +2556,7 @@ static zend_string* persistent_zend_resolve_path(zend_string *filename) SHM_PROTECT(); HANDLE_UNBLOCK_INTERRUPTIONS(); } else { - ZSTR_LEN(&ZCG(key)) = 0; + ZSTR_LEN(ZCG(key)) = 0; } ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL; ZCG(cache_persistent_script) = persistent_script; @@ -2927,7 +2928,15 @@ static void accel_globals_ctor(zend_accel_globals *accel_globals) ZEND_TSRMLS_CACHE_UPDATE(); #endif memset(accel_globals, 0, sizeof(zend_accel_globals)); + accel_globals->key = zend_string_alloc(ZCG_KEY_LEN, true); +} + +#ifdef ZTS +static void accel_globals_dtor(zend_accel_globals *accel_globals) +{ + zend_string_free(accel_globals->key); } +#endif #ifdef HAVE_HUGE_CODE_PAGES # ifndef _WIN32 @@ -3100,7 +3109,7 @@ static void accel_move_code_to_huge_pages(void) static int accel_startup(zend_extension *extension) { #ifdef ZTS - accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, NULL); + accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor); #else accel_globals_ctor(&accel_globals); #endif diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index e958e8fd65064..a25f9a1bdf037 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -223,8 +223,7 @@ typedef struct _zend_accel_globals { const zend_op *cache_opline; zend_persistent_script *cache_persistent_script; /* preallocated buffer for keys */ - zend_string key; - char _key[MAXPATHLEN * 8]; + zend_string *key; } zend_accel_globals; typedef struct _zend_string_table { From efcdcd7bdef109a47709a3e8c20f498516eaf94a Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 27 Jan 2025 20:00:34 +0100 Subject: [PATCH 4/6] Drop NetWare support from bundled libgd (GH-17596) This has been removed from upstream years ago[1], and PHP generally dropped NetWare support even earlier. [1] [2] --- ext/gd/libgd/gdft.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/gd/libgd/gdft.c b/ext/gd/libgd/gdft.c index 85637a61826c1..34a4064f5fcb1 100644 --- a/ext/gd/libgd/gdft.c +++ b/ext/gd/libgd/gdft.c @@ -408,9 +408,6 @@ static void *fontFetch (char **error, void *key) path = gdEstrdup (fontsearchpath); /* if name is an absolute filename then test directly */ -#ifdef NETWARE - if (*name == '/' || (name[0] != 0 && strstr(name, ":/"))) { -#else /* Actual length doesn't matter, just the minimum does up to length 2. */ unsigned int min_length = 0; if (name[0] != '\0') { @@ -422,7 +419,6 @@ static void *fontFetch (char **error, void *key) } ZEND_IGNORE_VALUE(min_length); /* On Posix systems this may be unused */ if (IS_ABSOLUTE_PATH(name, min_length)) { -#endif snprintf(fullname, sizeof(fullname) - 1, "%s", name); if (access(fullname, R_OK) == 0) { font_found++; From d17d58a9825675e75a7a5473cf4c7aae0334d056 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 27 Jan 2025 19:59:49 +0100 Subject: [PATCH 5/6] Fix cve-2014-3538 test Make sure we have a unique test file to work with, and increase the time for the nojit version to match the default version. Closes GH-17600 --- ext/fileinfo/tests/cve-2014-3538-mb.phpt | 2 +- ext/fileinfo/tests/cve-2014-3538-nojit.phpt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/fileinfo/tests/cve-2014-3538-mb.phpt b/ext/fileinfo/tests/cve-2014-3538-mb.phpt index 6e8d32ec56716..81733962ebe6a 100644 --- a/ext/fileinfo/tests/cve-2014-3538-mb.phpt +++ b/ext/fileinfo/tests/cve-2014-3538-mb.phpt @@ -32,7 +32,7 @@ if ($t < 3) { Done --CLEAN-- --EXPECTF-- string(%d) "%s" diff --git a/ext/fileinfo/tests/cve-2014-3538-nojit.phpt b/ext/fileinfo/tests/cve-2014-3538-nojit.phpt index f3a5fa7fb4f73..2010d538da951 100644 --- a/ext/fileinfo/tests/cve-2014-3538-nojit.phpt +++ b/ext/fileinfo/tests/cve-2014-3538-nojit.phpt @@ -13,7 +13,7 @@ if (getenv('SKIP_PERF_SENSITIVE')) pcre.jit=0 --FILE-- --EXPECTF-- string(%d) "%s" From ef74ea08cea556e8ce0e03eac74a1266c8dec1e7 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 27 Jan 2025 20:26:57 +0100 Subject: [PATCH 6/6] Drop superfluous php_com_dotnet_object.ce (GH-17603) * Drop superfluous php_com_dotnet_object.ce This is readily available via the `zend_object` (i.e. `zo.ce`), so there is no need to duplicate it. There is also no need to assign the ce to the std object, since this is done be `zend_object_std_init()` anyway. --- ext/com_dotnet/com_handlers.c | 5 ++--- ext/com_dotnet/com_misc.c | 4 ---- ext/com_dotnet/php_com_dotnet_internal.h | 2 -- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c index fcbae4e34621f..5a177457a4492 100644 --- a/ext/com_dotnet/com_handlers.c +++ b/ext/com_dotnet/com_handlers.c @@ -307,7 +307,7 @@ static zend_function *com_method_get(zend_object **object_ptr, zend_string *name f.type = ZEND_INTERNAL_FUNCTION; f.num_args = 0; f.arg_info = NULL; - f.scope = obj->ce; + f.scope = obj->zo.ce; f.fn_flags = ZEND_ACC_CALL_VIA_HANDLER; f.function_name = zend_string_copy(name); f.handler = PHP_FN(com_method_handler); @@ -392,7 +392,7 @@ static zend_string* com_class_name_get(const zend_object *object) { php_com_dotnet_object *obj = (php_com_dotnet_object *)object; - return zend_string_copy(obj->ce->name); + return zend_string_copy(obj->zo.ce->name); } /* This compares two variants for equality */ @@ -625,7 +625,6 @@ zend_object* php_com_object_new(zend_class_entry *ce) VariantInit(&obj->v); obj->code_page = CP_ACP; - obj->ce = ce; zend_object_std_init(&obj->zo, ce); diff --git a/ext/com_dotnet/com_misc.c b/ext/com_dotnet/com_misc.c index 3c85fc4ce5e4a..a5de6415b9a73 100644 --- a/ext/com_dotnet/com_misc.c +++ b/ext/com_dotnet/com_misc.c @@ -50,8 +50,6 @@ PHP_COM_DOTNET_API void php_com_wrap_dispatch(zval *z, IDispatch *disp, obj = emalloc(sizeof(*obj)); memset(obj, 0, sizeof(*obj)); obj->code_page = codepage; - obj->ce = php_com_variant_class_entry; - obj->zo.ce = php_com_variant_class_entry; VariantInit(&obj->v); V_VT(&obj->v) = VT_DISPATCH; @@ -72,8 +70,6 @@ PHP_COM_DOTNET_API void php_com_wrap_variant(zval *z, VARIANT *v, obj = emalloc(sizeof(*obj)); memset(obj, 0, sizeof(*obj)); obj->code_page = codepage; - obj->ce = php_com_variant_class_entry; - obj->zo.ce = php_com_variant_class_entry; VariantInit(&obj->v); VariantCopyInd(&obj->v, v); diff --git a/ext/com_dotnet/php_com_dotnet_internal.h b/ext/com_dotnet/php_com_dotnet_internal.h index 90c3ab2d40e41..09fe494393470 100644 --- a/ext/com_dotnet/php_com_dotnet_internal.h +++ b/ext/com_dotnet/php_com_dotnet_internal.h @@ -35,8 +35,6 @@ typedef struct _php_com_dotnet_object { ITypeInfo *typeinfo; - zend_class_entry *ce; - /* associated event sink */ IDispatch *sink_dispatch; GUID sink_id;