summaryrefslogtreecommitdiffstats
path: root/bug71039.patch
diff options
context:
space:
mode:
Diffstat (limited to 'bug71039.patch')
-rw-r--r--bug71039.patch300
1 files changed, 300 insertions, 0 deletions
diff --git a/bug71039.patch b/bug71039.patch
new file mode 100644
index 0000000..343364c
--- /dev/null
+++ b/bug71039.patch
@@ -0,0 +1,300 @@
+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)