From 0a351335249697ef3fe0b2f9441cf30ad5cb8499 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gustavo=20Andr=C3=A9=20dos=20Santos=20Lopes?= Date: Fri, 5 Nov 2010 01:29:08 +0000 Subject: [PATCH] - Fixed bug #53241 (stream casting that relies on fdopen/fopencookie fails with streams opened with, inter alia, the 'xb' mode). --- ext/standard/tests/file/bug53241.phpt | 18 ++++++++++ main/php_streams.h | 6 ++++ main/streams/cast.c | 50 ++++++++++++++++++++++++++- main/streams/plain_wrapper.c | 4 ++- 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/file/bug53241.phpt diff --git a/ext/standard/tests/file/bug53241.phpt b/ext/standard/tests/file/bug53241.phpt new file mode 100644 index 0000000000..e302e88140 --- /dev/null +++ b/ext/standard/tests/file/bug53241.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #53241 (stream casting that relies on fdopen/fopencookie fails with 'xb' mode) +--SKIPIF-- +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) diff --git a/main/streams/cast.c b/main/streams/cast.c index e196a005e2..89bfa0ab46 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -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; diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 2fc0e8989a..bc5137c01f 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -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; } -- 2.40.0