]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: debug: detect redefinition of symbols upon dlopen()
authorWilly Tarreau <w@1wt.eu>
Sun, 19 Jun 2022 14:49:51 +0000 (16:49 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 19 Jun 2022 15:58:32 +0000 (17:58 +0200)
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.

include/haproxy/bug.h
src/tools.c

index a4980af8a9b5c6be0275f93ff222e428093700f0..ce83536ea22a0d712ec027224d7b36a2c14664fa 100644 (file)
@@ -201,6 +201,7 @@ enum tainted_flags {
        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 */
index 5a35027502c2b7deb1d13755ca83cd4c386ff358..c2ee7c953c8b0c1bc2dcfabe4448d6262429ac4d 100644 (file)
@@ -5810,7 +5810,21 @@ int openssl_compare_current_name(const char *name)
 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 @@ void *dlopen(const char *filename, int flags)
                }
        }
 
+       /* 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 @@ void *dlopen(const char *filename, int flags)
 
        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