cataphract                               Fri, 05 Nov 2010 01:29:08 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=305095

Log:
- Fixed bug #53241 (stream casting that relies on fdopen/fopencookie fails
  with streams opened with, inter alia, the 'xb' mode).

Bug: http://bugs.php.net/53241 (Assigned) fopen(..., 'xb') and CURLOPT_FILE 
make cURL fail with: fopencookie failed
      
Changed paths:
    U   php/php-src/branches/PHP_5_3/NEWS
    A   php/php-src/branches/PHP_5_3/ext/standard/tests/file/bug53241.phpt
    U   php/php-src/branches/PHP_5_3/main/php_streams.h
    U   php/php-src/branches/PHP_5_3/main/streams/cast.c
    U   php/php-src/branches/PHP_5_3/main/streams/plain_wrapper.c
    A   php/php-src/trunk/ext/standard/tests/file/bug53241.phpt
    U   php/php-src/trunk/main/php_streams.h
    U   php/php-src/trunk/main/streams/cast.c
    U   php/php-src/trunk/main/streams/plain_wrapper.c

Modified: php/php-src/branches/PHP_5_3/NEWS
===================================================================
--- php/php-src/branches/PHP_5_3/NEWS	2010-11-04 23:28:35 UTC (rev 305094)
+++ php/php-src/branches/PHP_5_3/NEWS	2010-11-05 01:29:08 UTC (rev 305095)
@@ -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

Added: php/php-src/branches/PHP_5_3/ext/standard/tests/file/bug53241.phpt
===================================================================
--- php/php-src/branches/PHP_5_3/ext/standard/tests/file/bug53241.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_3/ext/standard/tests/file/bug53241.phpt	2010-11-05 01:29:08 UTC (rev 305095)
@@ -0,0 +1,18 @@
+--TEST--
+Bug #53241 (stream casting that relies on fdopen/fopencookie fails with 'xb' mode)
+--SKIPIF--
+<?php
+/* unfortunately no standard function does a cast to FILE*, so we need
+ * curl to test this */
+if (!extension_loaded("curl")) exit("skip curl extension not loaded");
+--FILE--
+<?php
+$fn = __DIR__ . "/test.tmp";
+...@unlink($fn);
+$fh = fopen($fn, 'xb');
+$ch = curl_init('http://www.yahoo.com/');
+var_dump(curl_setopt($ch, CURLOPT_FILE, $fh));
+echo "Done.\n";
+--EXPECT--
+bool(true)
+Done.

Modified: php/php-src/branches/PHP_5_3/main/php_streams.h
===================================================================
--- php/php-src/branches/PHP_5_3/main/php_streams.h	2010-11-04 23:28:35 UTC (rev 305094)
+++ php/php-src/branches/PHP_5_3/main/php_streams.h	2010-11-05 01:29:08 UTC (rev 305095)
@@ -462,6 +462,12 @@
 #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)

Modified: php/php-src/branches/PHP_5_3/main/streams/cast.c
===================================================================
--- php/php-src/branches/PHP_5_3/main/streams/cast.c	2010-11-04 23:28:35 UTC (rev 305094)
+++ php/php-src/branches/PHP_5_3/main/streams/cast.c	2010-11-05 01:29:08 UTC (rev 305095)
@@ -144,6 +144,50 @@
 #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 @@
 			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;

Modified: php/php-src/branches/PHP_5_3/main/streams/plain_wrapper.c
===================================================================
--- php/php-src/branches/PHP_5_3/main/streams/plain_wrapper.c	2010-11-04 23:28:35 UTC (rev 305094)
+++ php/php-src/branches/PHP_5_3/main/streams/plain_wrapper.c	2010-11-05 01:29:08 UTC (rev 305095)
@@ -490,7 +490,9 @@
 				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;
 					}

Added: php/php-src/trunk/ext/standard/tests/file/bug53241.phpt
===================================================================
--- php/php-src/trunk/ext/standard/tests/file/bug53241.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/standard/tests/file/bug53241.phpt	2010-11-05 01:29:08 UTC (rev 305095)
@@ -0,0 +1,18 @@
+--TEST--
+Bug #53241 (stream casting that relies on fdopen/fopencookie fails with 'xb' mode)
+--SKIPIF--
+<?php
+/* unfortunately no standard function does a cast to FILE*, so we need
+ * curl to test this */
+if (!extension_loaded("curl")) exit("skip curl extension not loaded");
+--FILE--
+<?php
+$fn = __DIR__ . "/test.tmp";
+...@unlink($fn);
+$fh = fopen($fn, 'xb');
+$ch = curl_init('http://www.yahoo.com/');
+var_dump(curl_setopt($ch, CURLOPT_FILE, $fh));
+echo "Done.\n";
+--EXPECT--
+bool(true)
+Done.

Modified: php/php-src/trunk/main/php_streams.h
===================================================================
--- php/php-src/trunk/main/php_streams.h	2010-11-04 23:28:35 UTC (rev 305094)
+++ php/php-src/trunk/main/php_streams.h	2010-11-05 01:29:08 UTC (rev 305095)
@@ -462,6 +462,12 @@
 #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)

Modified: php/php-src/trunk/main/streams/cast.c
===================================================================
--- php/php-src/trunk/main/streams/cast.c	2010-11-04 23:28:35 UTC (rev 305094)
+++ php/php-src/trunk/main/streams/cast.c	2010-11-05 01:29:08 UTC (rev 305095)
@@ -144,6 +144,50 @@
 #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 @@
 			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;

Modified: php/php-src/trunk/main/streams/plain_wrapper.c
===================================================================
--- php/php-src/trunk/main/streams/plain_wrapper.c	2010-11-04 23:28:35 UTC (rev 305094)
+++ php/php-src/trunk/main/streams/plain_wrapper.c	2010-11-05 01:29:08 UTC (rev 305095)
@@ -490,7 +490,9 @@
 				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;
 					}
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to