Wrong value from ini_get() for shared files because of opcache optimization
还没有人认领这个 Issue。
- 主要语言
- C
- 星标
- 40.4k
- 派生
- 8.2k
- 平均合并
- 2 天 13 小时
- 30 天内合并 PR
- 96
描述
Description
Opcache saves values from ini_get() for PHP_INI_SYSTEM type directives in the opcache when optimization is enabled.
This is a problem when a shared file uses ini_get() for PHP_INI_SYSTEM.
Example of this is a server with multiple virtual hosts where the sites use a common/shared framework.
The framework has an image uploading part where it fetches the upload_tmp_dir setting from ini to use a place for temporary image manipulations.
The virtual hosts are set up using PHP_ADMIN_VALUE to set a specific upload folder for each site.
fastcgi_param PHP_ADMIN_VALUE "upload_tmp_dir=/tmp/uploads/site1";
In this scenario the framework uses ini_get('upload_tmp_dir') to get the tmp folder value, but the value it gets is always the value for the first site that included that php file. So if site "foo.example.com" was first, then ALL other sites will get the upload_tmp_dir from "foo.example.com".
I reproduced this in all versions >=7.2, and for cli, fpm and mod_php.
Security
Not sure this counts as a security problem but this means that information could leak between sites.
Note that using PHP_ADMIN_VALUE for any option results in this problem.
Ways to reproduce:
Setup php files
mkdir /tmp/test/ && cd /tmp/test/
echo '<?php echo "S: " . ini_get("upload_tmp_dir") . "\n";' > shared.php
echo '<?php echo "1: " . ini_get("upload_tmp_dir") . "\n"; include "./shared.php";' > 1.php
echo '<?php echo "2: " . ini_get("upload_tmp_dir") . "\n"; include "./shared.php";' > 2.php
Cli
Create a cli-opcache.ini file in the "scan" additional .ini files directory:
zend_extension=opcache.so
[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.file_cache="/tmp/php-file-cache"
opcache.file_cache_only=1
opcache.file_cache_consistency_checks=1
Create file cache folder
mkdir /tmp/php-file-cache
Run the test
php -d upload_tmp_dir=/tmp/num1 /tmp/test/1.php
php -d upload_tmp_dir=/tmp/num2 /tmp/test/2.php
Expected result:
1: /tmp/num1
S: /tmp/num1
2: /tmp/num2
S: /tmp/num2
Actual result:
1: /tmp/num1
S: /tmp/num1
2: /tmp/num2
S: /tmp/num1
nginx + fpm
Make sure that opcache with optimizations is enabled.
Setup two virtual hosts and reload nginx:
server {
listen 80;
server_name p1;
root /tmp/test;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param PHP_ADMIN_VALUE "upload_tmp_dir=/tmp/cache1";
}
}
server {
listen 80;
server_name p2;
root /tmp/test;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param PHP_ADMIN_VALUE "upload_tmp_dir=/tmp/cache2";
}
}
Run the tests:
curl --resolve p1:80:127.0.0.1 http://p1/1.php
curl --resolve p2:80:127.0.0.1 http://p2/2.php
Expected result:
1: /tmp/cache1
S: /tmp/cache1
2: /tmp/cache2
S: /tmp/cache2
Actual result:
1: /tmp/cache1
S: /tmp/cache1
2: /tmp/cache2
S: /tmp/cache1
Workarounds
For me, I ended up with changing the shared framework, and now have to maintain my own fork of it.
You can turn off opcache optimizations.
Another way to work around this is to compile php yourself, with removing the ini_get if block, or even just
changing ini_get to ini_get_opcache_workaround in the zend_optimizer_eval_special_func_call function
in Zend/Optimizer/zend_optimizer.c.
PHP Version
7.2.0 - 8.1.6
Operating System
No response
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
首先阅读 Zend/Optimizer/zend_optimizer.c,尤其是 ini_get 条件和报告中提到的 zend_optimizer_eval_special_func_call。使用 opcache file caching 运行 CLI 复现,然后比较 shared-file 和 virtual-host 示例中的预期值与实际值。完成的标准是:PHP_INI_SYSTEM 值仍针对每个请求或主机保持独立,同时优化功能保持启用。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- c, php
- 领域
- backend, performance
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 停滞
- 描述清晰度
- 基本清楚
- 新手友好度
- 45/100