1 /*
2  * Copyright (C) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *	http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <dlfcn.h>
16 #include <pthread.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <link.h>
22 #include <sys/wait.h>
23 
24 #include "test.h"
25 
26 #define MAX_BUF 256
27 #define TEST_NUM 1000
28 #define TEST_SIZE 4096
29 
30 const char* g_libPath = "/data/local/tmp/libc-test-lib/libdlopen_dso.so";
31 const char* g_initlibPath = "/data/local/tmp/libc-test-lib/libdlopen_init.so";
32 void* g_init_handler = NULL;
33 static int test_value = 0;
34 
check_loaded(char* so)35 int check_loaded(char* so)
36 {
37     int pid = getpid();
38     char path[MAX_BUF] = { 0 };
39     sprintf(path, "/proc/%d/maps", pid);
40     FILE* fp = fopen(path, "r");
41     if (fp == NULL) {
42         return 0;
43     }
44 
45     char buffer[MAX_BUF] = { 0 };
46     while (fgets(buffer, MAX_BUF, fp) != NULL) {
47         if (strstr(buffer, so) != NULL) {
48             fclose(fp);
49             return 1;
50         }
51     }
52     fclose(fp);
53     return 0;
54 }
55 
CallBack001(struct dl_phdr_info* info, size_t size, void* data)56 static int CallBack001(struct dl_phdr_info* info, size_t size, void* data)
57 {
58     if (strcmp(info->dlpi_name, g_libPath) != 0 || strcmp(info->dlpi_name, g_initlibPath) != 0) {
59         return 0;
60     }
61     test_value++;
62     if (test_value != 1) {
63         t_error("test_value should be 1, but: %d\n", test_value);
64     }
65     test_value--;
66 
67     if (test_value != 0) {
68         t_error("test_value should be 0, but: %d\n", test_value);
69     }
70     return 0;
71 }
72 
CallBack002(struct dl_phdr_info* info, size_t size, void* data)73 static int CallBack002(struct dl_phdr_info* info, size_t size, void* data)
74 {
75     if (strcmp(info->dlpi_name, g_libPath) != 0 || strcmp(info->dlpi_name, g_initlibPath) != 0) {
76         return 0;
77     }
78     test_value = test_value + 2;
79     if (test_value != 2) {
80         t_error("test_value should be 2, but: %d\n", test_value);
81     }
82 
83     test_value = test_value - 2;
84     if (test_value != 0) {
85         t_error("test_value should be 0, but: %d\n", test_value);
86     }
87     return 0;
88 }
89 
CallBack003(void* arg)90 static void* CallBack003(void* arg)
91 {
92     pid_t pid = fork();
93     if (pid > 0) {
94         int status = 0;
95         int options = 0;
96         pid_t waitpid_for_pind = waitpid(pid, &status, options);
97         if (waitpid_for_pind != pid) {
98             t_error("%s waitpid get pid is %d are not want %d\n", __func__, waitpid_for_pind, pid);
99         }
100         if (status != 0) {
101             t_error("%s waitpid get status is %d are not 0\n", __func__, status);
102         }
103     } else if (pid == 0) {
104         sleep(1);
105         exit(0);
106     } else {
107         t_error("%s waitpid fork error\n");
108     }
109     return arg;
110 }
111 
CallBack004(struct dl_phdr_info* info, size_t size, void* data)112 static int CallBack004(struct dl_phdr_info* info, size_t size, void* data)
113 {
114     char *memory = (char *)malloc(TEST_SIZE);
115     free(memory);
116     return 0;
117 }
118 
dlopen_dlclose_test001(void* arg)119 static void* dlopen_dlclose_test001(void* arg)
120 {
121     void* handle = dlopen(g_libPath, RTLD_NOW);
122     if (!handle) {
123       t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_libPath, RTLD_NOW, dlerror());
124     }
125     dlclose(handle);
126     return arg;
127 }
128 
dlopen_dlclose_test002(void* arg)129 static void* dlopen_dlclose_test002(void* arg)
130 {
131     void* handle = dlopen(g_initlibPath, RTLD_NOW);
132     if (!handle) {
133       t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
134     }
135     g_init_handler = handle;
136     return arg;
137 }
138 
dlopen_dlclose_test003(void* arg)139 static void* dlopen_dlclose_test003(void* arg)
140 {
141     dlclose(g_init_handler);
142     return arg;
143 }
144 
dlopen_dlclose_test0041(void* arg)145 static void* dlopen_dlclose_test0041(void* arg)
146 {
147     dl_iterate_phdr(CallBack001, NULL);
148     return arg;
149 }
150 
dlopen_dlclose_test0042(void* arg)151 static void* dlopen_dlclose_test0042(void* arg)
152 {
153     dl_iterate_phdr(CallBack002, NULL);
154     return arg;
155 }
156 
dlopen_dlclose_test005(void* arg)157 static void* dlopen_dlclose_test005(void* arg)
158 {
159     int(* get_val)(void) = dlsym(g_init_handler, "getVal");
160     if (get_val == NULL) {
161         t_error("dlsym failed, don't find the symbol getVal\n");
162     }
163     if (get_val != NULL && get_val() != 1) {
164         t_error("This val after init should be 1, but %d\n", get_val());
165     }
166     return arg;
167 }
168 
dlopen_dlclose_test006(void* arg)169 static void* dlopen_dlclose_test006(void* arg)
170 {
171     dl_iterate_phdr(CallBack004, NULL);
172     return arg;
173 }
174 
do_test_concurrently(void *(*test) (void *arg), size_t num_threads)175 static void do_test_concurrently(void *(*test) (void *arg), size_t num_threads)
176 {
177     pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t) * num_threads);
178     if (threads == NULL) {
179         t_error("Failed to allocate memory: %s\n", strerror(errno));
180         return;
181     }
182 
183     size_t last = 0;
184     while (last < num_threads) {
185         if (pthread_create(&(threads[last]), NULL, test, NULL)) {
186             t_error("Failed to create thread: %s\n", strerror(errno));
187             break;
188         }
189         last++;
190     }
191 
192     for (size_t i = 0; i < last; i++) {
193         if (pthread_join(threads[i], NULL)) {
194             t_error("Failed to join thread: %s\n", strerror(errno));
195         }
196     }
197 
198     free(threads);
199     return;
200 }
201 
do_test_double_concurrently(void *(*test1) (void *arg), void *(*test2) (void *arg), size_t num_threads1, size_t num_threads2)202 static void do_test_double_concurrently(void *(*test1) (void *arg), void *(*test2) (void *arg),
203     size_t num_threads1, size_t num_threads2)
204 {
205     pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t) * (num_threads1 + num_threads2));
206     if (threads == NULL) {
207         t_error("Failed to allocate memory: %s\n", strerror(errno));
208         return;
209     }
210 
211     size_t last = 0;
212     while (last < num_threads1) {
213         if (pthread_create(&(threads[last]), NULL, test1, NULL)) {
214             t_error("Failed to create thread: %s\n", strerror(errno));
215             break;
216         }
217         last++;
218     }
219 
220     while (last < num_threads1 + num_threads2) {
221         if (pthread_create(&(threads[last]), NULL, test2, NULL)) {
222             t_error("Failed to create thread: %s\n", strerror(errno));
223             break;
224         }
225         last++;
226     }
227 
228     for (size_t i = 0; i < last; i++) {
229         if (pthread_join(threads[i], NULL)) {
230             t_error("Failed to join thread: %s\n", strerror(errno));
231         }
232     }
233 
234     free(threads);
235     return;
236 }
237 
238 /**
239  * @tc.name      : dl_multithread_lock_0100
240  * @tc.desc      : multithreaded dlopen/dlclose, at the end the expected so file should not be in memory.
241  * @tc.level     : Level 0
242  */
dl_multithread_lock_0100(void)243 void dl_multithread_lock_0100(void)
244 {
245     size_t num_threads = 1000;
246     do_test_concurrently(dlopen_dlclose_test001, num_threads);
247     if (check_loaded((char*)g_libPath)) {
248         t_error("This so file should not exist, %s\n", (char*)g_libPath);
249     }
250 }
251 
252 /**
253  * @tc.name      : dl_multithread_lock_0200
254  * @tc.desc      : multithreaded dlopen, the init constructor should be called only once.
255  * @tc.level     : Level 0
256  */
dl_multithread_lock_0200(void)257 void dl_multithread_lock_0200(void)
258 {
259     size_t num_threads = 20;
260     do_test_concurrently(dlopen_dlclose_test002, num_threads);
261     if (!check_loaded((char*)g_initlibPath)) {
262         t_error("This so file should exist, %s\n", (char*)g_initlibPath);
263     }
264     int(* get_val)(void) = dlsym(g_init_handler, "getVal");
265     if (get_val == NULL) {
266         t_error("dlsym failed, don't find the symbol getVal\n");
267     }
268     if (get_val != NULL && get_val() != 1) {
269         t_error("This val after init should be 1, but %d\n", get_val());
270     }
271 }
272 
273 /**
274  * @tc.name      : dl_multithread_lock_0300
275  * @tc.desc      : multithreaded dlopen, the deconstructor should be called only at the last dlclose.
276  * @tc.level     : Level 0
277  */
dl_multithread_lock_0300(void)278 void dl_multithread_lock_0300(void)
279 {
280     size_t num_threads = 19;
281     do_test_concurrently(dlopen_dlclose_test003, num_threads);
282     if (!check_loaded((char*)g_initlibPath)) {
283         t_error("This so file should exist, %s\n", (char*)g_initlibPath);
284     }
285     int(* get_val)(void) = dlsym(g_init_handler, "getVal");
286     if (get_val == NULL) {
287         t_error("dlsym failed, don't find the symbol getVal\n");
288     }
289     if (get_val != NULL && get_val() != 1) {
290         t_error("This val after init should be 1, but %d\n", get_val());
291     }
292     dlclose(g_init_handler);
293     if (check_loaded((char*)g_initlibPath)) {
294         t_error("This so file should not exist, %s\n", (char*)g_initlibPath);
295     }
296 }
297 
298 /**
299  * @tc.name      : dl_multithread_lock_0400
300  * @tc.desc      : multithreaded iterate Callback in dl_iterate_phdr, the static test_value should be thread safe.
301  * @tc.level     : Level 0
302  */
dl_multithread_lock_0400(void)303 void dl_multithread_lock_0400(void)
304 {
305     void* handle1 = dlopen(g_libPath, RTLD_NOW);
306     if (!handle1) {
307         t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_libPath, RTLD_NOW, dlerror());
308     }
309 
310     void* handle2 = dlopen(g_initlibPath, RTLD_NOW);
311     if (!handle2) {
312         t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
313     }
314 
315     size_t num_threads = 5;
316     do_test_double_concurrently(dlopen_dlclose_test0041, dlopen_dlclose_test0042, num_threads, num_threads);
317 
318     dlclose(handle1);
319     dlclose(handle2);
320     if (check_loaded((char*)g_initlibPath) || check_loaded((char*)g_libPath)) {
321         t_error("These so files should not exist\n");
322     }
323 }
324 
325 /**
326  * @tc.name      : dl_multithread_lock_0500
327  * @tc.desc      : multithreaded dlsym, dlsym should not be blocked by dlsym in other threads.
328  * @tc.level     : Level 0
329  */
dl_multithread_lock_0500(void)330 void dl_multithread_lock_0500(void)
331 {
332     void* handle = dlopen(g_initlibPath, RTLD_NOW);
333     if (!handle) {
334         t_error("dlopen(name=%s, mode=%d) failed: %s\n", g_initlibPath, RTLD_NOW, dlerror());
335     }
336 
337     g_init_handler = handle;
338     size_t num_threads = 1000;
339     do_test_concurrently(dlopen_dlclose_test005, num_threads);
340     g_init_handler = NULL;
341     dlclose(handle);
342 }
343 
344 /**
345  * @tc.name      : dl_multithread_lock_0600
346  * @tc.desc      : malloc and fork in different threads to check that there is no ABBA deadlock
347  *                (ld lock and jemalloc lock).
348  * @tc.level     : Level 0
349  */
dl_multithread_lock_0600(void)350 void dl_multithread_lock_0600(void)
351 {
352     size_t num_threads = 100;
353     do_test_double_concurrently(dlopen_dlclose_test006, CallBack003, num_threads, num_threads);
354 }
355 
main(int argc, char* argv[])356 int main(int argc, char* argv[])
357 {
358     dl_multithread_lock_0100();
359     dl_multithread_lock_0200();
360     dl_multithread_lock_0300();
361     dl_multithread_lock_0400();
362     dl_multithread_lock_0500();
363     dl_multithread_lock_0600();
364     return t_status;
365 }