[php-src] Issue #8699: Wrong value from ini_get() for shared files because of opcache optimization
| From: | bix0r | Date: | Fri, 03 Jun 2022 12:49:17 +0000 |
| Subject: | [php-src] Issue #8699: Wrong value from ini_get() for shared files because of opcache optimization | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-241660@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/8699
Author: bix0r
### 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
```bash
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
```bash
mkdir /tmp/php-file-cache
```
Run the test
```bash
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](https://github.com/php/php-src/blob/7c28196a8a50884635c54d0ca4133fda16ea65bb/Zend/Optimizer/zend_optimizer.c#L184-L199),
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_