1/* More debugging hooks for `malloc'.
2 Copyright (C) 1991-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Written April 2, 1991 by John Gilmore of Cygnus Support.
5 Based on mcheck.c by Mike Haertel.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, see
19 <https://www.gnu.org/licenses/>. */
20
21#ifndef _MALLOC_INTERNAL
22# define _MALLOC_INTERNAL
23# include <malloc.h>
24# include <mcheck.h>
25# include <libc-lock.h>
26#endif
27
28#include <dlfcn.h>
29#include <fcntl.h>
30#include <stdio.h>
31#include <string.h>
32#include <stdlib.h>
33
34#include <_itoa.h>
35
36#include <libc-internal.h>
37#include <dso_handle.h>
38
39#include <libio/iolibio.h>
40#define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
41#define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
42
43#include <kernel-features.h>
44
45#define TRACE_BUFFER_SIZE 512
46
47static FILE *mallstream;
48static const char mallenv[] = "MALLOC_TRACE";
49static char *malloc_trace_buffer;
50
51__libc_lock_define_initialized (static, lock);
52
53/* Address to breakpoint on accesses to... */
54void *mallwatch;
55
56/* Old hook values. */
57static void (*tr_old_free_hook) (void *ptr, const void *);
58static void *(*tr_old_malloc_hook) (size_t size, const void *);
59static void *(*tr_old_realloc_hook) (void *ptr, size_t size,
60 const void *);
61static void *(*tr_old_memalign_hook) (size_t __alignment, size_t __size,
62 const void *);
63
64/* This function is called when the block being alloc'd, realloc'd, or
65 freed has an address matching the variable "mallwatch". In a debugger,
66 set "mallwatch" to the address of interest, then put a breakpoint on
67 tr_break. */
68
69extern void tr_break (void) __THROW;
70libc_hidden_proto (tr_break)
71void
72tr_break (void)
73{
74}
75libc_hidden_def (tr_break)
76
77static void
78tr_where (const void *caller, Dl_info *info)
79{
80 if (caller != NULL)
81 {
82 if (info != NULL)
83 {
84 char *buf = (char *) "";
85 if (info->dli_sname != NULL)
86 {
87 size_t len = strlen (info->dli_sname);
88 buf = alloca (len + 6 + 2 * sizeof (void *));
89
90 buf[0] = '(';
91 __stpcpy (_fitoa (caller >= (const void *) info->dli_saddr
92 ? caller - (const void *) info->dli_saddr
93 : (const void *) info->dli_saddr - caller,
94 __stpcpy (__mempcpy (buf + 1, info->dli_sname,
95 len),
96 caller >= (void *) info->dli_saddr
97 ? "+0x" : "-0x"),
98 16, 0),
99 ")");
100 }
101
102 fprintf (mallstream, "@ %s%s%s[%p] ",
103 info->dli_fname ? : "", info->dli_fname ? ":" : "",
104 buf, caller);
105 }
106 else
107 fprintf (mallstream, "@ [%p] ", caller);
108 }
109}
110
111static Dl_info *
112lock_and_info (const void *caller, Dl_info *mem)
113{
114 if (caller == NULL)
115 return NULL;
116
117 Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL;
118
119 __libc_lock_lock (lock);
120
121 return res;
122}
123
124static void tr_freehook (void *, const void *);
125static void * tr_mallochook (size_t, const void *);
126static void * tr_reallochook (void *, size_t, const void *);
127static void * tr_memalignhook (size_t, size_t, const void *);
128
129/* Set all the default non-trace hooks. */
130static __always_inline void
131set_default_hooks (void)
132{
133 __free_hook = tr_old_free_hook;
134 __malloc_hook = tr_old_malloc_hook;
135 __realloc_hook = tr_old_realloc_hook;
136 __memalign_hook = tr_old_memalign_hook;
137}
138
139/* Set all of the tracing hooks used for mtrace. */
140static __always_inline void
141set_trace_hooks (void)
142{
143 __free_hook = tr_freehook;
144 __malloc_hook = tr_mallochook;
145 __realloc_hook = tr_reallochook;
146 __memalign_hook = tr_memalignhook;
147}
148
149/* Save the current set of hooks as the default hooks. */
150static __always_inline void
151save_default_hooks (void)
152{
153 tr_old_free_hook = __free_hook;
154 tr_old_malloc_hook = __malloc_hook;
155 tr_old_realloc_hook = __realloc_hook;
156 tr_old_memalign_hook = __memalign_hook;
157}
158
159static void
160tr_freehook (void *ptr, const void *caller)
161{
162 if (ptr == NULL)
163 return;
164
165 Dl_info mem;
166 Dl_info *info = lock_and_info (caller, &mem);
167 tr_where (caller, info);
168 /* Be sure to print it first. */
169 fprintf (mallstream, "- %p\n", ptr);
170 if (ptr == mallwatch)
171 {
172 __libc_lock_unlock (lock);
173 tr_break ();
174 __libc_lock_lock (lock);
175 }
176 set_default_hooks ();
177 if (tr_old_free_hook != NULL)
178 (*tr_old_free_hook)(ptr, caller);
179 else
180 free (ptr);
181 set_trace_hooks ();
182 __libc_lock_unlock (lock);
183}
184
185static void *
186tr_mallochook (size_t size, const void *caller)
187{
188 void *hdr;
189
190 Dl_info mem;
191 Dl_info *info = lock_and_info (caller, &mem);
192
193 set_default_hooks ();
194 if (tr_old_malloc_hook != NULL)
195 hdr = (void *) (*tr_old_malloc_hook)(size, caller);
196 else
197 hdr = (void *) malloc (size);
198 set_trace_hooks ();
199
200 tr_where (caller, info);
201 /* We could be printing a NULL here; that's OK. */
202 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
203
204 __libc_lock_unlock (lock);
205
206 if (hdr == mallwatch)
207 tr_break ();
208
209 return hdr;
210}
211
212static void *
213tr_reallochook (void *ptr, size_t size, const void *caller)
214{
215 void *hdr;
216
217 if (ptr == mallwatch)
218 tr_break ();
219
220 Dl_info mem;
221 Dl_info *info = lock_and_info (caller, &mem);
222
223 set_default_hooks ();
224 if (tr_old_realloc_hook != NULL)
225 hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller);
226 else
227 hdr = (void *) realloc (ptr, size);
228 set_trace_hooks ();
229
230 tr_where (caller, info);
231 if (hdr == NULL)
232 {
233 if (size != 0)
234 /* Failed realloc. */
235 fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
236 else
237 fprintf (mallstream, "- %p\n", ptr);
238 }
239 else if (ptr == NULL)
240 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
241 else
242 {
243 fprintf (mallstream, "< %p\n", ptr);
244 tr_where (caller, info);
245 fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
246 }
247
248 __libc_lock_unlock (lock);
249
250 if (hdr == mallwatch)
251 tr_break ();
252
253 return hdr;
254}
255
256static void *
257tr_memalignhook (size_t alignment, size_t size, const void *caller)
258{
259 void *hdr;
260
261 Dl_info mem;
262 Dl_info *info = lock_and_info (caller, &mem);
263
264 set_default_hooks ();
265 if (tr_old_memalign_hook != NULL)
266 hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller);
267 else
268 hdr = (void *) memalign (alignment, size);
269 set_trace_hooks ();
270
271 tr_where (caller, info);
272 /* We could be printing a NULL here; that's OK. */
273 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
274
275 __libc_lock_unlock (lock);
276
277 if (hdr == mallwatch)
278 tr_break ();
279
280 return hdr;
281}
282
283
284#ifdef _LIBC
285
286/* This function gets called to make sure all memory the library
287 allocates get freed and so does not irritate the user when studying
288 the mtrace output. */
289static void __libc_freeres_fn_section
290release_libc_mem (void)
291{
292 /* Only call the free function if we still are running in mtrace mode. */
293 if (mallstream != NULL)
294 __libc_freeres ();
295}
296#endif
297
298
299/* We enable tracing if either the environment variable MALLOC_TRACE
300 is set, or if the variable mallwatch has been patched to an address
301 that the debugging user wants us to stop on. When patching mallwatch,
302 don't forget to set a breakpoint on tr_break! */
303
304void
305mtrace (void)
306{
307#ifdef _LIBC
308 static int added_atexit_handler;
309#endif
310 char *mallfile;
311
312 /* Don't panic if we're called more than once. */
313 if (mallstream != NULL)
314 return;
315
316#ifdef _LIBC
317 /* When compiling the GNU libc we use the secure getenv function
318 which prevents the misuse in case of SUID or SGID enabled
319 programs. */
320 mallfile = __libc_secure_getenv (mallenv);
321#else
322 mallfile = getenv (mallenv);
323#endif
324 if (mallfile != NULL || mallwatch != NULL)
325 {
326 char *mtb = malloc (TRACE_BUFFER_SIZE);
327 if (mtb == NULL)
328 return;
329
330 mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
331 if (mallstream != NULL)
332 {
333 /* Be sure it doesn't malloc its buffer! */
334 malloc_trace_buffer = mtb;
335 setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
336 fprintf (mallstream, "= Start\n");
337 save_default_hooks ();
338 set_trace_hooks ();
339#ifdef _LIBC
340 if (!added_atexit_handler)
341 {
342 added_atexit_handler = 1;
343 __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
344 __dso_handle);
345 }
346#endif
347 }
348 else
349 free (mtb);
350 }
351}
352
353void
354muntrace (void)
355{
356 if (mallstream == NULL)
357 return;
358
359 /* Do the reverse of what done in mtrace: first reset the hooks and
360 MALLSTREAM, and only after that write the trailer and close the
361 file. */
362 FILE *f = mallstream;
363 mallstream = NULL;
364 set_default_hooks ();
365
366 fprintf (f, "= End\n");
367 fclose (f);
368}
369