12745e
#
12745e
# This is a special patch for rhel-6 to fix recursive dlopen.
12745e
# It is likely the upstream patch will always be too risky for
12745e
# rhel-6 and will involve reorganizing the way in which recursive
12745e
# dlopen is allowed to operate and how the _r_debug and stap
12745e
# points are used by gdb for the recursive case.
12745e
#
12745e
# This fix changes the internal API to duplicate the ldconfig
12745e
# cache data. This means that at any point the cache can be
12745e
# unmapped without any consequences. The caller is responsible
12745e
# fore freeing the returned string.
12745e
#
12745e
# A regression test is added to verify the assertion for _r_debug
12745e
# is no longer triggered due to the recursive dlopen. The test to
12745e
# verify the fix in _dl_load_cache_lookup is not automated and
12745e
# has to be run by hand.
12745e
#
12745e
# The original version of this patch was based on the first version
12745e
# of the upstream patch posted here:
12745e
#   https://sourceware.org/ml/libc-alpha/2014-12/msg00446.html
12745e
# The current version has been modified to reflect the changes
12745e
# made in the revision of the patch committed to trunk after
12745e
# being posted for review here:
12745e
#   https://sourceware.org/ml/libc-alpha/2014-12/msg00483.html
12745e
fa3bfd
This was committed upstream as:
fa3bfd
fa3bfd
commit ccdb048df457d581f6ac7ede8b0c7a593a891dfa
fa3bfd
Author: Carlos O'Donell <carlos@systemhalted.org>
fa3bfd
Date:   Wed Jan 21 01:51:10 2015 -0500
fa3bfd
fa3bfd
    Fix recursive dlopen.
fa3bfd
12745e
--- glibc-2.17-c758a686/dlfcn/Makefile	2012-12-24 22:02:13.000000000 -0500
12745e
+++ glibc-2.17-c758a686/dlfcn/Makefile	2015-06-22 12:44:41.000000000 -0400
12745e
@@ -35,12 +35,12 @@ endif
12745e
 ifeq (yes,$(build-shared))
12745e
 tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
12745e
 	bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
12745e
-	bug-atexit3 tstatexit
12745e
+	bug-atexit3 tstatexit tst-rec-dlopen
12745e
 endif
12745e
 modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
12745e
 		defaultmod2 errmsg1mod modatexit modcxaatexit \
12745e
 		bug-dlsym1-lib1 bug-dlsym1-lib2 bug-atexit1-lib \
12745e
-		bug-atexit2-lib bug-atexit3-lib
12745e
+		bug-atexit2-lib bug-atexit3-lib moddummy1 moddummy2
12745e
 
12745e
 failtestmod.so-no-z-defs = yes
12745e
 glreflib2.so-no-z-defs = yes
12745e
@@ -122,6 +122,8 @@ LDLIBS-bug-atexit3-lib.so = -lstdc++ -lg
12745e
 $(objpfx)bug-atexit3: $(libdl)
12745e
 $(objpfx)bug-atexit3.out: $(objpfx)bug-atexit3-lib.so
12745e
 
12745e
+$(objpfx)tst-rec-dlopen: $(libdl)
12745e
+$(objpfx)tst-rec-dlopen.out: $(objpfx)moddummy1.so $(objpfx)moddummy2.so
12745e
 
12745e
 # Depend on libc.so so a DT_NEEDED is generated in the shared objects.
12745e
 # This ensures they will load libc.so for needed symbols if loaded by
12745e
--- glibc-2.17-c758a686/elf/dl-cache.c	2012-12-24 22:02:13.000000000 -0500
12745e
+++ glibc-2.17-c758a686/elf/dl-cache.c	2015-06-22 12:44:39.000000000 -0400
12745e
@@ -174,9 +174,12 @@ _dl_cache_libcmp (const char *p1, const
12745e
 
12745e
 
12745e
 /* Look up NAME in ld.so.cache and return the file name stored there,
12745e
-   or null if none is found.  */
12745e
-
12745e
-const char *
12745e
+   or null if none is found. 
12745e
+   The caller is responsible for freeing the returned string.  The ld.so.cache
12745e
+   may be unmapped at any time by a completing recursive dlopen and
12745e
+   this function must take care that it does not return references to
12745e
+   any data in the mapping.  */
12745e
+char *
12745e
 internal_function
12745e
 _dl_load_cache_lookup (const char *name)
12745e
 {
12745e
@@ -289,7 +292,17 @@ _dl_load_cache_lookup (const char *name)
12745e
       && best != NULL)
12745e
     _dl_debug_printf ("  trying file=%s\n", best);
12745e
 
12745e
-  return best;
12745e
+  if (best == NULL)
12745e
+    return NULL;
12745e
+
12745e
+  /* The double copy is *required* since malloc may be interposed
12745e
+     and call dlopen itself whose completion would unmap the data
12745e
+     we are accessing. Therefore we must make the copy of the
12745e
+     mapping data without using malloc.  */
12745e
+  char *temp;
12745e
+  temp = alloca (strlen (best) + 1);
12745e
+  strcpy (temp, best);
12745e
+  return strdup (temp);
12745e
 }
12745e
 
12745e
 #ifndef MAP_COPY
12745e
--- glibc-2.17-c758a686/elf/dl-load.c	2015-06-22 12:41:10.748836414 -0400
12745e
+++ glibc-2.17-c758a686/elf/dl-load.c	2015-06-22 12:44:39.000000000 -0400
12745e
@@ -2232,7 +2232,7 @@ _dl_map_object (struct link_map *loader,
12745e
 	{
12745e
 	  /* Check the list of libraries in the file /etc/ld.so.cache,
12745e
 	     for compatibility with Linux's ldconfig program.  */
12745e
-	  const char *cached = _dl_load_cache_lookup (name);
12745e
+	  char *cached = _dl_load_cache_lookup (name);
12745e
 
12745e
 	  if (cached != NULL)
12745e
 	    {
12745e
@@ -2262,6 +2262,7 @@ _dl_map_object (struct link_map *loader,
12745e
 		      if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
12745e
 			{
12745e
 			  /* The prefix matches.  Don't use the entry.  */
12745e
+			  free (cached);
12745e
 			  cached = NULL;
12745e
 			  break;
12745e
 			}
12745e
@@ -2278,14 +2279,9 @@ _dl_map_object (struct link_map *loader,
12745e
 				    &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
12745e
 				    LA_SER_CONFIG, &found_other_class, false);
12745e
 		  if (__builtin_expect (fd != -1, 1))
12745e
-		    {
12745e
-		      realname = local_strdup (cached);
12745e
-		      if (realname == NULL)
12745e
-			{
12745e
-			  __close (fd);
12745e
-			  fd = -1;
12745e
-			}
12745e
-		    }
12745e
+		    realname = cached;
12745e
+		  else
12745e
+		    free (cached);
12745e
 		}
12745e
 	    }
12745e
 	}
12745e
--- glibc-2.17-c758a686/elf/dl-open.c	2015-06-22 12:41:16.348913620 -0400
12745e
+++ glibc-2.17-c758a686/elf/dl-open.c	2015-06-22 12:44:40.000000000 -0400
12745e
@@ -221,7 +221,11 @@ dl_open_worker (void *a)
12745e
 	}
12745e
     }
12745e
 
12745e
-  assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
12745e
+  /* One might be tempted to assert that we are RT_CONSISTENT at this point, but that
12745e
+     may not be true if this is a recursive call to dlopen.
12745e
+     TODO: Fix all of the debug state so we end up at RT_CONSISTENT only when the last
12745e
+     recursive dlopen completes.  */
12745e
+  _dl_debug_initialize (0, args->nsid);
12745e
 
12745e
   /* Load the named object.  */
12745e
   struct link_map *new;
12745e
--- glibc-2.17-c758a686/sysdeps/generic/ldsodefs.h	2015-06-22 12:41:16.328913344 -0400
12745e
+++ glibc-2.17-c758a686/sysdeps/generic/ldsodefs.h	2015-06-22 12:44:41.000000000 -0400
12745e
@@ -895,8 +895,8 @@
12745e
      internal_function;
12745e
 
12745e
 /* Look up NAME in ld.so.cache and return the file name stored there,
12745e
-   or null if none is found.  */
12745e
-extern const char *_dl_load_cache_lookup (const char *name)
12745e
+   or null if none is found.  Caller must free returned string.  */
12745e
+extern char *_dl_load_cache_lookup (const char *name)
12745e
      internal_function;
12745e
 
12745e
 /* If the system does not support MAP_COPY we cannot leave the file open
12745e
--- glibc-2.17-c758a686/dlfcn/tst-rec-dlopen.c	1969-12-31 19:00:00.000000000 -0500
12745e
+++ glibc-2.17-c758a686/dlfcn/tst-rec-dlopen.c	2015-06-22 12:44:41.000000000 -0400
12745e
@@ -0,0 +1,143 @@
12745e
+/* Test recursive dlopen using malloc hooks.
12745e
+   Copyright (C) 2015 Free Software Foundation, Inc.
12745e
+   This file is part of the GNU C Library.
12745e
+
12745e
+   The GNU C Library is free software; you can redistribute it and/or
12745e
+   modify it under the terms of the GNU Lesser General Public
12745e
+   License as published by the Free Software Foundation; either
12745e
+   version 2.1 of the License, or (at your option) any later version.
12745e
+
12745e
+   The GNU C Library is distributed in the hope that it will be useful,
12745e
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
12745e
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12745e
+   Lesser General Public License for more details.
12745e
+
12745e
+   You should have received a copy of the GNU Lesser General Public
12745e
+   License along with the GNU C Library; if not, see
12745e
+   <http://www.gnu.org/licenses/>.  */
12745e
+
12745e
+#include <stdio.h>
12745e
+#include <stdlib.h>
12745e
+#include <malloc.h>
12745e
+#include <dlfcn.h>
12745e
+
12745e
+#define DSO "moddummy1.so"
12745e
+#define FUNC "dummy1"
12745e
+
12745e
+#define DSO1 "moddummy2.so"
12745e
+#define FUNC1 "dummy2"
12745e
+
12745e
+/* Result of the called function.  */
12745e
+int func_result;
12745e
+
12745e
+/* Prototype for my hook.  */
12745e
+void *custom_malloc_hook (size_t, const void *);
12745e
+
12745e
+/* Pointer to old malloc hooks.  */
12745e
+void *(*old_malloc_hook) (size_t, const void *);
12745e
+
12745e
+/* Call function func_name in DSO dso_name via dlopen.  */
12745e
+void
12745e
+call_func (const char *dso_name, const char *func_name)
12745e
+{
12745e
+  int ret;
12745e
+  void *dso;
12745e
+  int (*func) (void);
12745e
+  char *err;
12745e
+
12745e
+  /* Open the DSO.  */
12745e
+  dso = dlopen (dso_name, RTLD_NOW|RTLD_GLOBAL);
12745e
+  if (dso == NULL)
12745e
+    {
12745e
+      err = dlerror ();
12745e
+      fprintf (stderr, "%s\n", err);
12745e
+      exit (1);
12745e
+    }
12745e
+  /* Clear any errors.  */
12745e
+  dlerror ();
12745e
+
12745e
+  /* Lookup func.  */
bca718
+  func = (int (*) (void)) dlsym (dso, func_name);
12745e
+  if (func == NULL)
12745e
+    {
12745e
+      err = dlerror ();
12745e
+      if (err != NULL)
12745e
+        {
12745e
+	  fprintf (stderr, "%s\n", err);
12745e
+	  exit (1);
12745e
+        }
12745e
+    }
12745e
+  /* Call func.  */
12745e
+  func_result = (*func) ();
12745e
+
12745e
+  /* Close the library and look for errors too.  */
12745e
+  ret = dlclose (dso);
12745e
+  if (ret != 0)
12745e
+    {
12745e
+      err = dlerror ();
12745e
+      fprintf (stderr, "%s\n", err);
12745e
+      exit (1);
12745e
+    }
12745e
+
12745e
+}
12745e
+
12745e
+/* Empty hook that does nothing.  */
12745e
+void *
12745e
+custom_malloc_hook (size_t size, const void *caller)
12745e
+{
12745e
+  void *result;
12745e
+  /* Restore old hooks.  */
12745e
+  __malloc_hook = old_malloc_hook;
12745e
+  /* First call a function in another library via dlopen.  */
12745e
+  call_func (DSO1, FUNC1);
12745e
+  /* Called recursively.  */
12745e
+  result = malloc (size);
12745e
+  /* Restore new hooks.  */
12745e
+  __malloc_hook = custom_malloc_hook;
12745e
+  return result;
12745e
+}
12745e
+
12745e
+static int
12745e
+do_test (void)
12745e
+{
12745e
+  /* Save old hook.  */
12745e
+  old_malloc_hook = __malloc_hook;
12745e
+  /* Install new hook.  */
12745e
+  __malloc_hook = custom_malloc_hook;
12745e
+
12745e
+  /* Bug 17702 fixes two things:
12745e
+       * A recursive dlopen unmapping the ld.so.cache.
12745e
+       * An assertion that _r_debug is RT_CONSISTENT at entry to dlopen.
12745e
+     We can only test the latter. Testing the former requires modifying
12745e
+     ld.so.conf to cache the dummy libraries, then running ldconfig,
12745e
+     then run the test. If you do all of that (and glibc's test
12745e
+     infrastructure doesn't support that yet) then the test will
12745e
+     SEGFAULT without the fix. If you don't do that, then the test
12745e
+     will abort because of the assert described in detail below.  */
12745e
+  call_func (DSO, FUNC);
12745e
+
12745e
+  /* Restore old hook.  */
12745e
+  __malloc_hook = old_malloc_hook;
12745e
+
12745e
+  /* The function dummy2() is called by the malloc hook. Check to
12745e
+     see that it was called. This ensures the second recursive
12745e
+     dlopen happened and we called the function in that library.
12745e
+     Before the fix you either get a SIGSEGV when accessing mmap'd
12745e
+     ld.so.cache data or an assertion failure about _r_debug not
12745e
+     beint RT_CONSISTENT.  We don't test for the SIGSEGV since it
12745e
+     would require finding moddummy1 or moddummy2 in the cache and
12745e
+     we don't have any infrastructure to test that, but the _r_debug
12745e
+     assertion triggers.  */
12745e
+  printf ("Returned result is %d\n", func_result);
12745e
+  if (func_result <= 0)
12745e
+    {
12745e
+      printf ("FAIL: Function call_func() not called.\n");
12745e
+      exit (1);
12745e
+    }
12745e
+
12745e
+  printf ("PASS: Function call_func() called more than once.\n");
12745e
+  return 0;
12745e
+}
12745e
+
12745e
+#define TEST_FUNCTION do_test ()
12745e
+#include "../test-skeleton.c"
12745e
--- glibc-2.17-c758a686/dlfcn/moddummy1.c	1969-12-31 19:00:00.000000000 -0500
12745e
+++ glibc-2.17-c758a686/dlfcn/moddummy1.c	2015-06-22 12:44:41.000000000 -0400
12745e
@@ -0,0 +1,10 @@
12745e
+/* Provide a dummy DSO for tst-rec-dlopen to use.  */
12745e
+#include <stdio.h>
12745e
+#include <stdlib.h>
12745e
+
12745e
+int
12745e
+dummy1 (void)
12745e
+{
12745e
+  printf ("Called dummy1()\n");
12745e
+  return 1;
12745e
+}
12745e
--- glibc-2.17-c758a686/dlfcn/moddummy2.c	1969-12-31 19:00:00.000000000 -0500
12745e
+++ glibc-2.17-c758a686/dlfcn/moddummy2.c	2015-06-22 12:44:41.000000000 -0400
12745e
@@ -0,0 +1,13 @@
12745e
+/* Provide a dummy DSO for tst-rec-dlopen to use.  */
12745e
+#include <stdio.h>
12745e
+#include <stdlib.h>
12745e
+
12745e
+int
12745e
+dummy2 (void)
12745e
+{
12745e
+  printf ("Called dummy2()\n");
12745e
+  /* If the outer dlopen is not dummy1 (becuase of some error)
12745e
+     then tst-rec-dlopen will see a value of -1 as the returned
12745e
+     result and fail.  */
12745e
+  return -1;
12745e
+}