aboutsummaryrefslogtreecommitdiffstats
path: root/pseudo_util.c
diff options
context:
space:
mode:
authorseebs <seebs@seebs-eee.(none)>2010-08-20 11:56:16 -0500
committerseebs <seebs@seebs-eee.(none)>2010-08-20 11:56:16 -0500
commit466472fb9b39656f0c90216b64615b3e0909997f (patch)
tree12df8565a72f4573a8b35bad39357f5bfd5d78a6 /pseudo_util.c
parent6ea4acf2af23b5940521825a849deefc07fe9d47 (diff)
downloadpseudo-466472fb9b39656f0c90216b64615b3e0909997f.tar.gz
pseudo-466472fb9b39656f0c90216b64615b3e0909997f.tar.bz2
pseudo-466472fb9b39656f0c90216b64615b3e0909997f.zip
Handle insane boundary condition for regcomp()/regexec()
For reasons that may never be known, the /usr/bin/find on RHEL5 contains its own local copies of regcomp() and regexec(). Thus, when pseudo makes calls to these functions, it gets the local copies in the binary instead of the ones in libc. But wait! That's not all. There's also the fascinating detail that, for reasons unknown, these local copies have an incompatible API, such that: regexec(pattern, list, 1, pmatch, 0); can write to more than one element of pmatch, and since that's a local array of one member, that means that they can crush something on the stack, such that a couple of function calls later you get a segfault in Nothing In Particular. So. We try to grab the real regcomp/regexec from libc, using dlsym, and if we can't, we fall back on whatever the defaults were. Inexplicably, this code actually made pseudo crash less often on at least one target. This is madness within madness, and I really have no idea why on earth /usr/bin/find, on a Linux system, would have its own local copies of regcomp/regexec, let alone why it would have copies that had substantially different semantics.
Diffstat (limited to 'pseudo_util.c')
-rw-r--r--pseudo_util.c32
1 files changed, 28 insertions, 4 deletions
diff --git a/pseudo_util.c b/pseudo_util.c
index 93aceb7..b8bd85b 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -17,6 +17,9 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
+/* we need access to RTLD_NEXT for a horrible workaround */
+#define _GNU_SOURCE
+
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
@@ -29,6 +32,9 @@
#include <unistd.h>
#include <limits.h>
+/* see the comments below about (*real_regcomp)() */
+#include <dlfcn.h>
+
#include "pseudo.h"
#include "pseudo_ipc.h"
#include "pseudo_db.h"
@@ -182,6 +188,18 @@ static char *libpseudo_name = "libpseudo.so";
static char *libpseudo_pattern = "(^|=| )libpseudo[^ ]*\\.so($| )";
static regex_t libpseudo_regex;
static int libpseudo_regex_compiled = 0;
+/* Okay, so, there's a funny story behind this. On one of the systems
+ * we need to run on, /usr/bin/find happens to provide its own
+ * definitions of regcomp and regexec which are INCOMPATIBLE with the
+ * ones in the C library, and not only that, but which have buggy and/or
+ * incompatible semantics, such that they trash elements of the pmatch
+ * array. So we do our best to call the "real" regcomp/regexec in the
+ * C library. If we can't find them, we just do our best and hope that
+ * no one called us from a program with incompatible variants.
+ *
+ */
+static int (*real_regcomp)(regex_t *__restrict __preg, const char *__restrict __pattern, int __cflags);
+static int (*real_regexec)(const regex_t *__restrict __preg, const char *__restrict __string, size_t __nmatch, regmatch_t __pmatch[__restrict_arr], int __eflags);
static int
libpseudo_regex_init(void) {
@@ -189,7 +207,13 @@ libpseudo_regex_init(void) {
if (libpseudo_regex_compiled)
return 0;
- rc = regcomp(&libpseudo_regex, libpseudo_pattern, REG_EXTENDED);
+ real_regcomp = dlsym(RTLD_NEXT, "regcomp");
+ if (!real_regcomp)
+ real_regcomp = regcomp;
+ real_regexec = dlsym(RTLD_NEXT, "regexec");
+ if (!real_regexec)
+ real_regexec = regexec;
+ rc = (*real_regcomp)(&libpseudo_regex, libpseudo_pattern, REG_EXTENDED);
if (rc == 0)
libpseudo_regex_compiled = 1;
return rc;
@@ -210,11 +234,11 @@ without_libpseudo(char *list) {
if (list[0] == '=' || list[0] == ' ')
skip_start = 1;
- if (regexec(&libpseudo_regex, list, 1, pmatch, 0)) {
+ if ((*real_regexec)(&libpseudo_regex, list, 1, pmatch, 0)) {
return list;
}
list = strdup(list);
- while (!regexec(&libpseudo_regex, list, 1, pmatch, 0)) {
+ while (!(*real_regexec)(&libpseudo_regex, list, 1, pmatch, 0)) {
char *start = list + pmatch[0].rm_so;
char *end = list + pmatch[0].rm_eo;
/* don't copy over the space or = */
@@ -234,7 +258,7 @@ with_libpseudo(char *list) {
regmatch_t pmatch[1];
if (libpseudo_regex_init())
return NULL;
- if (regexec(&libpseudo_regex, list, 1, pmatch, 0)) {
+ if ((*real_regexec)(&libpseudo_regex, list, 1, pmatch, 0)) {
/* <%s %s\0> */
size_t len = strlen(list) + 1 + strlen(libpseudo_name) + 1;
char *new = malloc(len);