diff --git a/NEWS b/NEWS index bd7c6b3a26ae0..e498fac1d82ef 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ PHP NEWS - BCMath: . Simplify `bc_divide()` code. (SakiTakamachi) . If the result is 0, n_scale is set to 0. (SakiTakamachi) + . If size of BC_VECTOR array is within 64 bytes, stack area is now used. + (SakiTakamachi) - CLI: . Add --ini=diff to print INI settings changed from the builtin default. @@ -33,6 +35,8 @@ PHP NEWS zend.exception_string_param_max_len=0. (timwolla) . Fixed bug GH-17959 (Relax missing trait fatal error to error exception). (ilutov) + . Fixed bug GH-18033 (NULL-ptr dereference when using register_tick_function + in destructor). (nielsdos) - Curl: . Added curl_multi_get_handles(). (timwolla) diff --git a/UPGRADING b/UPGRADING index 5d372c72e2139..11cca55bd1735 100644 --- a/UPGRADING +++ b/UPGRADING @@ -38,6 +38,9 @@ PHP 8.5 UPGRADE NOTES resources that were indirectly collected through cycles. . It is now allowed to substitute static with self or the concrete class name in final subclasses. + . The tick handlers are now deactivated after all shutdown functions, destructors + have run and the output handlers have been cleaned up. + This is a consequence of fixing GH-18033. - Intl: . The extension now requires at least ICU 57.1. diff --git a/Zend/tests/declare/gh18033_1.phpt b/Zend/tests/declare/gh18033_1.phpt new file mode 100644 index 0000000000000..ccce9360b4651 --- /dev/null +++ b/Zend/tests/declare/gh18033_1.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-18033 (NULL-ptr dereference when using register_tick_function in destructor) +--DESCRIPTION-- +Needs --repeat 2 or something similar to reproduce +--CREDITS-- +clesmian +--FILE-- + +--EXPECT-- +Done +In destructor diff --git a/Zend/tests/declare/gh18033_2.phpt b/Zend/tests/declare/gh18033_2.phpt new file mode 100644 index 0000000000000..8fdcff1b51e6c --- /dev/null +++ b/Zend/tests/declare/gh18033_2.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-18033 (NULL-ptr dereference when using register_tick_function in ob_start) +--DESCRIPTION-- +Needs --repeat 2 or something similar to reproduce +--CREDITS-- +clesmian +--FILE-- + +--EXPECT-- diff --git a/ext/bcmath/libbcmath/src/div.c b/ext/bcmath/libbcmath/src/div.c index ec7619fb77090..7da023df9a7e4 100644 --- a/ext/bcmath/libbcmath/src/div.c +++ b/ext/bcmath/libbcmath/src/div.c @@ -260,7 +260,15 @@ static void bc_do_div( size_t quot_arr_size = numerator_arr_size - divisor_arr_size + 1; size_t quot_real_arr_size = MIN(quot_arr_size, (quot_size + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE); - BC_VECTOR *numerator_vectors = safe_emalloc(numerator_arr_size + divisor_arr_size + quot_arr_size, sizeof(BC_VECTOR), 0); + BC_VECTOR stack_vectors[BC_STACK_VECTOR_SIZE]; + size_t allocation_arr_size = numerator_arr_size + divisor_arr_size + quot_arr_size; + + BC_VECTOR *numerator_vectors; + if (allocation_arr_size <= BC_STACK_VECTOR_SIZE) { + numerator_vectors = stack_vectors; + } else { + numerator_vectors = safe_emalloc(allocation_arr_size, sizeof(BC_VECTOR), 0); + } BC_VECTOR *divisor_vectors = numerator_vectors + numerator_arr_size; BC_VECTOR *quot_vectors = divisor_vectors + divisor_arr_size; @@ -302,7 +310,9 @@ static void bc_do_div( quot_vectors[i] /= BASE; } - efree(numerator_vectors); + if (allocation_arr_size > BC_STACK_VECTOR_SIZE) { + efree(numerator_vectors); + } } static inline void bc_divide_by_one(bc_num numerator, bc_num *quot, size_t quot_scale) @@ -370,12 +380,11 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) while (*numeratorptr == 0) { numeratorptr++; numerator_leading_zeros++; + if (numerator_leading_zeros == numerator_size) { + goto quot_zero; + } } - if (numerator_size > numerator_leading_zeros) { - numerator_size -= numerator_leading_zeros; - } else { - goto quot_zero; - } + numerator_size -= numerator_leading_zeros; /* check and remove divisor leading zeros */ while (*divisorptr == 0) { @@ -396,12 +405,7 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) divisor_trailing_zeros++; } divisor_size -= divisor_trailing_zeros; - - if (numerator_size > divisor_trailing_zeros) { - numerator_size -= divisor_trailing_zeros; - } else { - goto quot_zero; - } + numerator_size -= divisor_trailing_zeros; size_t quot_size = numerator_size - divisor_size + 1; /* numerator_size >= divisor_size */ if (quot_size > quot_scale) { diff --git a/ext/bcmath/libbcmath/src/private.h b/ext/bcmath/libbcmath/src/private.h index 1a911442dc9a1..7e972952c75e3 100644 --- a/ext/bcmath/libbcmath/src/private.h +++ b/ext/bcmath/libbcmath/src/private.h @@ -64,6 +64,9 @@ # define BC_LITTLE_ENDIAN 1 #endif +/* 64-bytes for 64-bit */ +#define BC_STACK_VECTOR_SIZE 8 + /* * Adding more than this many times may cause uint32_t/uint64_t to overflow. * Typically this is 1844 for 64bit and 42 for 32bit. diff --git a/ext/bcmath/libbcmath/src/recmul.c b/ext/bcmath/libbcmath/src/recmul.c index de06a4ca037ec..fc7efb37ebf02 100644 --- a/ext/bcmath/libbcmath/src/recmul.c +++ b/ext/bcmath/libbcmath/src/recmul.c @@ -149,15 +149,21 @@ static void bc_standard_mul(bc_num n1, size_t n1len, bc_num n2, size_t n2len, bc size_t n2_arr_size = (n2len + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE; size_t prod_arr_size = (prodlen + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE; - /* - * let's say that N is the max of n1len and n2len (and a multiple of BC_VECTOR_SIZE for simplicity), - * then this sum is <= N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE - 1 - * which is equal to N - 1 if BC_VECTOR_SIZE is 4, and N/2 - 1 if BC_VECTOR_SIZE is 8. - */ - BC_VECTOR *buf = safe_emalloc(n1_arr_size + n2_arr_size + prod_arr_size, sizeof(BC_VECTOR), 0); + BC_VECTOR stack_vectors[BC_STACK_VECTOR_SIZE]; + size_t allocation_arr_size = n1_arr_size + n2_arr_size + prod_arr_size; - BC_VECTOR *n1_vector = buf; - BC_VECTOR *n2_vector = buf + n1_arr_size; + BC_VECTOR *n1_vector; + if (allocation_arr_size <= BC_STACK_VECTOR_SIZE) { + n1_vector = stack_vectors; + } else { + /* + * let's say that N is the max of n1len and n2len (and a multiple of BC_VECTOR_SIZE for simplicity), + * then this sum is <= N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE - 1 + * which is equal to N - 1 if BC_VECTOR_SIZE is 4, and N/2 - 1 if BC_VECTOR_SIZE is 8. + */ + n1_vector = safe_emalloc(allocation_arr_size, sizeof(BC_VECTOR), 0); + } + BC_VECTOR *n2_vector = n1_vector + n1_arr_size; BC_VECTOR *prod_vector = n2_vector + n2_arr_size; for (i = 0; i < prod_arr_size; i++) { @@ -188,7 +194,9 @@ static void bc_standard_mul(bc_num n1, size_t n1len, bc_num n2, size_t n2len, bc bc_mul_finish_from_vector(prod_vector, prod_arr_size, prodlen, prod); - efree(buf); + if (allocation_arr_size > BC_STACK_VECTOR_SIZE) { + efree(n1_vector); + } } /** This is bc_standard_mul implementation for square */ diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 90f3ba6a54f98..95dc642222afc 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -2316,12 +2316,12 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext) SEPARATE_ARRAY(attribute_values); uint32_t num_values = zend_hash_num_elements(Z_ARRVAL_P(attribute_values)); if (num_values == 0) { - zend_argument_value_error(3, "list of attribute values must not be empty"); + zend_argument_value_error(3, "attribute \"%s\" must be a non-empty list of attribute values", ZSTR_VAL(attribute)); RETVAL_FALSE; goto cleanup; } if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(attribute_values))) { - zend_argument_value_error(3, "must be an array of attribute values with numeric keys"); + zend_argument_value_error(3, "attribute \"%s\" must be an array of attribute values with numeric keys", ZSTR_VAL(attribute)); RETVAL_FALSE; goto cleanup; } diff --git a/ext/ldap/tests/ldap_add_modify_delete_programming_errors.phpt b/ext/ldap/tests/ldap_add_modify_delete_programming_errors.phpt index 879b465dfeae4..a84e068ba3ace 100644 --- a/ext/ldap/tests/ldap_add_modify_delete_programming_errors.phpt +++ b/ext/ldap/tests/ldap_add_modify_delete_programming_errors.phpt @@ -137,7 +137,7 @@ ValueError: ldap_add(): Argument #3 ($entry) must be an associative array of att ValueError: ldap_add(): Argument #3 ($entry) key must not be empty ValueError: ldap_add(): Argument #3 ($entry) key must not contain any null bytes Error: Object of class stdClass could not be converted to string -ValueError: ldap_add(): Argument #3 ($entry) list of attribute values must not be empty -ValueError: ldap_add(): Argument #3 ($entry) must be an array of attribute values with numeric keys +ValueError: ldap_add(): Argument #3 ($entry) attribute "attribute2" must be a non-empty list of attribute values +ValueError: ldap_add(): Argument #3 ($entry) attribute "attribute2" must be an array of attribute values with numeric keys TypeError: LDAP value must be of type string|int|bool, array given Error: Object of class stdClass could not be converted to string diff --git a/ext/ldap/tests/ldap_add_modify_delete_references_programming_errors.phpt b/ext/ldap/tests/ldap_add_modify_delete_references_programming_errors.phpt index 4926dbb841d0c..3ded9699f459f 100644 --- a/ext/ldap/tests/ldap_add_modify_delete_references_programming_errors.phpt +++ b/ext/ldap/tests/ldap_add_modify_delete_references_programming_errors.phpt @@ -76,6 +76,6 @@ try { ?> --EXPECT-- Error: Object of class stdClass could not be converted to string -ValueError: ldap_add(): Argument #3 ($entry) list of attribute values must not be empty +ValueError: ldap_add(): Argument #3 ($entry) attribute "attribute2" must be a non-empty list of attribute values TypeError: LDAP value must be of type string|int|bool, array given TypeError: LDAP value must be of type string|int|bool, stdClass given diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 0666057b66b6e..6c2de01c85af6 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -684,7 +684,7 @@ static bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */ /* make all parameters nullable */ unsigned int i; - XSQLVAR* var; + XSQLVAR* var; for (i = 0, var = S->in_sqlda->sqlvar; i < S->in_sqlda->sqld; i++, var++) { /* The low bit of sqltype indicates that the parameter can take a NULL value */ var->sqltype |= 1; @@ -1422,7 +1422,7 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* "HY000", H->isc_status[1], errmsg); } - if (dbh->auto_commit && !H->tr) { + if (ret && dbh->auto_commit && !H->tr) { ret = php_firebird_begin_transaction(dbh, /* auto commit mode */ true); } diff --git a/main/main.c b/main/main.c index 4075be8b377e3..415cb02185e94 100644 --- a/main/main.c +++ b/main/main.c @@ -1904,8 +1904,6 @@ void php_request_shutdown(void *dummy) */ EG(current_execute_data) = NULL; - php_deactivate_ticks(); - /* 0. Call any open observer end handlers that are still open after a zend_bailout */ if (ZEND_OBSERVER_ENABLED) { zend_observer_fcall_end_all(); @@ -1926,6 +1924,8 @@ void php_request_shutdown(void *dummy) php_output_end_all(); } zend_end_try(); + php_deactivate_ticks(); + /* 4. Reset max_execution_time (no longer executing php code after response sent) */ zend_try { zend_unset_timeout();