Skip to content
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

PHP8.3 unserialize(), double values in Zend_Cache meta files #420

Open
C1977 opened this issue May 17, 2024 · 1 comment
Open

PHP8.3 unserialize(), double values in Zend_Cache meta files #420

C1977 opened this issue May 17, 2024 · 1 comment

Comments

@C1977
Copy link

C1977 commented May 17, 2024

Hello,

we use PHP8.3 and framework 1.24 but only components: MCV, Route, Zend_Cache and very successfull.
But frequently we have one problem. It looks like that Zend_Cache sometime, not reproducable, fills the meta-values double.
Example:

a:4:{s:4:"hash";i:1089196444;s:5:"mtime";i:1715922337;s:6:"expire";i:1715923537;s:4:"tags";a:0:{}}a:4:{s:4:"hash";i:1089196444;s:5:"mtime";i:1715922337;s:6:"expire";i:1715923537;s:4:"tags";a:0:{}}

this is exactly double. And beginning with PHP8.2 the bevahiour if unserialize has changed. Trailing values can result in a warning:
https://wiki.php.net/rfc/unserialize_warn_on_trailing_data

But I can't find the problem in Zend/Cache/Backend/File.php
It causes errors like: unserialize(): Extra data starting at offset 98 of 196 bytes

or I think it has also to do with file-errors like:
unlink(/var/www/cache/shoplinkbar/760/10/zend_cache---760_it_1002652......

Has anyone an idea or ran into same problem?

@rruchte
Copy link

rruchte commented May 17, 2024

From what I can see, this is likely a concurrency issue that occurs while metadata is being written in the _filePutContents function.

The file is opened for writing using the a+ mode, so simultaneous writes will result in the content from multiple threads being appended. I'm a bit confused about what's happening in the _filePutContents function:

if ($this->_options['file_locking']) {
    @flock($f, LOCK_EX);
}
fseek($f, 0);
ftruncate($f, 0);
$tmp = @fwrite($f, $string);

It looks like the fseek call is an attempt to set the pointer to the beginning of the file, but that only applies to reading in a+ mode, so it will not be doing anything useful here. The file is then truncated, then written to. If flock is disabled in the options, or does not work on the system, and the timing is just right, simultaneous writes from different threads will definitely result in the doubling of the content.

My guess is that the intention here was to fail as gracefully as possible when flock is disabled or does not work on the system, with the worst-case scenario being the double write that you're seeing, which until the "warn on trailing data" change, was acceptable.

I'm not sure if there is anything that can be done to make the writing more bulletproof, flock just doesn't work on 100% of systems. The answer may be to do some processing on the read side, but that would incur a performance penalty.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants