fix(php): 修复静态属性引用槽在动态引用绑定后的有效性问题

- 实现了对原生类型静态属性的引用管理机制
- 为整型和浮点型静态属性创建稳定的 zval* 槽位
- 添加了静态属性引用信息的条件检查和初始化逻辑
- 更新了静态属性访问的帮助函数选择机制
- 修改了变量声明生成以统一使用 zval* 类型
- 优化了头文件包含的代码生成格式
- 添加了相关的单元测试验证对象属性操作收集
- 新增了静态属性引用槽崩溃场景的测试用例
pull/3/head
韩天峰 2 months ago
parent ad617bcac8
commit e4e63e7b8c
  1. 14
      phpunit/src/SsaAnalysisTest.php
  2. 20
      src/Php/CompilerBase.php
  3. 2
      src/Php/Context/FunctionContext.php
  4. 2
      src/Php/Translator.php
  5. 26
      tests/aot/static/static-prop-ref-slot-crash.phpt
  6. 24
      tests/aot/static/static-prop-ref-slot-float-crash.phpt

@ -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'))]);

@ -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;

@ -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;
/**

@ -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

@ -0,0 +1,26 @@
--TEST--
Static property native slot becomes invalid after dynamic reference binding
--FILE--
<?php
use native_types;
class StaticRefSlotCrash {
public static int $i = 1;
}
function main(): void {
StaticRefSlotCrash::$i = 12;
var_dump(StaticRefSlotCrash::$i);
eval('function bind_static_ref(): void { $ref =& StaticRefSlotCrash::$i; $ref = 99; }');
bind_static_ref();
var_dump(StaticRefSlotCrash::$i);
StaticRefSlotCrash::$i += 1;
var_dump(StaticRefSlotCrash::$i);
}
?>
--EXPECT--
int(12)
int(99)
int(100)

@ -0,0 +1,24 @@
--TEST--
Static float property native slot becomes invalid after dynamic reference binding
--FILE--
<?php
use native_types;
class StaticFloatRefSlotCrash {
public static float $f = 1.5;
}
function main(): void {
StaticFloatRefSlotCrash::$f = 1.5;
eval('function bind_static_float_ref(): void { $ref =& StaticFloatRefSlotCrash::$f; $ref = 9.5; }');
bind_static_float_ref();
var_dump(StaticFloatRefSlotCrash::$f);
StaticFloatRefSlotCrash::$f += 0.5;
var_dump(StaticFloatRefSlotCrash::$f);
}
?>
--EXPECT--
float(9.5)
float(10)
Loading…
Cancel
Save