Hi developers,

The attached patch fixes a buffer overflow in at least one of the six string replacement implementations in mod_proxy_html.

Unfortunately I don't remember anymore how to reproduce the issue properly, but I know that some long time ago I fixed a segfault with this patch.

The patch tries to address the buffer overflow by introducing a new function preplace() dedicated to clean string replacement. This function is now used on all six places where the error-prone string replacement was previously implemented manually with memcpy() and memmove().

The patch is based on SVN trunk rev. 1579365.
I've filed it also in ASF bugzilla as issue #56289.

Regards,
Micha
Index: modules/filters/mod_proxy_html.c
===================================================================
--- modules/filters/mod_proxy_html.c	(Revision 1579365)
+++ modules/filters/mod_proxy_html.c	(Arbeitskopie)
@@ -29,6 +29,8 @@
 #define VERBOSEB(x) if (verbose) {x}
 #endif
 
+#include <assert.h>
+
 /* libxml2 */
 #include <libxml/HTMLparser.h>
 
@@ -191,6 +193,9 @@
     }
 }
 
+/**
+ * Appends the string buf with length len to ctx->buf at position ctx->offset
+ */
 static void pappend(saxctxt *ctx, const char *buf, const size_t len)
 {
     preserve(ctx, len);
@@ -198,6 +203,34 @@
     ctx->offset += len;
 }
 
+/**
+ * In ctx->buf replaces the substring starting at offset start with length old_length
+ * by the string new with length new_length
+ */
+static void preplace(saxctxt* ctx, const size_t start, const size_t old_length, const char* new, const size_t new_length) {
+  // check arguments
+  assert(ctx->offset > start + old_length);
+  assert(new != NULL);
+
+  // do the string replacement
+  if (old_length < new_length) {
+    int old_buffer_length = ctx->offset;
+    preserve(ctx, new_length - old_length);
+    memmove(ctx->buf + start + new_length,
+            ctx->buf + start + old_length,
+           ctx->offset - (start + old_length));
+    ctx->offset += new_length - old_length;
+  }
+  memcpy(ctx->buf + start, new, new_length);
+  if (old_length > new_length) {
+    memmove(ctx->buf + start + new_length,
+            ctx->buf + start + old_length,
+           ctx->offset - (start + old_length));
+    assert(ctx->offset >= old_length - new_length);
+    ctx->offset -= old_length - new_length;
+  }
+}
+
 static void dump_content(saxctxt *ctx)
 {
     urlmap *m;
@@ -236,17 +269,7 @@
                     ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
                                   "C/RX: match at %s, substituting %s", f, subs);
                 )
-                if (s_to > s_from) {
-                    preserve(ctx, s_to - s_from);
-                    memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from,
-                            len + 1 - s_from - offs);
-                    memcpy(ctx->buf+offs, subs, s_to);
-                }
-                else {
-                    memcpy(ctx->buf + offs, subs, s_to);
-                    memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from,
-                            len + 1 - s_from - offs);
-                }
+                preplace(ctx, offs, s_from, subs, s_to);
                 offs += s_to;
             }
         }
@@ -264,17 +287,7 @@
                 VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
                                       "C: matched %s, substituting %s",
                                       m->from.c, m->to));
-                if (s_to > s_from) {
-                    preserve(ctx, s_to - s_from);
-                    memmove(ctx->buf+match+s_to, ctx->buf+match+s_from,
-                            len + 1 - s_from - match);
-                    memcpy(ctx->buf+match, m->to, s_to);
-                }
-                else {
-                    memcpy(ctx->buf+match, m->to, s_to);
-                    memmove(ctx->buf+match+s_to, ctx->buf+match+s_from,
-                            len + 1 - s_from - match);
-                }
+                preplace(ctx, match, s_from, m->to, s_to);
             }
         }
     }
@@ -481,19 +494,7 @@
                                 })
                                 s_to = strlen(subs);
                                 len = strlen(ctx->buf);
-                                if (s_to > s_from) {
-                                    preserve(ctx, s_to - s_from);
-                                    memmove(ctx->buf+offs+s_to,
-                                            ctx->buf+offs+s_from,
-                                            len + 1 - s_from - offs);
-                                    memcpy(ctx->buf+offs, subs, s_to);
-                                }
-                                else {
-                                    memcpy(ctx->buf + offs, subs, s_to);
-                                    memmove(ctx->buf+offs+s_to,
-                                            ctx->buf+offs+s_from,
-                                            len + 1 - s_from - offs);
-                                }
+                                preplace(ctx, offs, s_from, subs, s_to);
                             }
                         } else {
                             s_from = strlen(m->from.c);
@@ -505,17 +506,7 @@
                                                       0, ctx->f->r,
                                               "H: matched %s, substituting %s",
                                                       m->from.c, m->to));
-                                if (s_to > s_from) {
-                                    preserve(ctx, s_to - s_from);
-                                    memmove(ctx->buf+s_to, ctx->buf+s_from,
-                                            len + 1 - s_from);
-                                    memcpy(ctx->buf, m->to, s_to);
-                                }
-                                else {     /* it fits in the existing space */
-                                    memcpy(ctx->buf, m->to, s_to);
-                                    memmove(ctx->buf+s_to, ctx->buf+s_from,
-                                            len + 1 - s_from);
-                                }
+                                preplace(ctx, 0, s_from, m->to, s_to);
                                 break;
                             }
                         }
@@ -550,19 +541,7 @@
                                 s_to = strlen(subs);
                                 offs += match;
                                 len = strlen(ctx->buf);
-                                if (s_to > s_from) {
-                                    preserve(ctx, s_to - s_from);
-                                    memmove(ctx->buf+offs+s_to,
-                                            ctx->buf+offs+s_from,
-                                            len + 1 - s_from - offs);
-                                    memcpy(ctx->buf+offs, subs, s_to);
-                                }
-                                else {
-                                    memcpy(ctx->buf + offs, subs, s_to);
-                                    memmove(ctx->buf+offs+s_to,
-                                            ctx->buf+offs+s_from,
-                                            len + 1 - s_from - offs);
-                                }
+                                preplace(ctx, offs, s_from, subs, s_to);
                                 offs += s_to;
                                 ++num_match;
                             }
@@ -590,19 +569,7 @@
                                               "E: matched %s, substituting %s",
                                                       m->from.c, m->to));
                                 len = strlen(ctx->buf);
-                                if (s_to > s_from) {
-                                    preserve(ctx, s_to - s_from);
-                                    memmove(ctx->buf+match+s_to,
-                                            ctx->buf+match+s_from,
-                                            len + 1 - s_from - match);
-                                    memcpy(ctx->buf+match, m->to, s_to);
-                                }
-                                else {
-                                    memcpy(ctx->buf+match, m->to, s_to);
-                                    memmove(ctx->buf+match+s_to,
-                                            ctx->buf+match+s_from,
-                                            len + 1 - s_from - match);
-                                }
+                                preplace(ctx, match, s_from, m->to, s_to);
                                 ++num_match;
                             }
                         }

Reply via email to