avoid a tight busy loop with memory allocations when the [N] flag
authorEric Covener <covener@apache.org>
Tue, 7 Jan 2014 13:07:51 +0000 (13:07 +0000)
committerEric Covener <covener@apache.org>
Tue, 7 Jan 2014 13:07:51 +0000 (13:07 +0000)
isn't making progress.

If backported, probably increase the hard-coded limit to 32k from 10k.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1556206 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/log-message-tags/next-number
docs/manual/rewrite/flags.xml
modules/mappers/mod_rewrite.c

diff --git a/CHANGES b/CHANGES
index 7346d520f00d8d629b45531f097a01aa721abb69..79359ea4bba25289bc9205b431b94f97ba0b357f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,10 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_rewrite: Protect against looping with the [N] flag by enforcing a 
+     default limit of 10000 iterations, and allowing each rule to change its
+     limit. [Eric Covener]
+
   *) mod_ssl: Fix config merging of SSLOCSPEnable and SSLOCSPOverrideResponder.
      [Jeff Trawick]
 
index 846c2b4e3621e13daa474521c326f39663f638a2..c882b49a4f5e01c984b97c88a4a13034778aa5da 100644 (file)
@@ -1 +1 @@
-2596
+2597
index efdb746c16b0f93f05fbb97cc5b93144be85c674..675eb8fa8e2e263b0b342bce638bcc9ffe171471 100644 (file)
@@ -392,14 +392,22 @@ certain string or letter repeatedly in a request. The example shown here
 will replace A with B everywhere in a request, and will continue doing
 so until there are no more As to be replaced.
 </p>
-
 <highlight language="config">RewriteRule (.*)A(.*) $1B$2 [N]</highlight>
-
 <p>You can think of this as a <code>while</code> loop: While this
 pattern still matches (i.e., while the URI still contains an
 <code>A</code>), perform this substitution (i.e., replace the
 <code>A</code> with a <code>B</code>).</p>
 
+<p>In 2.5.0 and later, this module returns an error after 10,000 iterations to
+protect against unintended looping.  An alternative maximum number of 
+iterations can be specified by adding to the N flag.  </p>
+<highlight language="config">
+# Be willing to replace 1 character in each pass of the loop
+RewriteRule (.+)[&gt;&lt;;]$ $1 [N=32000]
+# ... or, give up if after 10 loops
+RewriteRule (.+)[&gt;&lt;;]$ $1 [N=10]
+</highlight>
+
 </section>
 
 <section id="flag_nc"><title>NC|nocase</title>
index 27716a04036d2e86ae9dc01b6f315b995f209f52..1f1203edf9e4bb7130bafa8886bcfde85ec305c7 100644 (file)
@@ -235,6 +235,9 @@ static const char* really_last_key = "rewrite_really_last";
 #define subreq_ok(r) (!r->main || \
     (r->main->uri && r->uri && strcmp(r->main->uri, r->uri)))
 
+#ifndef REWRITE_MAX_ROUNDS
+#define REWRITE_MAX_ROUNDS 10000
+#endif
 
 /*
  * +-------------------------------------------------------+
@@ -312,6 +315,7 @@ typedef struct {
     data_item *env;                  /* added environment variables           */
     data_item *cookie;               /* added cookies                         */
     int        skip;                 /* number of next rules to skip          */
+    int        maxrounds;            /* limit on number of loops with N flag  */
 } rewriterule_entry;
 
 typedef struct {
@@ -3502,6 +3506,10 @@ static const char *cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg,
         }
         else if (!*key || !strcasecmp(key, "ext")) {       /* next */
             cfg->flags |= RULEFLAG_NEWROUND;
+            if (val && *val) { 
+                cfg->maxrounds = atoi(val);
+            }
+
         }
         else if (((*key == 'S' || *key == 's') && !key[1])
             || !strcasecmp(key, "osubreq")) {              /* nosubreq */
@@ -3653,6 +3661,7 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf,
     newrule->env = NULL;
     newrule->cookie = NULL;
     newrule->skip   = 0;
+    newrule->maxrounds = REWRITE_MAX_ROUNDS;
     if (a3 != NULL) {
         if ((err = cmd_parseflagfield(cmd->pool, newrule, a3,
                                       cmd_rewriterule_setflag)) != NULL) {
@@ -4196,6 +4205,7 @@ static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules,
     int rc;
     int s;
     rewrite_ctx *ctx;
+    int round = 1;
 
     ctx = apr_palloc(r->pool, sizeof(*ctx));
     ctx->perdir = perdir;
@@ -4284,6 +4294,15 @@ static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules,
              *  the rewriting ruleset again.
              */
             if (p->flags & RULEFLAG_NEWROUND) {
+                if (++round >= p->maxrounds) { 
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02596)
+                                  "RewriteRule '%s' and URI '%s' exceeded "
+                                  "maximum number of rounds (%d) via the [N] flag", 
+                                  p->pattern, r->uri, p->maxrounds);
+
+                    r->status = HTTP_INTERNAL_SERVER_ERROR;
+                    return ACTION_STATUS; 
+                }
                 goto loop;
             }