1 | /* Test recursive dlopen using malloc hooks. |
2 | Copyright (C) 2015-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <stdio.h> |
20 | #include <stdlib.h> |
21 | #include <dlfcn.h> |
22 | #include <stdbool.h> |
23 | #include <stdalign.h> |
24 | #include <sys/mman.h> |
25 | #include <unistd.h> |
26 | #include <string.h> |
27 | |
28 | #define DSO "moddummy1.so" |
29 | #define FUNC "dummy1" |
30 | |
31 | #define DSO1 "moddummy2.so" |
32 | #define FUNC1 "dummy2" |
33 | |
34 | /* Result of the called function. */ |
35 | int func_result; |
36 | |
37 | /* Call function func_name in DSO dso_name via dlopen. */ |
38 | void |
39 | call_func (const char *dso_name, const char *func_name) |
40 | { |
41 | int ret; |
42 | void *dso; |
43 | int (*func) (void); |
44 | char *err; |
45 | |
46 | /* Open the DSO. */ |
47 | dso = dlopen (file: dso_name, RTLD_NOW|RTLD_GLOBAL); |
48 | if (dso == NULL) |
49 | { |
50 | err = dlerror (); |
51 | fprintf (stderr, "%s\n" , err); |
52 | exit (1); |
53 | } |
54 | /* Clear any errors. */ |
55 | dlerror (); |
56 | |
57 | /* Lookup func. */ |
58 | func = (int (*) (void)) dlsym (handle: dso, name: func_name); |
59 | if (func == NULL) |
60 | { |
61 | err = dlerror (); |
62 | if (err != NULL) |
63 | { |
64 | fprintf (stderr, "%s\n" , err); |
65 | exit (1); |
66 | } |
67 | } |
68 | /* Call func. */ |
69 | func_result = (*func) (); |
70 | |
71 | /* Close the library and look for errors too. */ |
72 | ret = dlclose (handle: dso); |
73 | if (ret != 0) |
74 | { |
75 | err = dlerror (); |
76 | fprintf (stderr, "%s\n" , err); |
77 | exit (1); |
78 | } |
79 | |
80 | } |
81 | |
82 | /* If true, call another function from malloc. */ |
83 | static bool call_function; |
84 | |
85 | /* Set to true to indicate that the interposed malloc was called. */ |
86 | static bool interposed_malloc_called; |
87 | |
88 | /* Interposed malloc which optionally calls another function. */ |
89 | void * |
90 | malloc (size_t size) |
91 | { |
92 | interposed_malloc_called = true; |
93 | static void *(*original_malloc) (size_t); |
94 | |
95 | if (original_malloc == NULL) |
96 | { |
97 | static bool in_initialization; |
98 | if (in_initialization) |
99 | { |
100 | const char *message |
101 | = "error: malloc called recursively during initialization\n" ; |
102 | (void) write (STDOUT_FILENO, message, strlen (message)); |
103 | _exit (2); |
104 | } |
105 | in_initialization = true; |
106 | |
107 | original_malloc |
108 | = (__typeof (original_malloc)) dlsym (RTLD_NEXT, name: "malloc" ); |
109 | if (original_malloc == NULL) |
110 | { |
111 | const char *message |
112 | = "error: dlsym for malloc failed\n" ; |
113 | (void) write (STDOUT_FILENO, message, strlen (message)); |
114 | _exit (2); |
115 | } |
116 | } |
117 | |
118 | if (call_function) |
119 | { |
120 | call_function = false; |
121 | call_func (DSO1, FUNC1); |
122 | call_function = true; |
123 | } |
124 | return original_malloc (size); |
125 | } |
126 | |
127 | static int |
128 | do_test (void) |
129 | { |
130 | /* Ensure initialization. */ |
131 | { |
132 | void *volatile ptr = malloc (size: 1); |
133 | free (ptr: ptr); |
134 | } |
135 | |
136 | if (!interposed_malloc_called) |
137 | { |
138 | printf (format: "error: interposed malloc not called during initialization\n" ); |
139 | return 1; |
140 | } |
141 | |
142 | call_function = true; |
143 | |
144 | /* Bug 17702 fixes two things: |
145 | * A recursive dlopen unmapping the ld.so.cache. |
146 | * An assertion that _r_debug is RT_CONSISTENT at entry to dlopen. |
147 | We can only test the latter. Testing the former requires modifying |
148 | ld.so.conf to cache the dummy libraries, then running ldconfig, |
149 | then run the test. If you do all of that (and glibc's test |
150 | infrastructure doesn't support that yet) then the test will |
151 | SEGFAULT without the fix. If you don't do that, then the test |
152 | will abort because of the assert described in detail below. */ |
153 | call_func (DSO, FUNC); |
154 | |
155 | call_function = false; |
156 | |
157 | /* The function dummy2() is called by the malloc hook. Check to |
158 | see that it was called. This ensures the second recursive |
159 | dlopen happened and we called the function in that library. |
160 | Before the fix you either get a SIGSEGV when accessing mmap'd |
161 | ld.so.cache data or an assertion failure about _r_debug not |
162 | beint RT_CONSISTENT. We don't test for the SIGSEGV since it |
163 | would require finding moddummy1 or moddummy2 in the cache and |
164 | we don't have any infrastructure to test that, but the _r_debug |
165 | assertion triggers. */ |
166 | printf (format: "Returned result is %d\n" , func_result); |
167 | if (func_result <= 0) |
168 | { |
169 | printf (format: "FAIL: Function call_func() not called.\n" ); |
170 | exit (1); |
171 | } |
172 | |
173 | printf (format: "PASS: Function call_func() called more than once.\n" ); |
174 | return 0; |
175 | } |
176 | |
177 | #define TEST_FUNCTION do_test () |
178 | #include "../test-skeleton.c" |
179 | |