1 /* mtrace implementation for `malloc'.
2    Copyright (C) 1991-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 
20 #include <malloc.h>
21 #include <mcheck.h>
22 
23 #include <dlfcn.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <inttypes.h>
29 
30 #include <libc-internal.h>
31 #include <dso_handle.h>
32 
33 #include <kernel-features.h>
34 
35 static FILE *mallstream;
36 static const char mallenv[] = "MALLOC_TRACE";
37 
38 static void
tr_where(const void * caller,Dl_info * info)39 tr_where (const void *caller, Dl_info *info)
40 {
41   if (caller != NULL)
42     {
43       if (info != NULL)
44         {
45           char *buf = (char *) "";
46           if (info->dli_sname != NULL)
47             {
48               size_t len = strlen (info->dli_sname);
49               buf = alloca (len + 6 + 2 * sizeof (void *));
50 	      char sign;
51 	      ptrdiff_t offset =
52 		(ptrdiff_t) info->dli_saddr - (ptrdiff_t) caller;
53 
54 	      if (caller >= (const void *) info->dli_saddr)
55 		{
56 		  sign = '+';
57 		  offset = -offset;
58 		}
59 	      else
60 		  sign = '-';
61 
62 	      sprintf (buf, "(%s%c%" PRIxPTR ")", info->dli_sname, sign,
63 		       offset);
64             }
65 
66 	  fprintf (mallstream, "@ %s%s%s[0x%" PRIxPTR "] ",
67 		   info->dli_fname ? : "", info->dli_fname ? ":" : "", buf,
68 		   caller - info->dli_fbase);
69         }
70       else
71         fprintf (mallstream, "@ [%p] ", caller);
72     }
73 }
74 
75 static Dl_info *
lock_and_info(const void * caller,Dl_info * mem)76 lock_and_info (const void *caller, Dl_info *mem)
77 {
78   if (caller == NULL)
79     return NULL;
80 
81   Dl_info *res = dladdr (caller, mem) ? mem : NULL;
82 
83   flockfile (mallstream);
84 
85   return res;
86 }
87 
88 static void
free_mtrace(void * ptr,const void * caller)89 free_mtrace (void *ptr, const void *caller)
90 {
91   if (ptr == NULL)
92     return;
93 
94   Dl_info mem;
95   Dl_info *info = lock_and_info (caller, &mem);
96   tr_where (caller, info);
97   /* Be sure to print it first.  */
98   fprintf (mallstream, "- %p\n", ptr);
99   funlockfile (mallstream);
100 }
101 
102 static void
malloc_mtrace_after(void * block,size_t size,const void * caller)103 malloc_mtrace_after (void *block, size_t size, const void *caller)
104 {
105   Dl_info mem;
106   Dl_info *info = lock_and_info (caller, &mem);
107 
108   tr_where (caller, info);
109   /* We could be printing a NULL here; that's OK.  */
110   fprintf (mallstream, "+ %p %#lx\n", block, (unsigned long int) size);
111 
112   funlockfile (mallstream);
113 }
114 
115 static void
realloc_mtrace_after(void * block,const void * oldptr,size_t size,const void * caller)116 realloc_mtrace_after (void *block, const void *oldptr, size_t size,
117 		      const void *caller)
118 {
119   Dl_info mem;
120   Dl_info *info = lock_and_info (caller, &mem);
121 
122   tr_where (caller, info);
123   if (block == NULL)
124     {
125       if (size != 0)
126         /* Failed realloc.  */
127 	fprintf (mallstream, "! %p %#lx\n", oldptr, (unsigned long int) size);
128       else
129         fprintf (mallstream, "- %p\n", oldptr);
130     }
131   else if (oldptr == NULL)
132     fprintf (mallstream, "+ %p %#lx\n", block, (unsigned long int) size);
133   else
134     {
135       fprintf (mallstream, "< %p\n", oldptr);
136       tr_where (caller, info);
137       fprintf (mallstream, "> %p %#lx\n", block, (unsigned long int) size);
138     }
139 
140   funlockfile (mallstream);
141 }
142 
143 static void
memalign_mtrace_after(void * block,size_t size,const void * caller)144 memalign_mtrace_after (void *block, size_t size, const void *caller)
145 {
146   Dl_info mem;
147   Dl_info *info = lock_and_info (caller, &mem);
148 
149   tr_where (caller, info);
150   /* We could be printing a NULL here; that's OK.  */
151   fprintf (mallstream, "+ %p %#lx\n", block, (unsigned long int) size);
152 
153   funlockfile (mallstream);
154 }
155 
156 /* This function gets called to make sure all memory the library
157    allocates get freed and so does not irritate the user when studying
158    the mtrace output.  */
159 static void
release_libc_mem(void)160 release_libc_mem (void)
161 {
162   /* Only call the free function if we still are running in mtrace mode.  */
163   if (mallstream != NULL)
164     __libc_freeres ();
165 }
166 
167 /* We enable tracing if the environment variable MALLOC_TRACE is set.  */
168 
169 static void
do_mtrace(void)170 do_mtrace (void)
171 {
172   static int added_atexit_handler;
173   char *mallfile;
174 
175   /* Don't panic if we're called more than once.  */
176   if (mallstream != NULL)
177     return;
178 
179   mallfile = secure_getenv (mallenv);
180   if (mallfile != NULL)
181     {
182       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
183       if (mallstream != NULL)
184         {
185           /* Be sure it doesn't malloc its buffer!  */
186 	  static char tracebuf [512];
187 
188 	  setvbuf (mallstream, tracebuf, _IOFBF, sizeof (tracebuf));
189           fprintf (mallstream, "= Start\n");
190           if (!added_atexit_handler)
191             {
192               added_atexit_handler = 1;
193               __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
194 			    __dso_handle);
195             }
196 	  __malloc_debug_enable (MALLOC_MTRACE_HOOK);
197         }
198     }
199 }
200 
201 static void
do_muntrace(void)202 do_muntrace (void)
203 {
204   __malloc_debug_disable (MALLOC_MTRACE_HOOK);
205   if (mallstream == NULL)
206     return;
207 
208   /* Do the reverse of what done in mtrace: first reset the hooks and
209      MALLSTREAM, and only after that write the trailer and close the
210      file.  */
211   FILE *f = mallstream;
212   mallstream = NULL;
213 
214   fprintf (f, "= End\n");
215   fclose (f);
216 }
217