MEDIUM: debug: detect redefinition of symbols upon dlopen()
In order to better detect the danger caused by extra shared libraries
which replace some symbols, upon dlopen() we now compare a few critical
symbols such as malloc(), free(), and some OpenSSL symbols, to see if
the loaded library comes with its own version. If this happens, a
warning is emitted and TAINTED_REDEFINITION is set. This is important
because some external libs might be linked against different libraries
than the ones haproxy was linked with, and most often this will end
very badly (e.g. an OpenSSL object is allocated by haproxy and freed
by such libs).
Since the main source of dlopen() calls is the Lua lib, via a "require"
statement, it's worth trying to show a Lua call trace when detecting a
symbol redefinition during dlopen(). As such we emit a Lua backtrace if
Lua is detected as being in use.
diff --git a/include/haproxy/bug.h b/include/haproxy/bug.h
index a4980af..ce83536 100644
--- a/include/haproxy/bug.h
+++ b/include/haproxy/bug.h
@@ -201,6 +201,7 @@
TAINTED_WARN = 0x00000010, /* a WARN_ON triggered */
TAINTED_BUG = 0x00000020, /* a BUG_ON triggered */
TAINTED_SHARED_LIBS = 0x00000040, /* a shared library was loaded */
+ TAINTED_REDEFINITION = 0x00000080, /* symbol redefinition detected */
};
/* this is a bit field made of TAINTED_*, and is declared in haproxy.c */
diff --git a/src/tools.c b/src/tools.c
index 5a35027..c2ee7c9 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -5810,7 +5810,21 @@
void *dlopen(const char *filename, int flags)
{
static void *(*_dlopen)(const char *filename, int flags);
+ struct {
+ const char *name;
+ void *curr, *next;
+ } check_syms[] = {
+ { .name = "malloc", },
+ { .name = "free", },
+ { .name = "SSL_library_init", },
+ { .name = "X509_free", },
+ /* insert only above, 0 must be the last one */
+ { 0 },
+ };
+ const char *trace;
+ void *addr;
void *ret;
+ int sym = 0;
if (!_dlopen) {
_dlopen = get_sym_next_addr("dlopen");
@@ -5820,6 +5834,16 @@
}
}
+ /* save a few pointers to critical symbols. We keep a copy of both the
+ * current and the next value, because we might already have replaced
+ * some of them (e.g. malloc/free with DEBUG_MEM_STATS), and we're only
+ * interested in verifying that a loaded library doesn't come with a
+ * completely different definition that would be incompatible.
+ */
+ for (sym = 0; check_syms[sym].name; sym++) {
+ check_syms[sym].curr = get_sym_curr_addr(check_syms[sym].name);
+ check_syms[sym].next = get_sym_next_addr(check_syms[sym].name);
+ }
/* now open the requested lib */
ret = _dlopen(filename, flags);
@@ -5828,6 +5852,25 @@
mark_tainted(TAINTED_SHARED_LIBS);
+ /* and check that critical symbols didn't change */
+ for (sym = 0; check_syms[sym].name; sym++) {
+ if (!check_syms[sym].curr && !check_syms[sym].next)
+ continue;
+
+ addr = dlsym(ret, check_syms[sym].name);
+ if (!addr || addr == check_syms[sym].curr || addr == check_syms[sym].next)
+ continue;
+
+ /* OK it's clear that this symbol was redefined */
+ mark_tainted(TAINTED_REDEFINITION);
+
+ trace = hlua_show_current_location("\n ");
+ ha_warning("dlopen(): shared library '%s' brings a different definition of symbol '%s'. The process cannot be trusted anymore!%s%s\n",
+ filename, check_syms[sym].name,
+ trace ? " Suspected call location: \n " : "",
+ trace ? trace : "");
+ }
+
return ret;
}
#endif