Skip to content

Commit 683d81e

Browse files
authored
Fix incorrect short-circuiting in constant expressions (#10030)
Fixes GH-10014
1 parent d4c804b commit 683d81e

File tree

4 files changed

+59
-36
lines changed

4 files changed

+59
-36
lines changed

Zend/tests/gh10014.phpt

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
GH-10014: Incorrect short-circuiting in constant expressions
3+
--FILE--
4+
<?php
5+
6+
#[Attribute(+[[][2]?->y]->y)]
7+
class y {
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Warning: Undefined array key 2 in %s on line %d
13+
14+
Warning: Attempt to read property "y" on array in %s on line %d

Zend/zend_ast.c

+42-33
Original file line numberDiff line numberDiff line change
@@ -498,17 +498,23 @@ zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *scope)
498498
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);
499499
}
500500

501-
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *ast, zend_class_entry *scope, zend_ast_evaluate_ctx *ctx)
502-
{
501+
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(
502+
zval *result,
503+
zend_ast *ast,
504+
zend_class_entry *scope,
505+
bool *short_circuited_ptr,
506+
zend_ast_evaluate_ctx *ctx
507+
) {
503508
zval op1, op2;
504509
zend_result ret = SUCCESS;
505-
ctx->short_circuited = false;
510+
bool short_circuited;
511+
*short_circuited_ptr = false;
506512

507513
switch (ast->kind) {
508514
case ZEND_AST_BINARY_OP:
509-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
515+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
510516
ret = FAILURE;
511-
} else if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, ctx) != SUCCESS)) {
517+
} else if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
512518
zval_ptr_dtor_nogc(&op1);
513519
ret = FAILURE;
514520
} else {
@@ -520,9 +526,9 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
520526
break;
521527
case ZEND_AST_GREATER:
522528
case ZEND_AST_GREATER_EQUAL:
523-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
529+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
524530
ret = FAILURE;
525-
} else if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, ctx) != SUCCESS)) {
531+
} else if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
526532
zval_ptr_dtor_nogc(&op1);
527533
ret = FAILURE;
528534
} else {
@@ -535,7 +541,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
535541
}
536542
break;
537543
case ZEND_AST_UNARY_OP:
538-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
544+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
539545
ret = FAILURE;
540546
} else {
541547
unary_op_type op = get_unary_op(ast->attr);
@@ -588,12 +594,12 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
588594
}
589595
break;
590596
case ZEND_AST_AND:
591-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
597+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
592598
ret = FAILURE;
593599
break;
594600
}
595601
if (zend_is_true(&op1)) {
596-
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, ctx) != SUCCESS)) {
602+
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
597603
zval_ptr_dtor_nogc(&op1);
598604
ret = FAILURE;
599605
break;
@@ -606,14 +612,14 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
606612
zval_ptr_dtor_nogc(&op1);
607613
break;
608614
case ZEND_AST_OR:
609-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
615+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
610616
ret = FAILURE;
611617
break;
612618
}
613619
if (zend_is_true(&op1)) {
614620
ZVAL_TRUE(result);
615621
} else {
616-
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, ctx) != SUCCESS)) {
622+
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
617623
zval_ptr_dtor_nogc(&op1);
618624
ret = FAILURE;
619625
break;
@@ -624,23 +630,23 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
624630
zval_ptr_dtor_nogc(&op1);
625631
break;
626632
case ZEND_AST_CONDITIONAL:
627-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
633+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
628634
ret = FAILURE;
629635
break;
630636
}
631637
if (zend_is_true(&op1)) {
632638
if (!ast->child[1]) {
633639
*result = op1;
634640
} else {
635-
if (UNEXPECTED(zend_ast_evaluate_ex(result, ast->child[1], scope, ctx) != SUCCESS)) {
641+
if (UNEXPECTED(zend_ast_evaluate_ex(result, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
636642
zval_ptr_dtor_nogc(&op1);
637643
ret = FAILURE;
638644
break;
639645
}
640646
zval_ptr_dtor_nogc(&op1);
641647
}
642648
} else {
643-
if (UNEXPECTED(zend_ast_evaluate_ex(result, ast->child[2], scope, ctx) != SUCCESS)) {
649+
if (UNEXPECTED(zend_ast_evaluate_ex(result, ast->child[2], scope, &short_circuited, ctx) != SUCCESS)) {
644650
zval_ptr_dtor_nogc(&op1);
645651
ret = FAILURE;
646652
break;
@@ -649,14 +655,14 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
649655
}
650656
break;
651657
case ZEND_AST_COALESCE:
652-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
658+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
653659
ret = FAILURE;
654660
break;
655661
}
656662
if (Z_TYPE(op1) > IS_NULL) {
657663
*result = op1;
658664
} else {
659-
if (UNEXPECTED(zend_ast_evaluate_ex(result, ast->child[1], scope, ctx) != SUCCESS)) {
665+
if (UNEXPECTED(zend_ast_evaluate_ex(result, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
660666
zval_ptr_dtor_nogc(&op1);
661667
ret = FAILURE;
662668
break;
@@ -665,7 +671,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
665671
}
666672
break;
667673
case ZEND_AST_UNARY_PLUS:
668-
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[0], scope, ctx) != SUCCESS)) {
674+
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
669675
ret = FAILURE;
670676
} else {
671677
ZVAL_LONG(&op1, 0);
@@ -674,7 +680,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
674680
}
675681
break;
676682
case ZEND_AST_UNARY_MINUS:
677-
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[0], scope, ctx) != SUCCESS)) {
683+
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
678684
ret = FAILURE;
679685
} else {
680686
ZVAL_LONG(&op1, 0);
@@ -695,7 +701,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
695701
for (i = 0; i < list->children; i++) {
696702
zend_ast *elem = list->child[i];
697703
if (elem->kind == ZEND_AST_UNPACK) {
698-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, elem->child[0], scope, ctx) != SUCCESS)) {
704+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, elem->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
699705
zval_ptr_dtor_nogc(result);
700706
return FAILURE;
701707
}
@@ -708,14 +714,14 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
708714
continue;
709715
}
710716
if (elem->child[1]) {
711-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, elem->child[1], scope, ctx) != SUCCESS)) {
717+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, elem->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
712718
zval_ptr_dtor_nogc(result);
713719
return FAILURE;
714720
}
715721
} else {
716722
ZVAL_UNDEF(&op1);
717723
}
718-
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, elem->child[0], scope, ctx) != SUCCESS)) {
724+
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, elem->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
719725
zval_ptr_dtor_nogc(&op1);
720726
zval_ptr_dtor_nogc(result);
721727
return FAILURE;
@@ -734,11 +740,12 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
734740
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading");
735741
}
736742

737-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
743+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
738744
ret = FAILURE;
739745
break;
740746
}
741-
if (ctx->short_circuited) {
747+
if (short_circuited) {
748+
*short_circuited_ptr = true;
742749
ZVAL_NULL(result);
743750
return SUCCESS;
744751
}
@@ -751,7 +758,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
751758
break;
752759
}
753760

754-
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, ctx) != SUCCESS)) {
761+
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
755762
zval_ptr_dtor_nogc(&op1);
756763
ret = FAILURE;
757764
break;
@@ -785,7 +792,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
785792
zval case_value_zv;
786793
ZVAL_UNDEF(&case_value_zv);
787794
if (case_value_ast != NULL) {
788-
if (UNEXPECTED(zend_ast_evaluate_ex(&case_value_zv, case_value_ast, scope, ctx) != SUCCESS)) {
795+
if (UNEXPECTED(zend_ast_evaluate_ex(&case_value_zv, case_value_ast, scope, &short_circuited, ctx) != SUCCESS)) {
789796
return FAILURE;
790797
}
791798
}
@@ -847,7 +854,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
847854
name = zend_ast_get_str(arg_ast->child[0]);
848855
arg_ast = arg_ast->child[1];
849856
}
850-
if (zend_ast_evaluate_ex(&arg, arg_ast, scope, ctx) == FAILURE) {
857+
if (zend_ast_evaluate_ex(&arg, arg_ast, scope, &short_circuited, ctx) == FAILURE) {
851858
zend_array_destroy(args);
852859
zval_ptr_dtor(result);
853860
return FAILURE;
@@ -877,7 +884,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
877884
ALLOCA_FLAG(use_heap)
878885
zval *args = do_alloca(sizeof(zval) * args_ast->children, use_heap);
879886
for (uint32_t i = 0; i < args_ast->children; i++) {
880-
if (zend_ast_evaluate_ex(&args[i], args_ast->child[i], scope, ctx) == FAILURE) {
887+
if (zend_ast_evaluate_ex(&args[i], args_ast->child[i], scope, &short_circuited, ctx) == FAILURE) {
881888
for (uint32_t j = 0; j < i; j++) {
882889
zval_ptr_dtor(&args[j]);
883890
}
@@ -909,20 +916,21 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
909916
case ZEND_AST_PROP:
910917
case ZEND_AST_NULLSAFE_PROP:
911918
{
912-
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, ctx) != SUCCESS)) {
919+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
913920
return FAILURE;
914921
}
915-
if (ctx->short_circuited) {
922+
if (short_circuited) {
923+
*short_circuited_ptr = true;
916924
ZVAL_NULL(result);
917925
return SUCCESS;
918926
}
919927
if (ast->kind == ZEND_AST_NULLSAFE_PROP && Z_TYPE(op1) == IS_NULL) {
920-
ctx->short_circuited = true;
928+
*short_circuited_ptr = true;
921929
ZVAL_NULL(result);
922930
return SUCCESS;
923931
}
924932

925-
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, ctx) != SUCCESS)) {
933+
if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) {
926934
zval_ptr_dtor_nogc(&op1);
927935
return FAILURE;
928936
}
@@ -976,7 +984,8 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *
976984
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope)
977985
{
978986
zend_ast_evaluate_ctx ctx = {0};
979-
return zend_ast_evaluate_ex(result, ast, scope, &ctx);
987+
bool short_circuited;
988+
return zend_ast_evaluate_ex(result, ast, scope, &short_circuited, &ctx);
980989
}
981990

982991
static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)

Zend/zend_ast.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,10 @@ ZEND_API zend_ast *zend_ast_create_decl(
300300

301301
typedef struct {
302302
bool had_side_effects;
303-
bool short_circuited;
304303
} zend_ast_evaluate_ctx;
305304

306305
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);
307-
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *ast, zend_class_entry *scope, zend_ast_evaluate_ctx *ctx);
306+
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);
308307
ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix);
309308

310309
ZEND_API zend_ast_ref * ZEND_FASTCALL zend_ast_copy(zend_ast *ast);

Zend/zend_execute_API.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -686,8 +686,9 @@ ZEND_API zend_result ZEND_FASTCALL zval_update_constant_with_ctx(zval *p, zend_c
686686
ZVAL_COPY_OR_DUP(p, zv);
687687
} else {
688688
zval tmp;
689+
bool short_circuited;
689690

690-
if (UNEXPECTED(zend_ast_evaluate_ex(&tmp, ast, scope, ctx) != SUCCESS)) {
691+
if (UNEXPECTED(zend_ast_evaluate_ex(&tmp, ast, scope, &short_circuited, ctx) != SUCCESS)) {
691692
return FAILURE;
692693
}
693694
zval_ptr_dtor_nogc(p);

0 commit comments

Comments
 (0)