From 3fa9e283a0621b8ff18264d4ae53438d3cfc73f5 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 3 Feb 2025 16:42:29 +0100 Subject: [PATCH 01/16] Drop unused local variables (GH-17682) --- TSRM/tsrm_win32.c | 2 +- win32/sendmail.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/TSRM/tsrm_win32.c b/TSRM/tsrm_win32.c index 1fe2a47a87c24..4c8fc9d19aa9a 100644 --- a/TSRM/tsrm_win32.c +++ b/TSRM/tsrm_win32.c @@ -636,7 +636,7 @@ TSRM_API int shmget(key_t key, size_t size, int flags) {/*{{{*/ shm_pair *shm; char shm_segment[sizeof(SEGMENT_PREFIX INT_MIN_AS_STRING)]; - HANDLE shm_handle = NULL, info_handle = NULL; + HANDLE shm_handle = NULL; BOOL created = FALSE; if (key != IPC_PRIVATE) { diff --git a/win32/sendmail.c b/win32/sendmail.c index c5215bf35c6f8..3ee74707fd6c3 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -195,8 +195,6 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, } if (headers) { - char *pos = NULL; - /* Use PCRE to trim the header into the right format */ if (NULL == (headers_trim = php_win32_mail_trim_header(headers))) { *error = W32_SM_PCRE_ERROR; From 4e6a3cecf589b37375ab9e309f40144bb368cab7 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 3 Feb 2025 16:50:32 +0100 Subject: [PATCH 02/16] Don't forward declare static functions in sendmail.h (GH-17684) sendmail.h is not only included by sendmail.c, but also by php_win32_globals.h, because that header uses some of the defined macros. However, the forward declarations of the static functions are not needed anywhere else than in sendmail.c, and Clang warns about the unused functions elsewhere (`-Wunused-function`). Thus we move the forward declarations to sendmail.c. --- win32/sendmail.c | 9 +++++++++ win32/sendmail.h | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/win32/sendmail.c b/win32/sendmail.c index 3ee74707fd6c3..80dff3f390772 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -111,6 +111,15 @@ static char *ErrorMessages[] = #define PHP_WIN32_MAIL_DOT_PATTERN "\n." #define PHP_WIN32_MAIL_DOT_REPLACE "\n.." +static int SendText(char *RPath, const char *Subject, const char *mailTo, char *mailCc, char *mailBcc, const char *data, + const char *headers, char *headers_lc, char **error_message); +static int MailConnect(); +static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders); +static int Post(LPCSTR msg); +static int Ack(char **server_response); +static unsigned long GetAddr(LPSTR szHost); +static int FormatEmailAddress(char* Buf, char* EmailAddress, char* FormatString); + /* This function is meant to unify the headers passed to to mail() * This means, use PCRE to transform single occurrences of \n or \r in \r\n * As a second step we also eliminate all \r\n occurrences which are: diff --git a/win32/sendmail.h b/win32/sendmail.h index 486482c0c0c7b..e6096f789eb78 100644 --- a/win32/sendmail.h +++ b/win32/sendmail.h @@ -36,14 +36,5 @@ PHPAPI int TSendMail(const char *host, int *error, char **error_message, const char *headers, const char *Subject, const char *mailTo, const char *data, char *mailCc, char *mailBcc, char *mailRPath); PHPAPI void TSMClose(void); -static int SendText(char *RPath, const char *Subject, const char *mailTo, char *mailCc, char *mailBcc, const char *data, - const char *headers, char *headers_lc, char **error_message); PHPAPI char *GetSMErrorText(int index); - -static int MailConnect(); -static int PostHeader(char *RPath, const char *Subject, const char *mailTo, char *xheaders); -static int Post(LPCSTR msg); -static int Ack(char **server_response); -static unsigned long GetAddr(LPSTR szHost); -static int FormatEmailAddress(char* Buf, char* EmailAddress, char* FormatString); #endif /* sendmail_h */ From f88445bdf894b2f0b3f4efcf5176de3cc33c4350 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 31 Jan 2025 21:41:45 +0100 Subject: [PATCH 03/16] Fix GH-17654: Multiple classes using same trait causes function JIT crash This test has two classes that use the same trait. In function JIT mode the same cache slot will be used. This causes problems because it is primed for the first class and then reused for the second class, resulting in an incorrect type check failure. The current check for a megamorphic trait call requires current_frame to not be NULL, but this is only set in tracing mode and not in function mode. This patch corrects the check. Closes GH-17660. --- NEWS | 4 +++ ext/opcache/jit/zend_jit_arm64.dasc | 6 ++--- ext/opcache/jit/zend_jit_x86.dasc | 6 ++--- ext/opcache/tests/jit/gh17654.phpt | 38 +++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 ext/opcache/tests/jit/gh17654.phpt diff --git a/NEWS b/NEWS index 8469bdc9df857..aace72eb49f00 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ PHP NEWS . Fixed bug GH-17618 (UnhandledMatchError does not take zend.exception_ignore_args=1 into account). (timwolla) +- Opcache: + . Fixed bug GH-17654 (Multiple classes using same trait causes function + JIT crash). (nielsdos) + - PHPDBG: . Partially fixed bug GH-17387 (Trivial crash in phpdbg lexer). (nielsdos) . Fix memory leak in phpdbg calling registered function. (nielsdos) diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index 2cbf68643086a..ec6fae8819fcd 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -9200,9 +9200,9 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend func = call_info->callee_func; } if ((op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) - && JIT_G(current_frame) - && JIT_G(current_frame)->call - && !JIT_G(current_frame)->call->func) { + && (!JIT_G(current_frame) || + !JIT_G(current_frame)->call || + !JIT_G(current_frame)->call->func)) { call_info = NULL; func = NULL; /* megamorphic call from trait */ } } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 9cf0c6cd8e881..f65dc769db57d 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -9931,9 +9931,9 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend func = call_info->callee_func; } if ((op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) - && JIT_G(current_frame) - && JIT_G(current_frame)->call - && !JIT_G(current_frame)->call->func) { + && (!JIT_G(current_frame) || + !JIT_G(current_frame)->call || + !JIT_G(current_frame)->call->func)) { call_info = NULL; func = NULL; /* megamorphic call from trait */ } } diff --git a/ext/opcache/tests/jit/gh17654.phpt b/ext/opcache/tests/jit/gh17654.phpt new file mode 100644 index 0000000000000..59d9205b37f2a --- /dev/null +++ b/ext/opcache/tests/jit/gh17654.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-17654 (Multiple classes using same trait causes function JIT crash) +--EXTENSIONS-- +opcache +--INI-- +opcache.jit=1214 +opcache.jit_buffer_size=16M +--FILE-- +addUnit("test2"); + (new Test)->addUnit("test"); +} + +main(); +?> +--EXPECT-- +string(5) "test2" +string(4) "test" From 0c3cf1f311402126daaddeb8b86c5af1b2ce4204 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 26 Jan 2025 13:33:40 +0100 Subject: [PATCH 04/16] Fix GH-17577: JIT packed type guard crash When a guard check is created for a variable to check if it's a packed array, it is possible that there was no prior type check for that variable. This happens in the global scope for example when the variable aliases. In the test, this causes a dereference of address 8 because the integer element in `$a` is interpreted as an array address. This patch adds a check to see if the guard is handled. If we were not able to determine or guard the type then we also cannot know the array is packed. Closes GH-17584. --- NEWS | 1 + ext/opcache/jit/zend_jit_trace.c | 21 +++++++++++++++------ ext/opcache/tests/jit/gh17577.phpt | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 ext/opcache/tests/jit/gh17577.phpt diff --git a/NEWS b/NEWS index aace72eb49f00..49ecf9f3de08b 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ PHP NEWS - Opcache: . Fixed bug GH-17654 (Multiple classes using same trait causes function JIT crash). (nielsdos) + . Fixed bug GH-17577 (JIT packed type guard crash). (nielsdos, Dmitry) - PHPDBG: . Partially fixed bug GH-17387 (Trivial crash in phpdbg lexer). (nielsdos) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 5d824c207d18f..33c554a3f78ef 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1745,7 +1745,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin if (!(orig_op1_type & IS_TRACE_PACKED)) { zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use]; - if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) { + if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type) + && (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) { info->type |= MAY_BE_PACKED_GUARD; info->type &= ~MAY_BE_ARRAY_PACKED; } @@ -1754,7 +1755,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin && val_type != IS_UNDEF) { zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use]; - if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) { + if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type) + && (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) { info->type |= MAY_BE_PACKED_GUARD; info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH); } @@ -1833,7 +1835,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use]; - if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) { + if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type) + && (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) { info->type |= MAY_BE_PACKED_GUARD; if (orig_op1_type & IS_TRACE_PACKED) { info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH); @@ -1935,7 +1938,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use]; - if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) { + if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type) + && (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) { info->type |= MAY_BE_PACKED_GUARD; if (orig_op1_type & IS_TRACE_PACKED) { info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH); @@ -1965,7 +1969,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use]; - if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) { + if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type) + && (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) { info->type |= MAY_BE_PACKED_GUARD; info->type &= ~MAY_BE_ARRAY_PACKED; } @@ -4164,10 +4169,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if ((info & MAY_BE_PACKED_GUARD) != 0 && (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL - || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) + || (trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET + && (opline-1)->result_type == IS_VAR + && EX_VAR_TO_NUM((opline-1)->result.var) == i)) && (ssa->vars[i].use_chain != -1 || (ssa->vars[i].phi_use_chain && !(ssa->var_info[ssa->vars[i].phi_use_chain->ssa_var].type & MAY_BE_PACKED_GUARD)))) { + ZEND_ASSERT(STACK_TYPE(stack, i) == IS_ARRAY); + if (!zend_jit_packed_guard(&dasm_state, opline, EX_NUM_TO_VAR(i), info)) { goto jit_failure; } diff --git a/ext/opcache/tests/jit/gh17577.phpt b/ext/opcache/tests/jit/gh17577.phpt new file mode 100644 index 0000000000000..2eac2d05e432d --- /dev/null +++ b/ext/opcache/tests/jit/gh17577.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-17577 (JIT packed type guard crash) +--EXTENSIONS-- +opcache +--INI-- +opcache.jit_buffer_size=16M +opcache.jit_hot_func=1 +--FILE-- + +--EXPECTF-- +Warning: Trying to access array offset on int in %s on line %d + +Warning: Trying to access array offset on int in %s on line %d + +Warning: Trying to access array offset on int in %s on line %d From 4373c601eab03357e6b5953b36e7207b8cc9aca8 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 3 Feb 2025 20:06:29 +0100 Subject: [PATCH 05/16] Remove more unused local variables (GH-17688) Since `pdo_odbc_ucs22utf8()` doesn't actually use the `stmt`, we drop this parameter as well. --- ext/com_dotnet/com_handlers.c | 1 - ext/pdo_odbc/odbc_stmt.c | 7 +++---- sapi/phpdbg/phpdbg_win.c | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c index 5a177457a4492..af980b7b86f2a 100644 --- a/ext/com_dotnet/com_handlers.c +++ b/ext/com_dotnet/com_handlers.c @@ -249,7 +249,6 @@ static void function_dtor(zval *zv) static PHP_FUNCTION(com_method_handler) { zval *object = getThis(); - zend_string *method = EX(func)->common.function_name; zval *args = NULL; php_com_dotnet_object *obj = CDNO_FETCH(object); int nargs; diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 95840ec7f5391..cd7ee7f9ebe98 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -88,12 +88,11 @@ static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, return PDO_ODBC_CONV_NOT_REQUIRED; } -static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) +static int pdo_odbc_ucs22utf8(int is_unicode, zval *result) { #ifdef PHP_WIN32 ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING); if (is_unicode && Z_STRLEN_P(result) != 0) { - pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; DWORD ret; ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), NULL, 0, NULL, NULL); @@ -502,7 +501,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p if (P->len >= 0) { ZVAL_STRINGL(parameter, P->outbuf, P->len); - switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) { + switch (pdo_odbc_ucs22utf8(P->is_unicode, parameter)) { case PDO_ODBC_CONV_FAIL: /* something fishy, but allow it to come back as binary */ case PDO_ODBC_CONV_NOT_REQUIRED: @@ -751,7 +750,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo } unicode_conv: - switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, result)) { + switch (pdo_odbc_ucs22utf8(C->is_unicode, result)) { case PDO_ODBC_CONV_FAIL: /* oh well. They can have the binary version of it */ case PDO_ODBC_CONV_NOT_REQUIRED: diff --git a/sapi/phpdbg/phpdbg_win.c b/sapi/phpdbg/phpdbg_win.c index 0a61f78c039ab..dc1c1dba216af 100644 --- a/sapi/phpdbg/phpdbg_win.c +++ b/sapi/phpdbg/phpdbg_win.c @@ -26,7 +26,6 @@ int mprotect(void *addr, size_t size, int protection) { int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp) { EXCEPTION_RECORD *xr = xp->ExceptionRecord; - CONTEXT *xc = xp->ContextRecord; switch (xr->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: From c1f7b87fb1c4af693fb663f8a068490b76c30330 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 3 Feb 2025 21:02:15 +0100 Subject: [PATCH 06/16] Fix MSVC C4267 warnings in gd.c (GH-17680) These warnings are about conversion from `size_t` to a smaller type[1], and in this case because `gdIOCtx` works with `int` lengths. Two of these warnings are harmless, and we resolve them by using `size_t` in the first place, and adding a cast (plus an assertion), respectively. The others actually hint at potential issues when reading image data with more than `INT_MAX` bytes; we catch that upfront, and throw a `ValueError` and a warning, respectively. [1] --- ext/gd/gd.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index c5f7b65ce4c8b..b712861830bc9 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -1409,7 +1409,8 @@ gdImagePtr _php_image_create_from_string(zend_string *data, const char *tn, gdIm gdImagePtr im; gdIOCtx *io_ctx; - io_ctx = gdNewDynamicCtxEx(ZSTR_LEN(data), ZSTR_VAL(data), 0); + ZEND_ASSERT(ZSTR_LEN(data) <= INT_MAX); /* checked in imagecreatefromstring() */ + io_ctx = gdNewDynamicCtxEx((int) ZSTR_LEN(data), ZSTR_VAL(data), 0); if (!io_ctx) { return NULL; @@ -1439,6 +1440,10 @@ PHP_FUNCTION(imagecreatefromstring) Z_PARAM_STR(data) ZEND_PARSE_PARAMETERS_END(); + if (ZSTR_LEN(data) > INT_MAX) { + zend_argument_value_error(1, "is too long"); + } + imtype = _php_image_type(data); switch (imtype) { @@ -1562,17 +1567,23 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, buff = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0); if (!buff) { - php_error_docref(NULL, E_WARNING,"Cannot read image data"); + php_error_docref(NULL, E_WARNING, "Cannot read image data"); + goto out_err; + } + + if (ZSTR_LEN(buff) > INT_MAX) { + zend_string_release_ex(buff, 0); + php_error_docref(NULL, E_WARNING, "Cannot read images with more than %d bytes", INT_MAX); goto out_err; } /* needs to be malloc (persistent) - GD will free() it later */ pstr = pestrndup(ZSTR_VAL(buff), ZSTR_LEN(buff), 1); - io_ctx = gdNewDynamicCtxEx(ZSTR_LEN(buff), pstr, 0); + io_ctx = gdNewDynamicCtxEx((int) ZSTR_LEN(buff), pstr, 0); if (!io_ctx) { pefree(pstr, 1); zend_string_release_ex(buff, 0); - php_error_docref(NULL, E_WARNING,"Cannot allocate GD IO context"); + php_error_docref(NULL, E_WARNING, "Cannot allocate GD IO context"); goto out_err; } @@ -1796,7 +1807,7 @@ static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, cons fflush(fp); fclose(fp); } else { - int b; + size_t b; FILE *tmp; char buf[4096]; zend_string *path; @@ -2983,7 +2994,8 @@ static void php_imagechar(INTERNAL_FUNCTION_PARAMETERS, int mode) zend_long X, Y, COL; zend_string *C; gdImagePtr im; - int ch = 0, col, x, y, i, l = 0; + int ch = 0, col, x, y, i; + size_t l = 0; unsigned char *str = NULL; zend_object *font_obj = NULL; zend_long font_int = 0; @@ -4337,7 +4349,9 @@ static void _php_image_output_putc(struct gdIOCtx *ctx, int c) /* {{{ */ static int _php_image_output_putbuf(struct gdIOCtx *ctx, const void* buf, int l) /* {{{ */ { - return php_write((void *)buf, l); + size_t written = php_write((void *)buf, l); + ZEND_ASSERT(written <= INT_MAX); /* since l <= INT_MAX */ + return (int) written; } /* }}} */ static void _php_image_output_ctxfree(struct gdIOCtx *ctx) /* {{{ */ From 7e06a81bbd09100e50273a235c38fa8853dae5c9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 2 Feb 2025 00:59:08 +0100 Subject: [PATCH 07/16] Fix fallback paths in fast_long_{add,sub}_function This was asked to be checked in https://github.com/php/php-src/pull/17472#issuecomment-2591325036 There are 2 issues: 1) The UB in the if can overflow, and can be fixed by using zend_ulong for the sum/sub. 2) fast_long_sub_function() has a problem when result aliases. This is fixed in the same way as fast_long_add_function() works. Closes GH-17666. --- NEWS | 1 + Zend/zend_operators.h | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 49ecf9f3de08b..963a20eedce06 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ PHP NEWS compilation). (ilutov) . Fixed bug GH-17618 (UnhandledMatchError does not take zend.exception_ignore_args=1 into account). (timwolla) + . Fix fallback paths in fast_long_{add,sub}_function. (nielsdos) - Opcache: . Fixed bug GH-17654 (Multiple classes using same trait causes function diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index fd1db6f406328..76e95a92d4166 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -722,11 +722,13 @@ overflow: ZEND_ATTRIBUTE_COLD_LABEL * have read the values of op1 and op2. */ + zend_long sum = (zend_long) ((zend_ulong) Z_LVAL_P(op1) + (zend_ulong) Z_LVAL_P(op2)); + if (UNEXPECTED((Z_LVAL_P(op1) & LONG_SIGN_MASK) == (Z_LVAL_P(op2) & LONG_SIGN_MASK) - && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != ((Z_LVAL_P(op1) + Z_LVAL_P(op2)) & LONG_SIGN_MASK))) { + && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (sum & LONG_SIGN_MASK))) { ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2)); } else { - ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2)); + ZVAL_LONG(result, sum); } #endif } @@ -804,11 +806,19 @@ overflow: ZEND_ATTRIBUTE_COLD_LABEL ZVAL_LONG(result, llresult); } #else - ZVAL_LONG(result, Z_LVAL_P(op1) - Z_LVAL_P(op2)); + /* + * 'result' may alias with op1 or op2, so we need to + * ensure that 'result' is not updated until after we + * have read the values of op1 and op2. + */ + + zend_long sub = (zend_long) ((zend_ulong) Z_LVAL_P(op1) - (zend_ulong) Z_LVAL_P(op2)); if (UNEXPECTED((Z_LVAL_P(op1) & LONG_SIGN_MASK) != (Z_LVAL_P(op2) & LONG_SIGN_MASK) - && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (Z_LVAL_P(result) & LONG_SIGN_MASK))) { + && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (sub & LONG_SIGN_MASK))) { ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2)); + } else { + ZVAL_LONG(result, sub); } #endif } From 481bafe943f101612e2511e916a89795684b0713 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 30 Jan 2025 19:44:29 +0000 Subject: [PATCH 08/16] ext/pdo: Add static modifier for PDORow object handlers --- ext/pdo/pdo_stmt.c | 4 ++-- ext/pdo/php_pdo_int.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 0b779f6562781..824922b7e1ac8 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2369,7 +2369,7 @@ static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name return NULL; } -void pdo_row_free_storage(zend_object *std) +static void pdo_row_free_storage(zend_object *std) { pdo_row_t *row = php_pdo_row_fetch_object(std); if (row->stmt) { @@ -2378,7 +2378,7 @@ void pdo_row_free_storage(zend_object *std) } } -zend_object *pdo_row_new(zend_class_entry *ce) +static zend_object *pdo_row_new(zend_class_entry *ce) { pdo_row_t *row = zend_object_alloc(sizeof(pdo_row_t), ce); zend_object_std_init(&row->std, ce); diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index 13c2e3d9431b4..e8befe9f819a3 100644 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -42,10 +42,8 @@ bool pdo_stmt_describe_columns(pdo_stmt_t *stmt); bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num, zval *args, uint32_t variadic_num_args); -extern zend_object *pdo_row_new(zend_class_entry *ce); extern const zend_function_entry pdo_row_functions[]; extern zend_class_entry *pdo_row_ce; -void pdo_row_free_storage(zend_object *std); extern zend_object_handlers pdo_row_object_handlers; zend_object_iterator *php_pdo_dbstmt_iter_get(zend_class_entry *ce, zval *object); From 9054a8f2145960abe12b9fc57bfd35dbf739ad7d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 30 Jan 2025 19:43:51 +0000 Subject: [PATCH 09/16] ext/pdo: Add test for lazy fetch mode --- ext/pdo/tests/pdo_027.phpt | 30 ++++++- ext/pdo/tests/pdo_fetch_lazy_as_default.phpt | 90 +++++++++++++++++++ .../tests/pdo_fetch_lazy_as_not_default.phpt | 89 ++++++++++++++++++ ext/pdo/tests/pdo_query_fetch_lazy001.phpt | 37 ++++++++ 4 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 ext/pdo/tests/pdo_fetch_lazy_as_default.phpt create mode 100644 ext/pdo/tests/pdo_fetch_lazy_as_not_default.phpt create mode 100644 ext/pdo/tests/pdo_query_fetch_lazy001.phpt diff --git a/ext/pdo/tests/pdo_027.phpt b/ext/pdo/tests/pdo_027.phpt index a18f9b83c6ac5..6bb350f0c133b 100644 --- a/ext/pdo/tests/pdo_027.phpt +++ b/ext/pdo/tests/pdo_027.phpt @@ -19,7 +19,13 @@ $db->exec('create table test027 (id int, name varchar(10))'); $db->exec("INSERT INTO test027 (id,name) VALUES(1,'test1')"); $db->exec("INSERT INTO test027 (id,name) VALUES(2,'test2')"); -foreach ($db->query("SELECT * FROM test027", PDO::FETCH_LAZY) as $v) { +$alias = null; +$r = $db->query("SELECT * FROM test027", PDO::FETCH_LAZY); +var_dump($r); +foreach ($r as $v) { + var_dump($alias === $v); + var_dump($v); + $alias = $v; echo "lazy: " . $v->id.$v->name."\n"; } echo "End\n"; @@ -31,6 +37,28 @@ $db = PDOTest::factory(); PDOTest::dropTableIfExists($db, "test027"); ?> --EXPECT-- +object(PDOStatement)#2 (1) { + ["queryString"]=> + string(21) "SELECT * FROM test027" +} +bool(false) +object(PDORow)#4 (3) { + ["queryString"]=> + string(21) "SELECT * FROM test027" + ["id"]=> + string(1) "1" + ["name"]=> + string(5) "test1" +} lazy: 1test1 +bool(true) +object(PDORow)#4 (3) { + ["queryString"]=> + string(21) "SELECT * FROM test027" + ["id"]=> + string(1) "2" + ["name"]=> + string(5) "test2" +} lazy: 2test2 End diff --git a/ext/pdo/tests/pdo_fetch_lazy_as_default.phpt b/ext/pdo/tests/pdo_fetch_lazy_as_default.phpt new file mode 100644 index 0000000000000..578d758bfb34c --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_lazy_as_default.phpt @@ -0,0 +1,90 @@ +--TEST-- +PDO Common: PDO::FETCH_LAZY set via PDOStatement::setFetchMode() +--DESCRIPTION-- +This test uses an EXPECT section because we want to know the object handlers +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_lazy_as_default_changed(id int NOT NULL PRIMARY KEY, val1 VARCHAR(10), val2 VARCHAR(10), grp VARCHAR(10))'); +// Firebird does not allow inserting multiple rows with INSERT INTO +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (1, 'A', 'alpha', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (2, 'B', 'beta', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (3, 'C', 'gamma', 'Group2')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (4, 'D', 'delta', 'Group2')"); + +function to_upper_with_log(string $str): string { + echo __FUNCTION__, '(', var_export($str, true), ')', PHP_EOL; + return strtoupper($str); +} + +$pdoQuery = $db->prepare('SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed'); +$pdoQuery->execute(); +$pdoQuery->setFetchMode(PDO::FETCH_LAZY); + +class Test { + public $val1; + public $val2; +} +$o = new Test(); + +var_dump($pdoQuery->fetch()); +var_dump($pdoQuery->fetchObject(Test::class)); +var_dump($pdoQuery->fetch()); +$pdoQuery->setFetchMode(PDO::FETCH_INTO, $o); +var_dump($pdoQuery->fetch()); +var_dump($o); + +?> +--CLEAN-- + +--EXPECT-- +object(PDORow)#4 (3) { + ["queryString"]=> + string(56) "SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed" + ["val2"]=> + string(5) "alpha" + ["val1"]=> + string(1) "A" +} +object(Test)#4 (2) { + ["val1"]=> + string(1) "B" + ["val2"]=> + string(4) "beta" +} +object(PDORow)#4 (3) { + ["queryString"]=> + string(56) "SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed" + ["val2"]=> + string(5) "gamma" + ["val1"]=> + string(1) "C" +} +object(Test)#3 (2) { + ["val1"]=> + string(1) "D" + ["val2"]=> + string(5) "delta" +} +object(Test)#3 (2) { + ["val1"]=> + string(1) "D" + ["val2"]=> + string(5) "delta" +} diff --git a/ext/pdo/tests/pdo_fetch_lazy_as_not_default.phpt b/ext/pdo/tests/pdo_fetch_lazy_as_not_default.phpt new file mode 100644 index 0000000000000..ca11119c636e5 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_lazy_as_not_default.phpt @@ -0,0 +1,89 @@ +--TEST-- +PDO Common: PDO::FETCH_LAZY when FETCH_INTO set via PDOStatement::setFetchMode() +--DESCRIPTION-- +This test uses an EXPECT section because we want to know the object handlers +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_lazy_as_default_changed(id int NOT NULL PRIMARY KEY, val1 VARCHAR(10), val2 VARCHAR(10), grp VARCHAR(10))'); +// Firebird does not allow inserting multiple rows with INSERT INTO +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (1, 'A', 'alpha', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (2, 'B', 'beta', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (3, 'C', 'gamma', 'Group2')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (4, 'D', 'delta', 'Group2')"); + +function to_upper_with_log(string $str): string { + echo __FUNCTION__, '(', var_export($str, true), ')', PHP_EOL; + return strtoupper($str); +} + +$pdoQuery = $db->prepare('SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed'); +$pdoQuery->execute(); + +class Test { + public $val1; + public $val2; +} +$o = new Test(); + +$pdoQuery->setFetchMode(PDO::FETCH_INTO, $o); + +var_dump($pdoQuery->fetch()); +var_dump($pdoQuery->fetch(PDO::FETCH_LAZY)); +var_dump($pdoQuery->fetch()); + +$another_obj = new stdClass(); +var_dump($another_obj); + +var_dump($pdoQuery->fetch(PDO::FETCH_LAZY)); + +?> +--CLEAN-- + +--EXPECT-- +object(Test)#3 (2) { + ["val1"]=> + string(1) "A" + ["val2"]=> + string(5) "alpha" +} +object(PDORow)#4 (3) { + ["queryString"]=> + string(56) "SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed" + ["val2"]=> + string(4) "beta" + ["val1"]=> + string(1) "B" +} +object(Test)#3 (2) { + ["val1"]=> + string(1) "C" + ["val2"]=> + string(5) "gamma" +} +object(stdClass)#4 (0) { +} +object(PDORow)#5 (3) { + ["queryString"]=> + string(56) "SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed" + ["val2"]=> + string(5) "delta" + ["val1"]=> + string(1) "D" +} diff --git a/ext/pdo/tests/pdo_query_fetch_lazy001.phpt b/ext/pdo/tests/pdo_query_fetch_lazy001.phpt new file mode 100644 index 0000000000000..cec5e9f6d6c93 --- /dev/null +++ b/ext/pdo/tests/pdo_query_fetch_lazy001.phpt @@ -0,0 +1,37 @@ +--TEST-- +PDO Common: PDO::query() with PDO::FETCH_LAZY unused result +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('create table pdo_query_fetch_lazy_001 (id int, name varchar(10))'); +$db->exec("INSERT INTO pdo_query_fetch_lazy_001 (id,name) VALUES(1,'test1')"); +$db->exec("INSERT INTO pdo_query_fetch_lazy_001 (id,name) VALUES(2,'test2')"); + +$r = $db->query("SELECT * FROM pdo_query_fetch_lazy_001", PDO::FETCH_LAZY); +var_dump($r); +echo "End\n"; +?> +--CLEAN-- + +--EXPECT-- +object(PDOStatement)#2 (1) { + ["queryString"]=> + string(38) "SELECT * FROM pdo_query_fetch_lazy_001" +} +End From 4fcbdea974b302084cd10be3d6d1ea2f5cde70b0 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 30 Jan 2025 19:19:38 +0000 Subject: [PATCH 10/16] ext/pdo: Turn lazy_object_ref into a zend_object* from a zval This saves 8 bytes --- ext/pdo/pdo_dbh.c | 4 ---- ext/pdo/pdo_stmt.c | 12 ++++++------ ext/pdo/php_pdo_driver.h | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 10af61fa2b08f..89db4a106e861 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -659,8 +659,6 @@ PHP_METHOD(PDO, prepare) /* give it a reference to me */ GC_ADDREF(&dbh_obj->std); stmt->database_object_handle = &dbh_obj->std; - /* we haven't created a lazy object yet */ - ZVAL_UNDEF(&stmt->lazy_object_ref); if (dbh->methods->preparer(dbh, statement, stmt, options)) { if (Z_TYPE(ctor_args) == IS_ARRAY) { @@ -1225,8 +1223,6 @@ PHP_METHOD(PDO, query) /* give it a reference to me */ GC_ADDREF(&dbh_obj->std); stmt->database_object_handle = &dbh_obj->std; - /* we haven't created a lazy object yet */ - ZVAL_UNDEF(&stmt->lazy_object_ref); if (dbh->methods->preparer(dbh, statement, stmt, NULL)) { PDO_STMT_CLEAR_ERR(); diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 824922b7e1ac8..b2b4984ef8177 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -206,17 +206,17 @@ PDO_API void php_pdo_stmt_set_column_count(pdo_stmt_t *stmt, int new_count) stmt->column_count = new_count; } -static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value) /* {{{ */ +static void pdo_get_lazy_object(pdo_stmt_t *stmt, zval *return_value) /* {{{ */ { - if (Z_ISUNDEF(stmt->lazy_object_ref)) { + if (stmt->lazy_object_ref == NULL) { pdo_row_t *row = zend_object_alloc(sizeof(pdo_row_t), pdo_row_ce); row->stmt = stmt; zend_object_std_init(&row->std, pdo_row_ce); - ZVAL_OBJ(&stmt->lazy_object_ref, &row->std); + stmt->lazy_object_ref = &row->std; GC_ADDREF(&stmt->std); GC_DELREF(&row->std); } - ZVAL_COPY(return_value, &stmt->lazy_object_ref); + ZVAL_OBJ_COPY(return_value, stmt->lazy_object_ref); } /* }}} */ @@ -685,7 +685,7 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h } if (how == PDO_FETCH_LAZY) { - get_lazy_object(stmt, return_value); + pdo_get_lazy_object(stmt, return_value); return true; } @@ -2373,7 +2373,7 @@ static void pdo_row_free_storage(zend_object *std) { pdo_row_t *row = php_pdo_row_fetch_object(std); if (row->stmt) { - ZVAL_UNDEF(&row->stmt->lazy_object_ref); + row->stmt->lazy_object_ref = NULL; OBJ_RELEASE(&row->stmt->std); } } diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index a57a17bb81a4a..afaae4023d3ea 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -609,7 +609,7 @@ struct _pdo_stmt_t { /* for lazy fetches, we always return the same lazy object handle. * Let's keep it here. */ - zval lazy_object_ref; + zend_object *lazy_object_ref; pdo_dbh_t *dbh; /* we want to keep the dbh alive while we live, so we own a reference */ From d8aedb589c2de5f254c46f9da0dfdbc11433ec20 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 4 Feb 2025 14:51:33 +0100 Subject: [PATCH 11/16] [skip ci] Another flaky phar macOS test --- ext/phar/tests/zip/033.phpt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/phar/tests/zip/033.phpt b/ext/phar/tests/zip/033.phpt index de4ba2b71f027..cf5ccd9a51050 100644 --- a/ext/phar/tests/zip/033.phpt +++ b/ext/phar/tests/zip/033.phpt @@ -5,6 +5,12 @@ phar --INI-- phar.readonly=0 phar.require_hash=0 +--SKIPIF-- + --FILE-- Date: Tue, 4 Feb 2025 12:13:24 +0100 Subject: [PATCH 12/16] Fix GH-17503: Undefined float conversion in mb_convert_variables Conversion of floating point to integer values is undefined if the integral part of the float value cannot be represented by the integer type. We need to cater to that explicitly (in a manner similar to `zend_dval_to_lval_cap()`). Closes GH-17689. --- NEWS | 4 ++++ ext/mbstring/mbstring.c | 3 ++- ext/mbstring/tests/gh17503.phpt | 11 +++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 ext/mbstring/tests/gh17503.phpt diff --git a/NEWS b/NEWS index 963a20eedce06..779a2e05523e6 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,10 @@ PHP NEWS zend.exception_ignore_args=1 into account). (timwolla) . Fix fallback paths in fast_long_{add,sub}_function. (nielsdos) +- MBString: + . Fixed bug GH-17503 (Undefined float conversion in mb_convert_variables). + (cmb) + - Opcache: . Fixed bug GH-17654 (Multiple classes using same trait causes function JIT crash). (nielsdos) diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 2da957454a902..dbf012174c494 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -3092,7 +3092,8 @@ try_next_encoding:; } for (size_t i = 0; i < length; i++) { - array[i].demerits *= array[i].multiplier; + double demerits = array[i].demerits * (double) array[i].multiplier; + array[i].demerits = demerits < (double) UINT64_MAX ? (uint64_t) demerits : UINT64_MAX; } return length; diff --git a/ext/mbstring/tests/gh17503.phpt b/ext/mbstring/tests/gh17503.phpt new file mode 100644 index 0000000000000..92a2cf39cb10f --- /dev/null +++ b/ext/mbstring/tests/gh17503.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-17503 (Undefined float conversion in mb_convert_variables) +--EXTENSIONS-- +mbstring +--FILE-- +"); +var_dump(mb_convert_variables("ASCII", ["UTF-8", "UTF-16"], $a)); +?> +--EXPECT-- +string(5) "UTF-8" From 598f982a6b30962effeffd9c3a46a0a7ab38daf5 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 4 Feb 2025 11:47:01 +0000 Subject: [PATCH 13/16] ext/pdo: Add const modifiers to pdo_get_TYPE_param() functions --- ext/pdo/pdo_dbh.c | 4 ++-- ext/pdo/php_pdo_driver.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 89db4a106e861..7179c3f74caf0 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -777,7 +777,7 @@ PHP_METHOD(PDO, inTransaction) } /* }}} */ -PDO_API bool pdo_get_long_param(zend_long *lval, zval *value) +PDO_API bool pdo_get_long_param(zend_long *lval, const zval *value) { switch (Z_TYPE_P(value)) { case IS_LONG: @@ -795,7 +795,7 @@ PDO_API bool pdo_get_long_param(zend_long *lval, zval *value) return false; } } -PDO_API bool pdo_get_bool_param(bool *bval, zval *value) +PDO_API bool pdo_get_bool_param(bool *bval, const zval *value) { switch (Z_TYPE_P(value)) { case IS_TRUE: diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index afaae4023d3ea..f232241b51248 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -698,8 +698,8 @@ PDO_API void php_pdo_stmt_set_column_count(pdo_stmt_t *stmt, int new_count); PDO_API void php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAMETERS, zend_object *current_object, zend_class_entry *called_scope, zval *new_zval_object); /* Normalization for fetching long param for driver attributes */ -PDO_API bool pdo_get_long_param(zend_long *lval, zval *value); -PDO_API bool pdo_get_bool_param(bool *bval, zval *value); +PDO_API bool pdo_get_long_param(zend_long *lval, const zval *value); +PDO_API bool pdo_get_bool_param(bool *bval, const zval *value); PDO_API void pdo_throw_exception(unsigned int driver_errcode, char *driver_errmsg, pdo_error_type *pdo_error); From b6febd1356943462d680e9526fb82dd822288307 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 4 Feb 2025 14:15:08 +0000 Subject: [PATCH 14/16] ext/pdo: Revome useless ext/standard header include --- ext/pdo/pdo_dbh.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 7179c3f74caf0..107376ce48b08 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -24,7 +24,6 @@ #include "php.h" #include "php_ini.h" -#include "ext/standard/info.h" #include "php_pdo.h" #include "php_pdo_driver.h" #include "php_pdo_int.h" From 790286ab6c8954193bbbca778749550a51aceb97 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 4 Feb 2025 14:14:23 +0000 Subject: [PATCH 15/16] ext/pdo: Pass argument number to pdo_dbh_attribute_set() --- ext/pdo/pdo_dbh.c | 28 +++++++++---------- ext/pdo/tests/bug_44159.phpt | 6 ++-- .../tests/pdo_mysql_attr_errmode.phpt | 2 +- .../tests/pdo_mysql_attr_statement_class.phpt | 10 +++---- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 107376ce48b08..677e76f6caf74 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -35,7 +35,7 @@ #include "zend_observer.h" #include "zend_extensions.h" -static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value); +static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value, uint32_t value_arg_num); void pdo_throw_exception(unsigned int driver_errcode, char *driver_errmsg, pdo_error_type *pdo_error) { @@ -512,7 +512,7 @@ PDO_API void php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAMETERS, zen ZVAL_DEREF(attr_value); /* TODO: Should the constructor fail when the attribute cannot be set? */ - pdo_dbh_attribute_set(dbh, long_key, attr_value); + pdo_dbh_attribute_set(dbh, long_key, attr_value, 3); } ZEND_HASH_FOREACH_END(); } @@ -814,7 +814,7 @@ PDO_API bool pdo_get_bool_param(bool *bval, const zval *value) } /* Return false on failure, true otherwise */ -static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /* {{{ */ +static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value, uint32_t value_arg_num) /* {{{ */ { zend_long lval; bool bval; @@ -831,7 +831,7 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) / dbh->error_mode = lval; return true; default: - zend_value_error("Error mode must be one of the PDO::ERRMODE_* constants"); + zend_argument_value_error(value_arg_num, "Error mode must be one of the PDO::ERRMODE_* constants"); return false; } return false; @@ -847,7 +847,7 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) / dbh->desired_case = lval; return true; default: - zend_value_error("Case folding mode must be one of the PDO::CASE_* constants"); + zend_argument_value_error(value_arg_num, "Case folding mode must be one of the PDO::CASE_* constants"); return false; } return false; @@ -865,7 +865,7 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) / zval *tmp; if ((tmp = zend_hash_index_find(Z_ARRVAL_P(value), 0)) != NULL && Z_TYPE_P(tmp) == IS_LONG) { if (Z_LVAL_P(tmp) == PDO_FETCH_INTO || Z_LVAL_P(tmp) == PDO_FETCH_CLASS) { - zend_value_error("PDO::FETCH_INTO and PDO::FETCH_CLASS cannot be set as the default fetch mode"); + zend_argument_value_error(value_arg_num, "PDO::FETCH_INTO and PDO::FETCH_CLASS cannot be set as the default fetch mode"); return false; } } @@ -876,7 +876,7 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) / } } if (lval == PDO_FETCH_USE_DEFAULT) { - zend_value_error("Fetch mode must be a bitmask of PDO::FETCH_* constants"); + zend_argument_value_error(value_arg_num, "Fetch mode must be a bitmask of PDO::FETCH_* constants"); return false; } dbh->default_fetch_type = lval; @@ -906,25 +906,25 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) / return false; } if (Z_TYPE_P(value) != IS_ARRAY) { - zend_type_error("PDO::ATTR_STATEMENT_CLASS value must be of type array, %s given", + zend_argument_type_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS value must be of type array, %s given", zend_zval_value_name(value)); return false; } if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL) { - zend_value_error("PDO::ATTR_STATEMENT_CLASS value must be an array with the format " + zend_argument_value_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS value must be an array with the format " "array(classname, constructor_args)"); return false; } if (Z_TYPE_P(item) != IS_STRING || (pce = zend_lookup_class(Z_STR_P(item))) == NULL) { - zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be a valid class"); + zend_argument_type_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS class must be a valid class"); return false; } if (!instanceof_function(pce, pdo_dbstmt_ce)) { - zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement"); + zend_argument_type_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement"); return false; } if (pce->constructor && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) { - zend_type_error("User-supplied statement class cannot have a public constructor"); + zend_argument_type_error(value_arg_num, "User-supplied statement class cannot have a public constructor"); return false; } dbh->def_stmt_ce = pce; @@ -934,7 +934,7 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) / } if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) { if (Z_TYPE_P(item) != IS_ARRAY) { - zend_type_error("PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, %s given", + zend_argument_type_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, %s given", zend_zval_value_name(value)); return false; } @@ -980,7 +980,7 @@ PHP_METHOD(PDO, setAttribute) PDO_DBH_CLEAR_ERR(); PDO_CONSTRUCT_CHECK; - RETURN_BOOL(pdo_dbh_attribute_set(dbh, attr, value)); + RETURN_BOOL(pdo_dbh_attribute_set(dbh, attr, value, 2)); } /* }}} */ diff --git a/ext/pdo/tests/bug_44159.phpt b/ext/pdo/tests/bug_44159.phpt index 9d6b2876a15ef..1784324875edc 100644 --- a/ext/pdo/tests/bug_44159.phpt +++ b/ext/pdo/tests/bug_44159.phpt @@ -40,9 +40,9 @@ foreach ($attrs as $attr) { ?> --EXPECT-- -TypeError: PDO::ATTR_STATEMENT_CLASS value must be of type array, null given -TypeError: PDO::ATTR_STATEMENT_CLASS value must be of type array, int given -TypeError: PDO::ATTR_STATEMENT_CLASS value must be of type array, string given +TypeError: PDO::setAttribute(): Argument #2 ($value) PDO::ATTR_STATEMENT_CLASS value must be of type array, null given +TypeError: PDO::setAttribute(): Argument #2 ($value) PDO::ATTR_STATEMENT_CLASS value must be of type array, int given +TypeError: PDO::setAttribute(): Argument #2 ($value) PDO::ATTR_STATEMENT_CLASS value must be of type array, string given TypeError: Attribute value must be of type bool for selected attribute, null given bool(true) TypeError: Attribute value must be of type bool for selected attribute, string given diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt index df5f6c99d3483..95fcb16824c0f 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_errmode.phpt @@ -158,7 +158,7 @@ error_reporting=E_ALL TypeError: Attribute value must be of type int for selected attribute, array given TypeError: Attribute value must be of type int for selected attribute, stdClass given TypeError: Attribute value must be of type int for selected attribute, string given -ValueError: Error mode must be one of the PDO::ERRMODE_* constants +ValueError: PDO::setAttribute(): Argument #2 ($value) Error mode must be one of the PDO::ERRMODE_* constants Warning: PDO::query(): SQLSTATE[42000]: Syntax error or access violation: %d You have an error in your SQL syntax; check the manual that corresponds to your %s server version for the right syntax to use near '%s' at line %d in %s on line %d diff --git a/ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt b/ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt index a22af1cbdefb1..c932d14288d15 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_attr_statement_class.phpt @@ -136,11 +136,11 @@ array(1) { [0]=> string(12) "PDOStatement" } -PDO::ATTR_STATEMENT_CLASS value must be of type array, string given -PDO::ATTR_STATEMENT_CLASS class must be a valid class -PDO::ATTR_STATEMENT_CLASS class must be a valid class -PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement -TypeError: User-supplied statement class cannot have a public constructor +PDO::setAttribute(): Argument #2 ($value) PDO::ATTR_STATEMENT_CLASS value must be of type array, string given +PDO::setAttribute(): Argument #2 ($value) PDO::ATTR_STATEMENT_CLASS class must be a valid class +PDO::setAttribute(): Argument #2 ($value) PDO::ATTR_STATEMENT_CLASS class must be a valid class +PDO::setAttribute(): Argument #2 ($value) PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement +TypeError: PDO::setAttribute(): Argument #2 ($value) User-supplied statement class cannot have a public constructor array(2) { [0]=> string(12) "mystatement4" From dc7161cffeee7dd3bd75b91af7cc60cbb8680bda Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Tue, 4 Feb 2025 12:25:44 -0800 Subject: [PATCH 16/16] UPGRADING: fix some typos [skip ci] (#17701) --- UPGRADING | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UPGRADING b/UPGRADING index 9df115d1e735e..c35d9477af899 100644 --- a/UPGRADING +++ b/UPGRADING @@ -155,7 +155,7 @@ PHP 8.5 UPGRADE NOTES - Session: . session_start is stricter in regard of the option argument. - it throws a ValueError if the whole is not a hashmap or + It throws a ValueError if the whole is not a hashmap or a TypeError if read_on_close value is not a valid type compatible with int. @@ -170,7 +170,7 @@ PHP 8.5 UPGRADE NOTES . socket_create_listen, socket_bind and socket_sendto throw a ValueError if the port is lower than 0 or greater than 65535, also if any of the hints array entry is indexes numerically. - . socket_addrinfo_lookup throw a TypeError if any of the hints + . socket_addrinfo_lookup throws a TypeError if any of the hints values cannot be cast to a int and can throw a ValueError if any of these values overflow. . socket_set_option with MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP