e38cb5
From d0e357ff45a75553dee3b17ed7d303bfa544f6fe Mon Sep 17 00:00:00 2001
e38cb5
From: Florian Weimer <fweimer@redhat.com>
e38cb5
Date: Fri, 26 Aug 2022 21:15:43 +0200
e38cb5
Subject: elf: Call __libc_early_init for reused namespaces (bug 29528)
e38cb5
e38cb5
libc_map is never reset to NULL, neither during dlclose nor on a
e38cb5
dlopen call which reuses the namespace structure.  As a result, if a
e38cb5
namespace is reused, its libc is not initialized properly.  The most
e38cb5
visible result is a crash in the <ctype.h> functions.
e38cb5
e38cb5
To prevent similar bugs on namespace reuse from surfacing,
e38cb5
unconditionally initialize the chosen namespace to zero using memset.
e38cb5
e38cb5
[Note from DJ: Regenerated for new line numbers and context, added
e38cb5
link dependency on libdl]]
e38cb5
e38cb5
diff -rupN a/elf/Makefile b/elf/Makefile
e38cb5
--- a/elf/Makefile	2022-10-05 15:04:11.814901849 -0400
e38cb5
+++ b/elf/Makefile	2022-10-05 17:02:19.858635958 -0400
e38cb5
@@ -367,6 +367,7 @@ tests += \
e38cb5
   tst-dlmopen3 \
e38cb5
   tst-dlmopen-dlerror \
e38cb5
   tst-dlmopen-gethostbyname \
e38cb5
+  tst-dlmopen-twice \
e38cb5
   tst-dlopenfail \
e38cb5
   tst-dlopenfail-2 \
e38cb5
   tst-dlopenrpath \
e38cb5
@@ -671,6 +672,8 @@ modules-names = \
e38cb5
   tst-dlmopen1mod \
e38cb5
   tst-dlmopen-dlerror-mod \
e38cb5
   tst-dlmopen-gethostbyname-mod \
e38cb5
+  tst-dlmopen-twice-mod1 \
e38cb5
+  tst-dlmopen-twice-mod2 \
e38cb5
   tst-dlopenfaillinkmod \
e38cb5
   tst-dlopenfailmod1 \
e38cb5
   tst-dlopenfailmod2 \
e38cb5
@@ -2569,3 +2572,9 @@ $(objpfx)tst-audit-tlsdesc.out: $(objpfx
e38cb5
 tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
e38cb5
 $(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so
e38cb5
 tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
e38cb5
+
e38cb5
+
e38cb5
+$(objpfx)tst-dlmopen-twice: $(libdl)
e38cb5
+$(objpfx)tst-dlmopen-twice.out: \
e38cb5
+  $(objpfx)tst-dlmopen-twice-mod1.so \
e38cb5
+  $(objpfx)tst-dlmopen-twice-mod2.so
e38cb5
diff -rupN a/elf/dl-open.c b/elf/dl-open.c
e38cb5
--- a/elf/dl-open.c	2022-10-05 15:04:11.635894932 -0400
e38cb5
+++ b/elf/dl-open.c	2022-10-05 15:10:31.667638060 -0400
e38cb5
@@ -836,11 +836,14 @@ _dl_open (const char *file, int mode, co
e38cb5
 	  _dl_signal_error (EINVAL, file, NULL, N_("\
e38cb5
 no more namespaces available for dlmopen()"));
e38cb5
 	}
e38cb5
-      else if (nsid == GL(dl_nns))
e38cb5
-	{
e38cb5
-	  __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
e38cb5
-	  ++GL(dl_nns);
e38cb5
-	}
e38cb5
+
e38cb5
+      if (nsid == GL(dl_nns))
e38cb5
+	++GL(dl_nns);
e38cb5
+
e38cb5
+      /* Initialize the new namespace.  Most members are
e38cb5
+	 zero-initialized, only the lock needs special treatment.  */
e38cb5
+      memset (&GL(dl_ns)[nsid], 0, sizeof (GL(dl_ns)[nsid]));
e38cb5
+      __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock);
e38cb5
 
e38cb5
       _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
e38cb5
     }
e38cb5
diff -rupN a/elf/tst-dlmopen-twice-mod1.c b/elf/tst-dlmopen-twice-mod1.c
e38cb5
--- a/elf/tst-dlmopen-twice-mod1.c	1969-12-31 19:00:00.000000000 -0500
e38cb5
+++ b/elf/tst-dlmopen-twice-mod1.c	2022-10-05 15:10:31.671638216 -0400
e38cb5
@@ -0,0 +1,37 @@
e38cb5
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528).  Module 1.
e38cb5
+   Copyright (C) 2022 Free Software Foundation, Inc.
e38cb5
+   This file is part of the GNU C Library.
e38cb5
+
e38cb5
+   The GNU C Library is free software; you can redistribute it and/or
e38cb5
+   modify it under the terms of the GNU Lesser General Public
e38cb5
+   License as published by the Free Software Foundation; either
e38cb5
+   version 2.1 of the License, or (at your option) any later version.
e38cb5
+
e38cb5
+   The GNU C Library is distributed in the hope that it will be useful,
e38cb5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e38cb5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e38cb5
+   Lesser General Public License for more details.
e38cb5
+
e38cb5
+   You should have received a copy of the GNU Lesser General Public
e38cb5
+   License along with the GNU C Library; if not, see
e38cb5
+   <https://www.gnu.org/licenses/>.  */
e38cb5
+
e38cb5
+#include <stdio.h>
e38cb5
+
e38cb5
+static void __attribute__ ((constructor))
e38cb5
+init (void)
e38cb5
+{
e38cb5
+  puts ("info: tst-dlmopen-twice-mod1.so loaded");
e38cb5
+  fflush (stdout);
e38cb5
+}
e38cb5
+
e38cb5
+static void __attribute__ ((destructor))
e38cb5
+fini (void)
e38cb5
+{
e38cb5
+  puts ("info: tst-dlmopen-twice-mod1.so about to be unloaded");
e38cb5
+  fflush (stdout);
e38cb5
+}
e38cb5
+
e38cb5
+/* Large allocation.  The second module does not have this, so it
e38cb5
+   should load libc at a different address.  */
e38cb5
+char large_allocate[16 * 1024 * 1024];
e38cb5
diff -rupN a/elf/tst-dlmopen-twice-mod2.c b/elf/tst-dlmopen-twice-mod2.c
e38cb5
--- a/elf/tst-dlmopen-twice-mod2.c	1969-12-31 19:00:00.000000000 -0500
e38cb5
+++ b/elf/tst-dlmopen-twice-mod2.c	2022-10-05 15:10:31.676638411 -0400
e38cb5
@@ -0,0 +1,50 @@
e38cb5
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528).  Module 2.
e38cb5
+   Copyright (C) 2022 Free Software Foundation, Inc.
e38cb5
+   This file is part of the GNU C Library.
e38cb5
+
e38cb5
+   The GNU C Library is free software; you can redistribute it and/or
e38cb5
+   modify it under the terms of the GNU Lesser General Public
e38cb5
+   License as published by the Free Software Foundation; either
e38cb5
+   version 2.1 of the License, or (at your option) any later version.
e38cb5
+
e38cb5
+   The GNU C Library is distributed in the hope that it will be useful,
e38cb5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e38cb5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e38cb5
+   Lesser General Public License for more details.
e38cb5
+
e38cb5
+   You should have received a copy of the GNU Lesser General Public
e38cb5
+   License along with the GNU C Library; if not, see
e38cb5
+   <https://www.gnu.org/licenses/>.  */
e38cb5
+
e38cb5
+#include <ctype.h>
e38cb5
+#include <stdio.h>
e38cb5
+
e38cb5
+static void __attribute__ ((constructor))
e38cb5
+init (void)
e38cb5
+{
e38cb5
+  puts ("info: tst-dlmopen-twice-mod2.so loaded");
e38cb5
+  fflush (stdout);
e38cb5
+}
e38cb5
+
e38cb5
+static void __attribute__ ((destructor))
e38cb5
+fini (void)
e38cb5
+{
e38cb5
+  puts ("info: tst-dlmopen-twice-mod2.so about to be unloaded");
e38cb5
+  fflush (stdout);
e38cb5
+}
e38cb5
+
e38cb5
+int
e38cb5
+run_check (void)
e38cb5
+{
e38cb5
+  puts ("info: about to call isalpha");
e38cb5
+  fflush (stdout);
e38cb5
+
e38cb5
+  volatile char ch = 'a';
e38cb5
+  if (!isalpha (ch))
e38cb5
+    {
e38cb5
+      puts ("error: isalpha ('a') is not true");
e38cb5
+      fflush (stdout);
e38cb5
+      return 1;
e38cb5
+    }
e38cb5
+  return 0;
e38cb5
+}
e38cb5
diff -rupN a/elf/tst-dlmopen-twice.c b/elf/tst-dlmopen-twice.c
e38cb5
--- a/elf/tst-dlmopen-twice.c	1969-12-31 19:00:00.000000000 -0500
e38cb5
+++ b/elf/tst-dlmopen-twice.c	2022-10-05 15:10:31.679638528 -0400
e38cb5
@@ -0,0 +1,34 @@
e38cb5
+/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528).  Main.
e38cb5
+   Copyright (C) 2022 Free Software Foundation, Inc.
e38cb5
+   This file is part of the GNU C Library.
e38cb5
+
e38cb5
+   The GNU C Library is free software; you can redistribute it and/or
e38cb5
+   modify it under the terms of the GNU Lesser General Public
e38cb5
+   License as published by the Free Software Foundation; either
e38cb5
+   version 2.1 of the License, or (at your option) any later version.
e38cb5
+
e38cb5
+   The GNU C Library is distributed in the hope that it will be useful,
e38cb5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e38cb5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e38cb5
+   Lesser General Public License for more details.
e38cb5
+
e38cb5
+   You should have received a copy of the GNU Lesser General Public
e38cb5
+   License along with the GNU C Library; if not, see
e38cb5
+   <https://www.gnu.org/licenses/>.  */
e38cb5
+
e38cb5
+#include <support/xdlfcn.h>
e38cb5
+#include <support/check.h>
e38cb5
+
e38cb5
+static int
e38cb5
+do_test (void)
e38cb5
+{
e38cb5
+  void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so", RTLD_NOW);
e38cb5
+  xdlclose (handle);
e38cb5
+  handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod2.so", RTLD_NOW);
e38cb5
+  int (*run_check) (void) = xdlsym (handle, "run_check");
e38cb5
+  TEST_COMPARE (run_check (), 0);
e38cb5
+  xdlclose (handle);
e38cb5
+  return 0;
e38cb5
+}
e38cb5
+
e38cb5
+#include <support/test-driver.c>