Hi,

On Debian Sarge 3.1 rev2, httpd compiled with external libpcre 4.x and some
RewriteRules segfault occurs every time we start the daemon.
With a stacktrace :
#0  0xb7c66094 in mallopt () from /lib/tls/i686/cmov/libc.so.6
#1  0xb7c64ffb in free () from /lib/tls/i686/cmov/libc.so.6
#2  0xb7ca7bc3 in regfree () from /lib/tls/i686/cmov/libc.so.6
#3  0x080b9676 in regex_cleanup ()
#4  0xb7dc569f in run_cleanups () from /realsentry/wsp/engine-2.0
/lib/libapr-0.so.0
#5  0xb7dc4b13 in apr_pool_clear () from
/realsentry/wsp/engine-2.0/lib/libapr-0.so.0
#6  0x080b7831 in main ()

We can see that regfree() from libc is called, but it seems that the pointer
was returned by regcomp() from libpcre.

After a quick reading of httpd/server/util.c, it appears ap_pregcomp,
ap_pregfree, ap_regerror and ap_regexec call respectively regcomp(),
regfree(),
regerror() and regexec(). httpd is linked with both libc and libpcre that
both
export these 4 functions.

I tried to patch util.c to avoid the use of function with the same name than
libc ones. Basically this patch is a move of the code from pcreposix.c into
util.c, to use directly function from pcre (with pcre_ namespace) in httpd.

cheers,
--
*Francois Pesce*
--- server/util.c.orig	2006-11-22 09:36:40.000000000 +0100
+++ server/util.c	2006-11-22 10:14:27.000000000 +0100
@@ -256,7 +256,14 @@
 
 static apr_status_t regex_cleanup(void *preg)
 {
+#if HAVE_LIBPCRE
+    regex_t *pregp;
+
+    pregp = preg;
+    pcre_free(preg->re_pcre);
+#else
     regfree((regex_t *) preg);
+#endif
     return APR_SUCCESS;
 }
 
@@ -264,10 +271,29 @@
                                    int cflags)
 {
     regex_t *preg = apr_palloc(p, sizeof(regex_t));
+#if HAVE_LIBPCRE
+    const char *errorptr;
+    int erroffset;
+    int options = 0;
+
+    if ((cflags & REG_ICASE) != 0)
+        options |= PCRE_CASELESS;
+    if ((cflags & REG_NEWLINE) != 0)
+        options |= PCRE_MULTILINE;
+
+    preg->re_pcre =
+        pcre_compile(pattern, options, &errorptr, &erroffset, NULL);
+    preg->re_erroffset = erroffset;
+
+    if (preg->re_pcre == NULL)
+        return NULL;
 
+    preg->re_nsub = pcre_info(preg->re_pcre, NULL, NULL);
+#else
     if (regcomp(preg, pattern, cflags)) {
         return NULL;
     }
+#endif
 
     apr_pool_cleanup_register(p, (void *) preg, regex_cleanup,
                               apr_pool_cleanup_null);
@@ -277,7 +303,11 @@
 
 AP_DECLARE(void) ap_pregfree(apr_pool_t *p, regex_t * reg)
 {
+#if HAVE_LIBPCRE
+    pcre_free(reg->re_pcre);
+#else
     regfree(reg);
+#endif
     apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup);
 }
 
@@ -350,16 +380,141 @@
  * This is especially important for the DSO situations of modules.
  * DO NOT MAKE A MACRO OUT OF THIS FUNCTION!
  */
+#define SMALL_NMATCH 5
 AP_DECLARE(int) ap_regexec(regex_t *preg, const char *string,
                            size_t nmatch, regmatch_t pmatch[], int eflags)
 {
+#if HAVE_LIBPCRE
+    int rc;
+    int options = 0;
+    int small_ovector[SMALL_NMATCH * 3];
+    int *ovector = NULL;
+    int allocated_ovector = 0;
+
+    if ((eflags & REG_NOTBOL) != 0)
+        options |= PCRE_NOTBOL;
+    if ((eflags & REG_NOTEOL) != 0)
+        options |= PCRE_NOTEOL;
+
+    if (nmatch > 0) {
+        if (nmatch <= SMALL_NMATCH) {
+            ovector = &(small_ovector[0]);
+        }
+        else {
+            ovector = (int *) malloc(sizeof(int) * nmatch * 3);
+            if (ovector == NULL)
+                return REG_ESPACE;
+            allocated_ovector = 1;
+        }
+    }
+
+    rc = pcre_exec(preg->re_pcre, NULL, string, (int) strlen(string), 0,
+                   options, ovector, nmatch * 3);
+
+    /* All captured slots were filled in */
+    if (rc == 0)
+        rc = nmatch;
+
+    if (rc >= 0) {
+        size_t i;
+
+        for (i = 0; i < (size_t) rc; i++) {
+            pmatch[i].rm_so = ovector[i * 2];
+            pmatch[i].rm_eo = ovector[i * 2 + 1];
+        }
+
+        if (allocated_ovector)
+            free(ovector);
+
+        for (; i < nmatch; i++)
+            pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+
+        return 0;
+    }
+    else {
+        if (allocated_ovector)
+            free(ovector);
+
+        switch (rc) {
+        case PCRE_ERROR_NOMATCH:
+            return REG_NOMATCH;
+        case PCRE_ERROR_NULL:
+            return REG_INVARG;
+        case PCRE_ERROR_BADOPTION:
+            return REG_INVARG;
+        case PCRE_ERROR_BADMAGIC:
+            return REG_INVARG;
+        case PCRE_ERROR_UNKNOWN_NODE:
+            return REG_ASSERT;
+        case PCRE_ERROR_NOMEMORY:
+            return REG_ESPACE;
+        default:
+            return REG_ASSERT;
+        }
+    }
+
+#else
     return regexec(preg, string, nmatch, pmatch, eflags);
+#endif
 }
 
-AP_DECLARE(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf,
-                               size_t errbuf_size)
+#if HAVE_LIBPCRE
+static const char *pstring[] = {
+    "",                                /* Dummy for value 0 */
+    "internal error",                /* REG_ASSERT */
+    "invalid repeat counts in {}",        /* BADBR      */
+    "pattern error",                /* BADPAT     */
+    "? * + invalid",                /* BADRPT     */
+    "unbalanced {}",                /* EBRACE     */
+    "unbalanced []",                /* EBRACK     */
+    "collation error - not relevant",        /* ECOLLATE   */
+    "bad class",                /* ECTYPE     */
+    "bad escape sequence",        /* EESCAPE    */
+    "empty expression",                /* EMPTY      */
+    "unbalanced ()",                /* EPAREN     */
+    "bad range inside []",        /* ERANGE     */
+    "expression too big",        /* ESIZE      */
+    "failed to get memory",        /* ESPACE     */
+    "bad back reference",        /* ESUBREG    */
+    "bad argument",                /* INVARG     */
+    "match failed"                /* NOMATCH    */
+};
+#endif
+
+AP_DECLARE(size_t) ap_regerror(int errcode, const regex_t *preg,
+                               char *errbuf, size_t errbuf_size)
 {
+#if HAVE_LIBPCRE
+    const char *message, *addmessage;
+    size_t length, addlength;
+
+    message =
+        (errcode >=
+         (int) (sizeof(pstring) /
+                sizeof(char *)))? "unknown error code" : pstring[errcode];
+    length = strlen(message) + 1;
+
+    addmessage = " at offset ";
+    addlength = (preg != NULL
+                 && (int) preg->re_erroffset !=
+                 -1) ? strlen(addmessage) + 6 : 0;
+
+    if (errbuf_size > 0) {
+        if (addlength > 0 && errbuf_size >= length + addlength) {
+            sprintf(errbuf, "%s%s%-6d", message, addmessage,
+                    (int) preg->re_erroffset);
+        }
+        else {
+            strncpy(errbuf, message, errbuf_size - 1);
+            errbuf[errbuf_size - 1] = 0;
+        }
+    }
+
+    return length + addlength;
+
+#else
     return regerror(errcode, preg, errbuf, errbuf_size);
+#endif
 }
 
 

Reply via email to