summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnatol Belski <ab@php.net>2016-01-28 13:45:43 +0100
committerAnatol Belski <ab@php.net>2016-01-28 13:45:43 +0100
commitf4d7bbf4ace4ea21c8f95c2d1177bd56a21b86b9 (patch)
tree15d6d2c0f96ea32740b2320bcfcb5809c5f2dda2
parent828364e59ca08c2ff3328a648522f9eb05ebbaa3 (diff)
downloadphp-git-f4d7bbf4ace4ea21c8f95c2d1177bd56a21b86b9.tar.gz
backport the escapeshell* functions hardening branch
-rw-r--r--ext/standard/basic_functions.c1
-rw-r--r--ext/standard/exec.c76
-rw-r--r--ext/standard/exec.h1
3 files changed, 73 insertions, 5 deletions
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index f8cb91ef8e..50b6bc7b68 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 66d4537dab..461bef4f72 100644
--- a/ext/standard/exec.c
+++ b/ext/standard/exec.c
@@ -46,10 +46,42 @@
#include <fcntl.h>
#endif
-#if HAVE_NICE && HAVE_UNISTD_H
+#if HAVE_UNISTD_H
#include <unistd.h>
#endif
+#ifdef PHP_WIN32
+# include "win32/php_stdint.h"
+#else
+# if HAVE_INTTYPES_H
+# include <inttypes.h>
+# elif HAVE_STDINT_H
+# include <stdint.h>
+# 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 399325c759..b106838367 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 *);