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