Skip to content

Commit ffc250d

Browse files
committed
Add runtime type inference verification
Co-authored-by: Dmitry Stogov <dmitry@zend.com> Closes GH-12930
1 parent bf4ec8b commit ffc250d

File tree

12 files changed

+1302
-20
lines changed

12 files changed

+1302
-20
lines changed

.github/nightly_matrix.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function get_matrix_include(array $branches) {
6363
'branch' => $branch,
6464
'debug' => true,
6565
'zts' => true,
66-
'configuration_parameters' => "CFLAGS='-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1'",
66+
'configuration_parameters' => "CFLAGS='-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE'",
6767
'timeout_minutes' => 360,
6868
'test_function_jit' => true,
6969
'asan' => false,

.github/workflows/nightly.yml

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -349,9 +349,16 @@ jobs:
349349
fail-fast: false
350350
matrix:
351351
branch: ${{ fromJson(needs.GENERATE_MATRIX.outputs.branches) }}
352-
name: "${{ matrix.branch.name }}_COMMUNITY"
352+
type: ['asan', 'verify_type_inference']
353+
# These branches don't include type verification
354+
exclude:
355+
- { branch: { name: 'PHP-8.1', ref: 'PHP-8.1', major: 8, minor: 1 }, type: 'verify_type_inference' }
356+
- { branch: { name: 'PHP-8.2', ref: 'PHP-8.2', major: 8, minor: 2 }, type: 'verify_type_inference' }
357+
- { branch: { name: 'PHP-8.3', ref: 'PHP-8.3', major: 8, minor: 3 }, type: 'verify_type_inference' }
358+
name: "${{ matrix.branch.name }}_COMMUNITY_${{ matrix.type }}"
353359
runs-on: ubuntu-${{ matrix.branch.version.minor >= 3 && '22.04' || '20.04' }}
354360
env:
361+
ASAN_OPTIONS: exitcode=139
355362
UBSAN_OPTIONS: print_stacktrace=1
356363
USE_ZEND_ALLOC: 0
357364
USE_TRACKED_ALLOC: 1
@@ -365,11 +372,11 @@ jobs:
365372
- name: ./configure
366373
uses: ./.github/actions/configure-x64
367374
with:
375+
# CFLAGS removes O2, so we have to add it again...
368376
configurationParameters: >-
369-
--enable-debug
370377
--enable-zts
371-
CFLAGS='-fsanitize=undefined,address -fno-sanitize-recover -DZEND_TRACK_ARENA_ALLOC'
372-
LDFLAGS='-fsanitize=undefined,address'
378+
${{ matrix.type == 'asan' && '--enable-debug CFLAGS="-fsanitize=undefined,address -fno-sanitize-recover -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address"' || '' }}
379+
${{ matrix.type == 'verify_type_inference' && 'CFLAGS="-DZEND_VERIFY_TYPE_INFERENCE -O2"' || '' }}
373380
- name: make
374381
run: make -j$(/usr/bin/nproc) >/dev/null
375382
- name: make install
@@ -379,12 +386,20 @@ jobs:
379386
sudo service mysql start
380387
mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS test"
381388
mysql -uroot -proot -e "SET GLOBAL local_infile = true"
382-
- name: Enable Opcache and JIT
389+
- name: Enable Opcache
383390
run: |
391+
echo memory_limit=-1 >> /etc/php.d/opcache.ini
384392
echo zend_extension=opcache.so > /etc/php.d/opcache.ini
385393
echo opcache.enable_cli=1 >> /etc/php.d/opcache.ini
386394
echo opcache.enable=1 >> /etc/php.d/opcache.ini
387395
echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini
396+
echo opcache.memory_consumption=256M >> /etc/php.d/opcache.ini
397+
echo opcache.file_update_protection=0 >> /etc/php.d/opcache.ini
398+
echo opcache.interned_strings_buffer=64 >> /etc/php.d/opcache.ini
399+
echo opcache.max_accelerated_files=100000 >> /etc/php.d/opcache.ini
400+
- name: Enable JIT
401+
if: matrix.type != 'verify_type_inference'
402+
run: |
388403
echo opcache.jit=tracing >> /etc/php.d/opcache.ini
389404
echo opcache.jit_buffer_size=1G >> /etc/php.d/opcache.ini
390405
echo opcache.jit_max_root_traces=100000 >> /etc/php.d/opcache.ini
@@ -394,11 +409,6 @@ jobs:
394409
echo opcache.jit_hot_func=1 >> /etc/php.d/opcache.ini
395410
echo opcache.jit_hot_return=1 >> /etc/php.d/opcache.ini
396411
echo opcache.jit_hot_side_exit=1 >> /etc/php.d/opcache.ini
397-
echo opcache.file_update_protection=0 >> /etc/php.d/opcache.ini
398-
echo opcache.memory_consumption=256M >> /etc/php.d/opcache.ini
399-
echo opcache.interned_strings_buffer=64 >> /etc/php.d/opcache.ini
400-
echo opcache.max_accelerated_files=100000 >> /etc/php.d/opcache.ini
401-
echo memory_limit=-1 >> /etc/php.d/opcache.ini
402412
php -v
403413
- name: Test AMPHP
404414
run: |
@@ -410,7 +420,6 @@ jobs:
410420
cd "amphp-$repository"
411421
git rev-parse HEAD
412422
php /usr/bin/composer install --no-progress --ignore-platform-reqs
413-
export ASAN_OPTIONS=exitcode=139
414423
vendor/bin/phpunit || EXIT_CODE=$?
415424
if [ ${EXIT_CODE:-0} -gt 128 ]; then
416425
X=1;
@@ -426,7 +435,6 @@ jobs:
426435
php /usr/bin/composer install --no-progress --ignore-platform-reqs
427436
# Hack to disable a test that hangs
428437
php -r '$c = file_get_contents("tests/Filesystem/FilesystemTest.php"); $c = str_replace("public function testSharedGet()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testSharedGet()", $c); file_put_contents("tests/Filesystem/FilesystemTest.php", $c);'
429-
export ASAN_OPTIONS=exitcode=139
430438
php vendor/bin/phpunit --exclude-group skip || EXIT_CODE=$?
431439
if [ ${EXIT_CODE:-0} -gt 128 ]; then
432440
exit 1
@@ -441,7 +449,6 @@ jobs:
441449
cd "reactphp-$repository"
442450
git rev-parse HEAD
443451
php /usr/bin/composer install --no-progress --ignore-platform-reqs
444-
export ASAN_OPTIONS=exitcode=139
445452
vendor/bin/phpunit || EXIT_CODE=$?
446453
if [ $[EXIT_CODE:-0} -gt 128 ]; then
447454
X=1;
@@ -455,7 +462,6 @@ jobs:
455462
cd event-loop
456463
git rev-parse HEAD
457464
php /usr/bin/composer install --no-progress --ignore-platform-reqs
458-
export ASAN_OPTIONS=exitcode=139
459465
vendor/bin/phpunit || EXIT_CODE=$?
460466
if [ ${EXIT_CODE:-0} -gt 128 ]; then
461467
exit 1
@@ -471,7 +477,6 @@ jobs:
471477
php -r '$c = file_get_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php"); $c = str_replace("public function testSanitizeDeepNestedString()", "/** @group skip */\n public function testSanitizeDeepNestedString()", $c); file_put_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php", $c);'
472478
# Buggy FFI test in Symfony, see https://github.com/symfony/symfony/issues/47668
473479
php -r '$c = file_get_contents("src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php"); $c = str_replace("*/\n public function testCastNonTrailingCharPointer()", "* @group skip\n */\n public function testCastNonTrailingCharPointer()", $c); file_put_contents("src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php", $c);'
474-
export ASAN_OPTIONS=exitcode=139
475480
export SYMFONY_DEPRECATIONS_HELPER=max[total]=999
476481
X=0
477482
for component in $(find src/Symfony -mindepth 2 -type f -name phpunit.xml.dist -printf '%h\n'); do
@@ -487,7 +492,6 @@ jobs:
487492
git clone https://github.com/sebastianbergmann/phpunit.git --branch=main --depth=1
488493
cd phpunit
489494
git rev-parse HEAD
490-
export ASAN_OPTIONS=exitcode=139
491495
php /usr/bin/composer install --no-progress --ignore-platform-reqs
492496
php ./phpunit || EXIT_CODE=$?
493497
if [ ${EXIT_CODE:-0} -gt 128 ]; then
@@ -506,7 +510,6 @@ jobs:
506510
git clone https://github.com/WordPress/wordpress-develop.git wordpress --depth=1
507511
cd wordpress
508512
git rev-parse HEAD
509-
export ASAN_OPTIONS=exitcode=139
510513
php /usr/bin/composer install --no-progress --ignore-platform-reqs
511514
cp wp-tests-config-sample.php wp-tests-config.php
512515
sed -i 's/youremptytestdbnamehere/test/g' wp-tests-config.php

Zend/Optimizer/zend_optimizer.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,26 @@ static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
13321332
}
13331333
break;
13341334
}
1335+
#ifdef ZEND_VERIFY_TYPE_INFERENCE
1336+
if (ssa_op->op1_use >= 0) {
1337+
opline->op1_use_type = ssa->var_info[ssa_op->op1_use].type;
1338+
}
1339+
if (ssa_op->op2_use >= 0) {
1340+
opline->op2_use_type = ssa->var_info[ssa_op->op2_use].type;
1341+
}
1342+
if (ssa_op->result_use >= 0) {
1343+
opline->result_use_type = ssa->var_info[ssa_op->result_use].type;
1344+
}
1345+
if (ssa_op->op1_def >= 0) {
1346+
opline->op1_def_type = ssa->var_info[ssa_op->op1_def].type;
1347+
}
1348+
if (ssa_op->op2_def >= 0) {
1349+
opline->op2_def_type = ssa->var_info[ssa_op->op2_def].type;
1350+
}
1351+
if (ssa_op->result_def >= 0) {
1352+
opline->result_def_type = ssa->var_info[ssa_op->result_def].type;
1353+
}
1354+
#endif
13351355
zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info);
13361356
opline++;
13371357
}

Zend/tests/gh10168/wrong_assign_to_variable.phpt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
--TEST--
22
GH-10168: Wrong assign to variable
3+
--SKIPIF--
4+
<?php
5+
if (defined('ZEND_VERIFY_TYPE_INFERENCE')) die('skip Destructor side-effects violate type inference');
6+
?>
37
--FILE--
48
<?php
59

Zend/zend_compile.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ static void init_op(zend_op *op)
124124
MAKE_NOP(op);
125125
op->extended_value = 0;
126126
op->lineno = CG(zend_lineno);
127+
#ifdef ZEND_VERIFY_TYPE_INFERENCE
128+
op->op1_use_type = 0;
129+
op->op2_use_type = 0;
130+
op->result_use_type = 0;
131+
op->op1_def_type = 0;
132+
op->op2_def_type = 0;
133+
op->result_def_type = 0;
134+
#endif
127135
}
128136

129137
static zend_always_inline uint32_t get_next_op_number(void)

Zend/zend_compile.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ struct _zend_op {
143143
uint8_t op1_type; /* IS_UNUSED, IS_CONST, IS_TMP_VAR, IS_VAR, IS_CV */
144144
uint8_t op2_type; /* IS_UNUSED, IS_CONST, IS_TMP_VAR, IS_VAR, IS_CV */
145145
uint8_t result_type; /* IS_UNUSED, IS_CONST, IS_TMP_VAR, IS_VAR, IS_CV */
146+
#ifdef ZEND_VERIFY_TYPE_INFERENCE
147+
uint32_t op1_use_type;
148+
uint32_t op2_use_type;
149+
uint32_t result_use_type;
150+
uint32_t op1_def_type;
151+
uint32_t op2_def_type;
152+
uint32_t result_def_type;
153+
#endif
146154
};
147155

148156

Zend/zend_execute.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4639,6 +4639,16 @@ static void zend_swap_operands(zend_op *op) /* {{{ */
46394639
op->op1_type = op->op2_type;
46404640
op->op2 = tmp;
46414641
op->op2_type = tmp_type;
4642+
4643+
#ifdef ZEND_VERIFY_TYPE_INFERENCE
4644+
uint32_t tmp_info;
4645+
tmp_info = op->op1_use_type;
4646+
op->op1_use_type = op->op2_use_type;
4647+
op->op2_use_type = tmp_info;
4648+
tmp_info = op->op1_def_type;
4649+
op->op1_def_type = op->op2_def_type;
4650+
op->op2_def_type = tmp_info;
4651+
#endif
46424652
}
46434653
/* }}} */
46444654
#endif
@@ -5301,6 +5311,8 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint
53015311
# include "zend_vm_trace_lines.h"
53025312
#elif defined(ZEND_VM_TRACE_MAP)
53035313
# include "zend_vm_trace_map.h"
5314+
#elif defined(ZEND_VERIFY_TYPE_INFERENCE)
5315+
# include "zend_verify_type_inference.h"
53045316
#endif
53055317

53065318
#define ZEND_VM_NEXT_OPCODE_EX(check_exception, skip) \

0 commit comments

Comments
 (0)