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 }