-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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
phar:// tar parser and zero-length file header blocks #16695
Comments
It looks like that if not at least a header sized (512 bytes) Lines 594 to 606 in f37fd7f
as it is then at the end of the file and the actual As the loop is long (starts 254) still looking for the actual exit condition with the 512 NUL bytes added which works fine, so take this initial analysis with a grain of salt. |
Well, the first bug seems to be a classic off-by-one check, i.e. |
There are two issues: 1) There's an off-by-one in the check for the minimum file size for a tar (i.e. `>` instead of `>=`). 2) The loop in the tar parsing parses a header, and then unconditionally reads the next one. However, that doesn't necessarily exist. Instead, we remove the loop condition and check for the end of the file before reading the next header. Note that we can't use php_stream_eof as the flag may not be set yet when we're already at the end.
Please check #16700, it should fix the issues for you. |
Makes sense, was not so much concerned about that one yet thought as the second issue was my concrete issue (so the first).
Yes, starring at the code over the day. Started to compile locally to test a bit. Unfortunately having the problem that somehow |
|
Wow you already pushed a patch, nice! @nielsdos For the reproducers, the 3*512 bytes one is doing too much which I learned later. The last 512 bytes are the header of the entry named // header block of a file with 122 bytes, from an example "report.tar"
// $ tar -t -vf report.tar
// -rw-r--r-- 0/0 122 1970-01-01 01:00 meta.json
$buffer = base64_decode('bWV0YS5qc29uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDA2NDQAMDAwMDAwMAAwMDAwMDAwADAwMDAwMDAwMTcyADAwMDAwMDAwMDAwADAxMTAyNgAgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhcgAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwADAwMDAwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB7Ik5hbWUiOiJkZWZhdWx0IiwiTWV0YWRhdGEiOnt9LCJFbmRwb2ludHMiOnsiZG9ja2VyIjp7Ikhvc3QiOiJ1bml4Oi8vL3J1bi91c2VyLzEwMDAvZG9ja2VyLnNvY2siLCJTa2lwVExTVmVyaWZ5IjpmYWxzZX19fQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=='); And thinking: A good third test would be a tar consisting of a single header with an empty file, so the overall tar is just 512 bytes long but file_get_contents() of it should work. |
Thanks! Found the culprit, it is the following setting:
Have to check buildconf or configure parameters to change it to it's own location, at least assuming so @nielsdos |
There are two issues: 1) There's an off-by-one in the check for the minimum file size for a tar (i.e. `>` instead of `>=`). 2) The loop in the tar parsing parses a header, and then unconditionally reads the next one. However, that doesn't necessarily exist. Instead, we remove the loop condition and check for the end of the file before reading the next header. Note that we can't use php_stream_eof as the flag may not be set yet when we're already at the end.
Done, thanks!
Indeed, I've modified the first tar file by hand to create an extra test variation. There are now three tests.
If you want to change the INI location of your build, pass |
Found it already via $ sapi/cli/php -i | grep -F ini | head -n 1
Configuration File (php.ini) Path => /usr/local/lib/php/master/cli Thanks a ton @nielsdos Now double cross fingers |
Local testing looks good so far. Both for compiling and concrete scripts with the previously broken functionality. I'm only seeing untracked files on the php 8.2 branch, .gitignore in master seems more up-to-date. How are backports done in this regard? Finally I also have some minor changes for the buildconf script waiting to be cherry-picked. Should I file those again 8.2 first as well?
# Go to project root.
-cd $(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
+cd "$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)" || exit
-php_extra_version=$(grep '^AC_INIT(' configure.ac)
+php_extra_version=$(grep '^AC_INIT(' configure.ac) || exit
case "$php_extra_version" in
*-dev*)
|
Not sure I fully understand the question. Our pull requests target the lowest supported bugfix branch and then we merge the patch upwards to more recent branches. If you think something is missing in the gitignore feel free to open a PR.
I'm not a build system expert so I'm not entirely sure what this fixes, but yes please file a PR against 8.2 so someone more knowledgeable can have a look. |
Thanks, that helped clarifying it and also after checking it again, the reason was b/c building on top of master first, then testing on top of PHP 8.2 - but unclean.
|
* PHP-8.2: Fix GH-16695: phar:// tar parser and zero-length file header blocks
* PHP-8.3: Fix GH-16695: phar:// tar parser and zero-length file header blocks
* PHP-8.4: Fix GH-16695: phar:// tar parser and zero-length file header blocks
Description
The following code:
Resulted in this output:
But I expected no diagnostic messages to output instead.
Additional Information:
If there is a file with length in the tar archive beforehand, a different diagnostic message is given:
We believe in good faith that the file is NOT truncated and therefore the diagnostic message is wrong. That is, because if we add 512 NUL bytes to the file, the meta.json file contents can be properly obtained.
And then conclude by it, that the more minimal reproducer yields a different diagnostic.
Therefore here the original reproducer as a self-contained php script:
Resulted in this output:
Command the tar file has been created with (before base64 encoding it for the report):
As the tar file format is named ustar, and the tar writer yields an object of 3*512 / 1536 bytes (header, file data, header), and the GNU Tar info pages suggest not to write file data if there is none, and the two terminating 512 blocks of NUL chars are not an error ref, and the file is being read when adding only 512 NUL bytes, we'd like to report the original diagnostic message:
as we believe the file is not corrupted nor truncated. It is our understanding, that it only contains a zero-length file which suffices to have the file header block alone and must not be preceded by a data block consisting of only NUL chars.
PHP Version
PHP 8.4.0RC3 (and also: 8.3.13, 8.2.25, 8.1.30, 8.0.30, ... 5.3.29)
Operating System
The text was updated successfully, but these errors were encountered: