1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

Prevent the server from crashing when entering infinite loops. The

new LimitInternalRecursion directive configures limits of subsequent
internal redirects and nested subrequests, after which the request
will be aborted.
[William Rowe, Jeff Trawick, Andr� Malo]

PR: 19753 (and probably others)


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@99911 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
André Malo
2003-05-19 01:19:55 +00:00
parent 506c90cde1
commit 1af02389bb
5 changed files with 227 additions and 3 deletions

View File

@@ -2,6 +2,12 @@ Changes with Apache 2.1.0-dev
[Remove entries to the current 2.0 section below, when backported]
*) Prevent the server from crashing when entering infinite loops. The
new LimitInternalRecursion directive configures limits of subsequent
internal redirects and nested subrequests, after which the request
will be aborted. PR 19753 (and probably others).
[William Rowe, Jeff Trawick, Andr<64> Malo]
*) mod_rewrite: Fix LA-U variable look ahead which didn't work correctly
in directory context. Related to PR 8493. [Andr<64> Malo]

View File

@@ -134,6 +134,12 @@ extern "C" {
*/
#define AP_MIN_BYTES_TO_WRITE 8000
/* default maximum of internal redirects */
# define AP_DEFAULT_MAX_INTERNAL_REDIRECTS 10
/* default maximum subrequest nesting level */
# define AP_DEFAULT_MAX_SUBREQ_DEPTH 10
/**
* Retrieve the value of Options for this request
* @param r The current request
@@ -257,6 +263,22 @@ AP_DECLARE(size_t) ap_get_limit_xml_body(const request_rec *r);
*/
AP_DECLARE(void) ap_custom_response(request_rec *r, int status, const char *string);
/**
* Check if the current request is beyond the configured max. number of redirects
* @param r The current request
* @return true (is exceeded) or false
* @deffunc int ap_is_redirect_limit_exceeded(const request_rec *r)
*/
AP_DECLARE(int) ap_is_redirect_limit_exceeded(const request_rec *r);
/**
* Check if the current request is beyond the configured subreq nesting level
* @param r The current request
* @return true (is exceeded) or false
* @deffunc int ap_is_subreq_limit_exceeded(const request_rec *r)
*/
AP_DECLARE(int) ap_is_subreq_limit_exceeded(const request_rec *r);
/**
* Check for a definition from the server command line
* @param name The define to check for
@@ -560,6 +582,10 @@ typedef struct {
char *access_name;
apr_array_header_t *sec_dir;
apr_array_header_t *sec_url;
/* recursion backstopper */
int redirect_limit; /* maximum number of internal redirects */
int subreq_limit; /* maximum nesting level of subrequests */
} core_server_config;
/* for AddOutputFiltersByType in core.c */

View File

@@ -332,8 +332,14 @@ static apr_table_t *rename_original_env(apr_pool_t *p, apr_table_t *t)
static request_rec *internal_internal_redirect(const char *new_uri,
request_rec *r) {
int access_status;
request_rec *new = (request_rec *) apr_pcalloc(r->pool,
sizeof(request_rec));
request_rec *new;
if (ap_is_redirect_limit_exceeded(r)) {
ap_die(HTTP_INTERNAL_SERVER_ERROR, r);
return NULL;
}
new = (request_rec *) apr_pcalloc(r->pool, sizeof(request_rec));
new->connection = r->connection;
new->server = r->server;
@@ -499,7 +505,14 @@ AP_DECLARE(void) ap_internal_fast_redirect(request_rec *rr, request_rec *r)
AP_DECLARE(void) ap_internal_redirect(const char *new_uri, request_rec *r)
{
request_rec *new = internal_internal_redirect(new_uri, r);
int access_status = ap_process_request_internal(new);
int access_status;
/* ap_die was already called, if an error occured */
if (!new) {
return;
}
access_status = ap_process_request_internal(new);
if (access_status == OK) {
if ((access_status = ap_invoke_handler(new)) != 0) {
ap_die(access_status, new);
@@ -520,6 +533,12 @@ AP_DECLARE(void) ap_internal_redirect_handler(const char *new_uri, request_rec *
{
int access_status;
request_rec *new = internal_internal_redirect(new_uri, r);
/* ap_die was already called, if an error occured */
if (!new) {
return;
}
if (r->handler)
ap_set_content_type(new, r->content_type);
access_status = ap_process_request_internal(new);

View File

@@ -468,6 +468,10 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
conf->sec_dir = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
conf->sec_url = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
/* recursion stopper */
conf->redirect_limit = 0; /* 0 == unset */
conf->subreq_limit = 0;
return (void *)conf;
}
@@ -491,6 +495,14 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
conf->sec_dir = apr_array_append(p, base->sec_dir, virt->sec_dir);
conf->sec_url = apr_array_append(p, base->sec_url, virt->sec_url);
conf->redirect_limit = virt->redirect_limit
? virt->redirect_limit
: base->redirect_limit;
conf->subreq_limit = virt->subreq_limit
? virt->subreq_limit
: base->subreq_limit;
return conf;
}
@@ -2613,6 +2625,141 @@ static const char *set_limit_nproc(cmd_parms *cmd, void *conf_,
}
#endif
static const char *set_recursion_limit(cmd_parms *cmd, void *dummy,
const char *arg1, const char *arg2)
{
core_server_config *conf = ap_get_module_config(cmd->server->module_config,
&core_module);
int limit = atoi(arg1);
if (limit <= 0) {
return "The recursion limit must be greater than zero.";
}
if (limit < 4) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
"Limiting internal redirects to very low numbers may "
"cause normal requests to fail.");
}
conf->redirect_limit = limit;
if (arg2) {
limit = atoi(arg2);
if (limit <= 0) {
return "The recursion limit must be greater than zero.";
}
if (limit < 4) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
"Limiting the subrequest depth to a very low level may"
" cause normal requests to fail.");
}
}
conf->subreq_limit = limit;
return NULL;
}
static void log_backtrace(const request_rec *top, const request_rec *r)
{
while (top) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"redirected from r->uri = %s",
top->uri ? top->uri : "(unexpectedly NULL)");
if (!top->prev && top->main) {
top = top->main;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"subrequested from r->uri = %s",
top->uri ? top->uri : "(unexpectedly NULL)");
}
else {
top = top->prev;
}
}
}
/*
* check whether redirect limit is reached
*/
AP_DECLARE(int) ap_is_redirect_limit_exceeded(const request_rec *r)
{
core_server_config *conf = ap_get_module_config(r->server->module_config,
&core_module);
const request_rec *top = r;
int redirects = 0;
int limit = conf->redirect_limit
? conf->redirect_limit
: AP_DEFAULT_MAX_INTERNAL_REDIRECTS;
while (top->prev) {
if (++redirects >= limit) {
/* uuh, too much. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Request exceeded the limit of %d internal redirects "
"due to probable configuration error. Use "
"'LimitInternalRecursion' to increase the limit if "
"necessary. Use 'LogLevel debug' to get a "
"backtrace.", limit);
/* post backtrace */
log_backtrace(r->prev, r);
/* return failure */
return 1;
}
if (!top->prev && top->main) {
top = top->main;
}
else {
top = top->prev;
}
}
/* number of redirects is ok */
return 0;
}
/*
* check whether subrequest depth limit is reached
*/
AP_DECLARE(int) ap_is_subreq_limit_exceeded(const request_rec *r)
{
core_server_config *conf = ap_get_module_config(r->server->module_config,
&core_module);
const request_rec *top = r;
int subreqs = 0;
int limit = conf->subreq_limit
? conf->subreq_limit
: AP_DEFAULT_MAX_SUBREQ_DEPTH;
while (top->main) {
if (++subreqs >= limit) {
/* uuh, too much. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Request exceeded the limit of %d subrequest "
"nesting levels due to probable confguration error. "
"Use 'LimitInternalRecursion' to increase the limit "
"if necessary. Use 'LogLevel debug' to get a "
"backtrace.", limit);
/* post backtrace */
log_backtrace(r->main, r);
/* return failure */
return 1;
}
top = top->main;
}
/* number of subrequests is ok */
return 0;
}
static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
const char *arg, const char *arg2)
{
@@ -3063,6 +3210,10 @@ AP_INIT_TAKE12("RLimitNPROC", no_set_limit, NULL,
OR_ALL, "soft/hard limits for max number of processes per uid"),
#endif
/* internal recursion stopper */
AP_INIT_TAKE12("LimitInternalRecursion", set_recursion_limit, NULL, RSRC_CONF,
"maximum recursion depth of internal redirects and subrequests"),
AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower,
(void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO,
"a mime type that overrides other configured type"),

View File

@@ -1631,6 +1631,14 @@ AP_DECLARE(request_rec *) ap_sub_req_method_uri(const char *method,
udir = ap_escape_uri(rnew->pool, udir); /* re-escape it */
ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file));
}
/* We cannot return NULL without violating the API. So just turn this
* subrequest into a 500 to indicate the failure. */
if (ap_is_subreq_limit_exceeded(r)) {
rnew->status = HTTP_INTERNAL_SERVER_ERROR;
return rnew;
}
/* lookup_uri
* If the content can be served by the quick_handler, we can
* safely bypass request_internal processing.
@@ -1764,6 +1772,13 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_dirent(const apr_finfo_t *dirent,
ap_parse_uri(rnew, rnew->uri);
}
/* We cannot return NULL without violating the API. So just turn this
* subrequest into a 500. */
if (ap_is_subreq_limit_exceeded(r)) {
rnew->status = HTTP_INTERNAL_SERVER_ERROR;
return rnew;
}
if ((res = ap_process_request_internal(rnew))) {
rnew->status = res;
}
@@ -1851,6 +1866,13 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
rnew->uri = apr_pstrdup(rnew->pool, "");
}
/* We cannot return NULL without violating the API. So just turn this
* subrequest into a 500. */
if (ap_is_subreq_limit_exceeded(r)) {
rnew->status = HTTP_INTERNAL_SERVER_ERROR;
return rnew;
}
if ((res = ap_process_request_internal(rnew))) {
rnew->status = res;
}