nlopess Sun, 19 Jul 2009 14:52:27 +0000 URL: http://svn.php.net/viewvc?view=revision&revision=284353
Changed paths: U php/php-src/branches/PHP_5_3/NEWS U php/php-src/branches/PHP_5_3/ext/standard/proc_open.c U php/php-src/branches/PHP_5_3/ext/standard/proc_open.h A php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open03.phpt A php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open04.phpt A php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open05.phpt A php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open06.phpt A php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open07.phpt A php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open08.phpt U php/php-src/trunk/ext/standard/proc_open.c U php/php-src/trunk/ext/standard/proc_open.h A php/php-src/trunk/ext/standard/tests/general_functions/proc_open03.phpt A php/php-src/trunk/ext/standard/tests/general_functions/proc_open04.phpt A php/php-src/trunk/ext/standard/tests/general_functions/proc_open05.phpt A php/php-src/trunk/ext/standard/tests/general_functions/proc_open06.phpt A php/php-src/trunk/ext/standard/tests/general_functions/proc_open07.phpt A php/php-src/trunk/ext/standard/tests/general_functions/proc_open08.phpt Log: Add support for proc_open()'s bypass_shell feature for Unix systems (slightly modified patch from Gwynne)
Modified: php/php-src/branches/PHP_5_3/NEWS =================================================================== --- php/php-src/branches/PHP_5_3/NEWS 2009-07-19 14:36:16 UTC (rev 284352) +++ php/php-src/branches/PHP_5_3/NEWS 2009-07-19 14:52:27 UTC (rev 284353) @@ -5,6 +5,8 @@ Functors. (Christian Seiler) - Fixed open_basedir circumvention for mail.log. (Maksymilian Arciemowicz, Stas) +- Added support for proc_open()'s bypass_shell feature for Unix systems + (Gwynne, Nuno) - Fixed bug #48899 (is_callable returns true even if method does not exist in parent class). (Felipe) Modified: php/php-src/branches/PHP_5_3/ext/standard/proc_open.c =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/proc_open.c 2009-07-19 14:36:16 UTC (rev 284352) +++ php/php-src/branches/PHP_5_3/ext/standard/proc_open.c 2009-07-19 14:52:27 UTC (rev 284353) @@ -71,6 +71,56 @@ static int le_proc_open; +#if !defined(PHP_WIN32) && !defined(NETWARE) +/* {{{ _php_array_to_argv */ +static char **_php_array_to_argv(zval *arg_array, int is_persistent) +{ + zval **element, temp; + char **c_argv, **ap; + HashTable *target_hash; + HashPosition pos; + + target_hash = Z_ARRVAL_P(arg_array); + ap = c_argv = (char **)pecalloc(zend_hash_num_elements(target_hash) + 1, sizeof(char *), is_persistent); + + /* skip first element */ + zend_hash_internal_pointer_reset_ex(target_hash, &pos); + zend_hash_move_forward_ex(target_hash, &pos); + for ( ; + zend_hash_get_current_data_ex(target_hash, (void **) &element, &pos) == SUCCESS; + zend_hash_move_forward_ex(target_hash, &pos)) { + + temp = **element; + if (Z_TYPE_PP(element) != IS_STRING) { + zval_copy_ctor(&temp); + convert_to_string(&temp); + } + *ap++ = pestrndup(Z_STRVAL(temp), Z_STRLEN(temp), is_persistent); + if (Z_TYPE_PP(element) != IS_STRING) { + zval_dtor(&temp); + } + } + + return c_argv; +} +/* }}} */ + +/* {{{ _php_free_argv */ +static void _php_free_argv(char **argv, int is_persistent) +{ + if (argv) { + char **ap = NULL; + + for (ap = argv; *ap; ap++) { + pefree(*ap, is_persistent); + } + pefree(argv, is_persistent); + } +} +/* }}} */ + +#endif + /* {{{ _php_array_to_envp */ static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent TSRMLS_DC) { @@ -177,8 +227,6 @@ } assert(p - env.envp <= sizeenv); - - zend_hash_internal_pointer_reset_ex(target_hash, &pos); return env; } @@ -243,6 +291,7 @@ FG(pclose_ret) = -1; #endif _php_free_envp(proc->env, proc->is_persistent); + _php_free_argv(proc->argv, proc->is_persistent); pefree(proc->command, proc->is_persistent); pefree(proc, proc->is_persistent); @@ -465,6 +514,7 @@ Run a process with more control over it's file descriptors */ PHP_FUNCTION(proc_open) { + zval *command_with_args = NULL; char *command, *cwd=NULL; int command_len, cwd_len = 0; zval *descriptorspec; @@ -477,6 +527,7 @@ zval **descitem = NULL; HashPosition pos; struct php_proc_open_descriptor_item descriptors[PHP_PROC_OPEN_MAX_DESCRIPTORS]; + char** child_argv = NULL; #ifdef PHP_WIN32 PROCESS_INFORMATION pi; HANDLE childHandle; @@ -488,7 +539,6 @@ UINT old_error_mode; #endif #ifdef NETWARE - char** child_argv = NULL; char* command_dup = NULL; char* orig_cwd = NULL; int command_num_args = 0; @@ -499,43 +549,85 @@ int is_persistent = 0; /* TODO: ensure that persistent procs will work */ #ifdef PHP_WIN32 int suppress_errors = 0; +#endif int bypass_shell = 0; -#endif #if PHP_CAN_DO_PTS php_file_descriptor_t dev_ptmx = -1; /* master */ php_file_descriptor_t slave_pty = -1; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "saz|s!a!a!", &command, - &command_len, &descriptorspec, &pipes, &cwd, &cwd_len, &environment, + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zaz|s!a!a!", &command_with_args, + &descriptorspec, &pipes, &cwd, &cwd_len, &environment, &other_options) == FAILURE) { RETURN_FALSE; } - if (FAILURE == php_make_safe_mode_command(command, &command, is_persistent TSRMLS_CC)) { - RETURN_FALSE; - } - -#ifdef PHP_WIN32 if (other_options) { zval **item; +#ifdef PHP_WIN32 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(other_options), "suppress_errors", sizeof("suppress_errors"), (void**)&item)) { if ((Z_TYPE_PP(item) == IS_BOOL || Z_TYPE_PP(item) == IS_LONG) && Z_LVAL_PP(item)) { suppress_errors = 1; } - } + } +#endif if (SUCCESS == zend_hash_find(Z_ARRVAL_P(other_options), "bypass_shell", sizeof("bypass_shell"), (void**)&item)) { if ((Z_TYPE_PP(item) == IS_BOOL || Z_TYPE_PP(item) == IS_LONG) && Z_LVAL_PP(item)) { bypass_shell = 1; } - } + } } + +#if !defined(PHP_WIN32) && !defined(NETWARE) + if (bypass_shell) { + zval **item; + + if (Z_TYPE_P(command_with_args) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "first parameter must be array when bypass_shell is on"); + RETURN_FALSE; + } + if (zend_hash_num_elements(Z_ARRVAL_P(command_with_args)) < 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "arguments array must have at least one element"); + RETURN_FALSE; + } + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(command_with_args), &pos); + if (zend_hash_get_current_data_ex(Z_ARRVAL_P(command_with_args), (void **)&item, &pos) == SUCCESS) { + if (Z_TYPE_PP(item) == IS_STRING && Z_STRLEN_PP(item) > 0) { + command = Z_STRVAL_PP(item); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "first argument must be a nonempty string"); + RETURN_FALSE; + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "first argument must be at index 0"); + RETURN_FALSE; + } + } else { #endif - + if (Z_TYPE_P(command_with_args) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s() expects parameter 1 to be string, %s given", get_active_function_name(TSRMLS_C), + zend_zval_type_name(command_with_args)); + RETURN_FALSE; + } + command = Z_STRVAL_P(command_with_args); +#if !defined(PHP_WIN32) && !defined(NETWARE) + } +#endif + + if (FAILURE == php_make_safe_mode_command(command, &command, is_persistent TSRMLS_CC)) { + RETURN_FALSE; + } command_len = strlen(command); +#if !defined(PHP_WIN32) && !defined(NETWARE) + if (bypass_shell) { + child_argv = _php_array_to_argv(command_with_args, is_persistent); + } +#endif + if (environment) { env = _php_array_to_envp(environment, is_persistent TSRMLS_CC); } else { @@ -885,7 +977,11 @@ chdir(cwd); } - if (env.envarray) { + if (bypass_shell && env.envarray) { + execve(command, child_argv, env.envarray); + } else if (bypass_shell) { + execv(command, child_argv); + } else if (env.envarray) { execle("/bin/sh", "sh", "-c", command, NULL, env.envarray); } else { execl("/bin/sh", "sh", "-c", command, NULL); @@ -921,6 +1017,9 @@ proc->childHandle = childHandle; #endif proc->env = env; +#if !defined(PHP_WIN32) && !defined(NETWARE) + proc->argv = child_argv; +#endif if (pipes != NULL) { zval_dtor(pipes); @@ -995,6 +1094,9 @@ return; exit_fail: +#if !defined(PHP_WIN32) && !defined(NETWARE) + _php_free_argv(child_argv, is_persistent); +#endif _php_free_envp(env, is_persistent); pefree(command, is_persistent); #if PHP_CAN_DO_PTS Modified: php/php-src/branches/PHP_5_3/ext/standard/proc_open.h =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/proc_open.h 2009-07-19 14:36:16 UTC (rev 284352) +++ php/php-src/branches/PHP_5_3/ext/standard/proc_open.h 2009-07-19 14:52:27 UTC (rev 284353) @@ -48,5 +48,8 @@ char *command; int is_persistent; php_process_env_t env; +#if !defined(PHP_WIN32) && !defined(NETWARE) + char **argv; +#endif }; Added: php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open03.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open03.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open03.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,32 @@ +--TEST-- +proc_open with bypass_shell +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable("/bin/cat")) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if( substr(PHP_OS, 0, 3) == 'WIN' ) die("skip this test for non-Windows systems only "); +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array("/bin/cat"), + $ds, + $pipes, + NULL, + NULL, + array('bypass_shell' => TRUE) + ); + +proc_close($cat); + +echo "I didn't segfault!\n"; + +?> +--EXPECT-- +I didn't segfault! Added: php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open04.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open04.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open04.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,32 @@ +--TEST-- +proc_open with bypass_shell +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable("/bin/echo")) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if( substr(PHP_OS, 0, 3) == 'WIN' ) die("skip this test for non-Windows systems only "); +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array("/bin/echo", "echo", "asdf"), + $ds, + $pipes, + NULL, + NULL, + array('bypass_shell' => TRUE) + ); + +echo stream_get_contents($pipes[1]); + +proc_close($cat); + +?> +--EXPECT-- +asdf Added: php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open05.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open05.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open05.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,34 @@ +--TEST-- +proc_open with bypass_shell and environment +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable("/usr/bin/env")) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if( substr(PHP_OS, 0, 3) == 'WIN' ) die("skip this test for non-Windows systems only "); +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array("/usr/bin/env", "env"), + $ds, + $pipes, + NULL, + array("TEST_ENV" => 42, "TEST_ENV_2" => 84), + array('bypass_shell' => TRUE) + ); + +echo stream_get_contents($pipes[1]); + +proc_close($cat); + +// %A is put under EXPECTF as Valgrind will append extra environment +?> +--EXPECTF-- +TEST_ENV=42 +TEST_ENV_2=84%A Added: php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open06.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open06.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open06.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,32 @@ +--TEST-- +proc_open with bypass_shell, environment, and non-string arguments +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable("/bin/echo")) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if (substr(PHP_OS, 0, 3) == 'WIN') echo "skip this test for non-Windows systems only"; +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array("/bin/echo", "echo", 1, 2, 3, 4, 5), + $ds, + $pipes, + NULL, + array("TEST_ENV" => 42, "TEST_ENV_2" => 84), + array('bypass_shell' => TRUE) + ); + +echo stream_get_contents($pipes[1]); + +proc_close($cat); + +?> +--EXPECTF-- +1 2 3 4 5 Added: php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open07.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open07.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open07.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,31 @@ +--TEST-- +proc_open with no argv +--SKIPIF-- +<?php # vim:syn=php +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if (substr(PHP_OS, 0, 3) == 'WIN') echo "skip this test for non-Windows systems only"; +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array(), + $ds, + $pipes, + NULL, + array("TEST_ENV" => 42, "TEST_ENV_2" => 84), + array('bypass_shell' => TRUE) + ); + +var_dump($cat); + +?> +--EXPECTF-- + +Warning: proc_open(): arguments array must have at least one element in %s/proc_open07.php on line %d +bool(false) Added: php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open08.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open08.phpt (rev 0) +++ php/php-src/branches/PHP_5_3/ext/standard/tests/general_functions/proc_open08.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,44 @@ +--TEST-- +proc_open with only one argv +--DESCRIPTION-- +This test tries out a very esoteric functionality: Passing no argv[0] to a +program. There's absolutely no reason anyone would do this in practice, but the +entire point of the bypass_shell patch was to allow 100% control over the child +process, so the option is there. Keep in mind that actually using this +"feature" will probably crash most programs one could run, since the expression +argc > 0 in a main() function is pretty much guaranteed by POSIX. It's +interesting to note that PHP itself handles this case gracefully. +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable($_ENV['TEST_PHP_EXECUTABLE'])) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if (substr(PHP_OS, 0, 3) == 'WIN') echo "skip this test for non-Windows systems only"; +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array($_ENV['TEST_PHP_EXECUTABLE']), + $ds, + $pipes, + NULL, + NULL, + array('bypass_shell' => TRUE) + ); + +fprintf($pipes[0], '<?php error_reporting(E_ALL); var_dump($argv); ?>'); +fclose($pipes[0]); + +echo stream_get_contents($pipes[1]); + +proc_close($cat); + +?> +--EXPECTF-- +Notice: Undefined variable: argv in %s- on line 1 +NULL Modified: php/php-src/trunk/ext/standard/proc_open.c =================================================================== --- php/php-src/trunk/ext/standard/proc_open.c 2009-07-19 14:36:16 UTC (rev 284352) +++ php/php-src/trunk/ext/standard/proc_open.c 2009-07-19 14:52:27 UTC (rev 284353) @@ -70,6 +70,56 @@ static int le_proc_open; +#if !defined(PHP_WIN32) && !defined(NETWARE) +/* {{{ _php_array_to_argv */ +static char **_php_array_to_argv(zval *arg_array, int is_persistent) +{ + zval **element, temp; + char **c_argv, **ap; + HashTable *target_hash; + HashPosition pos; + + target_hash = Z_ARRVAL_P(arg_array); + ap = c_argv = (char **)pecalloc(zend_hash_num_elements(target_hash) + 1, sizeof(char *), is_persistent); + + /* skip first element */ + zend_hash_internal_pointer_reset_ex(target_hash, &pos); + zend_hash_move_forward_ex(target_hash, &pos); + for ( ; + zend_hash_get_current_data_ex(target_hash, (void **) &element, &pos) == SUCCESS; + zend_hash_move_forward_ex(target_hash, &pos)) { + + temp = **element; + if (Z_TYPE_PP(element) != IS_STRING) { + zval_copy_ctor(&temp); + convert_to_string(&temp); + } + *ap++ = pestrndup(Z_STRVAL(temp), Z_STRLEN(temp), is_persistent); + if (Z_TYPE_PP(element) != IS_STRING) { + zval_dtor(&temp); + } + } + + return c_argv; +} +/* }}} */ + +/* {{{ _php_free_argv */ +static void _php_free_argv(char **argv, int is_persistent) +{ + if (argv) { + char **ap = NULL; + + for (ap = argv; *ap; ap++) { + pefree(*ap, is_persistent); + } + pefree(argv, is_persistent); + } +} +/* }}} */ + +#endif + /* {{{ _php_array_to_envp */ static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent TSRMLS_DC) { @@ -177,8 +227,6 @@ } assert(p - env.envp <= sizeenv); - - zend_hash_internal_pointer_reset_ex(target_hash, &pos); return env; } @@ -243,6 +291,7 @@ FG(pclose_ret) = -1; #endif _php_free_envp(proc->env, proc->is_persistent); + _php_free_argv(proc->argv, proc->is_persistent); pefree(proc->command, proc->is_persistent); pefree(proc, proc->is_persistent); @@ -426,6 +475,7 @@ PHP_FUNCTION(proc_open) { zval **ppcommand, **ppcwd = NULL; + zval *command_with_args; char *command, *cwd=NULL; int command_len, cwd_len = 0; zval *descriptorspec; @@ -438,6 +488,7 @@ zval **descitem = NULL; HashPosition pos; struct php_proc_open_descriptor_item descriptors[PHP_PROC_OPEN_MAX_DESCRIPTORS]; + char** child_argv = NULL; #ifdef PHP_WIN32 PROCESS_INFORMATION pi; HANDLE childHandle; @@ -449,7 +500,6 @@ UINT old_error_mode; #endif #ifdef NETWARE - char** child_argv = NULL; char* command_dup = NULL; char* orig_cwd = NULL; int command_num_args = 0; @@ -460,8 +510,8 @@ int is_persistent = 0; /* TODO: ensure that persistent procs will work */ #ifdef PHP_WIN32 int suppress_errors = 0; +#endif int bypass_shell = 0; -#endif #if PHP_CAN_DO_PTS php_file_descriptor_t dev_ptmx = -1; /* master */ php_file_descriptor_t slave_pty = -1; @@ -469,8 +519,7 @@ php_stream_context *context = FG(default_context); zend_uchar binary_pipes = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zaz|Z!a!a!", &ppcommand, &descriptorspec, &pipes, &ppcwd, &environment, &other_options) == FAILURE || - php_stream_path_param_encode(ppcommand, &command, &command_len, REPORT_ERRORS, FG(default_context)) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zaz|Z!a!a!", &command_with_args, &descriptorspec, &pipes, &ppcwd, &environment, &other_options) == FAILURE) { RETURN_FALSE; } @@ -486,14 +535,14 @@ Z_LVAL_PP(item)) { suppress_errors = 1; } - } + } +#endif if (SUCCESS == zend_ascii_hash_find(Z_ARRVAL_P(other_options), "bypass_shell", sizeof("bypass_shell"), (void**)&item)) { if ((Z_TYPE_PP(item) == IS_BOOL || Z_TYPE_PP(item) == IS_LONG) && Z_LVAL_PP(item)) { bypass_shell = 1; } - } -#endif + } /* Suppresses automatic application of unicode filters when unicode.semantics=on */ if (SUCCESS == zend_ascii_hash_find(Z_ARRVAL_P(other_options), "binary_pipes", sizeof("binary_pipes"), (void**)&item)) { if (Z_TYPE_PP(item) == IS_BOOL && Z_BVAL_PP(item)) { @@ -504,9 +553,57 @@ /* Override FG(default_context) */ if (SUCCESS == zend_ascii_hash_find(Z_ARRVAL_P(other_options), "context", sizeof("context"), (void**)&item)) { context = php_stream_context_from_zval(*item, 0); - } + } } - + +#if !defined(PHP_WIN32) && !defined(NETWARE) + if (bypass_shell) { + zval **item; + + if (Z_TYPE_P(command_with_args) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "first parameter must be array when bypass_shell is on"); + RETURN_FALSE; + } + if (zend_hash_num_elements(Z_ARRVAL_P(command_with_args)) < 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "arguments array must have at least one element"); + RETURN_FALSE; + } + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(command_with_args), &pos); + if (zend_hash_get_current_data_ex(Z_ARRVAL_P(command_with_args), (void **)&item, &pos) == SUCCESS) { + if (Z_TYPE_PP(item) == IS_STRING || Z_TYPE_PP(item) == IS_UNICODE) { + ppcommand = item; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "first argument must be a nonempty string"); + RETURN_FALSE; + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "first argument must be at index 0"); + RETURN_FALSE; + } + } else { +#endif + if (Z_TYPE_P(command_with_args) != IS_STRING && Z_TYPE_P(command_with_args) != IS_UNICODE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s() expects parameter 1 to be string, %s given", get_active_function_name(TSRMLS_C), + zend_zval_type_name(command_with_args)); + RETURN_FALSE; + } + ppcommand = &command_with_args; + /* command_len will be set below */ +#if !defined(PHP_WIN32) && !defined(NETWARE) + } +#endif + + if (php_stream_path_param_encode(ppcommand, &command, &command_len, REPORT_ERRORS, FG(default_context)) == FAILURE) { + RETURN_FALSE; + } + +#if !defined(PHP_WIN32) && !defined(NETWARE) + if (bypass_shell) { + child_argv = _php_array_to_argv(command_with_args, is_persistent); + } +#endif + if (environment) { env = _php_array_to_envp(environment, is_persistent TSRMLS_CC); } else { @@ -875,7 +972,11 @@ chdir(cwd); } - if (env.envarray) { + if (bypass_shell && env.envarray) { + execve(command, child_argv, env.envarray); + } else if (bypass_shell) { + execv(command, child_argv); + } else if (env.envarray) { execle("/bin/sh", "sh", "-c", command, NULL, env.envarray); } else { execl("/bin/sh", "sh", "-c", command, NULL); @@ -911,6 +1012,9 @@ proc->childHandle = childHandle; #endif proc->env = env; +#if !defined(PHP_WIN32) && !defined(NETWARE) + proc->argv = child_argv; +#endif if (pipes != NULL) { zval_dtor(pipes); @@ -1005,6 +1109,9 @@ return; exit_fail: +#if !defined(PHP_WIN32) && !defined(NETWARE) + _php_free_argv(child_argv, is_persistent); +#endif _php_free_envp(env, is_persistent); #if PHP_CAN_DO_PTS if (dev_ptmx >= 0) { Modified: php/php-src/trunk/ext/standard/proc_open.h =================================================================== --- php/php-src/trunk/ext/standard/proc_open.h 2009-07-19 14:36:16 UTC (rev 284352) +++ php/php-src/trunk/ext/standard/proc_open.h 2009-07-19 14:52:27 UTC (rev 284353) @@ -48,5 +48,8 @@ char *command; int is_persistent; php_process_env_t env; +#if !defined(PHP_WIN32) && !defined(NETWARE) + char **argv; +#endif }; Added: php/php-src/trunk/ext/standard/tests/general_functions/proc_open03.phpt =================================================================== --- php/php-src/trunk/ext/standard/tests/general_functions/proc_open03.phpt (rev 0) +++ php/php-src/trunk/ext/standard/tests/general_functions/proc_open03.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,32 @@ +--TEST-- +proc_open with bypass_shell +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable("/bin/cat")) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if( substr(PHP_OS, 0, 3) == 'WIN' ) die("skip this test for non-Windows systems only "); +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array("/bin/cat"), + $ds, + $pipes, + NULL, + NULL, + array('bypass_shell' => TRUE) + ); + +proc_close($cat); + +echo "I didn't segfault!\n"; + +?> +--EXPECT-- +I didn't segfault! Added: php/php-src/trunk/ext/standard/tests/general_functions/proc_open04.phpt =================================================================== --- php/php-src/trunk/ext/standard/tests/general_functions/proc_open04.phpt (rev 0) +++ php/php-src/trunk/ext/standard/tests/general_functions/proc_open04.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,32 @@ +--TEST-- +proc_open with bypass_shell +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable("/bin/echo")) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if( substr(PHP_OS, 0, 3) == 'WIN' ) die("skip this test for non-Windows systems only "); +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array("/bin/echo", "echo", "asdf"), + $ds, + $pipes, + NULL, + NULL, + array('bypass_shell' => TRUE) + ); + +echo stream_get_contents($pipes[1]); + +proc_close($cat); + +?> +--EXPECT-- +asdf Added: php/php-src/trunk/ext/standard/tests/general_functions/proc_open05.phpt =================================================================== --- php/php-src/trunk/ext/standard/tests/general_functions/proc_open05.phpt (rev 0) +++ php/php-src/trunk/ext/standard/tests/general_functions/proc_open05.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,34 @@ +--TEST-- +proc_open with bypass_shell and environment +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable("/usr/bin/env")) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if( substr(PHP_OS, 0, 3) == 'WIN' ) die("skip this test for non-Windows systems only "); +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array("/usr/bin/env", "env"), + $ds, + $pipes, + NULL, + array(b"TEST_ENV" => 42, b"TEST_ENV_2" => 84), + array('bypass_shell' => TRUE) + ); + +echo stream_get_contents($pipes[1]); + +proc_close($cat); + +// %A is put under EXPECTF as Valgrind will append extra environment +?> +--EXPECTF-- +TEST_ENV=42 +TEST_ENV_2=84%A Added: php/php-src/trunk/ext/standard/tests/general_functions/proc_open06.phpt =================================================================== --- php/php-src/trunk/ext/standard/tests/general_functions/proc_open06.phpt (rev 0) +++ php/php-src/trunk/ext/standard/tests/general_functions/proc_open06.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,32 @@ +--TEST-- +proc_open with bypass_shell, environment, and non-string arguments +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable("/bin/echo")) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if (substr(PHP_OS, 0, 3) == 'WIN') echo "skip this test for non-Windows systems only"; +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array("/bin/echo", "echo", 1, 2, 3, 4, 5), + $ds, + $pipes, + NULL, + array("TEST_ENV" => 42, "TEST_ENV_2" => 84), + array('bypass_shell' => TRUE) + ); + +echo stream_get_contents($pipes[1]); + +proc_close($cat); + +?> +--EXPECTF-- +1 2 3 4 5 Added: php/php-src/trunk/ext/standard/tests/general_functions/proc_open07.phpt =================================================================== --- php/php-src/trunk/ext/standard/tests/general_functions/proc_open07.phpt (rev 0) +++ php/php-src/trunk/ext/standard/tests/general_functions/proc_open07.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,31 @@ +--TEST-- +proc_open with no argv +--SKIPIF-- +<?php # vim:syn=php +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if (substr(PHP_OS, 0, 3) == 'WIN') echo "skip this test for non-Windows systems only"; +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array(), + $ds, + $pipes, + NULL, + array("TEST_ENV" => 42, "TEST_ENV_2" => 84), + array('bypass_shell' => TRUE) + ); + +var_dump($cat); + +?> +--EXPECTF-- + +Warning: proc_open(): arguments array must have at least one element in %s/proc_open07.php on line %d +bool(false) Added: php/php-src/trunk/ext/standard/tests/general_functions/proc_open08.phpt =================================================================== --- php/php-src/trunk/ext/standard/tests/general_functions/proc_open08.phpt (rev 0) +++ php/php-src/trunk/ext/standard/tests/general_functions/proc_open08.phpt 2009-07-19 14:52:27 UTC (rev 284353) @@ -0,0 +1,44 @@ +--TEST-- +proc_open with only one argv +--DESCRIPTION-- +This test tries out a very esoteric functionality: Passing no argv[0] to a +program. There's absolutely no reason anyone would do this in practice, but the +entire point of the bypass_shell patch was to allow 100% control over the child +process, so the option is there. Keep in mind that actually using this +"feature" will probably crash most programs one could run, since the expression +argc > 0 in a main() function is pretty much guaranteed by POSIX. It's +interesting to note that PHP itself handles this case gracefully. +--SKIPIF-- +<?php # vim:syn=php +if (!is_executable($_ENV['TEST_PHP_EXECUTABLE'])) echo "skip"; +if (!function_exists("proc_open")) echo "skip proc_open() is not available"; +if (substr(PHP_OS, 0, 3) == 'WIN') echo "skip this test for non-Windows systems only"; +?> +--FILE-- +<?php +$ds = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + +$cat = proc_open( + array($_ENV[b'TEST_PHP_EXECUTABLE']), + $ds, + $pipes, + NULL, + NULL, + array('bypass_shell' => TRUE) + ); + +fprintf($pipes[0], '<?php error_reporting(E_ALL); var_dump($argv); ?>'); +fclose($pipes[0]); + +echo stream_get_contents($pipes[1]); + +proc_close($cat); + +?> +--EXPECT-- +Notice: Undefined variable: argv in - on line 1 +NULL
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php