>> * 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

Reply via email to