]> granicus.if.org Git - php/commitdiff
- Fixed bug #53241 (stream casting that relies on fdopen/fopencookie fails
authorGustavo André dos Santos Lopes <cataphract@php.net>
Fri, 5 Nov 2010 01:29:08 +0000 (01:29 +0000)
committerGustavo André dos Santos Lopes <cataphract@php.net>
Fri, 5 Nov 2010 01:29:08 +0000 (01:29 +0000)
  with streams opened with, inter alia, the 'xb' mode).

NEWS
ext/standard/tests/file/bug53241.phpt [new file with mode: 0644]
main/php_streams.h
main/streams/cast.c
main/streams/plain_wrapper.c

diff --git a/NEWS b/NEWS
index 7c66a5e89b83733f491988ce9d48e91d453a55b1..e17a2582dfc6ae212cefa2b745061eede82e2e23 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -53,6 +53,8 @@
   obtained with ReflectionClass::getProperties(). (Gustavo)
 - Fixed covariance of return-by-ref constraints. (Etienne)
 
+- Fixed bug #53241 (stream casting that relies on fdopen/fopencookie fails
+  with streams opened with, inter alia, the 'xb' mode). (Gustavo)
 - Fixed bug #53198 (changing INI setting "from" with ini_set did not have any
   effect). (Gustavo)
 - Fixed bug #53180 (post_max_size=0 not disabling the limit when the content
diff --git a/ext/standard/tests/file/bug53241.phpt b/ext/standard/tests/file/bug53241.phpt
new file mode 100644 (file)
index 0000000..e302e88
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--\r
+Bug #53241 (stream casting that relies on fdopen/fopencookie fails with 'xb' mode)\r
+--SKIPIF--\r
+<?php\r
+/* unfortunately no standard function does a cast to FILE*, so we need\r
+ * curl to test this */\r
+if (!extension_loaded("curl")) exit("skip curl extension not loaded");\r
+--FILE--\r
+<?php\r
+$fn = __DIR__ . "/test.tmp";\r
+@unlink($fn);\r
+$fh = fopen($fn, 'xb');\r
+$ch = curl_init('http://www.yahoo.com/');\r
+var_dump(curl_setopt($ch, CURLOPT_FILE, $fh));\r
+echo "Done.\n";\r
+--EXPECT--\r
+bool(true)\r
+Done.\r
index 5cc78aa4bf224ed83ecbe82e0b3adbd917338dc4..5e1aa4f63be83f582c7bed391b1c211de6d4125c 100755 (executable)
@@ -462,6 +462,12 @@ END_EXTERN_C()
 #define PHP_STREAM_CAST_MASK           (PHP_STREAM_CAST_TRY_HARD | PHP_STREAM_CAST_RELEASE | PHP_STREAM_CAST_INTERNAL)
 BEGIN_EXTERN_C()
 PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC);
+/* This functions transforms the first char to 'w' if it's not 'r', 'a' or 'w'
+ * and strips any subsequent chars except '+' and 'b'.
+ * Use this to sanitize stream->mode if you call e.g. fdopen, fopencookie or
+ * any other function that expects standard modes and you allow non-standard
+ * ones. result should be a char[5]. */
+PHPAPI void php_stream_rep_nonstand_mode(php_stream *stream, char *result);
 END_EXTERN_C()
 /* use this to check if a stream can be cast into another form */
 #define php_stream_can_cast(stream, as)        _php_stream_cast((stream), (as), NULL, 0 TSRMLS_CC)
index e196a005e2287f2d36d59511768a0115c04e55ee..89bfa0ab46a79d0e8265aad13c7d6556784c3af6 100644 (file)
@@ -144,6 +144,50 @@ static COOKIE_IO_FUNCTIONS_T stream_cookie_functions =
 #endif
 /* }}} */
 
+/* {{{ php_stream_rep_nonstand_mode
+ * Result should have at least size 5, e.g. to write wbx+\0 */
+PHPAPI void php_stream_rep_nonstand_mode(php_stream *stream, char *result)
+{
+       /* replace modes not supported by fdopen and fopencookie, but supported 
+        * by PHP's fread(), so that their calls won't fail */
+       const char *cur_mode = stream->mode;
+       int         has_plus = 0,
+                       has_bin  = 0,
+                               i,
+                               res_curs = 0;
+
+       if (cur_mode[0] == 'r' || cur_mode[0] == 'w' || cur_mode[0] == 'a') {
+               result[res_curs++] = cur_mode[0];
+       } else {
+               /* assume cur_mode[0] is 'c' or 'x'; substitute by 'w', which should not
+                * truncate anything in fdopen/fopencookie */
+               result[res_curs++] = 'w';
+
+               /* x is allowed (at least by glibc & compat), but not as the 1st mode
+                * as in PHP and in any case is (at best) ignored by fdopen and fopencookie */
+       }
+       
+       /* assume current mode has at most length 4 (e.g. wbn+) */
+       for (i = 1; i < 4 && cur_mode[i] != '\0'; i++) {
+               if (cur_mode[i] == 'b') {
+                       has_bin = 1;
+               } else if (cur_mode[i] == '+') {
+                       has_plus = 1;
+               }
+               /* ignore 'n', 't' or other stuff */
+       }
+
+       if (has_bin) {
+               result[res_curs++] = 'b';
+       }
+       if (has_plus) {
+               result[res_curs++] = '+';
+       }
+
+       result[res_curs] = '\0';
+}
+/* }}} */
+
 /* {{{ php_stream_cast */
 PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC)
 {
@@ -187,7 +231,11 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show
                        goto exit_success;
                }
 
-               *(FILE**)ret = fopencookie(stream, stream->mode, PHP_STREAM_COOKIE_FUNCTIONS);
+               {
+                       char fixed_mode[5];
+                       php_stream_rep_nonstand_mode(stream, fixed_mode);
+                       *(FILE**)ret = fopencookie(stream, fixed_mode, PHP_STREAM_COOKIE_FUNCTIONS);
+               }
 
                if (*ret != NULL) {
                        off_t pos;
index 9ba1d290fbd3ac47bd1b2e39c3022a133402b51a..a3cb1b28d37564e364fbc4bfaf14a040392e874e 100644 (file)
@@ -490,7 +490,9 @@ static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
                                if (data->file == NULL) {
                                        /* we were opened as a plain file descriptor, so we
                                         * need fdopen now */
-                                       data->file = fdopen(data->fd, stream->mode);
+                                       char fixed_mode[5];
+                                       php_stream_rep_nonstand_mode(stream, fixed_mode);
+                                       data->file = fdopen(data->fd, fixed_mode);
                                        if (data->file == NULL) {
                                                return FAILURE;
                                        }