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