From b00d3a77f7a5341310ca268dac143cba3c0fbf0c Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 4 Aug 2022 14:12:45 +0200 Subject: [PATCH] Fix lineno for all constant expressions Fixes GH-8821 --- Zend/tests/bug41633_2.phpt | 3 +- Zend/tests/bug41633_3.phpt | 3 +- Zend/tests/bug47572.phpt | 3 +- Zend/tests/bug74657.phpt | 3 +- ...nt_expressions_self_referencing_array.phpt | 3 +- Zend/tests/enum/offsetGet-in-const-expr.phpt | 3 +- Zend/tests/gh7771_1.phpt | 3 +- Zend/tests/gh7771_2.phpt | 3 +- Zend/tests/gh8821.phpt | 22 ++++++++++++ .../typed_properties_022.phpt | 3 +- Zend/zend_ast.c | 34 +++++++++++++++++++ Zend/zend_ast.h | 3 ++ Zend/zend_builtin_functions.c | 26 ++++++++++++++ Zend/zend_string.h | 1 + ext/standard/tests/streams/bug77664.phpt | 5 +-- tests/classes/constants_error_004.phpt | 3 +- 16 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 Zend/tests/gh8821.phpt diff --git a/Zend/tests/bug41633_2.phpt b/Zend/tests/bug41633_2.phpt index bb768c0e11743..998610d291cdf 100644 --- a/Zend/tests/bug41633_2.phpt +++ b/Zend/tests/bug41633_2.phpt @@ -10,5 +10,6 @@ echo Foo::A."\n"; --EXPECTF-- Fatal error: Uncaught Error: Undefined constant self::B in %s:%d Stack trace: -#0 {main} +#0 %s(%d): [constant expression]() +#1 {main} thrown in %sbug41633_2.php on line 3 diff --git a/Zend/tests/bug41633_3.phpt b/Zend/tests/bug41633_3.phpt index 4f78194a7e7a1..84f4c0c146760 100644 --- a/Zend/tests/bug41633_3.phpt +++ b/Zend/tests/bug41633_3.phpt @@ -11,5 +11,6 @@ echo Foo::A; --EXPECTF-- Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::B in %s:%d Stack trace: -#0 {main} +#0 %s(%d): [constant expression]() +#1 {main} thrown in %sbug41633_3.php on line %d diff --git a/Zend/tests/bug47572.phpt b/Zend/tests/bug47572.phpt index 1e424b350c4e9..4e3a99ccd5cad 100644 --- a/Zend/tests/bug47572.phpt +++ b/Zend/tests/bug47572.phpt @@ -16,5 +16,6 @@ $foo = new Foo(); --EXPECTF-- Fatal error: Uncaught Error: Undefined constant "FOO" in %s:%d Stack trace: -#0 {main} +#0 %s(%d): [constant expression]() +#1 {main} thrown in %s on line %d diff --git a/Zend/tests/bug74657.phpt b/Zend/tests/bug74657.phpt index 055501066b9f2..b024833f28a8b 100644 --- a/Zend/tests/bug74657.phpt +++ b/Zend/tests/bug74657.phpt @@ -21,5 +21,6 @@ var_dump((new C)->options); --EXPECTF-- Fatal error: Uncaught Error: Undefined constant I::FOO in %s:%d Stack trace: -#0 {main} +#0 %s(%d): [constant expression]() +#1 {main} thrown in %sbug74657.php on line %d diff --git a/Zend/tests/constant_expressions_self_referencing_array.phpt b/Zend/tests/constant_expressions_self_referencing_array.phpt index 214862071d74a..0c0436a456326 100644 --- a/Zend/tests/constant_expressions_self_referencing_array.phpt +++ b/Zend/tests/constant_expressions_self_referencing_array.phpt @@ -11,5 +11,6 @@ var_dump(A::FOO); --EXPECTF-- Fatal error: Uncaught Error: Cannot declare self-referencing constant self::BAR in %s:%d Stack trace: -#0 {main} +#0 %s(%d): [constant expression]() +#1 {main} thrown in %s on line %d diff --git a/Zend/tests/enum/offsetGet-in-const-expr.phpt b/Zend/tests/enum/offsetGet-in-const-expr.phpt index 0928235ac2e8b..bd74f6b564b21 100644 --- a/Zend/tests/enum/offsetGet-in-const-expr.phpt +++ b/Zend/tests/enum/offsetGet-in-const-expr.phpt @@ -25,5 +25,6 @@ var_dump(X::FOO_BAR); --EXPECTF-- Fatal error: Uncaught Error: Cannot use [] on objects in constant expression in %s:%d Stack trace: -#0 {main} +#0 %s(%d): [constant expression]() +#1 {main} thrown in %s on line %d diff --git a/Zend/tests/gh7771_1.phpt b/Zend/tests/gh7771_1.phpt index dc24b4041c716..eee6379e15f28 100644 --- a/Zend/tests/gh7771_1.phpt +++ b/Zend/tests/gh7771_1.phpt @@ -11,5 +11,6 @@ new Foo(); --EXPECTF-- Fatal error: Uncaught Error: Class "NonExistent" not found in %sgh7771_1_definition.inc:4 Stack trace: -#0 {main} +#0 %sgh7771_1.php(5): [constant expression]() +#1 {main} thrown in %sgh7771_1_definition.inc on line 4 diff --git a/Zend/tests/gh7771_2.phpt b/Zend/tests/gh7771_2.phpt index 780c970407715..c8d865ee106d5 100644 --- a/Zend/tests/gh7771_2.phpt +++ b/Zend/tests/gh7771_2.phpt @@ -11,5 +11,6 @@ new Foo(); --EXPECTF-- Fatal error: Uncaught Error: Class "NonExistent" not found in %sgh7771_2_definition.inc:6 Stack trace: -#0 {main} +#0 %sgh7771_2.php(5): [constant expression]() +#1 {main} thrown in %sgh7771_2_definition.inc on line 6 diff --git a/Zend/tests/gh8821.phpt b/Zend/tests/gh8821.phpt new file mode 100644 index 0000000000000..8f1b3ceff34ab --- /dev/null +++ b/Zend/tests/gh8821.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-8821: Fix reported line number of constant expression +--FILE-- + 3]; +} + +new Bravo(); + +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Illegal offset type in %sgh8821.php:8 +Stack trace: +#0 %sgh8821.php(11): [constant expression]() +#1 {main} + thrown in %sgh8821.php on line 8 diff --git a/Zend/tests/type_declarations/typed_properties_022.phpt b/Zend/tests/type_declarations/typed_properties_022.phpt index aeed60dcacca9..d6712df55f6cd 100644 --- a/Zend/tests/type_declarations/typed_properties_022.phpt +++ b/Zend/tests/type_declarations/typed_properties_022.phpt @@ -11,5 +11,6 @@ $foo = new Foo(); --EXPECTF-- Fatal error: Uncaught Error: Class "BAR" not found in %s:%d Stack trace: -#0 {main} +#0 %s(%d): [constant expression]() +#1 {main} thrown in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 70646856d3a11..faa806a600f6b 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -496,12 +496,43 @@ zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *scope) return zend_fetch_class_with_scope(zend_ast_get_str(ast), (ast->attr >> ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT) | ZEND_FETCH_CLASS_EXCEPTION, scope); } +ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( + zval *result, + zend_ast *ast, + zend_class_entry *scope, + bool *short_circuited_ptr, + zend_ast_evaluate_ctx *ctx +); + ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex( zval *result, zend_ast *ast, zend_class_entry *scope, bool *short_circuited_ptr, zend_ast_evaluate_ctx *ctx +) { + zend_string *previous_filename; + zend_long previous_lineno; + if (scope) { + previous_filename = EG(filename_override); + previous_lineno = EG(lineno_override); + EG(filename_override) = scope->info.user.filename; + EG(lineno_override) = zend_ast_get_lineno(ast); + } + zend_result r = zend_ast_evaluate_inner(result, ast, scope, short_circuited_ptr, ctx); + if (scope) { + EG(filename_override) = previous_filename; + EG(lineno_override) = previous_lineno; + } + return r; +} + +ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( + zval *result, + zend_ast *ast, + zend_class_entry *scope, + bool *short_circuited_ptr, + zend_ast_evaluate_ctx *ctx ) { zval op1, op2; zend_result ret = SUCCESS; @@ -1023,11 +1054,13 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) new->attr = ast->attr; ZVAL_COPY(&new->val, zend_ast_get_zval(ast)); buf = (void*)((char*)buf + sizeof(zend_ast_zval)); + // Lineno gets copied with ZVAL_COPY } else if (ast->kind == ZEND_AST_CONSTANT) { zend_ast_zval *new = (zend_ast_zval*)buf; new->kind = ZEND_AST_CONSTANT; new->attr = ast->attr; ZVAL_STR_COPY(&new->val, zend_ast_get_constant_name(ast)); + Z_LINENO(new->val) = zend_ast_get_lineno(ast); buf = (void*)((char*)buf + sizeof(zend_ast_zval)); } else if (zend_ast_is_list(ast)) { zend_ast_list *list = zend_ast_get_list(ast); @@ -1036,6 +1069,7 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) new->kind = list->kind; new->attr = list->attr; new->children = list->children; + new->lineno = list->lineno; buf = (void*)((char*)buf + zend_ast_list_size(list->children)); for (i = 0; i < list->children; i++) { if (list->child[i]) { diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index b5df2a55a2098..ca12afd6b2149 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -352,6 +352,9 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) { if (ast->kind == ZEND_AST_ZVAL) { zval *zv = zend_ast_get_zval(ast); return Z_LINENO_P(zv); + } else if (ast->kind == ZEND_AST_CONSTANT) { + zval *zv = &((zend_ast_zval *) ast)->val; + return Z_LINENO_P(zv); } else { return ast->lineno; } diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 66f196e5c82b9..90cfe3ec329ba 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1724,6 +1724,32 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int return; } + if (EG(filename_override)) { + // Add the current execution point to the frame so we don't lose it + zend_string *filename_override = EG(filename_override); + zend_long lineno_override = EG(lineno_override); + EG(filename_override) = NULL; + EG(lineno_override) = -1; + + zend_string *filename = zend_get_executed_filename_ex(); + zend_long lineno = zend_get_executed_lineno(); + if (filename && (!zend_string_equals(filename, filename_override) || lineno != lineno_override)) { + stack_frame = zend_new_array(8); + zend_hash_real_init_mixed(stack_frame); + ZVAL_STR_COPY(&tmp, filename); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1); + ZVAL_LONG(&tmp, lineno); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1); + ZVAL_STR_COPY(&tmp, ZSTR_KNOWN(ZEND_STR_CONST_EXPR_PLACEHOLDER)); + _zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1); + ZVAL_ARR(&tmp, stack_frame); + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); + } + + EG(filename_override) = filename_override; + EG(lineno_override) = lineno_override; + } + if (skip_last) { /* skip debug_backtrace() */ call = call->prev_execute_data; diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 7f38394ecbd40..d7bca020c3e19 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -594,6 +594,7 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_AUTOGLOBAL_REQUEST, "_REQUEST") \ _(ZEND_STR_COUNT, "count") \ _(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \ + _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ typedef enum _zend_known_string_id { diff --git a/ext/standard/tests/streams/bug77664.phpt b/ext/standard/tests/streams/bug77664.phpt index 4d9a2e0cb22a5..0a22175133335 100644 --- a/ext/standard/tests/streams/bug77664.phpt +++ b/ext/standard/tests/streams/bug77664.phpt @@ -12,6 +12,7 @@ file_get_contents('error://test'); --EXPECTF-- Fatal error: Uncaught Error: Undefined constant self::INVALID in %s:%d Stack trace: -#0 %sbug77664.php(%d): file_get_contents('error://test') -#1 {main} +#0 %s(%d): [constant expression]() +#1 %s(%d): file_get_contents('error://test') +#2 {main} thrown in %sbug77664.php on line %d diff --git a/tests/classes/constants_error_004.phpt b/tests/classes/constants_error_004.phpt index 3d675652ba241..bd7a3fe69381a 100644 --- a/tests/classes/constants_error_004.phpt +++ b/tests/classes/constants_error_004.phpt @@ -12,5 +12,6 @@ Class constant whose initial value references a non-existent class --EXPECTF-- Fatal error: Uncaught Error: Class "D" not found in %s:%d Stack trace: -#0 {main} +#0 %s(%d): [constant expression]() +#1 {main} thrown in %s on line %d