From 22a5ccab720fdff4bb56f2af6efe9ca7d3045a48 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Tue, 12 Jan 2016 14:41:44 +0100 Subject: [PATCH] Follow up on bug #71270 Using the max allowed command line length for an underlying OS. --- ext/standard/basic_functions.c | 1 + ext/standard/exec.c | 56 +++++++++++++++++-- ext/standard/exec.h | 1 + .../escapeshellarg_bug71270.phpt | 12 ++++ .../escapeshellcmd_bug71270.phpt | 12 ++++ 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/general_functions/escapeshellarg_bug71270.phpt create mode 100644 ext/standard/tests/general_functions/escapeshellcmd_bug71270.phpt diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index ad9cf1ce28..c6d8713d73 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -3657,6 +3657,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 c4afce3337..747f765dd4 100644 --- a/ext/standard/exec.c +++ b/ext/standard/exec.c @@ -46,10 +46,32 @@ #include #endif -#if HAVE_NICE && HAVE_UNISTD_H +#if (HAVE_NICE || defined(__linux__)) && HAVE_UNISTD_H #include #endif +static int cmd_max_len; + +/* {{{ PHP_MINIT_FUNCTION(exec) */ +PHP_MINIT_FUNCTION(exec) +{ +#ifdef __linux__ + 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) @@ -245,13 +267,19 @@ PHP_FUNCTION(passthru) */ PHPAPI zend_string *php_escape_shell_cmd(char *str) { - register int x, y, l = (int)strlen(str); - size_t estimate = (2 * l) + 1; + register int x, y; + size_t l = strlen(str); + uint64_t estimate = (2 * (uint64_t)l) + 1; zend_string *cmd; #ifndef PHP_WIN32 char *p = NULL; #endif + /* max command line length - two single quotes - \0 byte length */ + if (l > cmd_max_len - 2 - 1) { + php_error_docref(NULL, E_ERROR, "Command exceeds the allowed length of %d bytes", cmd_max_len); + return ZSTR_EMPTY_ALLOC(); + } cmd = zend_string_safe_alloc(2, l, 0, 0); @@ -324,6 +352,12 @@ PHPAPI zend_string *php_escape_shell_cmd(char *str) } ZSTR_VAL(cmd)[y] = '\0'; + if (y - 1 > cmd_max_len) { + php_error_docref(NULL, E_ERROR, "Escaped command exceeds the allowed length of %d bytes", cmd_max_len); + zend_string_release(cmd); + return ZSTR_EMPTY_ALLOC(); + } + if ((estimate - y) > 4096) { /* realloc if the estimate was way overill * Arbitrary cutoff point of 4096 */ @@ -340,10 +374,16 @@ PHPAPI zend_string *php_escape_shell_cmd(char *str) */ PHPAPI zend_string *php_escape_shell_arg(char *str) { - int x, y = 0, l = (int)strlen(str); + int x, y = 0; + size_t l = strlen(str); zend_string *cmd; - size_t estimate = (4 * l) + 3; + uint64_t estimate = (4 * (uint64_t)l) + 3; + /* max command line length - two single quotes - \0 byte length */ + if (l > cmd_max_len - 2 - 1) { + php_error_docref(NULL, E_ERROR, "Argument exceeds the allowed length of %d bytes", cmd_max_len); + return ZSTR_EMPTY_ALLOC(); + } cmd = zend_string_safe_alloc(4, l, 2, 0); /* worst case */ @@ -399,6 +439,12 @@ PHPAPI zend_string *php_escape_shell_arg(char *str) #endif ZSTR_VAL(cmd)[y] = '\0'; + if (y - 1 > cmd_max_len) { + php_error_docref(NULL, E_ERROR, "Escaped argument exceeds the allowed length of %d bytes", cmd_max_len); + zend_string_release(cmd); + return ZSTR_EMPTY_ALLOC(); + } + if ((estimate - y) > 4096) { /* realloc if the estimate was way overill * Arbitrary cutoff point of 4096 */ diff --git a/ext/standard/exec.h b/ext/standard/exec.h index e53abf8145..b1cbf29c13 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 zend_string *php_escape_shell_cmd(char *); PHPAPI zend_string *php_escape_shell_arg(char *); diff --git a/ext/standard/tests/general_functions/escapeshellarg_bug71270.phpt b/ext/standard/tests/general_functions/escapeshellarg_bug71270.phpt new file mode 100644 index 0000000000..c57da3691c --- /dev/null +++ b/ext/standard/tests/general_functions/escapeshellarg_bug71270.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test escapeshellarg() allowed argument length +--FILE-- + +===DONE=== +--EXPECTF-- +Fatal error: escapeshellarg(): Argument exceeds the allowed length of %d bytes in %s on line %d diff --git a/ext/standard/tests/general_functions/escapeshellcmd_bug71270.phpt b/ext/standard/tests/general_functions/escapeshellcmd_bug71270.phpt new file mode 100644 index 0000000000..4686193d41 --- /dev/null +++ b/ext/standard/tests/general_functions/escapeshellcmd_bug71270.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test escapeshellcmd() allowed argument length +--FILE-- + +===DONE=== +--EXPECTF-- +Fatal error: escapeshellcmd(): Command exceeds the allowed length of %d bytes in %s on line %d -- 2.40.0