]> granicus.if.org Git - php/commitdiff
Encapsulate FastCGI implementation details.
authorDmitry Stogov <dmitry@zend.com>
Tue, 23 Jun 2015 13:27:21 +0000 (16:27 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 23 Jun 2015 13:27:21 +0000 (16:27 +0300)
Previously fcgi_request defined in main/fastcgi.h might be treated differently in different files, because of different behavior of #ifdef TCP_NODELAY. This leaded to stack memory corruption and unpredictable crashes.

main/fastcgi.c
main/fastcgi.h
sapi/cgi/cgi_main.c
sapi/fpm/fpm/fpm_main.c

index 03dcc57b3cc8e2703061a28ad0fc78900ab19721..cd8ca4523cceae0370a460abbc54b06c7429413f 100644 (file)
@@ -132,6 +132,106 @@ static int is_impersonate = 0;
 
 #include "fastcgi.h"
 
+typedef struct _fcgi_header {
+       unsigned char version;
+       unsigned char type;
+       unsigned char requestIdB1;
+       unsigned char requestIdB0;
+       unsigned char contentLengthB1;
+       unsigned char contentLengthB0;
+       unsigned char paddingLength;
+       unsigned char reserved;
+} fcgi_header;
+
+typedef struct _fcgi_begin_request {
+       unsigned char roleB1;
+       unsigned char roleB0;
+       unsigned char flags;
+       unsigned char reserved[5];
+} fcgi_begin_request;
+
+typedef struct _fcgi_begin_request_rec {
+       fcgi_header hdr;
+       fcgi_begin_request body;
+} fcgi_begin_request_rec;
+
+typedef struct _fcgi_end_request {
+    unsigned char appStatusB3;
+    unsigned char appStatusB2;
+    unsigned char appStatusB1;
+    unsigned char appStatusB0;
+    unsigned char protocolStatus;
+    unsigned char reserved[3];
+} fcgi_end_request;
+
+typedef struct _fcgi_end_request_rec {
+       fcgi_header hdr;
+       fcgi_end_request body;
+} fcgi_end_request_rec;
+
+typedef struct _fcgi_hash_bucket {
+       unsigned int              hash_value;
+       unsigned int              var_len;
+       char                     *var;
+       unsigned int              val_len;
+       char                     *val;
+       struct _fcgi_hash_bucket *next;
+       struct _fcgi_hash_bucket *list_next;
+} fcgi_hash_bucket;
+
+typedef struct _fcgi_hash_buckets {
+       unsigned int               idx;
+       struct _fcgi_hash_buckets *next;
+       struct _fcgi_hash_bucket   data[FCGI_HASH_TABLE_SIZE];
+} fcgi_hash_buckets;
+
+typedef struct _fcgi_data_seg {
+       char                  *pos;
+       char                  *end;
+       struct _fcgi_data_seg *next;
+       char                   data[1];
+} fcgi_data_seg;
+
+typedef struct _fcgi_hash {
+       fcgi_hash_bucket  *hash_table[FCGI_HASH_TABLE_SIZE];
+       fcgi_hash_bucket  *list;
+       fcgi_hash_buckets *buckets;
+       fcgi_data_seg     *data;
+} fcgi_hash;
+
+typedef struct _fcgi_req_hook  fcgi_req_hook;
+
+struct _fcgi_req_hook {
+       void(*on_accept)();
+       void(*on_read)();
+       void(*on_close)();
+};
+
+struct _fcgi_request {
+       int            listen_socket;
+       int            tcp;
+       int            fd;
+       int            id;
+       int            keep;
+#ifdef TCP_NODELAY
+       int            nodelay;
+#endif
+       int            closed;
+       int            in_len;
+       int            in_pad;
+
+       fcgi_header   *out_hdr;
+
+       unsigned char *out_pos;
+       unsigned char  out_buf[1024*8];
+       unsigned char  reserved[sizeof(fcgi_end_request_rec)];
+
+       fcgi_req_hook  hook;
+
+       int            has_env;
+       fcgi_hash      env;
+};
+
 /* maybe it's better to use weak name instead */
 #ifndef HAVE_ATTRIBUTE_WEAK
 static fcgi_logger fcgi_log;
@@ -772,9 +872,9 @@ static void fcgi_hook_dummy() {
        return;
 }
 
-fcgi_request *fcgi_init_request(fcgi_request *req, int listen_socket)
+fcgi_request *fcgi_init_request(int listen_socket, void(*on_accept)(), void(*on_read)(), void(*on_close)())
 {
-       memset(req, 0, sizeof(fcgi_request));
+       fcgi_request *req = calloc(1, sizeof(fcgi_request));
        req->listen_socket = listen_socket;
        req->fd = -1;
        req->id = -1;
@@ -794,9 +894,9 @@ fcgi_request *fcgi_init_request(fcgi_request *req, int listen_socket)
 
        */
        req->out_pos = req->out_buf;
-       req->hook.on_accept = fcgi_hook_dummy;
-       req->hook.on_read = fcgi_hook_dummy;
-       req->hook.on_close = fcgi_hook_dummy;
+       req->hook.on_accept = on_accept ? on_accept : fcgi_hook_dummy;
+       req->hook.on_read = on_read ? on_read : fcgi_hook_dummy;
+       req->hook.on_close = on_close ? on_close : fcgi_hook_dummy;
 
 #ifdef _WIN32
        req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
@@ -809,6 +909,7 @@ fcgi_request *fcgi_init_request(fcgi_request *req, int listen_socket)
 
 void fcgi_destroy_request(fcgi_request *req) {
        fcgi_hash_destroy(&req->env);
+       free(req);
 }
 
 static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
@@ -1553,6 +1654,11 @@ int fcgi_finish_request(fcgi_request *req, int force_close)
        return ret;
 }
 
+int fcgi_has_env(fcgi_request *req)
+{
+       return req && req->has_env;
+}
+
 char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
 {
        unsigned int val_len;
index 7e56f0ef8924877ab901d23376a5d47985cc6fde..df7d9ed314fcef2e7db0a33570c0be6f015dd7bf 100644 (file)
@@ -77,43 +77,6 @@ typedef enum _fcgi_protocol_status {
        FCGI_UNKNOWN_ROLE               = 3
 } dcgi_protocol_status;
 
-typedef struct _fcgi_header {
-       unsigned char version;
-       unsigned char type;
-       unsigned char requestIdB1;
-       unsigned char requestIdB0;
-       unsigned char contentLengthB1;
-       unsigned char contentLengthB0;
-       unsigned char paddingLength;
-       unsigned char reserved;
-} fcgi_header;
-
-typedef struct _fcgi_begin_request {
-       unsigned char roleB1;
-       unsigned char roleB0;
-       unsigned char flags;
-       unsigned char reserved[5];
-} fcgi_begin_request;
-
-typedef struct _fcgi_begin_request_rec {
-       fcgi_header hdr;
-       fcgi_begin_request body;
-} fcgi_begin_request_rec;
-
-typedef struct _fcgi_end_request {
-    unsigned char appStatusB3;
-    unsigned char appStatusB2;
-    unsigned char appStatusB1;
-    unsigned char appStatusB0;
-    unsigned char protocolStatus;
-    unsigned char reserved[3];
-} fcgi_end_request;
-
-typedef struct _fcgi_end_request_rec {
-       fcgi_header hdr;
-       fcgi_end_request body;
-} fcgi_end_request_rec;
-
 /* FastCGI client API */
 
 typedef void (*fcgi_apply_func)(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg);
@@ -122,69 +85,7 @@ typedef void (*fcgi_apply_func)(char *var, unsigned int var_len, char *val, unsi
 #define FCGI_HASH_TABLE_MASK (FCGI_HASH_TABLE_SIZE - 1)
 #define FCGI_HASH_SEG_SIZE   4096
 
-typedef struct _fcgi_hash_bucket {
-       unsigned int              hash_value;
-       unsigned int              var_len;
-       char                     *var;
-       unsigned int              val_len;
-       char                     *val;
-       struct _fcgi_hash_bucket *next;
-       struct _fcgi_hash_bucket *list_next;
-} fcgi_hash_bucket;
-
-typedef struct _fcgi_hash_buckets {
-       unsigned int               idx;
-       struct _fcgi_hash_buckets *next;
-       struct _fcgi_hash_bucket   data[FCGI_HASH_TABLE_SIZE];
-} fcgi_hash_buckets;
-
-typedef struct _fcgi_data_seg {
-       char                  *pos;
-       char                  *end;
-       struct _fcgi_data_seg *next;
-       char                   data[1];
-} fcgi_data_seg;
-
-typedef struct _fcgi_hash {
-       fcgi_hash_bucket  *hash_table[FCGI_HASH_TABLE_SIZE];
-       fcgi_hash_bucket  *list;
-       fcgi_hash_buckets *buckets;
-       fcgi_data_seg     *data;
-} fcgi_hash;
-
-typedef struct _fcgi_request   fcgi_request;
-typedef struct _fcgi_req_hook  fcgi_req_hook;
-
-struct _fcgi_req_hook {
-       void(*on_accept)();
-       void(*on_read)();
-       void(*on_close)();
-};
-
-struct _fcgi_request {
-       int            listen_socket;
-       int            tcp;
-       int            fd;
-       int            id;
-       int            keep;
-#ifdef TCP_NODELAY
-       int            nodelay;
-#endif
-       int            closed;
-       int            in_len;
-       int            in_pad;
-
-       fcgi_header   *out_hdr;
-
-       unsigned char *out_pos;
-       unsigned char  out_buf[1024*8];
-       unsigned char  reserved[sizeof(fcgi_end_request_rec)];
-
-       fcgi_req_hook  hook;
-
-       int            has_env;
-       fcgi_hash      env;
-};
+typedef struct _fcgi_request fcgi_request;
 
 int fcgi_init(void);
 void fcgi_shutdown(void);
@@ -194,7 +95,7 @@ void fcgi_close(fcgi_request *req, int force, int destroy);
 int fcgi_in_shutdown(void);
 void fcgi_terminate(void);
 int fcgi_listen(const char *path, int backlog);
-fcgi_request* fcgi_init_request(fcgi_request *request, int listen_socket);
+fcgi_request* fcgi_init_request(int listen_socket, void(*on_accept)(), void(*on_read)(), void(*on_close)());
 void fcgi_destroy_request(fcgi_request *req);
 void fcgi_set_allowed_clients(char *ip);
 int fcgi_accept_request(fcgi_request *req);
@@ -207,6 +108,7 @@ typedef void (*fcgi_logger)(int type, const char *fmt, ...);
 void fcgi_set_logger(fcgi_logger lg);
 #endif
 
+int   fcgi_has_env(fcgi_request *req);
 char* fcgi_getenv(fcgi_request *req, const char* var, int var_len);
 char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val);
 char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value);
index eb6ef664270a895fc3a8fdcf2a7be048b867a3ee..73b9d774a71582cb1511066cde8c6118d9504bfd 100644 (file)
@@ -1036,12 +1036,12 @@ static int is_valid_path(const char *path)
 /* }}} */
 
 #define CGI_GETENV(name) \
-       ((request->has_env) ? \
+       ((has_env) ? \
                FCGI_GETENV(request, name) : \
        getenv(name))
 
 #define CGI_PUTENV(name, value) \
-       ((request->has_env) ? \
+       ((has_env) ? \
                FCGI_PUTENV(request, name, value) : \
                _sapi_cgi_putenv(name, sizeof(name)-1, value))
 
@@ -1113,6 +1113,7 @@ static int is_valid_path(const char *path)
  */
 static void init_request_info(fcgi_request *request)
 {
+       int has_env = fcgi_has_env(request);
        char *env_script_filename = CGI_GETENV("SCRIPT_FILENAME");
        char *env_path_translated = CGI_GETENV("PATH_TRANSLATED");
        char *script_path_translated = env_script_filename;
@@ -1734,7 +1735,7 @@ int main(int argc, char *argv[])
        int fastcgi;
        char *bindpath = NULL;
        int fcgi_fd = 0;
-       fcgi_request request = {0};
+       fcgi_request *request = NULL;
        int warmup_repeats = 0;
        int repeats = 1;
        int benchmark = 0;
@@ -1972,7 +1973,7 @@ consult the installation file that came with this distribution, or visit \n\
                php_import_environment_variables = cgi_php_import_environment_variables;
 
                /* library is already initialized, now init our request */
-               fcgi_init_request(&request, fcgi_fd);
+               request = fcgi_init_request(fcgi_fd, NULL, NULL, NULL);
 
 #ifndef PHP_WIN32
                /* Pre-fork, if required */
@@ -2101,8 +2102,8 @@ consult the installation file that came with this distribution, or visit \n\
                                        break;
                                case 'h':
                                case '?':
-                                       if (request.listen_socket) {
-                                               fcgi_destroy_request(&request);
+                                       if (request) {
+                                               fcgi_destroy_request(request);
                                        }
                                        fcgi_shutdown();
                                        no_headers = 1;
@@ -2125,9 +2126,9 @@ consult the installation file that came with this distribution, or visit \n\
                        fcgi_impersonate();
                }
 #endif
-               while (!fastcgi || fcgi_accept_request(&request) >= 0) {
-                       SG(server_context) = fastcgi ? (void *)&request : (void *) 1;
-                       init_request_info(&request);
+               while (!fastcgi || fcgi_accept_request(request) >= 0) {
+                       SG(server_context) = fastcgi ? (void *)request : (void *) 1;
+                       init_request_info(request);
 
                        if (!cgi && !fastcgi) {
                                while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
@@ -2312,7 +2313,7 @@ consult the installation file that came with this distribution, or visit \n\
                         * get path_translated */
                        if (php_request_startup() == FAILURE) {
                                if (fastcgi) {
-                                       fcgi_finish_request(&request, 1);
+                                       fcgi_finish_request(request, 1);
                                }
                                SG(server_context) = NULL;
                                php_module_shutdown();
@@ -2523,7 +2524,7 @@ fastcgi_request_done:
                        /* only fastcgi will get here */
                        requests++;
                        if (max_requests && (requests == max_requests)) {
-                               fcgi_finish_request(&request, 1);
+                               fcgi_finish_request(request, 1);
                                if (bindpath) {
                                        free(bindpath);
                                }
@@ -2536,8 +2537,8 @@ fastcgi_request_done:
                        /* end of fastcgi loop */
                }
                
-               if (request.listen_socket) {
-                       fcgi_destroy_request(&request);
+               if (request) {
+                       fcgi_destroy_request(request);
                }
                fcgi_shutdown();
 
index 001355ec1e8960419dbacafd539810eaed9b6458..e836c7a284e5323aa59d931e22d68c688640f59f 100644 (file)
@@ -561,7 +561,7 @@ static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, uns
 
 void cgi_php_import_environment_variables(zval *array_ptr) /* {{{ */
 {
-       fcgi_request *request;
+       fcgi_request *request = NULL;
 
        if (Z_TYPE(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
                Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_ENV]) &&
@@ -1391,11 +1391,12 @@ static void init_request_info(void)
 }
 /* }}} */
 
-static void fpm_init_request(fcgi_request *req, int listen_fd) /* {{{ */ {
-       fcgi_init_request(req, listen_fd);
-       req->hook.on_accept = fpm_request_accepting;
-       req->hook.on_read = fpm_request_reading_headers;
-       req->hook.on_close = fpm_request_finished;
+static fcgi_request *fpm_init_request(int listen_fd) /* {{{ */ {
+       fcgi_request *req = fcgi_init_request(listen_fd,
+               fpm_request_accepting,
+               fpm_request_reading_headers,
+               fpm_request_finished);
+       return req;
 }
 /* }}} */
 
@@ -1562,7 +1563,7 @@ int main(int argc, char *argv[])
        int max_requests = 500;
        int requests = 0;
        int fcgi_fd = 0;
-       fcgi_request request;
+       fcgi_request *request;
        char *fpm_config = NULL;
        char *fpm_prefix = NULL;
        char *fpm_pid = NULL;
@@ -1862,13 +1863,13 @@ consult the installation file that came with this distribution, or visit \n\
        php_import_environment_variables = cgi_php_import_environment_variables;
 
        /* library is already initialized, now init our request */
-       fpm_init_request(&request, fcgi_fd);
+       request = fpm_init_request(fcgi_fd);
 
        zend_first_try {
-               while (EXPECTED(fcgi_accept_request(&request) >= 0)) {
+               while (EXPECTED(fcgi_accept_request(request) >= 0)) {
                        char *primary_script = NULL;
                        request_body_fd = -1;
-                       SG(server_context) = (void *) &request;
+                       SG(server_context) = (void *) request;
                        init_request_info();
 
                        fpm_request_info();
@@ -1876,7 +1877,7 @@ consult the installation file that came with this distribution, or visit \n\
                        /* request startup only after we've done all we can to
                         *            get path_translated */
                        if (UNEXPECTED(php_request_startup() == FAILURE)) {
-                               fcgi_finish_request(&request, 1);
+                               fcgi_finish_request(request, 1);
                                SG(server_context) = NULL;
                                php_module_shutdown();
                                return FPM_EXIT_SOFTWARE;
@@ -1969,12 +1970,12 @@ fastcgi_request_done:
 
                        requests++;
                        if (UNEXPECTED(max_requests && (requests == max_requests))) {
-                               fcgi_finish_request(&request, 1);
+                               fcgi_finish_request(request, 1);
                                break;
                        }
                        /* end of fastcgi loop */
                }
-               fcgi_destroy_request(&request);
+               fcgi_destroy_request(request);
                fcgi_shutdown();
 
                if (cgi_sapi_module.php_ini_path_override) {