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
}