]> granicus.if.org Git - php/commitdiff
Merge branch 'PHP-7.4'
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 26 Aug 2020 09:32:56 +0000 (11:32 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 26 Aug 2020 09:32:56 +0000 (11:32 +0200)
* PHP-7.4:
  Fix memory leak when yielding from non-iterable

1  2 
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

index a585a60fbf38c911b3b88fa650c77b8d53f0f04f,b8243f5c5709666a1a616b0bf84d528494278b18..1780f6bbe67ef42bb3c2b165d91069a786910697
@@@ -8062,11 -7904,9 +8062,12 @@@ ZEND_VM_C_LABEL(yield_from_try_again)
  
                        ZVAL_OBJ(&generator->values, &iter->std);
                }
 +      } else if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(val) == IS_REFERENCE) {
 +              val = Z_REFVAL_P(val);
 +              ZEND_VM_C_GOTO(yield_from_try_again);
        } else {
                zend_throw_error(NULL, "Can use \"yield from\" only with arrays and Traversables");
+               FREE_OP1();
                UNDEF_RESULT();
                HANDLE_EXCEPTION();
        }
index 6f0bb9197b603fd75fc7a06d89be0a34eb1fa76c,24fb84511417fb20066e5a38b518add09d00f274..34a90d00b0bd991aab681092a572002f162d1a44
@@@ -4540,11 -4481,9 +4540,12 @@@ yield_from_try_again
  
                        ZVAL_OBJ(&generator->values, &iter->std);
                }
 +      } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(val) == IS_REFERENCE) {
 +              val = Z_REFVAL_P(val);
 +              goto yield_from_try_again;
        } else {
                zend_throw_error(NULL, "Can use \"yield from\" only with arrays and Traversables");
                UNDEF_RESULT();
                HANDLE_EXCEPTION();
        }
@@@ -13626,111 -13731,6 +13627,112 @@@ static ZEND_OPCODE_HANDLER_RET ZEND_FAS
        ZEND_VM_NEXT_OPCODE();
  }
  
 +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 +{
 +      USE_OPLINE
 +      zend_generator *generator = zend_get_running_generator(EXECUTE_DATA_C);
 +      zval *val;
 +
 +      SAVE_OPLINE();
 +      val = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
 +
 +      if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) {
 +              zend_throw_error(NULL, "Cannot use \"yield from\" in a force-closed generator");
 +              zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
 +              UNDEF_RESULT();
 +              HANDLE_EXCEPTION();
 +      }
 +
 +yield_from_try_again:
 +      if (Z_TYPE_P(val) == IS_ARRAY) {
 +              ZVAL_COPY_VALUE(&generator->values, val);
 +              if (Z_OPT_REFCOUNTED_P(val)) {
 +                      Z_ADDREF_P(val);
 +              }
 +              Z_FE_POS(generator->values) = 0;
 +              zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
 +      } else if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val)->get_iterator) {
 +              zend_class_entry *ce = Z_OBJCE_P(val);
 +              if (ce == zend_ce_generator) {
 +                      zend_generator *new_gen = (zend_generator *) Z_OBJ_P(val);
 +
 +                      Z_ADDREF_P(val);
 +                      zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
 +
 +                      if (Z_ISUNDEF(new_gen->retval)) {
 +                              if (UNEXPECTED(zend_generator_get_current(new_gen) == generator)) {
 +                                      zend_throw_error(NULL, "Impossible to yield from the Generator being currently run");
 +                                      zval_ptr_dtor(val);
 +                                      UNDEF_RESULT();
 +                                      HANDLE_EXCEPTION();
 +                              } else {
 +                                      zend_generator_yield_from(generator, new_gen);
 +                              }
 +                      } else if (UNEXPECTED(new_gen->execute_data == NULL)) {
 +                              zend_throw_error(NULL, "Generator passed to yield from was aborted without proper return and is unable to continue");
 +                              zval_ptr_dtor(val);
 +                              UNDEF_RESULT();
 +                              HANDLE_EXCEPTION();
 +                      } else {
 +                              if (RETURN_VALUE_USED(opline)) {
 +                                      ZVAL_COPY(EX_VAR(opline->result.var), &new_gen->retval);
 +                              }
 +                              ZEND_VM_NEXT_OPCODE();
 +                      }
 +              } else {
 +                      zend_object_iterator *iter = ce->get_iterator(ce, val, 0);
 +                      zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
 +
 +                      if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
 +                              if (!EG(exception)) {
 +                                      zend_throw_error(NULL, "Object of type %s did not create an Iterator", ZSTR_VAL(ce->name));
 +                              }
 +                              UNDEF_RESULT();
 +                              HANDLE_EXCEPTION();
 +                      }
 +
 +                      iter->index = 0;
 +                      if (iter->funcs->rewind) {
 +                              iter->funcs->rewind(iter);
 +                              if (UNEXPECTED(EG(exception) != NULL)) {
 +                                      OBJ_RELEASE(&iter->std);
 +                                      UNDEF_RESULT();
 +                                      HANDLE_EXCEPTION();
 +                              }
 +                      }
 +
 +                      ZVAL_OBJ(&generator->values, &iter->std);
 +              }
 +      } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_TYPE_P(val) == IS_REFERENCE) {
 +              val = Z_REFVAL_P(val);
 +              goto yield_from_try_again;
 +      } else {
 +              zend_throw_error(NULL, "Can use \"yield from\" only with arrays and Traversables");
++              zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
 +              UNDEF_RESULT();
 +              HANDLE_EXCEPTION();
 +      }
 +
 +      /* This is the default return value
 +       * when the expression is a Generator, it will be overwritten in zend_generator_resume() */
 +      if (RETURN_VALUE_USED(opline)) {
 +              ZVAL_NULL(EX_VAR(opline->result.var));
 +      }
 +
 +      /* This generator has no send target (though the generator we delegate to might have one) */
 +      generator->send_target = NULL;
 +
 +      /* We increment to the next op, so we are at the correct position when the
 +       * generator is resumed. */
 +      ZEND_VM_INC_OPCODE();
 +
 +      /* The GOTO VM uses a local opline variable. We need to set the opline
 +       * variable in execute_data so we don't resume at an old position. */
 +      SAVE_OPLINE();
 +
 +      ZEND_VM_RETURN();
 +}
 +
  static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
  {
        USE_OPLINE
@@@ -37723,11 -38124,9 +37725,12 @@@ yield_from_try_again
  
                        ZVAL_OBJ(&generator->values, &iter->std);
                }
 +      } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(val) == IS_REFERENCE) {
 +              val = Z_REFVAL_P(val);
 +              goto yield_from_try_again;
        } else {
                zend_throw_error(NULL, "Can use \"yield from\" only with arrays and Traversables");
                UNDEF_RESULT();
                HANDLE_EXCEPTION();
        }