diff --git a/UPGRADING b/UPGRADING index e28498efe9a80..7ae56581edad4 100644 --- a/UPGRADING +++ b/UPGRADING @@ -342,6 +342,91 @@ PHP 8.2 UPGRADE NOTES 11. Changes to INI File Handling ======================================== +- Parsing of some ill-formatted values will now trigger a warning when this was + silently ignored before. Interpretation of these values is not changed, for + backwards compatibility. This affects the following settings: + . bcmath.scale + . com.code_page + . default_socket_timeout + . fiber.stack_size + . hard_timeout + . intl.error_level + . ldap.max_links + . max_input_nesting_level + . max_input_vars + . mbstring.regex_retry_limit + . mbstring.regex_stack_limit + . mysqli.allow_local_infile + . mysqli.allow_persistent + . mysqli.default_port + . mysqli.max_links + . mysqli.max_persistent + . mysqli.reconnect + . mysqli.rollback_on_cached_plink + . mysqlnd.log_mask + . mysqlnd.mempool_default_size + . mysqlnd.net_read_buffer_size + . mysqlnd.net_read_timeout + . oci8.default_prefetch + . oci8.max_persistent + . oci8.persistent_timeout + . oci8.ping_interval + . oci8.prefetch_lob_size + . oci8.privileged_connect + . oci8.statement_cache_size + . odbc.allow_persistent + . odbc.check_persistent + . odbc.defaultbinmode + . odbc.default_cursortype + . odbc.defaultlrl + . odbc.max_links + . odbc.max_persistent + . opcache.consistency_checks + . opcache.file_update_protection + . opcache.force_restart_timeout + . opcache.interned_strings_buffer + . opcache.jit_bisect_limit + . opcache.jit_blacklist_root_trace + . opcache.jit_blacklist_side_trace + . opcache.jit_debug + . opcache.jit_hot_func + . opcache.jit_hot_loop + . opcache.jit_hot_return + . opcache.jit_hot_side_exit + . opcache.jit_max_exit_counters + . opcache.jit_max_loop_unrolls + . opcache.jit_max_polymorphic_calls + . opcache.jit_max_recursive_calls + . opcache.jit_max_recursive_returns + . opcache.jit_max_root_traces + . opcache.jit_max_side_traces + . opcache.log_verbosity_level + . opcache.max_file_size + . opcache.opt_debug_level + . opcache.optimization_level + . opcache.revalidate_freq + . output_buffering + . pcre.backtrack_limit + . pcre.recursion_limit + . pgsql.max_links + . pgsql.max_persistent + . post_max_size + . realpath_cache_size + . realpath_cache_ttl + . session.cache_expire + . session.cookie_lifetime + . session.gc_divisor + . session.gc_maxlifetime + . session.gc_probability + . soap.wsdl_cache_limit + . soap.wsdl_cache_ttl + . unserialize_max_depth + . upload_max_filesize + . user_ini.cache_ttl + . xmlrpc_error_number + . zend.assertions + . zlib.output_compression_level + ======================================== 12. Windows Support ======================================== diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 64cfbee3cf11b..e411bdba99031 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -29,6 +29,9 @@ PHP 8.2 INTERNALS UPGRADE NOTES * A new ZEND_THREEWAY_COMPARE() macro has been introduced which does a three-way comparison of two integers and returns -1, 0 or 1 if the LHS is smaller, equal or larger than the RHS +* Deprecated zend_atoi() and zend_atol(). Use ZEND_STRTOL() for general purpose + string to long conversion, or a variant of zend_ini_parse_quantity() for + parsing ini quantities. ======================== 2. Build system changes diff --git a/Zend/tests/zend_ini_parse_quantity.phpt b/Zend/tests/zend_ini_parse_quantity.phpt new file mode 100644 index 0000000000000..09f0a731452b8 --- /dev/null +++ b/Zend/tests/zend_ini_parse_quantity.phpt @@ -0,0 +1,897 @@ +--TEST-- +Test parsing of quantities +--EXTENSIONS-- +zend_test +--FILE-- + '0', + 'No overflow 002' => '1', + 'No overflow 003' => '100', + 'No overflow 004' => strval(PHP_INT_MAX), + 'No overflow 005' => strval(PHP_INT_MIN), + 'No overflow 006' => '2K', + 'No overflow 007' => '-2K', + 'Subject overflow 001' => increment(strval(PHP_INT_MAX)), + 'Subject overflow 002' => decrement(strval(PHP_INT_MIN)), + 'Multiplier overflow 001' => strval(PHP_INT_MAX).'K', + 'Multiplier overflow 002' => strval(PHP_INT_MIN).'K', +]; + +foreach ($tests as $name => $value) { + printf("# %s: \"%s\"\n", $name, $value); + printf("%d\n", zend_test_zend_ini_parse_quantity($value)); + print "\n"; + print "----------\n"; +} + +--EXPECTF-- +# No overflow 001: "0" +0 + +---------- +# No overflow 002: "1" +1 + +---------- +# No overflow 003: "100" +100 + +---------- +# No overflow 004: "%d" +%d + +---------- +# No overflow 005: "-%d" +-%d + +---------- +# No overflow 006: "2K" +2048 + +---------- +# No overflow 007: "-2K" +-2048 + +---------- +# Subject overflow 001: "%d" + +Warning: Invalid quantity "%d": value is out of range, using overflow result for backwards compatibility in %s on line %d +%s + +---------- +# Subject overflow 002: "-%d" + +Warning: Invalid quantity "-%d": value is out of range, using overflow result for backwards compatibility in %s on line %d +%s + +---------- +# Multiplier overflow 001: "%dK" + +Warning: Invalid quantity "%dK": value is out of range, using overflow result for backwards compatibility in %s on line %d +%s + +---------- +# Multiplier overflow 002: "-%dK" + +Warning: Invalid quantity "-%dK": value is out of range, using overflow result for backwards compatibility in %s on line %d +%s + +---------- diff --git a/Zend/tests/zend_ini_parse_uquantity_overflow.phpt b/Zend/tests/zend_ini_parse_uquantity_overflow.phpt new file mode 100644 index 0000000000000..6dd3ed05076be --- /dev/null +++ b/Zend/tests/zend_ini_parse_uquantity_overflow.phpt @@ -0,0 +1,117 @@ +--TEST-- +Test zend_ini_parse_uquantity() overflow handling +--EXTENSIONS-- +zend_test +--FILE-- + '0', + 'No overflow 002' => '1', + 'No overflow 003' => '100', + 'No overflow 004' => strval(PHP_INT_MAX), + 'No overflow 005' => '2K', + 'No overflow 006' => '-1', + 'No overflow 007' => ' -1', + 'No overflow 008' => '-1 ', + 'No overflow 009' => ' -1 ', + 'Subject overflow 001' => base_convert(str_repeat('1', PHP_INT_SIZE*8+1), 2, 10), + 'Subject overflow 002' => '-'.base_convert(str_repeat('1', PHP_INT_SIZE*8+1), 2, 10), + 'Subject overflow 003' => strval(PHP_INT_MIN), + 'Subject overflow 004' => '-2', + 'Subject overflow 005' => '-1K', + 'Subject overflow 006' => '-1 K', + 'Multiplier overflow 001' => strval(PHP_INT_MAX).'K', +]; + +foreach ($tests as $name => $value) { + printf("# %s: \"%s\"\n", $name, $value); + printf("%u\n", zend_test_zend_ini_parse_uquantity($value)); + print "\n"; + print "----------\n"; +} + +printf("# zend_test_zend_ini_parse_uquantity(\"-1\") === -1\n"); +var_dump(zend_test_zend_ini_parse_uquantity("-1") === -1); + +--EXPECTF-- +# No overflow 001: "0" +0 + +---------- +# No overflow 002: "1" +1 + +---------- +# No overflow 003: "100" +100 + +---------- +# No overflow 004: "%d" +%d + +---------- +# No overflow 005: "2K" +2048 + +---------- +# No overflow 006: "-1" +%d + +---------- +# No overflow 007: " -1" +%d + +---------- +# No overflow 008: "-1 " +%d + +---------- +# No overflow 009: " -1 " +%d + +---------- +# Subject overflow 001: "%d" + +Warning: Invalid quantity "%d": value is out of range, using overflow result for backwards compatibility in %s on line %d +%d + +---------- +# Subject overflow 002: "-%d" + +Warning: Invalid quantity "-%d": value is out of range, using overflow result for backwards compatibility in %s on line %d +%d + +---------- +# Subject overflow 003: "-%d" + +Warning: Invalid quantity "-%d": value is out of range, using overflow result for backwards compatibility in %s on line %d +%d + +---------- +# Subject overflow 004: "-2" + +Warning: Invalid quantity "-2": value is out of range, using overflow result for backwards compatibility in %s on line %d +%d + +---------- +# Subject overflow 005: "-1K" + +Warning: Invalid quantity "-1K": value is out of range, using overflow result for backwards compatibility in %s on line %d +%d + +---------- +# Subject overflow 006: "-1 K" + +Warning: Invalid quantity "-1 K": value is out of range, using overflow result for backwards compatibility in %s on line %d +%d + +---------- +# Multiplier overflow 001: "%dK" + +Warning: Invalid quantity "%dK": value is out of range, using overflow result for backwards compatibility in %s on line %d +%d + +---------- +# zend_test_zend_ini_parse_uquantity("-1") === -1 +bool(true) diff --git a/Zend/zend.c b/Zend/zend.c index 97d937db5a2ed..7d0eacda58c65 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -146,7 +146,7 @@ static ZEND_INI_MH(OnUpdateAssertions) /* {{{ */ { zend_long *p = (zend_long *) ZEND_INI_GET_ADDR(); - zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name); if (stage != ZEND_INI_STAGE_STARTUP && stage != ZEND_INI_STAGE_SHUTDOWN && @@ -176,7 +176,7 @@ static ZEND_INI_MH(OnSetExceptionStringParamMaxLen) /* {{{ */ static ZEND_INI_MH(OnUpdateFiberStackSize) /* {{{ */ { if (new_value) { - EG(fiber_stack_size) = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + EG(fiber_stack_size) = zend_ini_parse_quantity_warn(new_value, entry->name); } else { EG(fiber_stack_size) = ZEND_FIBER_DEFAULT_C_STACK_SIZE; } diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c index f4f17c7ba18ef..406de810e2b5c 100644 --- a/Zend/zend_ini.c +++ b/Zend/zend_ini.c @@ -24,12 +24,17 @@ #include "zend_operators.h" #include "zend_strtod.h" #include "zend_modules.h" +#include "zend_smart_str.h" static HashTable *registered_zend_ini_directives; #define NO_VALUE_PLAINTEXT "no value" #define NO_VALUE_HTML "no value" +static inline bool zend_is_whitespace(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f'; +} + /* * hash_apply functions */ @@ -536,6 +541,207 @@ ZEND_API bool zend_ini_parse_bool(zend_string *str) } } +typedef enum { + ZEND_INI_PARSE_QUANTITY_SIGNED, + ZEND_INI_PARSE_QUANTITY_UNSIGNED, +} zend_ini_parse_quantity_signed_result_t; + +static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */ +{ + char *digits_end = NULL; + char *str = ZSTR_VAL(value); + char *str_end = &str[ZSTR_LEN(value)]; + char *digits = str; + bool overflow = false; + zend_ulong factor; + smart_str invalid = {0}; + smart_str interpreted = {0}; + smart_str chr = {0}; + + /* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces, + * but we need the position of the first non-whitespace later. */ + while (digits < str_end && zend_is_whitespace(*digits)) ++digits; + + /* Ignore trailing whitespace */ + while (digits < str_end && zend_is_whitespace(*(str_end-1))) --str_end; + + if (digits == str_end) { + *errstr = NULL; + return 0; + } + + zend_ulong retval; + errno = 0; + + if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) { + retval = (zend_ulong) ZEND_STRTOL(digits, &digits_end, 0); + } else { + retval = ZEND_STRTOUL(digits, &digits_end, 0); + } + + if (errno == ERANGE) { + overflow = true; + } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) { + /* ZEND_STRTOUL() does not report a range error when the subject starts + * with a minus sign, so we check this here. Ignore "-1" as it is + * commonly used as max value, for instance in memory_limit=-1. */ + if (digits[0] == '-' && !(digits_end - digits == 2 && digits_end == str_end && digits[1] == '1')) { + overflow = true; + } + } + + if (UNEXPECTED(digits_end == digits)) { + /* No leading digits */ + + /* Escape the string to avoid null bytes and to make non-printable chars + * visible */ + smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value)); + smart_str_0(&invalid); + + *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility", + ZSTR_VAL(invalid.s)); + + smart_str_free(&invalid); + return 0; + } + + /* Allow for whitespace between integer portion and any suffix character */ + while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end; + + /* No exponent suffix. */ + if (digits_end == str_end) { + goto end; + } + + switch (*(str_end-1)) { + case 'g': + case 'G': + factor = 1<<30; + break; + case 'm': + case 'M': + factor = 1<<20; + break; + case 'k': + case 'K': + factor = 1<<10; + break; + default: + /* Unknown suffix */ + smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value)); + smart_str_0(&invalid); + smart_str_append_escaped(&interpreted, str, digits_end - str); + smart_str_0(&interpreted); + smart_str_append_escaped(&chr, str_end-1, 1); + smart_str_0(&chr); + + *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multipler \"%s\", interpreting as \"%s\" for backwards compatibility", + ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s)); + + smart_str_free(&invalid); + smart_str_free(&interpreted); + smart_str_free(&chr); + + return retval; + } + + if (!overflow) { + if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) { + zend_long sretval = (zend_long)retval; + if (sretval > 0) { + overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor; + } else { + overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor; + } + } else { + overflow = retval > ZEND_ULONG_MAX / factor; + } + } + + retval *= factor; + + if (UNEXPECTED(digits_end != str_end-1)) { + /* More than one character in suffix */ + smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value)); + smart_str_0(&invalid); + smart_str_append_escaped(&interpreted, str, digits_end - str); + smart_str_0(&interpreted); + smart_str_append_escaped(&chr, str_end-1, 1); + smart_str_0(&chr); + + *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility", + ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s)); + + smart_str_free(&invalid); + smart_str_free(&interpreted); + smart_str_free(&chr); + + return retval; + } + +end: + if (UNEXPECTED(overflow)) { + smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value)); + smart_str_0(&invalid); + + /* Not specifying the resulting value here because the caller may make + * additional conversions. Not specifying the allowed range + * because the caller may do narrower range checks. */ + *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility", + ZSTR_VAL(invalid.s)); + + smart_str_free(&invalid); + smart_str_free(&interpreted); + smart_str_free(&chr); + + return retval; + } + + *errstr = NULL; + return retval; +} +/* }}} */ + +ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr) /* {{{ */ +{ + return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr); +} +/* }}} */ + +ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr) /* {{{ */ +{ + return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr); +} +/* }}} */ + +ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting) /* {{{ */ +{ + zend_string *errstr; + zend_long retval = zend_ini_parse_quantity(value, &errstr); + + if (errstr) { + zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr)); + zend_string_release(errstr); + } + + return retval; +} +/* }}} */ + +ZEND_API zend_long zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting) /* {{{ */ +{ + zend_string *errstr; + zend_long retval = zend_ini_parse_uquantity(value, &errstr); + + if (errstr) { + zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr)); + zend_string_release(errstr); + } + + return retval; +} +/* }}} */ + ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */ { int value; @@ -624,14 +830,14 @@ ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */ ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */ { zend_long *p = (zend_long *) ZEND_INI_GET_ADDR(); - *p = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + *p = zend_ini_parse_quantity_warn(new_value, entry->name); return SUCCESS; } /* }}} */ ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */ { - zend_long tmp = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name); if (tmp < 0) { return FAILURE; } diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h index 23258ec0ca8b5..5f6847f11a132 100644 --- a/Zend/zend_ini.h +++ b/Zend/zend_ini.h @@ -90,6 +90,52 @@ ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig ZEND_API zend_string *zend_ini_get_value(zend_string *name); ZEND_API bool zend_ini_parse_bool(zend_string *str); +/** + * Parses an ini quantity + * + * The value parameter must be a string in the form + * + * sign? digits ws* multipler? + * + * with + * + * sign: [+-] + * digit: [0-9] + * digits: digit+ + * ws: [ \t\n\r\v\f] + * multipler: [KMG] + * + * Leading and trailing whitespaces are ignored. + * + * If the string is empty or consists only of only whitespaces, 0 is returned. + * + * Digits is parsed as decimal unless the first digit is '0', in which case + * digits is parsed as octal. + * + * The multiplier is case-insensitive. K, M, and G multiply the quantity by + * 2**10, 2**20, and 2**30, respectively. + * + * For backwards compatibility, ill-formatted values are handled as follows: + * - No leading digits: value is treated as '0' + * - Invalid multiplier: multiplier is ignored + * - Invalid characters between digits and multiplier: invalid characters are + * ignored + * - Integer overflow: The result of the overflow is returned + * + * In any of these cases an error string is stored in *errstr (caller must + * release it), otherwise *errstr is set to NULL. + */ +ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr); + +/** + * Unsigned variant of zend_ini_parse_quantity + */ +ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr); + +ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting); + +ZEND_API zend_long zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting); + ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)); ZEND_API ZEND_INI_DISP(zend_ini_boolean_displayer_cb); diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index a35edc01501c0..3690162418fa3 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -118,7 +118,7 @@ ZEND_API const unsigned char zend_toupper_map[256] = { 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf, 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf, 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef, -0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff +0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff }; @@ -136,7 +136,7 @@ ZEND_API const unsigned char zend_toupper_map[256] = { zend_binary_strncasecmp */ -ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len) /* {{{ */ +static zend_long ZEND_FASTCALL zend_atol_internal(const char *str, size_t str_len) /* {{{ */ { if (!str_len) { str_len = strlen(str); @@ -168,9 +168,14 @@ ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len) /* { } /* }}} */ +ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len) +{ + return zend_atol_internal(str, str_len); +} + ZEND_API int ZEND_FASTCALL zend_atoi(const char *str, size_t str_len) { - return (int) zend_atol(str, str_len); + return (int) zend_atol_internal(str, str_len); } /* {{{ convert_object_to_type: dst will be either ctype or UNDEF */ diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 346706d22beae..de02a406d575e 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -468,8 +468,11 @@ ZEND_API int ZEND_FASTCALL zend_compare_symbol_tables(HashTable *ht1, HashTable ZEND_API int ZEND_FASTCALL zend_compare_arrays(zval *a1, zval *a2); ZEND_API int ZEND_FASTCALL zend_compare_objects(zval *o1, zval *o2); -ZEND_API int ZEND_FASTCALL zend_atoi(const char *str, size_t str_len); -ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len); +/** Deprecatd in favor of ZEND_STRTOL() */ +ZEND_ATTRIBUTE_DEPRECATED ZEND_API int ZEND_FASTCALL zend_atoi(const char *str, size_t str_len); + +/** Deprecatd in favor of ZEND_STRTOL() */ +ZEND_ATTRIBUTE_DEPRECATED ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len); #define convert_to_null_ex(zv) convert_to_null(zv) #define convert_to_boolean_ex(zv) convert_to_boolean(zv) diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index 1b50764b828aa..d6147269b0ebb 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -62,7 +62,7 @@ ZEND_INI_MH(OnUpdateScale) int *p; zend_long tmp; - tmp = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + tmp = zend_ini_parse_quantity_warn(new_value, entry->name); if (tmp < 0 || tmp > INT_MAX) { return FAILURE; } diff --git a/ext/opcache/tests/bug71843.phpt b/ext/opcache/tests/bug71843.phpt index 66ccd823627f4..71756d3b7e87a 100644 --- a/ext/opcache/tests/bug71843.phpt +++ b/ext/opcache/tests/bug71843.phpt @@ -3,7 +3,7 @@ Bug #71843 (null ptr deref ZEND_RETURN_SPEC_CONST_HANDLER (zend_vm_execute.h:347 --INI-- opcache.enable=1 opcache.enable_cli=1 -opcache.optimization_level=0xFFFFBFFF +opcache.optimization_level=0x7FFFBFFF --EXTENSIONS-- opcache --FILE-- diff --git a/ext/opcache/tests/bug74431.phpt b/ext/opcache/tests/bug74431.phpt index bbfe0b9d6a551..87233ea02197a 100644 --- a/ext/opcache/tests/bug74431.phpt +++ b/ext/opcache/tests/bug74431.phpt @@ -3,7 +3,7 @@ Bug #74431 - foreach infinite loop --INI-- opcache.enable=1 opcache.enable_cli=1 -opcache.optimization_level=0xffffffff +opcache.optimization_level=0x7fffffff --EXTENSIONS-- opcache --FILE-- diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 5bdfe7a373a73..2271920b302c7 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -165,7 +165,7 @@ static ZEND_INI_MH(OnUpdateJit) static ZEND_INI_MH(OnUpdateJitDebug) { zend_long *p = (zend_long *) ZEND_INI_GET_ADDR(); - zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name); if (zend_jit_debug_config(*p, val, stage) == SUCCESS) { *p = val; @@ -176,7 +176,7 @@ static ZEND_INI_MH(OnUpdateJitDebug) static ZEND_INI_MH(OnUpdateCounter) { - zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name); if (val >= 0 && val < 256) { zend_long *p = (zend_long *) ZEND_INI_GET_ADDR(); *p = val; @@ -188,7 +188,7 @@ static ZEND_INI_MH(OnUpdateCounter) static ZEND_INI_MH(OnUpdateUnrollC) { - zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name); if (val > 0 && val < ZEND_JIT_TRACE_MAX_CALL_DEPTH) { zend_long *p = (zend_long *) ZEND_INI_GET_ADDR(); *p = val; @@ -201,7 +201,7 @@ static ZEND_INI_MH(OnUpdateUnrollC) static ZEND_INI_MH(OnUpdateUnrollR) { - zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name); if (val >= 0 && val < ZEND_JIT_TRACE_MAX_RET_DEPTH) { zend_long *p = (zend_long *) ZEND_INI_GET_ADDR(); *p = val; @@ -214,7 +214,7 @@ static ZEND_INI_MH(OnUpdateUnrollR) static ZEND_INI_MH(OnUpdateUnrollL) { - zend_long val = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name); if (val > 0 && val < ZEND_JIT_TRACE_MAX_LOOPS_UNROLL) { zend_long *p = (zend_long *) ZEND_INI_GET_ADDR(); *p = val; diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 91a5ae6519c5f..5123174569fa3 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -2506,8 +2506,9 @@ static void php_simple_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int cal break; } + /* entry in the form x[a]=b where x might need to be an array index */ if (!(Z_STRLEN_P(arg1) > 1 && Z_STRVAL_P(arg1)[0] == '0') && is_numeric_string(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), NULL, NULL, 0) == IS_LONG) { - zend_ulong key = (zend_ulong) zend_atol(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)); + zend_ulong key = (zend_ulong) ZEND_STRTOUL(Z_STRVAL_P(arg1), NULL, 0); if ((find_hash = zend_hash_index_find(Z_ARRVAL_P(arr), key)) == NULL) { array_init(&hash); find_hash = zend_hash_index_add_new(Z_ARRVAL_P(arr), key, &hash); diff --git a/ext/standard/tests/general_functions/parse_ini_numeric_entry_name.phpt b/ext/standard/tests/general_functions/parse_ini_numeric_entry_name.phpt new file mode 100644 index 0000000000000..a359a9f38285b --- /dev/null +++ b/ext/standard/tests/general_functions/parse_ini_numeric_entry_name.phpt @@ -0,0 +1,23 @@ +--TEST-- +parse_ini_string with numeric entry name +--FILE-- + + array(1) { + [0]=> + string(1) "1" + } + ["2M"]=> + array(1) { + [0]=> + string(1) "2" + } +} diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index a08c080ee3730..1c0b24f0d0849 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -53,6 +53,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) int register_passes; bool print_stderr_mshutdown; zend_test_fiber *active_fiber; + zend_long quantity_value; ZEND_END_MODULE_GLOBALS(zend_test) extern ZEND_DECLARE_MODULE_GLOBALS(zend_test) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 6adab9f255cbf..33a0e62e505dc 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -380,6 +380,40 @@ static ZEND_FUNCTION(zend_get_unit_enum) RETURN_OBJ_COPY(zend_enum_get_case_cstr(zend_test_unit_enum, "Foo")); } +static ZEND_FUNCTION(zend_test_zend_ini_parse_quantity) +{ + zend_string *str; + zend_string *errstr; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + RETVAL_LONG(zend_ini_parse_quantity(str, &errstr)); + + if (errstr) { + zend_error(E_WARNING, "%s", ZSTR_VAL(errstr)); + zend_string_release(errstr); + } +} + +static ZEND_FUNCTION(zend_test_zend_ini_parse_uquantity) +{ + zend_string *str; + zend_string *errstr; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + RETVAL_LONG((zend_long)zend_ini_parse_uquantity(str, &errstr)); + + if (errstr) { + zend_error(E_WARNING, "%s", ZSTR_VAL(errstr)); + zend_string_release(errstr); + } +} + static ZEND_FUNCTION(namespaced_func) { ZEND_PARSE_PARAMETERS_NONE(); @@ -571,6 +605,7 @@ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("zend_test.replace_zend_execute_ex", "0", PHP_INI_SYSTEM, OnUpdateBool, replace_zend_execute_ex, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.print_stderr_mshutdown", "0", PHP_INI_SYSTEM, OnUpdateBool, print_stderr_mshutdown, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_ENTRY("zend_test.quantity_value", "0", PHP_INI_ALL, OnUpdateLong, quantity_value, zend_zend_test_globals, zend_test_globals) PHP_INI_END() void (*old_zend_execute_ex)(zend_execute_data *execute_data); @@ -768,7 +803,10 @@ ZEND_GET_MODULE(zend_test) /* The important part here is the ZEND_FASTCALL. */ PHP_ZEND_TEST_API int ZEND_FASTCALL bug78270(const char *str, size_t str_len) { - return (int) zend_atol(str, str_len); + char * copy = zend_strndup(str, str_len); + int r = (int) ZEND_ATOL(copy); + free(copy); + return r; } PHP_ZEND_TEST_API struct bug79096 bug79096(void) diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 08122b6c02581..13b2fd346e7b1 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -125,6 +125,9 @@ function zend_test_parameter_with_attribute(string $parameter): int {} function zend_get_current_func_name(): string {} function zend_call_method(object|string $obj_or_class, string $method, mixed $arg1 = UNKNOWN, mixed $arg2 = UNKNOWN): mixed {} + + function zend_test_zend_ini_parse_quantity(string $str): int {} + function zend_test_zend_ini_parse_uquantity(string $str): int {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 26ed9f72f78a3..8c8e67d26954a 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2d42ea64a5114eae618a6dfb642c2640f568f5db */ + * Stub hash: 1a23b7473e5b4525352445545c6b3ab374c4e949 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -85,6 +85,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_call_method, 0, 2, IS_MIXED ZEND_ARG_TYPE_INFO(0, arg2, IS_MIXED, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_zend_ini_parse_quantity, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_zend_test_zend_ini_parse_uquantity arginfo_zend_test_zend_ini_parse_quantity + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -146,6 +152,8 @@ static ZEND_FUNCTION(zend_get_unit_enum); static ZEND_FUNCTION(zend_test_parameter_with_attribute); static ZEND_FUNCTION(zend_get_current_func_name); static ZEND_FUNCTION(zend_call_method); +static ZEND_FUNCTION(zend_test_zend_ini_parse_quantity); +static ZEND_FUNCTION(zend_test_zend_ini_parse_uquantity); static ZEND_FUNCTION(namespaced_func); static ZEND_METHOD(_ZendTestClass, is_object); static ZEND_METHOD(_ZendTestClass, __toString); @@ -186,6 +194,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_parameter_with_attribute, arginfo_zend_test_parameter_with_attribute) ZEND_FE(zend_get_current_func_name, arginfo_zend_get_current_func_name) ZEND_FE(zend_call_method, arginfo_zend_call_method) + ZEND_FE(zend_test_zend_ini_parse_quantity, arginfo_zend_test_zend_ini_parse_quantity) + ZEND_FE(zend_test_zend_ini_parse_uquantity, arginfo_zend_test_zend_ini_parse_uquantity) ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func) ZEND_FE_END }; diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index a53fb2acc4593..37e35eb8c277c 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -1277,7 +1277,7 @@ static PHP_INI_MH(OnUpdate_zlib_output_compression) } else if (zend_string_equals_literal_ci(new_value, "on")) { int_value = 1; } else { - int_value = zend_atoi(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + int_value = (int) zend_ini_parse_quantity_warn(new_value, entry->name); } ini_value = zend_ini_string("output_handler", sizeof("output_handler"), 0); diff --git a/main/main.c b/main/main.c index aa1763e5e5642..5c8a079e87de0 100644 --- a/main/main.c +++ b/main/main.c @@ -263,7 +263,7 @@ static PHP_INI_MH(OnChangeMemoryLimit) { size_t value; if (new_value) { - value = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + value = zend_ini_parse_uquantity_warn(new_value, entry->name); } else { value = Z_L(1)<<30; /* effectively, no limit */ }