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

Reply via email to