]> granicus.if.org Git - php/commitdiff
Follow up on bug #71270
authorAnatol Belski <ab@php.net>
Tue, 12 Jan 2016 13:41:44 +0000 (14:41 +0100)
committerAnatol Belski <ab@php.net>
Tue, 12 Jan 2016 13:41:44 +0000 (14:41 +0100)
Using the max allowed command line length for an underlying OS.

ext/standard/basic_functions.c
ext/standard/exec.c
ext/standard/exec.h
ext/standard/tests/general_functions/escapeshellarg_bug71270.phpt [new file with mode: 0644]
ext/standard/tests/general_functions/escapeshellcmd_bug71270.phpt [new file with mode: 0644]

index ad9cf1ce2860efd2c9e30513d4e2409daa45bd35..c6d8713d73b3c1dbc7079c1dd1b9da2f891b6de9 100644 (file)
@@ -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)
index c4afce333735961fd33360bec7e0812185ec3465..747f765dd4b508af788b479bce811ced1abbb7e2 100644 (file)
 #include <fcntl.h>
 #endif
 
-#if HAVE_NICE && HAVE_UNISTD_H
+#if (HAVE_NICE || defined(__linux__)) && HAVE_UNISTD_H
 #include <unistd.h>
 #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 */
index e53abf8145ef381b20c8a9bcd58beb6bbafa33c3..b1cbf29c1336da1c3a87cdc7b38f5d6bce119cda 100644 (file)
@@ -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 (file)
index 0000000..c57da36
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+Test escapeshellarg() allowed argument length
+--FILE--
+<?php
+ini_set('memory_limit', -1);
+$var_2  = str_repeat('A', 1024*1024*64);
+escapeshellarg($var_2);
+
+?>
+===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 (file)
index 0000000..4686193
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+Test escapeshellcmd() allowed argument length
+--FILE--
+<?php
+ini_set('memory_limit', -1);
+$var_2  = str_repeat('A', 1024*1024*64);
+escapeshellcmd($var_2);
+
+?>
+===DONE===
+--EXPECTF--
+Fatal error: escapeshellcmd(): Command exceeds the allowed length of %d bytes in %s on line %d