From f44cd6f30eec1d4459efbef0eda5a4e4f6ddbd99 Mon Sep 17 00:00:00 2001 From: Aaron Bannert Date: Thu, 11 Apr 2002 19:27:27 +0000 Subject: [PATCH] PHP filters and Apache 2 aren't quite a perfect match yet, so we have to do some trickery with the server_context to make sure it is always valid within the current thread. This patch makes sure the server_context is created in apache's post_read_request hook phase, and then registeres a cleanup that will NULL out the server context when the request goes out of scope. Then, inside the output filters, if the server_context is null we throw an error. Finally, instead of saving the output filter in the server_context, now we store the entire request_rec pointer in there. POST bodies appear to be working now, although they are very inefficient. The input filter is still just realloc()ing for whatever data comes down the input pipe, and then sending this to PHP. This means that we are doing some really nasty memory management on big POST bodies. For now this it allows for unlimited input bodies, which means that a big POST could potentially DoS a box by making it run out of memory. We might want to put a limit on here just in case, at least until we figure out how to consume input data more efficiently into php. --- sapi/apache2filter/php_apache.h | 2 +- sapi/apache2filter/sapi_apache2.c | 92 +++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/sapi/apache2filter/php_apache.h b/sapi/apache2filter/php_apache.h index e758a860e4..cc60ad2293 100644 --- a/sapi/apache2filter/php_apache.h +++ b/sapi/apache2filter/php_apache.h @@ -21,7 +21,7 @@ typedef struct php_struct { int state; - ap_filter_t *f; + request_rec *r; /* Length of post_data buffer */ int post_len; /* Index for reading from buffer */ diff --git a/sapi/apache2filter/sapi_apache2.c b/sapi/apache2filter/sapi_apache2.c index f0d3b6d4cf..6b44c7ec1d 100644 --- a/sapi/apache2filter/sapi_apache2.c +++ b/sapi/apache2filter/sapi_apache2.c @@ -49,14 +49,16 @@ php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC) apr_bucket *b; apr_bucket_brigade *bb; apr_bucket_alloc_t *ba; + ap_filter_t *f; /* output filters */ php_struct *ctx; ctx = SG(server_context); + f = ctx->r->output_filters; if (str_length == 0) return 0; - ba = ctx->f->c->bucket_alloc; - bb = apr_brigade_create(ctx->f->r->pool, ba); + ba = f->c->bucket_alloc; + bb = apr_brigade_create(ctx->r->pool, ba); b = apr_bucket_transient_create(str, str_length, ba); APR_BRIGADE_INSERT_TAIL(bb, b); @@ -68,7 +70,7 @@ php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC) b = apr_bucket_flush_create(ba); APR_BRIGADE_INSERT_TAIL(bb, b); - if (ap_pass_brigade(ctx->f->next, bb) != APR_SUCCESS) { + if (ap_pass_brigade(f->next, bb) != APR_SUCCESS) { php_handle_aborted_connection(); } @@ -78,9 +80,13 @@ php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC) static int php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC) { - php_struct *ctx = SG(server_context); + php_struct *ctx; + ap_filter_t *f; char *val; + ctx = SG(server_context); + f = ctx->r->output_filters; + val = strchr(sapi_header->header, ':'); if (!val) return 0; @@ -92,11 +98,11 @@ php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_str } while (*val == ' '); if (!strcasecmp(sapi_header->header, "content-type")) - ctx->f->r->content_type = apr_pstrdup(ctx->f->r->pool, val); + ctx->r->content_type = apr_pstrdup(ctx->r->pool, val); else if (sapi_header->replace) - apr_table_set(ctx->f->r->headers_out, sapi_header->header, val); + apr_table_set(ctx->r->headers_out, sapi_header->header, val); else - apr_table_add(ctx->f->r->headers_out, sapi_header->header, val); + apr_table_add(ctx->r->headers_out, sapi_header->header, val); sapi_free_header(sapi_header); @@ -108,7 +114,7 @@ php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) { php_struct *ctx = SG(server_context); - ctx->f->r->status = SG(sapi_headers).http_response_code; + ctx->r->status = SG(sapi_headers).http_response_code; return SAPI_HEADER_SENT_SUCCESSFULLY; } @@ -140,7 +146,7 @@ php_apache_sapi_read_cookies(TSRMLS_D) php_struct *ctx = SG(server_context); const char *http_cookie; - http_cookie = apr_table_get(ctx->f->r->headers_in, "cookie"); + http_cookie = apr_table_get(ctx->r->headers_in, "cookie"); /* The SAPI interface should use 'const char *' */ return (char *) http_cookie; @@ -150,7 +156,7 @@ static void php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC) { php_struct *ctx = SG(server_context); - const apr_array_header_t *arr = apr_table_elts(ctx->f->r->subprocess_env); + const apr_array_header_t *arr = apr_table_elts(ctx->r->subprocess_env); char *key, *val; APR_ARRAY_FOREACH_OPEN(arr, key, val) @@ -158,30 +164,37 @@ php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC) php_register_variable(key, val, track_vars_array TSRMLS_CC); APR_ARRAY_FOREACH_CLOSE() - php_register_variable("PHP_SELF", ctx->f->r->uri, track_vars_array TSRMLS_CC); + php_register_variable("PHP_SELF", ctx->r->uri, track_vars_array TSRMLS_CC); } static void php_apache_sapi_flush(void *server_context) { - php_struct *ctx = server_context; + php_struct *ctx; apr_bucket_brigade *bb; apr_bucket_alloc_t *ba; apr_bucket *b; + ap_filter_t *f; /* output filters */ + + ctx = server_context; + /* If we haven't registered a server_context yet, + * then don't bother flushing. */ if (!server_context) return; + f = ctx->r->output_filters; + /* Send a flush bucket down the filter chain. The current default * handler seems to act on the first flush bucket, but ignores * all further flush buckets. */ - ba = ctx->f->r->connection->bucket_alloc; - bb = apr_brigade_create(ctx->f->r->pool, ba); + ba = ctx->r->connection->bucket_alloc; + bb = apr_brigade_create(ctx->r->pool, ba); b = apr_bucket_flush_create(ba); APR_BRIGADE_INSERT_TAIL(bb, b); - if (ap_pass_brigade(ctx->f->next, bb) != APR_SUCCESS) { + if (ap_pass_brigade(f->next, bb) != APR_SUCCESS) { php_handle_aborted_connection(); } } @@ -198,7 +211,7 @@ static void php_apache_sapi_log_message(char *msg) * line. Not sure if this is correct, but it mirrors what happens * with Apache 1.3 -- rbb */ - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO | APLOG_STARTUP, 0, ctx->f->r->server, "%s", msg); + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO | APLOG_STARTUP, 0, ctx->r->server, "%s", msg); } static sapi_module_struct apache2_sapi_module = { @@ -252,9 +265,11 @@ static int php_input_filter(ap_filter_t *f, apr_bucket_brigade *bb, return ap_get_brigade(f->next, bb, mode, block, readbytes); } - if (SG(server_context) == NULL) { - /* Initialize filter context */ - SG(server_context) = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); + ctx = SG(server_context); + if (ctx == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, f->r, + "php failed to get server context"); + return HTTP_INTERNAL_SERVER_ERROR; } if ((rv = ap_get_brigade(f->next, bb, mode, block, readbytes)) != APR_SUCCESS) { @@ -328,10 +343,11 @@ static int php_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) ap_add_common_vars(f->r); ap_add_cgi_vars(f->r); - if (SG(server_context) == NULL) { - /* Initialize filter context */ - SG(server_context) = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); - ctx->f = f; + ctx = SG(server_context); + if (ctx == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, f->r, + "php failed to get server context"); + return HTTP_INTERNAL_SERVER_ERROR; } if (ctx->request_processed) { @@ -454,10 +470,40 @@ static void php_insert_filter(request_rec *r) } } +static apr_status_t php_server_context_cleanup(void *data_) +{ + void **data = data_; + *data = NULL; + return APR_SUCCESS; +} + +static int php_post_read_request(request_rec *r) +{ + php_struct *ctx; + TSRMLS_FETCH(); + + /* Initialize filter context */ + SG(server_context) = ctx = apr_pcalloc(r->pool, sizeof(*ctx)); + + /* register a cleanup so we clear out the SG(server_context) + * after each request. Note: We pass in the pointer to the + * server_context in case this is handled by a different thread. */ + apr_pool_cleanup_register(r->pool, (void *)&SG(server_context), + php_server_context_cleanup, + apr_pool_cleanup_null); + + /* Save the entire request, so we can get the input or output + * filters if we need them. */ + ctx->r = r; + + return OK; +} + static void php_register_hook(apr_pool_t *p) { ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_insert_filter(php_insert_filter, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_read_request(php_post_read_request, NULL, NULL, APR_HOOK_MIDDLE); ap_register_output_filter("PHP", php_output_filter, AP_FTYPE_RESOURCE); ap_register_input_filter("PHP", php_input_filter, AP_FTYPE_RESOURCE); } -- 2.40.0