diff --git a/phpunit/src/SsaAnalysisTest.php b/phpunit/src/SsaAnalysisTest.php index cec5703a..f10f710d 100644 --- a/phpunit/src/SsaAnalysisTest.php +++ b/phpunit/src/SsaAnalysisTest.php @@ -832,6 +832,20 @@ class SsaAnalysisTest extends TestCase $this->assertSame(['a' => true], $result); } + public function testCollectDangerousPropOpsPropertyArgumentDoesNotExposeObject(): void + { + $propFetch = new Expr\PropertyFetch(new Expr\Variable('obj'), 'a'); + $funcCall = new Expr\FuncCall(new Node\Name('mutate'), [new Arg($propFetch)]); + $stmt = new Stmt\Expression($funcCall); + $read = new Stmt\Expression(new Expr\Assign( + new Expr\Variable('value'), + new Expr\PropertyFetch(new Expr\Variable('obj'), 'a') + )); + + $result = $this->invoke('collectDangerousPropOps', 'obj', [$stmt, $read]); + $this->assertSame([], $result, 'Passing a property value by value does not expose the owning object'); + } + public function testCollectDangerousPropOpsInternalFunctionObjectArgumentIsSafe(): void { $funcCall = new Expr\FuncCall(new Node\Name('gettype'), [new Arg(new Expr\Variable('obj'))]); diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 6a9ba502..6cda5c9a 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -4937,10 +4937,25 @@ class CompilerBase extends \PhpAot\Core\Translator if ($this->nativeTypes && $expr->hasAttribute('nativePropertyDef')) { /** @var PropertyDef $def */ $def = $expr->getAttribute('nativePropertyDef'); + $info = $this->getHoistedObjectPropInfo($def->type); $propName = $this->parseIdentifier($expr->name); $refVar = '_static_' . str_replace('\\', '_', $class) . '_' . $propName; + + if ($info['kind'] === 'zval') { + if (!isset($this->context->staticPropRefs[$refVar])) { + $classPtr = $this->getClassEntryPtr($class); + $this->context->staticPropRefs[$refVar] = [ + 'type' => $info['type'], + 'classPtr' => $classPtr, + 'offsetExpr' => $nativeProp, + 'kind' => $info['kind'], + ]; + } + $helper = $def->type === self::TYPE_FLOAT ? 'php_aot_static_float_ref' : 'php_aot_static_int_ref'; + return $helper . '(' . $refVar . ')'; + } + if (!isset($this->context->staticPropRefs[$refVar])) { - $info = $this->getHoistedObjectPropInfo($def->type); $classPtr = $this->getClassEntryPtr($class); $this->context->staticPropRefs[$refVar] = [ 'type' => $info['type'], @@ -5695,8 +5710,7 @@ class CompilerBase extends \PhpAot\Core\Translator if (($info['kind'] ?? 'zval') === 'var') { $code .= $this->getIndent() . self::TYPE_VAR . ' ' . $name . ' = ' . $getter . ';' . PHP_EOL; } else { - $zvalMacro = ($info['type'] === self::TYPE_FLOAT) ? 'Z_DVAL_P' : 'Z_LVAL_P'; - $code .= $this->getIndent() . $info['type'] . ' &' . $name . ' = ' . $zvalMacro . '(' . $getter . '.unwrap_ptr());' . PHP_EOL; + $code .= $this->getIndent() . 'zval *' . $name . ' = ' . $getter . '.unwrap_ptr();' . PHP_EOL; } } return $code; diff --git a/src/Php/Context/FunctionContext.php b/src/Php/Context/FunctionContext.php index 0c0d5ed5..342157a6 100644 --- a/src/Php/Context/FunctionContext.php +++ b/src/Php/Context/FunctionContext.php @@ -64,7 +64,7 @@ class FunctionContext public array $beforeStmtLines = []; public array $afterStmtLines = []; public array $objectProps; - /** Map of static property local slots. int/float use zval refs; other types use Var slots. */ + /** Map of static property local slots. int/float keep stable zval* slots; other types use Var slots. */ public array $staticPropRefs = []; public int $scopeLevel = 0; /** diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 38e01484..70747305 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -1663,7 +1663,7 @@ CODE; $lines[] = '#include <' . $header . '>'; } - return implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL; + return implode(PHP_EOL, $lines) . PHP_EOL; } public function genClassPropertyInit(): string diff --git a/tests/aot/static/static-prop-ref-slot-crash.phpt b/tests/aot/static/static-prop-ref-slot-crash.phpt new file mode 100644 index 00000000..e5ac68e3 --- /dev/null +++ b/tests/aot/static/static-prop-ref-slot-crash.phpt @@ -0,0 +1,26 @@ +--TEST-- +Static property native slot becomes invalid after dynamic reference binding +--FILE-- + +--EXPECT-- +int(12) +int(99) +int(100) diff --git a/tests/aot/static/static-prop-ref-slot-float-crash.phpt b/tests/aot/static/static-prop-ref-slot-float-crash.phpt new file mode 100644 index 00000000..ec5eb78c --- /dev/null +++ b/tests/aot/static/static-prop-ref-slot-float-crash.phpt @@ -0,0 +1,24 @@ +--TEST-- +Static float property native slot becomes invalid after dynamic reference binding +--FILE-- + +--EXPECT-- +float(9.5) +float(10)