From bd5e12c69f4f391219a0a570b073a84c6c59e382 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 14 Dec 2023 20:32:12 +0100 Subject: [PATCH] Fix GH-12953: SSA integrity verification failed when loading composer classmaps with more than 11k elements This is a false positive. The cycle detection code stops at 10.000 iterations. Instead of stopping at a fixed amount, make it more robust by implementing Floyd's cycle detection algorithm. --- Zend/Optimizer/ssa_integrity.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/Zend/Optimizer/ssa_integrity.c b/Zend/Optimizer/ssa_integrity.c index d92fc2e18d1f..a2de466669ab 100644 --- a/Zend/Optimizer/ssa_integrity.c +++ b/Zend/Optimizer/ssa_integrity.c @@ -122,7 +122,7 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex /* Vars */ for (i = 0; i < ssa->vars_count; i++) { zend_ssa_var *var = &ssa->vars[i]; - int use, c; + int use; uint32_t type = ssa->var_info[i].type; if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) { @@ -148,23 +148,43 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex } } - c = 0; - FOREACH_USE(var, use) { - if (++c > 10000) { + use = var->use_chain; + int second_use = use; + while (use >= 0 && second_use >= 0) { + use = zend_ssa_next_use(ssa->ops, var - ssa->vars, use); + second_use = zend_ssa_next_use(ssa->ops, var - ssa->vars, second_use); + if (second_use < 0) { + break; + } + second_use = zend_ssa_next_use(ssa->ops, var - ssa->vars, second_use); + if (use == second_use) { FAIL("cycle in uses of " VARFMT "\n", VAR(i)); goto finish; } + } + + FOREACH_USE(var, use) { if (!is_used_by_op(ssa, use, i)) { fprintf(stderr, "var " VARFMT " not in uses of op %d\n", VAR(i), use); } } FOREACH_USE_END(); - c = 0; - FOREACH_PHI_USE(var, phi) { - if (++c > 10000) { + phi = var->phi_use_chain; + zend_ssa_phi *second_phi = phi; + while (phi && second_phi) { + phi = zend_ssa_next_use_phi(ssa, var - ssa->vars, phi); + second_phi = zend_ssa_next_use_phi(ssa, var - ssa->vars, second_phi); + if (!second_phi) { + break; + } + second_phi = zend_ssa_next_use_phi(ssa, var - ssa->vars, second_phi); + if (phi == second_phi) { FAIL("cycle in phi uses of " VARFMT "\n", VAR(i)); goto finish; } + } + + FOREACH_PHI_USE(var, phi) { if (!is_in_phi_sources(ssa, phi, i)) { FAIL("var " VARFMT " not in phi sources of %d\n", VAR(i), phi->ssa_var); }