Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ PHP NEWS
. Fixed bug GH-22046 (The unserialize function can lead to segfault when
non-Serializable internal classes are serialized back with the C format).
(kocsismate)
. Fixed bug GH-22292 (AST pretty printing does not correctly handle
invalid variable names). (timwolla)

- BCMath:
. Added NUL-byte validation to BCMath functions. (jorgsowa)
Expand Down
3 changes: 3 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES
. ZSTR_INIT_LITERAL(), zend_string_starts_with_literal(), and
zend_string_starts_with_literal_ci() now support strings containing NUL
bytes. Passing non-literal char* is no longer supported.
. Added zend_string_equals_cstr_ci().
. The misnamed ZVAL_IS_NULL() has been removed. Use Z_ISNULL() instead.
. New zend_class_entry.ce_flags2 and zend_function.fn_flags2 fields were
added, given the primary flags were running out of bits.
Expand Down Expand Up @@ -103,6 +104,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES
. The deprecated Z_IMMUTABLE(), Z_IMMUTABLE_P(), Z_OPT_IMMUTABLE(), and
Z_OPT_IMMUTABLE_P() macros have been removed. Check for
IS_ARRAY && !REFCOUNTED directly.
. The unused Z_GC_*() macros have been removed. Use the corresponding
GC_*() macro on the result of Z_COUNTED().
. The zend_binary_zval_strcmp() and zend_binary_zval_strncmp() functions
have been removed, because they are unsafe by relying on the zvals
having a specific type. Use zend_binary_strcmp() / zend_binary_strncmp(),
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/assert/expect_015.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ assert(0 && ($a = function (): ?static {
$x = "{$a}b";
$x = "{$a}b";
$x = " {$foo->bar} {${$foo->bar}} ";
$x = " ${---} ";
$x = " ${'---'} ";
foo();
\foo();
namespace\foo();
Expand Down
19 changes: 19 additions & 0 deletions Zend/tests/try/gh22280.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
GH-22280: goto to label before try/finally after try/catch
--FILE--
<?php
goto d;
try {
} catch (Throwable) {
}
d: try {
echo "try\n";
} finally {
echo "finally\n";
}
echo "done\n";
?>
--EXPECT--
try
finally
done
35 changes: 35 additions & 0 deletions Zend/tests/type_coercion/gh22112.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
GH-22112 (Assertion failure when error handler throws during NaN to bool/string coercion at function entry)
--FILE--
<?php

set_error_handler(function ($errno, $errstr) {
throw new Exception($errstr);
});

function take_bool(bool $v) {
echo "take_bool entered\n";
}

function take_string(string $v) {
echo "take_string entered\n";
}

$nan = fdiv(0, 0);

try {
take_bool($nan);
} catch (Exception $e) {
echo "bool: ", $e->getMessage(), "\n";
}

try {
take_string($nan);
} catch (Exception $e) {
echo "string: ", $e->getMessage(), "\n";
}

?>
--EXPECT--
bool: unexpected NAN value was coerced to bool
string: unexpected NAN value was coerced to string
9 changes: 8 additions & 1 deletion Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,11 @@ ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_weak(const zval
if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("bool", arg_num)) {
return ZPP_PARSE_BOOL_STATUS_ERROR;
}
return zend_is_true(arg);
bool result = zend_is_true(arg);
if (UNEXPECTED(EG(exception))) {
return ZPP_PARSE_BOOL_STATUS_ERROR;
}
return result;
}
return ZPP_PARSE_BOOL_STATUS_ERROR;
}
Expand Down Expand Up @@ -735,6 +739,9 @@ ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t
return NULL;
}
convert_to_string(arg);
if (UNEXPECTED(EG(exception))) {
return NULL;
}
return Z_STR_P(arg);
} else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
zend_object *zobj = Z_OBJ_P(arg);
Expand Down
11 changes: 8 additions & 3 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1723,9 +1723,14 @@ static ZEND_COLD void zend_ast_export_var(smart_str *str, zend_ast *ast, int ind
{
if (ast->kind == ZEND_AST_ZVAL) {
zval *zv = zend_ast_get_zval(ast);
if (Z_TYPE_P(zv) == IS_STRING &&
zend_ast_valid_var_name(Z_STRVAL_P(zv), Z_STRLEN_P(zv))) {
smart_str_append(str, Z_STR_P(zv));
if (Z_TYPE_P(zv) == IS_STRING) {
if (zend_ast_valid_var_name(Z_STRVAL_P(zv), Z_STRLEN_P(zv))) {
smart_str_append(str, Z_STR_P(zv));
} else {
smart_str_appends(str, "{'");
zend_ast_export_str(str, Z_STR_P(zv));
smart_str_appends(str, "'}");
}
return;
}
} else if (ast->kind == ZEND_AST_VAR) {
Expand Down
12 changes: 12 additions & 0 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,18 @@ ZEND_API ZEND_ATTRIBUTE_NONNULL zend_function *zend_get_call_trampoline_func(
}
/* }}} */

ZEND_API void zend_free_trampoline(zend_function *func)
{
ZEND_ASSERT(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE);

if (func == &EG(trampoline)) {
EG(trampoline).common.attributes = NULL;
EG(trampoline).common.function_name = NULL;
} else {
efree(func);
}
}

static ZEND_FUNCTION(zend_parent_hook_get_trampoline)
{
zend_object *obj = Z_PTR_P(ZEND_THIS);
Expand Down
56 changes: 25 additions & 31 deletions Zend/zend_object_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,10 @@ struct _zend_object_handlers {
BEGIN_EXTERN_C()
extern const ZEND_API zend_object_handlers std_object_handlers;

#define zend_get_std_object_handlers() \
(&std_object_handlers)

#define zend_get_function_root_class(fbc) \
((fbc)->common.prototype ? (fbc)->common.prototype->common.scope : (fbc)->common.scope)
static zend_always_inline const zend_object_handlers *zend_get_std_object_handlers(void)
{
return &std_object_handlers;
}

#define ZEND_PROPERTY_ISSET 0x0 /* Property exists and is not NULL */
#define ZEND_PROPERTY_NOT_EMPTY ZEND_ISEMPTY /* Property is not empty */
Expand Down Expand Up @@ -290,18 +289,20 @@ static zend_always_inline HashTable *zend_std_get_properties_ex(zend_object *obj
/* Implements the fast path for array cast */
ZEND_API HashTable *zend_std_build_object_properties_array(zend_object *zobj);

#define ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(object) ( \
/* We can use zend_std_build_object_properties_array() for objects \
* without properties ht and with standard handlers */ \
Z_OBJ_P(object)->properties == NULL \
&& Z_OBJ_HT_P(object)->get_properties_for == NULL \
&& Z_OBJ_HT_P(object)->get_properties == zend_std_get_properties \
/* For initialized proxies we need to forward to the real instance */ \
&& ( \
!zend_object_is_lazy_proxy(Z_OBJ_P(object)) \
|| !zend_lazy_object_initialized(Z_OBJ_P(object)) \
) \
)
static zend_always_inline bool ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(const zval *zv) {
/* We can use zend_std_build_object_properties_array() for objects
* without properties ht and with standard handlers */
const zend_object *obj = Z_OBJ_P(zv);

return obj->properties == NULL
&& obj->handlers->get_properties_for == NULL
&& obj->handlers->get_properties == zend_std_get_properties
/* For initialized proxies we need to forward to the real instance */
&& (
!zend_object_is_lazy_proxy(obj)
|| !zend_lazy_object_initialized(obj)
);
}

/* Handler for objects that cannot be meaningfully compared.
* Only objects with the same identity will be considered equal. */
Expand All @@ -312,6 +313,7 @@ ZEND_API bool zend_check_protected(const zend_class_entry *ce, const zend_class_
ZEND_API zend_result zend_check_property_access(const zend_object *zobj, zend_string *prop_info_name, bool is_dynamic);

ZEND_API ZEND_ATTRIBUTE_NONNULL zend_function *zend_get_call_trampoline_func(const zend_function *fbc, zend_string *method_name);
ZEND_API void zend_free_trampoline(zend_function *func);

ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member);

Expand All @@ -335,20 +337,12 @@ ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_p

void zend_object_handlers_startup(void);

#define zend_release_properties(ht) do { \
if (ht) { \
zend_array_release(ht); \
} \
} while (0)

#define zend_free_trampoline(func) do { \
if ((func) == &EG(trampoline)) { \
EG(trampoline).common.attributes = NULL; \
EG(trampoline).common.function_name = NULL; \
} else { \
efree(func); \
} \
} while (0)
static zend_always_inline void zend_release_properties(HashTable *ht)
{
if (ht) {
zend_array_release(ht);
}
}

/* Fallback to default comparison implementation if the arguments aren't both objects
* and have the same compare() handler. You'll likely want to use this unless you
Expand Down
5 changes: 5 additions & 0 deletions Zend/zend_objects_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ static inline zend_property_info *zend_get_typed_property_info_for_slot(zend_obj
return NULL;
}

static zend_always_inline zend_class_entry *zend_get_function_root_class(const zend_function *fbc)
{
return fbc->common.prototype ? fbc->common.prototype->common.scope : fbc->common.scope;
}

static zend_always_inline bool zend_check_method_accessible(const zend_function *fn, const zend_class_entry *scope)
{
if (!(fn->common.fn_flags & ZEND_ACC_PUBLIC)
Expand Down
29 changes: 15 additions & 14 deletions Zend/zend_observer.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ extern ZEND_API bool zend_observer_errors_observed;
extern ZEND_API bool zend_observer_function_declared_observed;
extern ZEND_API bool zend_observer_class_linked_observed;

#define ZEND_OBSERVER_HANDLE(function) (ZEND_USER_CODE((function)->type) \
? zend_observer_fcall_op_array_extension : zend_observer_fcall_internal_function_extension)
static zend_always_inline int ZEND_OBSERVER_HANDLE(const zend_function *function) {
return ZEND_USER_CODE(function->common.type) ? zend_observer_fcall_op_array_extension : zend_observer_fcall_internal_function_extension;
}

#define ZEND_OBSERVER_DATA(function) \
((zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(function)->common), ZEND_OBSERVER_HANDLE(function)))
Expand All @@ -45,18 +46,6 @@ extern ZEND_API bool zend_observer_class_linked_observed;
/* Omit zend_observer_fcall_internal_function_extension check, they are set at the same time. */
#define ZEND_OBSERVER_ENABLED (zend_observer_fcall_op_array_extension != -1)

#define ZEND_OBSERVER_FCALL_BEGIN(execute_data) do { \
if (ZEND_OBSERVER_ENABLED) { \
zend_observer_fcall_begin(execute_data); \
} \
} while (0)

#define ZEND_OBSERVER_FCALL_END(execute_data, return_value) do { \
if (ZEND_OBSERVER_ENABLED) { \
zend_observer_fcall_end(execute_data, return_value); \
} \
} while (0)

typedef void (*zend_observer_fcall_begin_handler)(zend_execute_data *execute_data);
typedef void (*zend_observer_fcall_end_handler)(zend_execute_data *execute_data, zval *retval);

Expand Down Expand Up @@ -87,6 +76,12 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(zend_execute_data *execute
/* prechecked: the call is actually observed. */
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin_prechecked(zend_execute_data *execute_data, zend_observer_fcall_begin_handler *observer_data);

static zend_always_inline void ZEND_OBSERVER_FCALL_BEGIN(zend_execute_data *execute_data) {
if (ZEND_OBSERVER_ENABLED) {
zend_observer_fcall_begin(execute_data);
}
}

static zend_always_inline bool zend_observer_handler_is_unobserved(const zend_observer_fcall_begin_handler *handler) {
return *handler == ZEND_OBSERVER_NONE_OBSERVED;
}
Expand Down Expand Up @@ -126,6 +121,12 @@ static zend_always_inline void zend_observer_fcall_end(zend_execute_data *execut
}
}

static zend_always_inline void ZEND_OBSERVER_FCALL_END(zend_execute_data *execute_data, zval *return_value) {
if (ZEND_OBSERVER_ENABLED) {
zend_observer_fcall_end(execute_data, return_value);
}
}

ZEND_API void zend_observer_fcall_end_all(void);

typedef void (*zend_observer_function_declared_cb)(zend_op_array *op_array, zend_string *name);
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,10 @@ static void zend_extension_op_array_handler(zend_extension *extension, zend_op_a
static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num, uint32_t dst_num)
{
for (uint32_t i = 0; i < op_array->last_try_catch; i++) {
if (!op_array->try_catch_array[i].finally_op) {
continue;
}

if ((op_num < op_array->try_catch_array[i].finally_op ||
op_num >= op_array->try_catch_array[i].finally_end)
&& (dst_num >= op_array->try_catch_array[i].finally_op &&
Expand Down
1 change: 0 additions & 1 deletion Zend/zend_operators.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
#include "zend_portability.h"
#include "zend_strtod.h"
#include "zend_multiply.h"
#include "zend_object_handlers.h"

#define LONG_SIGN_MASK ZEND_LONG_MIN

Expand Down
19 changes: 15 additions & 4 deletions Zend/zend_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,22 @@ static zend_always_inline bool zend_string_equals(const zend_string *s1, const z
return s1 == s2 || zend_string_equal_content(s1, s2);
}

#define zend_string_equals_ci(s1, s2) \
(ZSTR_LEN(s1) == ZSTR_LEN(s2) && !zend_binary_strcasecmp(ZSTR_VAL(s1), ZSTR_LEN(s1), ZSTR_VAL(s2), ZSTR_LEN(s2)))
BEGIN_EXTERN_C()
ZEND_API int ZEND_FASTCALL zend_binary_strcasecmp(const char *s1, size_t len1, const char *s2, size_t len2);
END_EXTERN_C()

static zend_always_inline bool zend_string_equals_cstr_ci(const zend_string *s1, const char *s2, size_t s2_length)
{
return ZSTR_LEN(s1) == s2_length && !zend_binary_strcasecmp(ZSTR_VAL(s1), ZSTR_LEN(s1), s2, s2_length);
}

#define zend_string_equals_literal_ci(str, literal) \
zend_string_equals_cstr_ci(str, "" literal, sizeof(literal) - 1)

#define zend_string_equals_literal_ci(str, c) \
(ZSTR_LEN(str) == sizeof("" c) - 1 && !zend_binary_strcasecmp(ZSTR_VAL(str), ZSTR_LEN(str), (c), sizeof(c) - 1))
static zend_always_inline bool zend_string_equals_ci(const zend_string *s1, const zend_string *s2)
{
return zend_string_equals_cstr_ci(s1, ZSTR_VAL(s2), ZSTR_LEN(s2));
}

#define zend_string_equals_literal(str, literal) \
zend_string_equals_cstr(str, "" literal, sizeof(literal) - 1)
Expand Down
11 changes: 0 additions & 11 deletions Zend/zend_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -864,17 +864,6 @@ static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint3
} \
} while (0)

#define Z_GC_TYPE(zval) GC_TYPE(Z_COUNTED(zval))
#define Z_GC_TYPE_P(zval_p) Z_GC_TYPE(*(zval_p))

#define Z_GC_FLAGS(zval) GC_FLAGS(Z_COUNTED(zval))
#define Z_GC_FLAGS_P(zval_p) Z_GC_FLAGS(*(zval_p))

#define Z_GC_INFO(zval) GC_INFO(Z_COUNTED(zval))
#define Z_GC_INFO_P(zval_p) Z_GC_INFO(*(zval_p))
#define Z_GC_TYPE_INFO(zval) GC_TYPE_INFO(Z_COUNTED(zval))
#define Z_GC_TYPE_INFO_P(zval_p) Z_GC_TYPE_INFO(*(zval_p))

#define GC_NULL (IS_NULL | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT))
#define GC_STRING (IS_STRING | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT))
#define GC_ARRAY IS_ARRAY
Expand Down
4 changes: 2 additions & 2 deletions ext/exif/exif.c
Original file line number Diff line number Diff line change
Expand Up @@ -2318,13 +2318,13 @@ static void exif_iif_add_value(image_info_type *image_info, int section_index, c
#ifdef EXIF_DEBUG
php_error_docref(NULL, E_WARNING, "Found value of type single");
#endif
info_value->f = php_ifd_get_float(value);
info_value->f = php_ifd_get_float(vptr);
break;
case TAG_FMT_DOUBLE:
#ifdef EXIF_DEBUG
php_error_docref(NULL, E_WARNING, "Found value of type double");
#endif
info_value->d = php_ifd_get_double(value);
info_value->d = php_ifd_get_double(vptr);
break;
}
}
Expand Down
Loading
Loading