diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index f8cb91ef8ef..50b6bc7b684 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -3674,6 +3674,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #ifdef PHP_CAN_SUPPORT_PROC_OPEN BASIC_MINIT_SUBMODULE(proc_open) #endif + BASIC_MINIT_SUBMODULE(exec) BASIC_MINIT_SUBMODULE(user_streams) BASIC_MINIT_SUBMODULE(imagetypes) diff --git a/ext/standard/exec.c b/ext/standard/exec.c index 66d4537dab1..461bef4f72e 100644 --- a/ext/standard/exec.c +++ b/ext/standard/exec.c @@ -46,10 +46,42 @@ #include #endif -#if HAVE_NICE && HAVE_UNISTD_H +#if HAVE_UNISTD_H #include #endif +#ifdef PHP_WIN32 +# include "win32/php_stdint.h" +#else +# if HAVE_INTTYPES_H +# include +# elif HAVE_STDINT_H +# include +# endif +#endif + +static int cmd_max_len; + +/* {{{ PHP_MINIT_FUNCTION(exec) */ +PHP_MINIT_FUNCTION(exec) +{ +#ifdef _SC_ARG_MAX + cmd_max_len = sysconf(_SC_ARG_MAX); +#elif defined(ARG_MAX) + cmd_max_len = ARG_MAX; +#elif defined(PHP_WIN32) + /* Executed commands will run through cmd.exe. As long as it's the case, + it's just the constant limit.*/ + cmd_max_len = 8192; +#else + /* This is just an arbitrary value for the fallback case. */ + cmd_max_len = 4096; +#endif + + return SUCCESS; +} +/* }}} */ + /* {{{ php_exec * If type==0, only last line of output is returned (exec) * If type==1, all lines will be printed and last lined returned (system) @@ -244,13 +276,20 @@ PHP_FUNCTION(passthru) */ PHPAPI char *php_escape_shell_cmd(char *str) { - register int x, y, l = strlen(str); + register int x, y; + size_t l = strlen(str); + uint64_t estimate = (2 * (uint64_t)l) + 1; char *cmd; char *p = NULL; - size_t estimate = (2 * l) + 1; TSRMLS_FETCH(); + /* max command line length - two single quotes - \0 byte length */ + if (l > cmd_max_len - 2 - 1) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Command exceeds the allowed length of %d bytes", cmd_max_len); + return NULL; + } + cmd = safe_emalloc(2, l, 1); for (x = 0, y = 0; x < l; x++) { @@ -322,6 +361,12 @@ PHPAPI char *php_escape_shell_cmd(char *str) } cmd[y] = '\0'; + if (y - 1 > cmd_max_len) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Escaped command exceeds the allowed length of %d bytes", cmd_max_len); + efree(cmd); + return NULL; + } + if ((estimate - y) > 4096) { /* realloc if the estimate was way overill * Arbitrary cutoff point of 4096 */ @@ -336,12 +381,19 @@ PHPAPI char *php_escape_shell_cmd(char *str) */ PHPAPI char *php_escape_shell_arg(char *str) { - int x, y = 0, l = strlen(str); + int x, y = 0; + size_t l = strlen(str); char *cmd; - size_t estimate = (4 * l) + 3; + uint64_t estimate = (4 * (uint64_t)l) + 3; TSRMLS_FETCH(); + /* max command line length - two single quotes - \0 byte length */ + if (l > cmd_max_len - 2 - 1) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Argument exceeds the allowed length of %d bytes", cmd_max_len); + return NULL; + } + cmd = safe_emalloc(4, l, 3); /* worst case */ #ifdef PHP_WIN32 @@ -396,6 +448,12 @@ PHPAPI char *php_escape_shell_arg(char *str) #endif cmd[y] = '\0'; + if (y - 1 > cmd_max_len) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Escaped argument exceeds the allowed length of %d bytes", cmd_max_len); + efree(cmd); + return NULL; + } + if ((estimate - y) > 4096) { /* realloc if the estimate was way overill * Arbitrary cutoff point of 4096 */ @@ -418,6 +476,10 @@ PHP_FUNCTION(escapeshellcmd) } if (command_len) { + if (command_len != strlen(command)) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Input string contains NULL bytes"); + return; + } cmd = php_escape_shell_cmd(command); RETVAL_STRING(cmd, 0); } else { @@ -439,6 +501,10 @@ PHP_FUNCTION(escapeshellarg) } if (argument) { + if (argument_len != strlen(argument)) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Input string contains NULL bytes"); + return; + } cmd = php_escape_shell_arg(argument); RETVAL_STRING(cmd, 0); } diff --git a/ext/standard/exec.h b/ext/standard/exec.h index 399325c759f..b106838367e 100644 --- a/ext/standard/exec.h +++ b/ext/standard/exec.h @@ -33,6 +33,7 @@ PHP_FUNCTION(proc_close); PHP_FUNCTION(proc_terminate); PHP_FUNCTION(proc_nice); PHP_MINIT_FUNCTION(proc_open); +PHP_MINIT_FUNCTION(exec); PHPAPI char *php_escape_shell_cmd(char *); PHPAPI char *php_escape_shell_arg(char *);