-
Notifications
You must be signed in to change notification settings - Fork 8k
Fix bug #76524 - ZipArchive memory leak #3330
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
Conversation
|
cc @remicollet |
|
Hmm, if I correctly understand the AppVeyor build log,
It's not directly related with patch, looks like |
ext/zip/tests/bug76524.phpt
Outdated
| $filename = __DIR__ . '/nonexistent.zip'; | ||
|
|
||
| // The error is not reproduced when file already exist: | ||
| if (!file_exists($filename)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just ensure the filename doesn't exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw., how would it behave if something was added to that non existent archive? Perhaps this case deserves a separate test.
Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just ensure the filename doesn't exist?
Of course, it's not necessary. I thought this is clearer shows the conditions which cause the problem. If it's not obvious, then I'll rather remove this.
Btw., how would it behave if something was added to that non existent archive? Perhaps this case deserves a separate test.
In this case all right: it just doesn't create an empty archives.
If an archive is not exist and we add some files then new zip file will be created.
If an archive already exists and we don't add any files, but use OVERWRITE then it remove the file without warning. I found this is weird, but it works so.
But you right, I'll check the test suite of the extension for coverage this cases.
| if (intern->za) { | ||
| if (zip_close(intern->za) != 0) { | ||
| php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context: %s", zip_strerror(intern->za)); | ||
| return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intern->za was not NULL, so it's probably not destroyed at this point. In further, there are also two superfluous NULL assignments.
Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll check it. Thanks!
|
So, now the zip resources freeing is well as in Also, tests are depends on OS now, because on Windows it doesn't show the warning message. I tried to debug this, but I'm not very well on Windows development tools. The difference in this case is occurred because this |
ext/zip/php_zip.c
Outdated
| if (intern->za) { | ||
| if (zip_close(intern->za) != 0) { | ||
| #if LIBZIP_VERSION_MAJOR == 1 && LIBZIP_VERSION_MINOR == 3 && LIBZIP_VERSION_MICRO == 1 | ||
| php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context: %s", zip_strerror(intern->za)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, why this condition is necessary, is there something special with the exact version? Also, the call to php_error_docref is same in both branches.
Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, why this condition is necessary, is there something special with the exact version?
Yes, in the 1.3.1 a call zip_discard() after faulty zip_close() may cause segfault (because of use after free). For example, on the build with libzip 1.3.1 and without this condition, next code reproduces the failure:
<?php
$zip = new ZipArchive();
$zip->open(__DIR__.'/filename.zip', ZipArchive::CREATE); // regardless OVERWRITE flag
$zip->addFile(__DIR__); // just an error actionIn the gdb:
Warning: Unknown: Cannot destroy the zip context: Read error: Is a directory in Unknown on line 0
Program received signal SIGSEGV, Segmentation fault.
zip_source_free (src=0x20000000b) at zip_source_free.c:46
46 if (src->refcount > 0) {
(gdb) backtrace
#0 zip_source_free (src=0x20000000b) at zip_source_free.c:46
#1 0x00007ffff7bcbf7d in zip_source_free (src=0xc83960) at zip_source_free.c:68
#2 0x00007ffff7bcbf7d in zip_source_free (src=0xc7f3f0) at zip_source_free.c:68
#3 0x00007ffff7bc4867 in zip_discard (za=0xc7f680) at zip_discard.c:54
#4 0x00000000005d3d93 in php_zip_object_free_storage (object=0x7ffff6c681e8) at /home/timur/dev/c/php-src/ext/zip/php_zip.c:1008\
...
As @remicollet noticed in the comments for bug 75540, only this libzip version is affected (just in case, I checked the 1.3.0 and 1.3.2, they are ok).
Also, the call to php_error_docref is same in both branches.
Yes, they should be different: the first branch shouldn't call zip_strerror (702ef2736). I'm going to fix it in next commit.
|
The behavior difference in throwing the warning seems to regard to libzip and platform. I guess, it should be ok to workaround the test by using the Thanks. |
Unfortunatetly, the What about regular expression in the It works, but I am not sure that is looks fine. |
|
You can use EXPECTREGEX instead of EXPECTF. |
|
Merged as 08f0885. Thanks! |
(This PR duplicates the previous one, but targets to the 7.1 branch instead of 5.6)
The memory leak occurs when
ZipArchiveis opened withZipArchive::CREATE|ZipArchive::OVERWRITEflags,close()method call.The leak does not occur if the zip file already exist.
https://bugs.php.net/bug.php?id=76524