1 /* Test DTV size oveflow when pthread_create reuses old DTV and TLS is
2    used by dlopened shared object.
3    Copyright (C) 2014-2022 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <https://www.gnu.org/licenses/>.  */
19 
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <dlfcn.h>
23 #include <assert.h>
24 #include <pthread.h>
25 
26 /* The choices of thread count, and file counts are arbitary.
27    The point is simply to run enough threads that an exiting
28    thread has it's stack reused by another thread at the same
29    time as new libraries have been loaded.  */
30 #define DSO_SHARED_FILES 20
31 #define DSO_OPEN_THREADS 20
32 #define DSO_EXEC_THREADS 2
33 
34 /* Used to make sure that only one thread is calling dlopen and dlclose
35    at a time.  */
36 pthread_mutex_t g_lock;
37 
38 typedef void (*function) (void);
39 
40 void *
dso_invoke(void * dso_fun)41 dso_invoke(void *dso_fun)
42 {
43   function *fun_vec = (function *) dso_fun;
44   int dso;
45 
46   for (dso = 0; dso < DSO_SHARED_FILES; dso++)
47     (*fun_vec[dso]) ();
48 
49   pthread_exit (NULL);
50 }
51 
52 void *
dso_process(void * p)53 dso_process (void * p)
54 {
55   void *handle[DSO_SHARED_FILES];
56   function fun_vec[DSO_SHARED_FILES];
57   char dso_path[DSO_SHARED_FILES][100];
58   int dso;
59   int t = (int) (uintptr_t) p;
60 
61   /* Open DSOs and get a function.  */
62   for (dso = 0; dso < DSO_SHARED_FILES; dso++)
63     {
64       sprintf (dso_path[dso], "tst-stack4mod-%i-%i.so", t, dso);
65 
66       pthread_mutex_lock (&g_lock);
67 
68       handle[dso] = dlopen (dso_path[dso], RTLD_NOW);
69       assert (handle[dso]);
70 
71       fun_vec[dso] = (function) dlsym (handle[dso], "function");
72       assert (fun_vec[dso]);
73 
74       pthread_mutex_unlock (&g_lock);
75     }
76 
77   /* Spawn workers.  */
78   pthread_t thread[DSO_EXEC_THREADS];
79   int i, ret;
80   uintptr_t result = 0;
81   for (i = 0; i < DSO_EXEC_THREADS; i++)
82     {
83       pthread_mutex_lock (&g_lock);
84       ret = pthread_create (&thread[i], NULL, dso_invoke, (void *) fun_vec);
85       if (ret != 0)
86 	{
87 	  printf ("pthread_create failed: %d\n", ret);
88 	  result = 1;
89 	}
90       pthread_mutex_unlock (&g_lock);
91     }
92 
93   if (!result)
94     for (i = 0; i < DSO_EXEC_THREADS; i++)
95       {
96 	ret = pthread_join (thread[i], NULL);
97 	if (ret != 0)
98 	  {
99 	    printf ("pthread_join failed: %d\n", ret);
100 	    result = 1;
101 	  }
102       }
103 
104   /* Close all DSOs.  */
105   for (dso = 0; dso < DSO_SHARED_FILES; dso++)
106     {
107       pthread_mutex_lock (&g_lock);
108       dlclose (handle[dso]);
109       pthread_mutex_unlock (&g_lock);
110     }
111 
112   /* Exit.  */
113   pthread_exit ((void *) result);
114 }
115 
116 static int
do_test(void)117 do_test (void)
118 {
119   pthread_t thread[DSO_OPEN_THREADS];
120   int i,j;
121   int ret;
122   int result = 0;
123 
124   pthread_mutex_init (&g_lock, NULL);
125 
126   /* 100 is arbitrary here and is known to trigger PR 13862.  */
127   for (j = 0; j < 100; j++)
128     {
129       for (i = 0; i < DSO_OPEN_THREADS; i++)
130 	{
131 	  ret = pthread_create (&thread[i], NULL, dso_process,
132 				(void *) (uintptr_t) i);
133 	  if (ret != 0)
134 	    {
135 	      printf ("pthread_create failed: %d\n", ret);
136 	      result = 1;
137 	    }
138 	}
139 
140       if (result)
141 	break;
142 
143       for (i = 0; i < DSO_OPEN_THREADS; i++)
144 	{
145 	  ret = pthread_join (thread[i], NULL);
146 	  if (ret != 0)
147 	    {
148 	      printf ("pthread_join failed: %d\n", ret);
149 	      result = 1;
150 	    }
151 	}
152     }
153 
154   return result;
155 }
156 
157 #define TEST_FUNCTION do_test ()
158 #define TIMEOUT 100
159 #include "../test-skeleton.c"
160