301 lines
8.7 KiB
Diff
301 lines
8.7 KiB
Diff
|
Backported from 5.5 for 5.4 by Remi Collet
|
||
|
|
||
|
From f4d7bbf4ace4ea21c8f95c2d1177bd56a21b86b9 Mon Sep 17 00:00:00 2001
|
||
|
From: Anatol Belski <ab@php.net>
|
||
|
Date: Thu, 28 Jan 2016 13:45:43 +0100
|
||
|
Subject: [PATCH] backport the escapeshell* functions hardening branch
|
||
|
|
||
|
---
|
||
|
ext/standard/basic_functions.c | 1 +
|
||
|
ext/standard/exec.c | 76 +++++++++++++++++++++++++++++++++++++++---
|
||
|
ext/standard/exec.h | 1 +
|
||
|
3 files changed, 73 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
|
||
|
index f8cb91e..50b6bc7 100644
|
||
|
--- a/ext/standard/basic_functions.c
|
||
|
+++ b/ext/standard/basic_functions.c
|
||
|
@@ -3613,6 +3613,7 @@
|
||
|
#ifdef PHP_CAN_SUPPORT_PROC_OPEN
|
||
|
PHP_MINIT(proc_open)(INIT_FUNC_ARGS_PASSTHRU);
|
||
|
#endif
|
||
|
+ PHP_MINIT(exec)(INIT_FUNC_ARGS_PASSTHRU);
|
||
|
|
||
|
PHP_MINIT(user_streams)(INIT_FUNC_ARGS_PASSTHRU);
|
||
|
PHP_MINIT(imagetypes)(INIT_FUNC_ARGS_PASSTHRU);
|
||
|
diff --git a/ext/standard/exec.c b/ext/standard/exec.c
|
||
|
index 66d4537..461bef4 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 399325c..b106838 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 *);
|
||
|
From 828364e59ca08c2ff3328a648522f9eb05ebbaa3 Mon Sep 17 00:00:00 2001
|
||
|
From: Anatol Belski <ab@php.net>
|
||
|
Date: Thu, 28 Jan 2016 13:27:26 +0100
|
||
|
Subject: [PATCH] add tests
|
||
|
|
||
|
---
|
||
|
.../tests/general_functions/escapeshellarg_bug71039.phpt | 10 ++++++++++
|
||
|
.../tests/general_functions/escapeshellarg_bug71270.phpt | 12 ++++++++++++
|
||
|
.../tests/general_functions/escapeshellcmd_bug71039.phpt | 10 ++++++++++
|
||
|
.../tests/general_functions/escapeshellcmd_bug71270.phpt | 12 ++++++++++++
|
||
|
4 files changed, 44 insertions(+)
|
||
|
create mode 100644 ext/standard/tests/general_functions/escapeshellarg_bug71039.phpt
|
||
|
create mode 100644 ext/standard/tests/general_functions/escapeshellarg_bug71270.phpt
|
||
|
create mode 100644 ext/standard/tests/general_functions/escapeshellcmd_bug71039.phpt
|
||
|
create mode 100644 ext/standard/tests/general_functions/escapeshellcmd_bug71270.phpt
|
||
|
|
||
|
diff --git a/ext/standard/tests/general_functions/escapeshellarg_bug71039.phpt b/ext/standard/tests/general_functions/escapeshellarg_bug71039.phpt
|
||
|
new file mode 100644
|
||
|
index 0000000..cbb3f6f
|
||
|
--- /dev/null
|
||
|
+++ b/ext/standard/tests/general_functions/escapeshellarg_bug71039.phpt
|
||
|
@@ -0,0 +1,10 @@
|
||
|
+--TEST--
|
||
|
+Test escapeshellarg() string with \0 bytes
|
||
|
+--FILE--
|
||
|
+<?php
|
||
|
+escapeshellarg("hello\0world");
|
||
|
+
|
||
|
+?>
|
||
|
+===DONE===
|
||
|
+--EXPECTF--
|
||
|
+Fatal error: escapeshellarg(): Input string contains NULL bytes in %s on line %d
|
||
|
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 0000000..c57da36
|
||
|
--- /dev/null
|
||
|
+++ b/ext/standard/tests/general_functions/escapeshellarg_bug71270.phpt
|
||
|
@@ -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_bug71039.phpt b/ext/standard/tests/general_functions/escapeshellcmd_bug71039.phpt
|
||
|
new file mode 100644
|
||
|
index 0000000..0a4d7ea
|
||
|
--- /dev/null
|
||
|
+++ b/ext/standard/tests/general_functions/escapeshellcmd_bug71039.phpt
|
||
|
@@ -0,0 +1,10 @@
|
||
|
+--TEST--
|
||
|
+Test escapeshellcmd() string with \0 bytes
|
||
|
+--FILE--
|
||
|
+<?php
|
||
|
+escapeshellcmd("hello\0world");
|
||
|
+
|
||
|
+?>
|
||
|
+===DONE===
|
||
|
+--EXPECTF--
|
||
|
+Fatal error: escapeshellcmd(): Input string contains NULL 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 0000000..4686193
|
||
|
--- /dev/null
|
||
|
+++ b/ext/standard/tests/general_functions/escapeshellcmd_bug71270.phpt
|
||
|
@@ -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
|
||
|
From 377d353c9f8aad6f79f3cf84aad3e2f6d65fa456 Mon Sep 17 00:00:00 2001
|
||
|
From: Anatol Belski <ab@php.net>
|
||
|
Date: Tue, 2 Feb 2016 14:19:10 +0100
|
||
|
Subject: [PATCH] add error check to sysconf call
|
||
|
|
||
|
---
|
||
|
ext/standard/exec.c | 11 +++++++++++
|
||
|
1 file changed, 11 insertions(+)
|
||
|
|
||
|
diff --git a/ext/standard/exec.c b/ext/standard/exec.c
|
||
|
index 461bef4..329066e 100644
|
||
|
--- a/ext/standard/exec.c
|
||
|
+++ b/ext/standard/exec.c
|
||
|
@@ -50,6 +50,10 @@
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
|
||
|
+#if HAVE_LIMITS_H
|
||
|
+#include <limits.h>
|
||
|
+#endif
|
||
|
+
|
||
|
#ifdef PHP_WIN32
|
||
|
# include "win32/php_stdint.h"
|
||
|
#else
|
||
|
@@ -67,6 +71,13 @@ PHP_MINIT_FUNCTION(exec)
|
||
|
{
|
||
|
#ifdef _SC_ARG_MAX
|
||
|
cmd_max_len = sysconf(_SC_ARG_MAX);
|
||
|
+ if (-1 == cmd_max_len) {
|
||
|
+#ifdef _POSIX_ARG_MAX
|
||
|
+ cmd_max_len = _POSIX_ARG_MAX;
|
||
|
+#else
|
||
|
+ cmd_max_len = 4096;
|
||
|
+#endif
|
||
|
+ }
|
||
|
#elif defined(ARG_MAX)
|
||
|
cmd_max_len = ARG_MAX;
|
||
|
#elif defined(PHP_WIN32)
|