>> * I think a better strategy would be to just dup the file descriptor gotten
>> after the cast in curl_setopt, store it (instead of storing the zval) and
>> close it on curl handle destruction. This way we wouldn't have to worry
>> about zval refcounts or whether the file descriptor obtained is still valid.
>> Could you see if this other strategy works? (otherwise I can do it later
>> this week)
Ok, I made it to work (thanks for consultation to Pierre and
Johannes). You were right, the bug remains with curl_multi_exec too.
BTW it happens not only with CURL_STDERR but also with all other
options that take fp as a parameter (CURLOPT_FILE,
CURLOPT_WRITEHEADER, CURLOPT_INFILE) so I added them to the test and
made separate test for curl_multi_exec (see the patch).
After some thoughts I think this is the case when user really wants to
shoot into his leg - probably from user pov we should generate a
warning that we can't write into the supplied pointer (but actually we
can :)) like my previous patch did, but it raised a couple of
problems:
1) curl_multi_exec is called directly without interception from php
and create a wrapper just for this check seems like an overkill to me
2) we need to add 3 more checks to restore default values for all fp
options (see above) in two places: curl_exec and curl_multi_exec -
again overkill.
So I decided to go with this patch.
What do you think?
P.S. This patch is just againt trunk, if it's ok I will add 5.3 and
5.4 versions too.
--
Regards,
Shein Alexey
Index: trunk/ext/curl/php_curl.h
===================================================================
--- trunk/ext/curl/php_curl.h (revision 306938)
+++ trunk/ext/curl/php_curl.h (revision )
@@ -109,7 +109,7 @@
php_curl_write *write_header;
php_curl_read *read;
zval *passwd;
- zval *std_err;
+ FILE *std_err;
php_curl_progress *progress;
} php_curl_handlers;
Index: trunk/ext/curl/tests/bug48203.phpt
===================================================================
--- trunk/ext/curl/tests/bug48203.phpt (revision 281989)
+++ trunk/ext/curl/tests/bug48203.phpt (revision )
@@ -1,5 +1,5 @@
--TEST--
-Bug #48203 (Crash when CURLOPT_STDERR is set to regular file)
+Bug #48203 (Crash when file pointers passed to curl are closed before calling
curl_exec)
--SKIPIF--
<?php
if (!extension_loaded("curl")) {
@@ -12,22 +12,43 @@
--FILE--
<?php
+function checkForClosedFilePointer($curl_option, $description) {
-$fp = fopen(dirname(__FILE__) . '/bug48203.tmp', 'w');
+ $fp = fopen(dirname(__FILE__) . '/bug48203.tmp', 'w');
-$ch = curl_init();
+ $ch = curl_init();
+ // we also need CURLOPT_VERBOSE to be set to test CURLOPT_STDERR
properly
+ if (CURLOPT_STDERR == $curl_option) {
-curl_setopt($ch, CURLOPT_VERBOSE, 1);
+ curl_setopt($ch, CURLOPT_VERBOSE, 1);
-curl_setopt($ch, CURLOPT_STDERR, $fp);
-curl_setopt($ch, CURLOPT_URL, "");
+ }
+ curl_setopt($ch, $curl_option, $fp);
+
+ curl_setopt($ch, CURLOPT_URL, getenv("PHP_CURL_HTTP_REMOTE_SERVER"));
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+
-fclose($fp); // <-- premature close of $fp caused a crash!
+ fclose($fp); // <-- premature close of $fp caused a crash!
-curl_exec($ch);
+ curl_exec($ch);
-echo "Ok\n";
+ curl_close($ch);
+ echo "Ok for $description\n";
+}
+
+$options_to_check = array(
+ "CURLOPT_STDERR", "CURLOPT_WRITEHEADER", "CURLOPT_FILE",
"CURLOPT_INFILE"
+);
+
+foreach($options_to_check as $option) {
+ checkForClosedFilePointer(constant($option), $option);
+}
+
?>
--CLEAN--
<?php @unlink(dirname(__FILE__) . '/bug48203.tmp'); ?>
--EXPECT--
-Ok
+Ok for CURLOPT_STDERR
+Ok for CURLOPT_WRITEHEADER
+Ok for CURLOPT_FILE
+Ok for CURLOPT_INFILE
Index: trunk/ext/curl/interface.c
===================================================================
--- trunk/ext/curl/interface.c (revision 309881)
+++ trunk/ext/curl/interface.c (revision )
@@ -1810,7 +1810,10 @@
RETVAL_FALSE;
return 1;
}
-
+
+ // dup fp to be safe if user closes fp before calling
curl_exec
+ fp = fdopen(dup(fileno(fp)), ((php_stream *)
what)->mode);
+
error = CURLE_OK;
switch (option) {
case CURLOPT_FILE:
@@ -1845,11 +1848,7 @@
break;
case CURLOPT_STDERR:
if (((php_stream *) what)->mode[0] !=
'r' || ((php_stream *) what)->mode[1] == '+') {
- if (ch->handlers->std_err) {
-
zval_ptr_dtor(&ch->handlers->std_err);
- }
- zval_add_ref(zvalue);
- ch->handlers->std_err = *zvalue;
+ ch->handlers->std_err = fp;
} else {
php_error_docref(NULL
TSRMLS_CC, E_WARNING, "the provided file handle is not writable");
RETVAL_FALSE;
@@ -2520,11 +2519,6 @@
fprintf(stderr, "DTOR CALLED, ch = %x\n", ch);
#endif
- /* Prevent crash inside cURL if passed file has already been closed */
- if (ch->handlers->std_err && Z_REFCOUNT_P(ch->handlers->std_err) <= 0) {
- curl_easy_setopt(ch->cp, CURLOPT_STDERR, stderr);
- }
-
curl_easy_cleanup(ch->cp);
zend_llist_clean(&ch->to_free.str);
@@ -2559,9 +2553,6 @@
if (ch->handlers->passwd) {
zval_ptr_dtor(&ch->handlers->passwd);
}
- if (ch->handlers->std_err) {
- zval_ptr_dtor(&ch->handlers->std_err);
- }
if (ch->header.str_len > 0) {
efree(ch->header.str);
}
Index: trunk/ext/curl/tests/bug48203_multi.phpt
===================================================================
--- trunk/ext/curl/tests/bug48203_multi.phpt (revision )
+++ trunk/ext/curl/tests/bug48203_multi.phpt (revision )
@@ -0,0 +1,70 @@
+--TEST--
+Variation of bug #48203 with curl_multi_exec (Crash when file pointers passed
to curl are closed before calling curl_multi_exec)
+--SKIPIF--
+<?php
+if (!extension_loaded("curl")) {
+ exit("skip curl extension not loaded");
+}
+if (false === getenv('PHP_CURL_HTTP_REMOTE_SERVER')) {
+ exit("skip PHP_CURL_HTTP_REMOTE_SERVER env variable is not defined");
+}
+?>
+--FILE--
+<?php
+
+function checkForClosedFilePointer($curl_option, $description) {
+ $fp = fopen(dirname(__FILE__) . '/bug48203.tmp', 'w');
+
+ $ch1 = curl_init();
+ $ch2 = curl_init();
+
+ $options = array(
+ CURLOPT_RETURNTRANSFER => 1,
+ $curl_option => $fp,
+ CURLOPT_URL => getenv("PHP_CURL_HTTP_REMOTE_SERVER")
+ );
+
+ // we also need to set CURLOPT_VERBOSE to test CURLOPT_STDERR properly
+ if (CURLOPT_STDERR == $curl_option) {
+ $options[CURLOPT_VERBOSE] = 1;
+ }
+
+ curl_setopt_array($ch1, $options);
+ curl_setopt_array($ch2, $options);
+
+ fclose($fp); // <-- premature close of $fp caused a crash!
+
+ $mh = curl_multi_init();
+
+ curl_multi_add_handle($mh,$ch1);
+ curl_multi_add_handle($mh,$ch2);
+
+ $active = 0;
+ do {
+ curl_multi_exec($mh, $active);
+ } while ($active > 0);
+
+
+ curl_multi_remove_handle($mh, $ch1);
+ curl_multi_remove_handle($mh, $ch2);
+ curl_multi_close($mh);
+
+ echo "Ok for $description\n";
+}
+
+$options_to_check = array(
+ "CURLOPT_STDERR", "CURLOPT_WRITEHEADER", "CURLOPT_FILE",
"CURLOPT_INFILE"
+);
+
+foreach($options_to_check as $option) {
+ checkForClosedFilePointer(constant($option), $option);
+}
+
+?>
+--CLEAN--
+<?php @unlink(dirname(__FILE__) . '/bug48203.tmp'); ?>
+--EXPECT--
+Ok for CURLOPT_STDERR
+Ok for CURLOPT_WRITEHEADER
+Ok for CURLOPT_FILE
+Ok for CURLOPT_INFILE
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php