]> granicus.if.org Git - apache/commitdiff
Prevent the server from crashing when entering infinite loops. The
authorAndré Malo <nd@apache.org>
Mon, 19 May 2003 01:19:55 +0000 (01:19 +0000)
committerAndré Malo <nd@apache.org>
Mon, 19 May 2003 01:19:55 +0000 (01:19 +0000)
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

CHANGES
include/http_core.h
modules/http/http_request.c
server/core.c
server/request.c

diff --git a/CHANGES b/CHANGES
index 2debfde428c0f277cbf916b0f50a2919ac784651..966119f77275cf25dde8a055b2191dacf3342f54 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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é Malo]
+
   *) mod_rewrite: Fix LA-U variable look ahead which didn't work correctly
      in directory context. Related to PR 8493.  [André Malo]
 
index 1e700f8cad9745ef7f830cc7b7806c62e613ca3c..b4d8068bbfd678369b172e801d2b2ab25c956234 100644 (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 */
index 0817bba7b6d7983937421d9e5d8409775d9e2762..ea2fca452c88c62c835b4d653a09cd50e43fa70d 100644 (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);
index 8897d9cf9e996204b4b3d368874a9209e48e2b61..0c26d129d7dd99d2f2e1cdf4705945992d4e8726 100644 (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"),
index 09e784a12b55324ef7a37b4024ae100eb18cf10c..4254c8e446382d5e06d967d5c9a5681b9194b748 100644 (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;
     }