From: Eric Covener
Date: Tue, 7 Jan 2014 13:07:51 +0000 (+0000)
Subject: avoid a tight busy loop with memory allocations when the [N] flag
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0c4ffdb3058d9006cc61ef4ecbcd3658023fd6e0;p=thirdparty%2Fapache%2Fhttpd.git
avoid a tight busy loop with memory allocations when the [N] flag
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
---
diff --git a/CHANGES b/CHANGES
index 7346d520f00..79359ea4bba 100644
--- 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]
diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number
index 846c2b4e362..c882b49a4f5 100644
--- a/docs/log-message-tags/next-number
+++ b/docs/log-message-tags/next-number
@@ -1 +1 @@
-2596
+2597
diff --git a/docs/manual/rewrite/flags.xml b/docs/manual/rewrite/flags.xml
index efdb746c16b..675eb8fa8e2 100644
--- a/docs/manual/rewrite/flags.xml
+++ b/docs/manual/rewrite/flags.xml
@@ -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.
-
RewriteRule (.*)A(.*) $1B$2 [N]
-
You can think of this as a while loop: While this
pattern still matches (i.e., while the URI still contains an
A), perform this substitution (i.e., replace the
A with a B).
+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.
+
+# Be willing to replace 1 character in each pass of the loop
+RewriteRule (.+)[><;]$ $1 [N=32000]
+# ... or, give up if after 10 loops
+RewriteRule (.+)[><;]$ $1 [N=10]
+
+
NC|nocase
diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
index 27716a04036..1f1203edf9e 100644
--- a/modules/mappers/mod_rewrite.c
+++ b/modules/mappers/mod_rewrite.c
@@ -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;
}