Skip to content

Commit f4d7bbf

Browse files
committed
backport the escapeshell* functions hardening branch
1 parent 828364e commit f4d7bbf

File tree

3 files changed

+73
-5
lines changed

3 files changed

+73
-5
lines changed

ext/standard/basic_functions.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3674,6 +3674,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
36743674
#ifdef PHP_CAN_SUPPORT_PROC_OPEN
36753675
BASIC_MINIT_SUBMODULE(proc_open)
36763676
#endif
3677+
BASIC_MINIT_SUBMODULE(exec)
36773678

36783679
BASIC_MINIT_SUBMODULE(user_streams)
36793680
BASIC_MINIT_SUBMODULE(imagetypes)

ext/standard/exec.c

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,42 @@
4646
#include <fcntl.h>
4747
#endif
4848

49-
#if HAVE_NICE && HAVE_UNISTD_H
49+
#if HAVE_UNISTD_H
5050
#include <unistd.h>
5151
#endif
5252

53+
#ifdef PHP_WIN32
54+
# include "win32/php_stdint.h"
55+
#else
56+
# if HAVE_INTTYPES_H
57+
# include <inttypes.h>
58+
# elif HAVE_STDINT_H
59+
# include <stdint.h>
60+
# endif
61+
#endif
62+
63+
static int cmd_max_len;
64+
65+
/* {{{ PHP_MINIT_FUNCTION(exec) */
66+
PHP_MINIT_FUNCTION(exec)
67+
{
68+
#ifdef _SC_ARG_MAX
69+
cmd_max_len = sysconf(_SC_ARG_MAX);
70+
#elif defined(ARG_MAX)
71+
cmd_max_len = ARG_MAX;
72+
#elif defined(PHP_WIN32)
73+
/* Executed commands will run through cmd.exe. As long as it's the case,
74+
it's just the constant limit.*/
75+
cmd_max_len = 8192;
76+
#else
77+
/* This is just an arbitrary value for the fallback case. */
78+
cmd_max_len = 4096;
79+
#endif
80+
81+
return SUCCESS;
82+
}
83+
/* }}} */
84+
5385
/* {{{ php_exec
5486
* If type==0, only last line of output is returned (exec)
5587
* If type==1, all lines will be printed and last lined returned (system)
@@ -244,13 +276,20 @@ PHP_FUNCTION(passthru)
244276
*/
245277
PHPAPI char *php_escape_shell_cmd(char *str)
246278
{
247-
register int x, y, l = strlen(str);
279+
register int x, y;
280+
size_t l = strlen(str);
281+
uint64_t estimate = (2 * (uint64_t)l) + 1;
248282
char *cmd;
249283
char *p = NULL;
250-
size_t estimate = (2 * l) + 1;
251284

252285
TSRMLS_FETCH();
253286

287+
/* max command line length - two single quotes - \0 byte length */
288+
if (l > cmd_max_len - 2 - 1) {
289+
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Command exceeds the allowed length of %d bytes", cmd_max_len);
290+
return NULL;
291+
}
292+
254293
cmd = safe_emalloc(2, l, 1);
255294

256295
for (x = 0, y = 0; x < l; x++) {
@@ -322,6 +361,12 @@ PHPAPI char *php_escape_shell_cmd(char *str)
322361
}
323362
cmd[y] = '\0';
324363

364+
if (y - 1 > cmd_max_len) {
365+
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Escaped command exceeds the allowed length of %d bytes", cmd_max_len);
366+
efree(cmd);
367+
return NULL;
368+
}
369+
325370
if ((estimate - y) > 4096) {
326371
/* realloc if the estimate was way overill
327372
* Arbitrary cutoff point of 4096 */
@@ -336,12 +381,19 @@ PHPAPI char *php_escape_shell_cmd(char *str)
336381
*/
337382
PHPAPI char *php_escape_shell_arg(char *str)
338383
{
339-
int x, y = 0, l = strlen(str);
384+
int x, y = 0;
385+
size_t l = strlen(str);
340386
char *cmd;
341-
size_t estimate = (4 * l) + 3;
387+
uint64_t estimate = (4 * (uint64_t)l) + 3;
342388

343389
TSRMLS_FETCH();
344390

391+
/* max command line length - two single quotes - \0 byte length */
392+
if (l > cmd_max_len - 2 - 1) {
393+
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Argument exceeds the allowed length of %d bytes", cmd_max_len);
394+
return NULL;
395+
}
396+
345397
cmd = safe_emalloc(4, l, 3); /* worst case */
346398

347399
#ifdef PHP_WIN32
@@ -396,6 +448,12 @@ PHPAPI char *php_escape_shell_arg(char *str)
396448
#endif
397449
cmd[y] = '\0';
398450

451+
if (y - 1 > cmd_max_len) {
452+
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Escaped argument exceeds the allowed length of %d bytes", cmd_max_len);
453+
efree(cmd);
454+
return NULL;
455+
}
456+
399457
if ((estimate - y) > 4096) {
400458
/* realloc if the estimate was way overill
401459
* Arbitrary cutoff point of 4096 */
@@ -418,6 +476,10 @@ PHP_FUNCTION(escapeshellcmd)
418476
}
419477

420478
if (command_len) {
479+
if (command_len != strlen(command)) {
480+
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Input string contains NULL bytes");
481+
return;
482+
}
421483
cmd = php_escape_shell_cmd(command);
422484
RETVAL_STRING(cmd, 0);
423485
} else {
@@ -439,6 +501,10 @@ PHP_FUNCTION(escapeshellarg)
439501
}
440502

441503
if (argument) {
504+
if (argument_len != strlen(argument)) {
505+
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Input string contains NULL bytes");
506+
return;
507+
}
442508
cmd = php_escape_shell_arg(argument);
443509
RETVAL_STRING(cmd, 0);
444510
}

ext/standard/exec.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ PHP_FUNCTION(proc_close);
3333
PHP_FUNCTION(proc_terminate);
3434
PHP_FUNCTION(proc_nice);
3535
PHP_MINIT_FUNCTION(proc_open);
36+
PHP_MINIT_FUNCTION(exec);
3637

3738
PHPAPI char *php_escape_shell_cmd(char *);
3839
PHPAPI char *php_escape_shell_arg(char *);

0 commit comments

Comments
 (0)