avoid loading of multiple libc versions via explicit pathname
authorRich Felker <dalias@aerifal.cx>
Tue, 14 Mar 2017 22:51:27 +0000 (18:51 -0400)
committerRich Felker <dalias@aerifal.cx>
Tue, 14 Mar 2017 22:51:27 +0000 (18:51 -0400)
such loading is unsafe, and can happen when programs use their own
logic to locate a .so file then pass the absolute pathname to dlopen,
or if an absolute pathname ends up in DT_NEEDED headers. multiple
loads with only the base name were already precluded, provided libc
was named appropriately, by special-casing standard library names.

one function symbol (in the reserved namespace, but public, since it's
part of the crt1 entry point ABI) and one data symbol are checked.
this way we avoid likely false positives, particularly from libraries
interposing and wrapping functions. there is no hard requirement to
avoid breaking such usage, since trying to run a hook before libc is
even initialized is not a supported usage case, but it's friendlier
not to break things.

ldso/dynlink.c

index f8db1f82da99d36f9e5a68dd7fd3f1acca449b02..80d85e949dcb6981817a6aa75f3716f8001536bf 100644 (file)
@@ -1042,6 +1042,17 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
        close(fd);
        if (!map) return 0;
 
+       /* Avoid the danger of getting two versions of libc mapped into the
+        * same process when an absolute pathname was used. The symbols
+        * checked are chosen to catch both musl and glibc, and to avoid
+        * false positives from interposition-hack libraries. */
+       decode_dyn(&temp_dso);
+       if (find_sym(&temp_dso, "__libc_start_main", 1).sym &&
+           find_sym(&temp_dso, "stdin", 1).sym) {
+               unmap_library(&temp_dso);
+               return load_library("libc.so", needed_by);
+       }
+
        /* Allocate storage for the new DSO. When there is TLS, this
         * storage must include a reservation for all pre-existing
         * threads to obtain copies of both the new TLS, and an
@@ -1061,7 +1072,6 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
                return 0;
        }
        memcpy(p, &temp_dso, sizeof temp_dso);
-       decode_dyn(p);
        p->dev = st.st_dev;
        p->ino = st.st_ino;
        p->refcnt = 1;