Skip to content

Add runtime type inference verification #12930

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/nightly_matrix.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function get_matrix_include(array $branches) {
'branch' => $branch,
'debug' => true,
'zts' => true,
'configuration_parameters' => "CFLAGS='-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1'",
'configuration_parameters' => "CFLAGS='-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE'",
'timeout_minutes' => 360,
'test_function_jit' => true,
'asan' => false,
Expand Down
37 changes: 20 additions & 17 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,16 @@ jobs:
fail-fast: false
matrix:
branch: ${{ fromJson(needs.GENERATE_MATRIX.outputs.branches) }}
name: "${{ matrix.branch.name }}_COMMUNITY"
type: ['asan', 'verify_type_inference']
# These branches don't include type verification
exclude:
- { branch: { name: 'PHP-8.1', ref: 'PHP-8.1', major: 8, minor: 1 }, type: 'verify_type_inference' }
- { branch: { name: 'PHP-8.2', ref: 'PHP-8.2', major: 8, minor: 2 }, type: 'verify_type_inference' }
- { branch: { name: 'PHP-8.3', ref: 'PHP-8.3', major: 8, minor: 3 }, type: 'verify_type_inference' }
name: "${{ matrix.branch.name }}_COMMUNITY_${{ matrix.type }}"
runs-on: ubuntu-${{ matrix.branch.version.minor >= 3 && '22.04' || '20.04' }}
env:
ASAN_OPTIONS: exitcode=139
UBSAN_OPTIONS: print_stacktrace=1
USE_ZEND_ALLOC: 0
USE_TRACKED_ALLOC: 1
Expand All @@ -365,11 +372,11 @@ jobs:
- name: ./configure
uses: ./.github/actions/configure-x64
with:
# CFLAGS removes O2, so we have to add it again...
configurationParameters: >-
--enable-debug
--enable-zts
CFLAGS='-fsanitize=undefined,address -fno-sanitize-recover -DZEND_TRACK_ARENA_ALLOC'
LDFLAGS='-fsanitize=undefined,address'
${{ matrix.type == 'asan' && '--enable-debug CFLAGS="-fsanitize=undefined,address -fno-sanitize-recover -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address"' || '' }}
${{ matrix.type == 'verify_type_inference' && 'CFLAGS="-DZEND_VERIFY_TYPE_INFERENCE -O2"' || '' }}
- name: make
run: make -j$(/usr/bin/nproc) >/dev/null
- name: make install
Expand All @@ -379,12 +386,20 @@ jobs:
sudo service mysql start
mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS test"
mysql -uroot -proot -e "SET GLOBAL local_infile = true"
- name: Enable Opcache and JIT
- name: Enable Opcache
run: |
echo memory_limit=-1 >> /etc/php.d/opcache.ini
echo zend_extension=opcache.so > /etc/php.d/opcache.ini
echo opcache.enable_cli=1 >> /etc/php.d/opcache.ini
echo opcache.enable=1 >> /etc/php.d/opcache.ini
echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini
echo opcache.memory_consumption=256M >> /etc/php.d/opcache.ini
echo opcache.file_update_protection=0 >> /etc/php.d/opcache.ini
echo opcache.interned_strings_buffer=64 >> /etc/php.d/opcache.ini
echo opcache.max_accelerated_files=100000 >> /etc/php.d/opcache.ini
- name: Enable JIT
if: matrix.type != 'verify_type_inference'
run: |
echo opcache.jit=tracing >> /etc/php.d/opcache.ini
echo opcache.jit_buffer_size=1G >> /etc/php.d/opcache.ini
echo opcache.jit_max_root_traces=100000 >> /etc/php.d/opcache.ini
Expand All @@ -394,11 +409,6 @@ jobs:
echo opcache.jit_hot_func=1 >> /etc/php.d/opcache.ini
echo opcache.jit_hot_return=1 >> /etc/php.d/opcache.ini
echo opcache.jit_hot_side_exit=1 >> /etc/php.d/opcache.ini
echo opcache.file_update_protection=0 >> /etc/php.d/opcache.ini
echo opcache.memory_consumption=256M >> /etc/php.d/opcache.ini
echo opcache.interned_strings_buffer=64 >> /etc/php.d/opcache.ini
echo opcache.max_accelerated_files=100000 >> /etc/php.d/opcache.ini
echo memory_limit=-1 >> /etc/php.d/opcache.ini
php -v
- name: Test AMPHP
run: |
Expand All @@ -410,7 +420,6 @@ jobs:
cd "amphp-$repository"
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
export ASAN_OPTIONS=exitcode=139
vendor/bin/phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
X=1;
Expand All @@ -426,7 +435,6 @@ jobs:
php /usr/bin/composer install --no-progress --ignore-platform-reqs
# Hack to disable a test that hangs
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);'
export ASAN_OPTIONS=exitcode=139
php vendor/bin/phpunit --exclude-group skip || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
exit 1
Expand All @@ -441,7 +449,6 @@ jobs:
cd "reactphp-$repository"
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
export ASAN_OPTIONS=exitcode=139
vendor/bin/phpunit || EXIT_CODE=$?
if [ $[EXIT_CODE:-0} -gt 128 ]; then
X=1;
Expand All @@ -455,7 +462,6 @@ jobs:
cd event-loop
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
export ASAN_OPTIONS=exitcode=139
vendor/bin/phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
exit 1
Expand All @@ -471,7 +477,6 @@ jobs:
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);'
# Buggy FFI test in Symfony, see https://github.com/symfony/symfony/issues/47668
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);'
export ASAN_OPTIONS=exitcode=139
export SYMFONY_DEPRECATIONS_HELPER=max[total]=999
X=0
for component in $(find src/Symfony -mindepth 2 -type f -name phpunit.xml.dist -printf '%h\n'); do
Expand All @@ -487,7 +492,6 @@ jobs:
git clone https://github.com/sebastianbergmann/phpunit.git --branch=main --depth=1
cd phpunit
git rev-parse HEAD
export ASAN_OPTIONS=exitcode=139
php /usr/bin/composer install --no-progress --ignore-platform-reqs
php ./phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
Expand All @@ -506,7 +510,6 @@ jobs:
git clone https://github.com/WordPress/wordpress-develop.git wordpress --depth=1
cd wordpress
git rev-parse HEAD
export ASAN_OPTIONS=exitcode=139
php /usr/bin/composer install --no-progress --ignore-platform-reqs
cp wp-tests-config-sample.php wp-tests-config.php
sed -i 's/youremptytestdbnamehere/test/g' wp-tests-config.php
Expand Down
165 changes: 160 additions & 5 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ permissions:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.url || github.run_id }}
cancel-in-progress: true
env:
CC: ccache gcc
CXX: ccache g++
# env:
# CC: ccache gcc
# CXX: ccache g++
Comment on lines -41 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a problem with ccache?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just for debugging. All changes from push.yml will be reverted before merging.

jobs:
LINUX_X64:
services:
Expand Down Expand Up @@ -121,7 +121,7 @@ jobs:
configurationParameters: >-
--${{ matrix.debug && 'enable' || 'disable' }}-debug
--${{ matrix.zts && 'enable' || 'disable' }}-zts
${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -fno-sanitize=pointer-overflow -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address -fno-sanitize=pointer-overflow" CC=clang-16 CXX=clang++-16' || '' }}
${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -fno-sanitize=pointer-overflow -DZEND_TRACK_ARENA_ALLOC -DZEND_VERIFY_TYPE_INFERENCE" LDFLAGS="-fsanitize=undefined,address -fno-sanitize=pointer-overflow" CC=clang-16 CXX=clang++-16' || '' }}
skipSlow: ${{ matrix.asan }}
- name: make
run: make -j$(/usr/bin/nproc) >/dev/null
Expand All @@ -148,6 +148,7 @@ jobs:
if: ${{ !matrix.asan }}
uses: ./.github/actions/verify-generated-files
MACOS_DEBUG_NTS:
if: false
runs-on: macos-13
steps:
- name: git checkout
Expand Down Expand Up @@ -180,6 +181,7 @@ jobs:
- name: Verify generated files are up to date
uses: ./.github/actions/verify-generated-files
WINDOWS:
if: false
name: WINDOWS_X64_ZTS
runs-on: windows-2019
env:
Expand All @@ -206,7 +208,8 @@ jobs:
run: .github/scripts/windows/test.bat
BENCHMARKING:
name: BENCHMARKING
if: github.repository_owner == 'php' || github.event_name == 'pull_request'
# if: github.repository_owner == 'php' || github.event_name == 'pull_request'
if: false
runs-on: ubuntu-22.04
steps:
- name: git checkout
Expand Down Expand Up @@ -305,3 +308,155 @@ jobs:
name: profiles
path: ${{ github.workspace }}/benchmark/profiles
retention-days: 30
COMMUNITY:
strategy:
fail-fast: false
matrix:
type: ['verify_type_inference']
name: "COMMUNITY_verify_type_inference"
runs-on: ubuntu-22.04
env:
ASAN_OPTIONS: exitcode=139
UBSAN_OPTIONS: print_stacktrace=1
USE_ZEND_ALLOC: 0
USE_TRACKED_ALLOC: 1
steps:
- name: git checkout
uses: actions/checkout@v4
- name: apt
uses: ./.github/actions/apt-x64
- name: ./configure
uses: ./.github/actions/configure-x64
with:
# CFLAGS removes O2, so we have to add it again...
configurationParameters: >-
--enable-zts
CFLAGS="-DZEND_VERIFY_TYPE_INFERENCE -O2"
- name: make
run: make -j$(/usr/bin/nproc) >/dev/null
- name: make install
uses: ./.github/actions/install-linux
- name: Setup
run: |
sudo service mysql start
mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS test"
mysql -uroot -proot -e "SET GLOBAL local_infile = true"
- name: Enable Opcache
run: |
echo memory_limit=-1 >> /etc/php.d/opcache.ini
echo zend_extension=opcache.so > /etc/php.d/opcache.ini
echo opcache.enable_cli=1 >> /etc/php.d/opcache.ini
echo opcache.enable=1 >> /etc/php.d/opcache.ini
echo opcache.memory_consumption=256M >> /etc/php.d/opcache.ini
echo opcache.file_update_protection=0 >> /etc/php.d/opcache.ini
echo opcache.interned_strings_buffer=64 >> /etc/php.d/opcache.ini
echo opcache.max_accelerated_files=100000 >> /etc/php.d/opcache.ini
- name: Test AMPHP
run: |
repositories="amp cache dns file http parallel parser pipeline process serialization socket sync websocket-client websocket-server"
X=0
for repository in $repositories; do
printf "Testing amp/%s\n" "$repository"
git clone "https://github.com/amphp/$repository.git" "amphp-$repository" --depth 1
cd "amphp-$repository"
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
vendor/bin/phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
X=1;
fi
cd ..
done
exit $X
- name: Test Laravel
run: |
git clone https://github.com/laravel/framework.git --branch=master --depth=1
cd framework
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
# Hack to disable a test that hangs
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);'
php vendor/bin/phpunit --exclude-group skip || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
exit 1
fi
- name: Test ReactPHP
run: |
repositories="async cache child-process datagram dns event-loop promise promise-stream promise-timer stream"
X=0
for repository in $repositories; do
printf "Testing reactphp/%s\n" "$repository"
git clone "https://github.com/reactphp/$repository.git" "reactphp-$repository" --depth 1
cd "reactphp-$repository"
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
vendor/bin/phpunit || EXIT_CODE=$?
if [ $[EXIT_CODE:-0} -gt 128 ]; then
X=1;
fi
cd ..
done
exit $X
- name: Test Revolt PHP
run: |
git clone https://github.com/revoltphp/event-loop.git --depth=1
cd event-loop
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
vendor/bin/phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
exit 1
fi
- name: Test Symfony
run: |
git clone https://github.com/symfony/symfony.git --depth=1
cd symfony
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
php ./phpunit install
# Test causes a heap-buffer-overflow but I cannot reproduce it locally...
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);'
# Buggy FFI test in Symfony, see https://github.com/symfony/symfony/issues/47668
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);'
export SYMFONY_DEPRECATIONS_HELPER=max[total]=999
X=0
for component in $(find src/Symfony -mindepth 2 -type f -name phpunit.xml.dist -printf '%h\n'); do
php ./phpunit $component --exclude-group tty,benchmark,intl-data,transient --exclude-group skip || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
X=1;
fi
done
exit $X
- name: Test PHPUnit
if: always()
run: |
git clone https://github.com/sebastianbergmann/phpunit.git --branch=main --depth=1
cd phpunit
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
php ./phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
exit 1
fi
- name: 'Symfony Preloading'
run: |
php /usr/bin/composer create-project symfony/symfony-demo symfony_demo --no-progress --ignore-platform-reqs
cd symfony_demo
git rev-parse HEAD
sed -i 's/PHP_SAPI/"cli-server"/g' var/cache/dev/App_KernelDevDebugContainer.preload.php
php -d opcache.preload=var/cache/dev/App_KernelDevDebugContainer.preload.php public/index.php
- name: Test Wordpress
if: always()
run: |
git clone https://github.com/WordPress/wordpress-develop.git wordpress --depth=1
cd wordpress
git rev-parse HEAD
php /usr/bin/composer install --no-progress --ignore-platform-reqs
cp wp-tests-config-sample.php wp-tests-config.php
sed -i 's/youremptytestdbnamehere/test/g' wp-tests-config.php
sed -i 's/yourusernamehere/root/g' wp-tests-config.php
sed -i 's/yourpasswordhere/root/g' wp-tests-config.php
php vendor/bin/phpunit || EXIT_CODE=$?
if [ $EXIT_CODE -gt 128 ]; then
exit 1
fi
20 changes: 20 additions & 0 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,26 @@ static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
}
break;
}
#ifdef ZEND_VERIFY_TYPE_INFERENCE
if (ssa_op->op1_use >= 0) {
opline->op1_use_type = ssa->var_info[ssa_op->op1_use].type;
}
if (ssa_op->op2_use >= 0) {
opline->op2_use_type = ssa->var_info[ssa_op->op2_use].type;
}
if (ssa_op->result_use >= 0) {
opline->result_use_type = ssa->var_info[ssa_op->result_use].type;
}
if (ssa_op->op1_def >= 0) {
opline->op1_def_type = ssa->var_info[ssa_op->op1_def].type;
}
if (ssa_op->op2_def >= 0) {
opline->op2_def_type = ssa->var_info[ssa_op->op2_def].type;
}
if (ssa_op->result_def >= 0) {
opline->result_def_type = ssa->var_info[ssa_op->result_def].type;
}
#endif
zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info);
opline++;
}
Expand Down
4 changes: 4 additions & 0 deletions Zend/tests/gh10168/wrong_assign_to_variable.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
--TEST--
GH-10168: Wrong assign to variable
--SKIPIF--
<?php
if (defined('ZEND_VERIFY_TYPE_INFERENCE')) die('skip Destructor side-effects violate type inference');
?>
--FILE--
<?php

Expand Down
8 changes: 8 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ static void init_op(zend_op *op)
MAKE_NOP(op);
op->extended_value = 0;
op->lineno = CG(zend_lineno);
#ifdef ZEND_VERIFY_TYPE_INFERENCE
op->op1_use_type = 0;
op->op2_use_type = 0;
op->result_use_type = 0;
op->op1_def_type = 0;
op->op2_def_type = 0;
op->result_def_type = 0;
#endif
}

static zend_always_inline uint32_t get_next_op_number(void)
Expand Down
Loading