From 7585cf6952e6f663fc2b3e822beab4fb2e92e4ef 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. Closes GH-12954. --- NEWS | 4 ++++ Zend/Optimizer/ssa_integrity.c | 36 +++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index 96b5ca80dcd..02d57ac8793 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.15 +- Core: + . Fixed bug GH-12953 (false positive SSA integrity verification failed when + loading composer classmaps with more than 11k elements). (nielsdos) + - Cli: . Fix incorrect timeout in built-in web server when using router script and max_input_time. (ilutov) diff --git a/Zend/Optimizer/ssa_integrity.c b/Zend/Optimizer/ssa_integrity.c index 321041d2418..6f8b879aa30 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,45 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex } } - c = 0; - FOREACH_USE(var, use) { - if (++c > 10000) { + /* Floyd's cycle detection algorithm, applied for use chain. */ + 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) { + /* Floyd's cycle detection algorithm, applied for phi nodes. */ + 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); }