]> granicus.if.org Git - php/commitdiff
1) Make the output of system() binary safe
authorIlia Alshanetsky <iliaa@php.net>
Wed, 26 Feb 2003 22:11:12 +0000 (22:11 +0000)
committerIlia Alshanetsky <iliaa@php.net>
Wed, 26 Feb 2003 22:11:12 +0000 (22:11 +0000)
2) Solved a memory leak when the return_value variable passed by reference is
   not an integer in system()/exec()/passthru().
3) Solved a bug in exec(), which would make it append to the 2nd parameter
   (passed by reference) if the parameter is an array instead of overwriting it.
4) Changed the code to use the streams code, resulting in a smaller code base.
5) Various cleanups resulting in reduction of overall code base inside the file
   by ~ 1/3.
6) Speed improvements of ~2.5 times compared to previous performance (based on
   attached PHP script).

ext/standard/exec.c
ext/standard/exec.h

index 375609abc542fb778f57f45d22cc2cfb06197756..7d17c8e00f0247b5a6508e285755307098da85b6 100644 (file)
@@ -12,7 +12,8 @@
    | obtain it through the world-wide-web, please send a note to          |
    | license@php.net so we can mail you a copy immediately.               |
    +----------------------------------------------------------------------+
-   | Author: Rasmus Lerdorf                                               |
+   | Author: Rasmus Lerdorf <rasmus@php.net>                              |
+   |         Ilia Alshanetsky <iliaa@php.net>                             |
    +----------------------------------------------------------------------+
  */
 /* $Id$ */
 #include <unistd.h>
 #endif
 
-/* {{{ php_Exec
+/* {{{ 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)
  * If type==2, all lines will be saved to given array (exec with &$array)
  * If type==3, output will be printed binary, no lines will be saved or returned (passthru)
  *
  */
-int php_Exec(int type, char *cmd, pval *array, pval *return_value TSRMLS_DC)
+int php_exec(int type, char *cmd, pval *array, pval *return_value TSRMLS_DC)
 {
        FILE *fp;
        char *buf, *tmp=NULL;
-       int buflen = 0;
-       int t, l, output=1;
-       int overflow_limit, lcmd, ldir;
-       char *b, *c, *d=NULL;
-       php_stream *stream = NULL;
-       int pclose_return = 0;
+       int buflen, l, pclose_return;
+       char *cmd_p, *b, *c, *d=NULL;
+       php_stream *stream;
+       size_t bufl = 0;
 #if PHP_SIGCHILD
        void (*sig_handler)();
 #endif
 
-       buf = (char *) emalloc(EXEC_INPUT_BUF);
-       buflen = EXEC_INPUT_BUF;
-
        if (PG(safe_mode)) {
-               lcmd = strlen(cmd);
-               ldir = strlen(PG(safe_mode_exec_dir));
-               l = lcmd + ldir + 2;
-               overflow_limit = l;
-               c = strchr(cmd, ' ');
-               if (c) *c = '\0';
+               if ((c = strchr(cmd, ' '))) {
+                       *c = '\0';
+                       c++;
+               }
                if (strstr(cmd, "..")) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "No '..' components allowed in path");
-                       efree(buf);
-                       return -1;
+                       goto err;
                }
-               d = emalloc(l);
-               strcpy(d, PG(safe_mode_exec_dir));
-               overflow_limit -= ldir;
                b = strrchr(cmd, PHP_DIR_SEPARATOR);
-               if (b) {
-                       strcat(d, b);
-                       overflow_limit -= strlen(b);
-               } else {
-                       strcat(d, "/");
-                       strcat(d, cmd);
-                       overflow_limit-=(strlen(cmd)+1);
-               }
+               spprintf(&d, 0, "%s%s%s%s", PG(safe_mode_exec_dir), (b ? "" : "/"), (b ? b : cmd), (c ? " " : ""), (c ? c : ""));
                if (c) {
-                       *c = ' ';
-                       strncat(d, c, overflow_limit);
+                       *(c - 1) = ' ';
                }
-               tmp = php_escape_shell_cmd(d);
+               cmd_p = php_escape_shell_cmd(d);
                efree(d);
-               d = tmp;
-#if PHP_SIGCHILD
-               sig_handler = signal (SIGCHLD, SIG_DFL);
-#endif
-#ifdef PHP_WIN32
-               fp = VCWD_POPEN(d, "rb");
-#else
-               fp = VCWD_POPEN(d, "r");
-#endif
-               if (!fp) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", d);
-                       efree(d);
-                       efree(buf);
-#if PHP_SIGCHILD
-                       signal (SIGCHLD, sig_handler);
-#endif
-                       return -1;
-               }
+               d = cmd_p;
+       } else {
+               cmd_p = cmd;
+       }
 
-       } else { /* not safe_mode */
 #if PHP_SIGCHILD
-               sig_handler = signal (SIGCHLD, SIG_DFL);
+       sig_handler = signal (SIGCHLD, SIG_DFL);
 #endif
+
 #ifdef PHP_WIN32
-               fp = VCWD_POPEN(cmd, "rb");
+       fp = VCWD_POPEN(cmd_p, "rb");
 #else
-               fp = VCWD_POPEN(cmd, "r");
+       fp = VCWD_POPEN(cmd_p, "r");
 #endif
-               if (!fp) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
-                       efree(buf);
-#if PHP_SIGCHILD
-                       signal (SIGCHLD, sig_handler);
-#endif
-                       return -1;
-               }
+       if (!fp) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
+               goto err;
        }
-       buf[0] = '\0';
-       if (type==2) {
-               if (Z_TYPE_P(array) != IS_ARRAY) {
-                       pval_destructor(array);
-                       array_init(array);
-               }
-       }
-
-       /* we register the resource so that case of an aborted connection the 
-        * fd gets pclosed
-        */
 
        stream = php_stream_fopen_from_pipe(fp, "rb");
 
-       if (type != 3) {
-               l=0;
-               while ( !feof(fp) || l != 0 ) {
-                       l = 0;
-                       /* Read a line or fill the buffer, whichever comes first */
-                       do {
-                               if ( buflen <= (l+1) ) {
-                                       buf = erealloc(buf, buflen + EXEC_INPUT_BUF);
-                                       buflen += EXEC_INPUT_BUF;
-                               }
+       buf = (char *) emalloc(EXEC_INPUT_BUF);
+       buflen = EXEC_INPUT_BUF;
 
-                               if ( fgets(&(buf[l]), buflen - l, fp) == NULL ) {
-                                       /* eof */
-                                       break;
+       if (type != 3) {
+               b = buf;
+               
+               while (php_stream_get_line(stream, b, EXEC_INPUT_BUF, &bufl)) {
+                       /* no new line found, let's read some more */
+                       if (b[bufl - 1] != '\n' && !php_stream_eof(stream)) {
+                               if (buflen < (bufl + EXEC_INPUT_BUF)) {
+                                       bufl += b - buf;
+                                       buflen = bufl + EXEC_INPUT_BUF;
+                                       buf = erealloc(buf, buflen);
+                                       b = buf + bufl;
+                               } else {
+                                       b += bufl;
                                }
-                               l += strlen(&(buf[l]));
-                       } while ( (l > 0) && (buf[l-1] != '\n') );
-
-                       if ( feof(fp) && (l == 0) ) {
-                               break;
+                               continue;
+                       } else if (b != buf) {
+                               bufl += buflen - EXEC_INPUT_BUF;
                        }
 
-               
                        if (type == 1) {
-                               if (output) PUTS(buf);
+                               PHPWRITE(buf, bufl);
                                sapi_flush(TSRMLS_C);
-                       }
-                       else if (type == 2) {
+                       } else if (type == 2) {
                                /* strip trailing whitespaces */        
-                               l = strlen(buf);
-                               t = l;
-                               while (l-- && isspace((int)buf[l]));
-                               if (l < t) {
-                                       buf[l + 1] = '\0';
+                               l = bufl;
+                               while (l-- && isspace(buf[l]));
+                               if (l != (bufl - 1)) {
+                                       bufl = l + 1;
+                                       buf[bufl] = '\0';
                                }
-                               add_next_index_string(array, buf, 1);
+                               add_next_index_stringl(array, buf, bufl, 1);
                        }
+                       b = buf;
                }
+               if (bufl) {
+                       /* strip trailing whitespaces if we have not done so already */ 
+                       if (type != 2) {
+                               l = bufl;
+                               while (l-- && isspace(buf[l]));
+                               if (l != (bufl - 1)) {
+                                       bufl = l + 1;
+                                       buf[bufl] = '\0';
+                               }
+                       }
 
-               /* strip trailing spaces */
-               l = strlen(buf);
-               t = l;
-               while (l && isspace((int)buf[l - 1])) {
-                       l--;
-               }
-               if (l < t) buf[l] = '\0';
-
-               /* Return last line from the shell command */
-               if (PG(magic_quotes_runtime)) {
-                       int len;
-
-                       tmp = php_addslashes(buf, 0, &len, 0 TSRMLS_CC);
-                       RETVAL_STRINGL(tmp, len, 0);
-               } else {
-                       RETVAL_STRINGL(buf, l, 1);
+                       /* Return last line from the shell command */
+                       if (PG(magic_quotes_runtime)) {
+                               int len;
+       
+                               tmp = php_addslashes(buf, bufl, &len, 0 TSRMLS_CC);
+                               RETVAL_STRINGL(tmp, len, 0);
+                       } else {
+                               RETVAL_STRINGL(buf, bufl, 1);
+                       }
+               } else { /* should return NULL, but for BC we return "" */
+                       RETVAL_EMPTY_STRING();
                }
        } else {
-               size_t b;
-
-               while((b = php_stream_read(stream, buf, EXEC_INPUT_BUF)) > 0) {
-                       if (output) {
-                               PHPWRITE(buf, b);
-                       }
+               while((bufl = php_stream_read(stream, buf, EXEC_INPUT_BUF)) > 0) {
+                       PHPWRITE(buf, bufl);
                }
        }
 
        pclose_return = php_stream_close(stream); 
+       efree(buf);
 
+done:
 #if PHP_SIGCHILD
        signal (SIGCHLD, sig_handler);
 #endif
        if (d) {
                efree(d);
        }
-       efree(buf);
        return pclose_return;
+err:
+       pclose_return = -1;
+       goto done;
 }
 /* }}} */
 
-/* {{{ proto string exec(string command [, array output [, int return_value]])
-   Execute an external program */
-PHP_FUNCTION(exec)
+static void php_exec_ex(INTERNAL_FUNCTION_PARAMETERS, int mode)
 {
-       pval **arg1, **arg2, **arg3;
-       int arg_count = ZEND_NUM_ARGS();
+       char *cmd;
+       int cmd_len;
+       zval *ret_code=NULL, *ret_array=NULL;
        int ret;
 
-       if (arg_count < 1 || arg_count > 3 || zend_get_parameters_ex(arg_count, &arg1, &arg2, &arg3) == FAILURE) {
-               WRONG_PARAM_COUNT;
+       if (mode) {
+               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &cmd, &cmd_len, &ret_code) == FAILURE) {
+                       RETURN_FALSE;
+               }
+       } else {
+               if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|zz", &cmd, &cmd_len, &ret_array, &ret_code) == FAILURE) {
+                       RETURN_FALSE;
+               }
        }
-       
-       if (!Z_STRLEN_PP(arg1)) {
-               PHP_EMPTY_EXEC_PARAM;
+       if (!cmd_len) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute a blank command");
+               RETURN_FALSE;
        }
-       
-       switch (arg_count) {
-               case 1:
-                       ret = php_Exec(0, Z_STRVAL_PP(arg1), NULL, return_value TSRMLS_CC);
-                       break;
-               case 2:
-                       ret = php_Exec(2, Z_STRVAL_PP(arg1), *arg2, return_value TSRMLS_CC);
-                       break;
-               case 3:
-                       ret = php_Exec(2, Z_STRVAL_PP(arg1), *arg2, return_value TSRMLS_CC);
-                       Z_TYPE_PP(arg3) = IS_LONG;
-                       Z_LVAL_PP(arg3)=ret;
-                       break;
+
+       if (!ret_array) {
+               ret = php_exec(mode, cmd, NULL, return_value TSRMLS_CC);
+       } else {
+               zval_dtor(ret_array);
+               array_init(ret_array);
+               ret = php_exec(2, cmd, ret_array, return_value TSRMLS_CC);
+       }
+       if (ret_code) {
+               zval_dtor(ret_code);
+               ZVAL_LONG(ret_code, ret);
        }
 }
 
+/* {{{ proto string exec(string command [, array &output [, int &return_value]])
+   Execute an external program */
+PHP_FUNCTION(exec)
+{
+       php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+
 /* }}} */
 
-/* {{{ proto int system(string command [, int return_value])
+/* {{{ proto int system(string command [, int &return_value])
    Execute an external program and display output */
 PHP_FUNCTION(system)
 {
-       pval **arg1, **arg2;
-       int arg_count = ZEND_NUM_ARGS();
-       int ret;
-
-       if (arg_count < 1 || arg_count > 2 || zend_get_parameters_ex(arg_count, &arg1, &arg2) == FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
-       
-       if (!Z_STRLEN_PP(arg1)) {
-               PHP_EMPTY_EXEC_PARAM;
-       }
-       
-       switch (arg_count) {
-               case 1:
-                       ret = php_Exec(1, Z_STRVAL_PP(arg1), NULL, return_value TSRMLS_CC);
-                       break;
-               case 2:
-                       ret = php_Exec(1, Z_STRVAL_PP(arg1), NULL, return_value TSRMLS_CC);
-                       Z_TYPE_PP(arg2) = IS_LONG;
-                       Z_LVAL_PP(arg2)=ret;
-                       break;
-       }
+       php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
 }
 /* }}} */
 
-/* {{{ proto void passthru(string command [, int return_value])
+/* {{{ proto void passthru(string command [, int &return_value])
    Execute an external program and display raw output */
 PHP_FUNCTION(passthru)
 {
-       pval **arg1, **arg2;
-       int arg_count = ZEND_NUM_ARGS();
-       int ret;
-
-       if (arg_count < 1 || arg_count > 2 || zend_get_parameters_ex(arg_count, &arg1, &arg2) == FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
-       
-       if (!Z_STRLEN_PP(arg1)) {
-               PHP_EMPTY_EXEC_PARAM;
-       }
-       
-       switch (arg_count) {
-               case 1:
-                       ret = php_Exec(3, Z_STRVAL_PP(arg1), NULL, return_value TSRMLS_CC);
-                       break;
-               case 2:
-                       ret = php_Exec(3, Z_STRVAL_PP(arg1), NULL, return_value TSRMLS_CC);
-                       Z_TYPE_PP(arg2) = IS_LONG;
-                       Z_LVAL_PP(arg2)=ret;
-                       break;
-       }
+       php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
 }
 /* }}} */
 
index a0d4ab46aef5aa6a97da38baf890bd07fb89182c..6d5fb66f701794c2550159e129ba3ae9744742fd 100644 (file)
@@ -36,8 +36,6 @@ PHP_MINIT_FUNCTION(proc_open);
 
 char *php_escape_shell_cmd(char *);
 char *php_escape_shell_arg(char *);
-int php_Exec(int type, char *cmd, pval *array, pval *return_value TSRMLS_DC);
-
-#define PHP_EMPTY_EXEC_PARAM { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute a blank command"); RETURN_FALSE; }
+int php_exec(int type, char *cmd, pval *array, pval *return_value TSRMLS_DC);
 
 #endif /* EXEC_H */