• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 
23 #include "curl_setup.h"
24 
25 #ifdef CURLDEBUG
26 
27 #include <curl/curl.h>
28 
29 #include "urldata.h"
30 
31 #define MEMDEBUG_NODEFINES /* don't redefine the standard functions */
32 
33 /* The last 3 #include files should be in this order */
34 #include "curl_printf.h"
35 #include "curl_memory.h"
36 #include "memdebug.h"
37 
38 struct memdebug {
39   size_t size;
40   union {
41     curl_off_t o;
42     double d;
43     void *p;
44   } mem[1];
45   /* I'm hoping this is the thing with the strictest alignment
46    * requirements.  That also means we waste some space :-( */
47 };
48 
49 /*
50  * Note that these debug functions are very simple and they are meant to
51  * remain so. For advanced analysis, record a log file and write perl scripts
52  * to analyze them!
53  *
54  * Don't use these with multithreaded test programs!
55  */
56 
57 FILE *curl_dbg_logfile = NULL;
58 static bool registered_cleanup = FALSE; /* atexit registered cleanup */
59 static bool memlimit = FALSE; /* enable memory limit */
60 static long memsize = 0;  /* set number of mallocs allowed */
61 
62 /* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected
63    on exit so the logfile must be closed explicitly or data could be lost.
64    Though _exit() does not call atexit handlers such as this, LSAN's call to
65    _exit() comes after the atexit handlers are called. curl/curl#6620 */
curl_dbg_cleanup(void)66 static void curl_dbg_cleanup(void)
67 {
68   if(curl_dbg_logfile &&
69      curl_dbg_logfile != stderr &&
70      curl_dbg_logfile != stdout) {
71     fclose(curl_dbg_logfile);
72   }
73   curl_dbg_logfile = NULL;
74 }
75 
76 /* this sets the log file name */
curl_dbg_memdebug(const char * logname)77 void curl_dbg_memdebug(const char *logname)
78 {
79   if(!curl_dbg_logfile) {
80     if(logname && *logname)
81       curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT);
82     else
83       curl_dbg_logfile = stderr;
84 #ifdef MEMDEBUG_LOG_SYNC
85     /* Flush the log file after every line so the log isn't lost in a crash */
86     if(curl_dbg_logfile)
87       setbuf(curl_dbg_logfile, (char *)NULL);
88 #endif
89   }
90   if(!registered_cleanup)
91     registered_cleanup = !atexit(curl_dbg_cleanup);
92 }
93 
94 /* This function sets the number of malloc() calls that should return
95    successfully! */
curl_dbg_memlimit(long limit)96 void curl_dbg_memlimit(long limit)
97 {
98   if(!memlimit) {
99     memlimit = TRUE;
100     memsize = limit;
101   }
102 }
103 
104 /* returns TRUE if this isn't allowed! */
countcheck(const char * func,int line,const char * source)105 static bool countcheck(const char *func, int line, const char *source)
106 {
107   /* if source is NULL, then the call is made internally and this check
108      should not be made */
109   if(memlimit && source) {
110     if(!memsize) {
111       /* log to file */
112       curl_dbg_log("LIMIT %s:%d %s reached memlimit\n",
113                    source, line, func);
114       /* log to stderr also */
115       fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
116               source, line, func);
117       fflush(curl_dbg_logfile); /* because it might crash now */
118       errno = ENOMEM;
119       return TRUE; /* RETURN ERROR! */
120     }
121     else
122       memsize--; /* countdown */
123 
124 
125   }
126 
127   return FALSE; /* allow this */
128 }
129 
curl_dbg_malloc(size_t wantedsize,int line,const char * source)130 void *curl_dbg_malloc(size_t wantedsize, int line, const char *source)
131 {
132   struct memdebug *mem;
133   size_t size;
134 
135   DEBUGASSERT(wantedsize != 0);
136 
137   if(countcheck("malloc", line, source))
138     return NULL;
139 
140   /* alloc at least 64 bytes */
141   size = sizeof(struct memdebug) + wantedsize;
142 
143   mem = (Curl_cmalloc)(size);
144   if(mem) {
145     mem->size = wantedsize;
146   }
147 
148   if(source)
149     curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n",
150                  source, line, wantedsize,
151                  mem ? (void *)mem->mem : (void *)0);
152 
153   return (mem ? mem->mem : NULL);
154 }
155 
curl_dbg_calloc(size_t wanted_elements,size_t wanted_size,int line,const char * source)156 void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
157                       int line, const char *source)
158 {
159   struct memdebug *mem;
160   size_t size, user_size;
161 
162   DEBUGASSERT(wanted_elements != 0);
163   DEBUGASSERT(wanted_size != 0);
164 
165   if(countcheck("calloc", line, source))
166     return NULL;
167 
168   /* alloc at least 64 bytes */
169   user_size = wanted_size * wanted_elements;
170   size = sizeof(struct memdebug) + user_size;
171 
172   mem = (Curl_ccalloc)(1, size);
173   if(mem)
174     mem->size = user_size;
175 
176   if(source)
177     curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n",
178                  source, line, wanted_elements, wanted_size,
179                  mem ? (void *)mem->mem : (void *)0);
180 
181   return (mem ? mem->mem : NULL);
182 }
183 
curl_dbg_strdup(const char * str,int line,const char * source)184 char *curl_dbg_strdup(const char *str, int line, const char *source)
185 {
186   char *mem;
187   size_t len;
188 
189   DEBUGASSERT(str != NULL);
190 
191   if(countcheck("strdup", line, source))
192     return NULL;
193 
194   len = strlen(str) + 1;
195 
196   mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */
197   if(mem)
198     memcpy(mem, str, len);
199 
200   if(source)
201     curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n",
202                  source, line, (const void *)str, len, (const void *)mem);
203 
204   return mem;
205 }
206 
207 #if defined(WIN32) && defined(UNICODE)
curl_dbg_wcsdup(const wchar_t * str,int line,const char * source)208 wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source)
209 {
210   wchar_t *mem;
211   size_t wsiz, bsiz;
212 
213   DEBUGASSERT(str != NULL);
214 
215   if(countcheck("wcsdup", line, source))
216     return NULL;
217 
218   wsiz = wcslen(str) + 1;
219   bsiz = wsiz * sizeof(wchar_t);
220 
221   mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */
222   if(mem)
223     memcpy(mem, str, bsiz);
224 
225   if(source)
226     curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
227                 source, line, (void *)str, bsiz, (void *)mem);
228 
229   return mem;
230 }
231 #endif
232 
233 /* We provide a realloc() that accepts a NULL as pointer, which then
234    performs a malloc(). In order to work with ares. */
curl_dbg_realloc(void * ptr,size_t wantedsize,int line,const char * source)235 void *curl_dbg_realloc(void *ptr, size_t wantedsize,
236                       int line, const char *source)
237 {
238   struct memdebug *mem = NULL;
239 
240   size_t size = sizeof(struct memdebug) + wantedsize;
241 
242   DEBUGASSERT(wantedsize != 0);
243 
244   if(countcheck("realloc", line, source))
245     return NULL;
246 
247 #ifdef __INTEL_COMPILER
248 #  pragma warning(push)
249 #  pragma warning(disable:1684)
250    /* 1684: conversion from pointer to same-sized integral type */
251 #endif
252 
253   if(ptr)
254     mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
255 
256 #ifdef __INTEL_COMPILER
257 #  pragma warning(pop)
258 #endif
259 
260   mem = (Curl_crealloc)(mem, size);
261   if(source)
262     curl_dbg_log("MEM %s:%d realloc(%p, %zu) = %p\n",
263                 source, line, (void *)ptr, wantedsize,
264                 mem ? (void *)mem->mem : (void *)0);
265 
266   if(mem) {
267     mem->size = wantedsize;
268     return mem->mem;
269   }
270 
271   return NULL;
272 }
273 
curl_dbg_free(void * ptr,int line,const char * source)274 void curl_dbg_free(void *ptr, int line, const char *source)
275 {
276   if(ptr) {
277     struct memdebug *mem;
278 
279 #ifdef __INTEL_COMPILER
280 #  pragma warning(push)
281 #  pragma warning(disable:1684)
282    /* 1684: conversion from pointer to same-sized integral type */
283 #endif
284 
285     mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
286 
287 #ifdef __INTEL_COMPILER
288 #  pragma warning(pop)
289 #endif
290 
291     /* free for real */
292     (Curl_cfree)(mem);
293   }
294 
295   if(source && ptr)
296     curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr);
297 }
298 
curl_dbg_socket(int domain,int type,int protocol,int line,const char * source)299 curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
300                              int line, const char *source)
301 {
302   const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
303     "FD %s:%d socket() = %d\n" :
304     (sizeof(curl_socket_t) == sizeof(long)) ?
305     "FD %s:%d socket() = %ld\n" :
306     "FD %s:%d socket() = %zd\n";
307 
308   curl_socket_t sockfd;
309 
310   if(countcheck("socket", line, source))
311     return CURL_SOCKET_BAD;
312 
313   sockfd = socket(domain, type, protocol);
314 
315   if(source && (sockfd != CURL_SOCKET_BAD))
316     curl_dbg_log(fmt, source, line, sockfd);
317 
318   return sockfd;
319 }
320 
curl_dbg_send(SEND_TYPE_ARG1 sockfd,SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,SEND_TYPE_ARG3 len,SEND_TYPE_ARG4 flags,int line,const char * source)321 SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
322                             SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
323                             SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line,
324                             const char *source)
325 {
326   SEND_TYPE_RETV rc;
327   if(countcheck("send", line, source))
328     return -1;
329   rc = send(sockfd, buf, len, flags);
330   if(source)
331     curl_dbg_log("SEND %s:%d send(%lu) = %ld\n",
332                 source, line, (unsigned long)len, (long)rc);
333   return rc;
334 }
335 
curl_dbg_recv(RECV_TYPE_ARG1 sockfd,RECV_TYPE_ARG2 buf,RECV_TYPE_ARG3 len,RECV_TYPE_ARG4 flags,int line,const char * source)336 RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf,
337                             RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line,
338                             const char *source)
339 {
340   RECV_TYPE_RETV rc;
341   if(countcheck("recv", line, source))
342     return -1;
343   rc = recv(sockfd, buf, len, flags);
344   if(source)
345     curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n",
346                 source, line, (unsigned long)len, (long)rc);
347   return rc;
348 }
349 
350 #ifdef HAVE_SOCKETPAIR
curl_dbg_socketpair(int domain,int type,int protocol,curl_socket_t socket_vector[2],int line,const char * source)351 int curl_dbg_socketpair(int domain, int type, int protocol,
352                        curl_socket_t socket_vector[2],
353                        int line, const char *source)
354 {
355   const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
356     "FD %s:%d socketpair() = %d %d\n" :
357     (sizeof(curl_socket_t) == sizeof(long)) ?
358     "FD %s:%d socketpair() = %ld %ld\n" :
359     "FD %s:%d socketpair() = %zd %zd\n";
360 
361   int res = socketpair(domain, type, protocol, socket_vector);
362 
363   if(source && (0 == res))
364     curl_dbg_log(fmt, source, line, socket_vector[0], socket_vector[1]);
365 
366   return res;
367 }
368 #endif
369 
curl_dbg_accept(curl_socket_t s,void * saddr,void * saddrlen,int line,const char * source)370 curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
371                              int line, const char *source)
372 {
373   const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
374     "FD %s:%d accept() = %d\n" :
375     (sizeof(curl_socket_t) == sizeof(long)) ?
376     "FD %s:%d accept() = %ld\n" :
377     "FD %s:%d accept() = %zd\n";
378 
379   struct sockaddr *addr = (struct sockaddr *)saddr;
380   curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
381 
382   curl_socket_t sockfd = accept(s, addr, addrlen);
383 
384   if(source && (sockfd != CURL_SOCKET_BAD))
385     curl_dbg_log(fmt, source, line, sockfd);
386 
387   return sockfd;
388 }
389 
390 /* separate function to allow libcurl to mark a "faked" close */
curl_dbg_mark_sclose(curl_socket_t sockfd,int line,const char * source)391 void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
392 {
393   const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
394     "FD %s:%d sclose(%d)\n":
395     (sizeof(curl_socket_t) == sizeof(long)) ?
396     "FD %s:%d sclose(%ld)\n":
397     "FD %s:%d sclose(%zd)\n";
398 
399   if(source)
400     curl_dbg_log(fmt, source, line, sockfd);
401 }
402 
403 /* this is our own defined way to close sockets on *ALL* platforms */
curl_dbg_sclose(curl_socket_t sockfd,int line,const char * source)404 int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source)
405 {
406   int res = sclose(sockfd);
407   curl_dbg_mark_sclose(sockfd, line, source);
408   return res;
409 }
410 
curl_dbg_fopen(const char * file,const char * mode,int line,const char * source)411 FILE *curl_dbg_fopen(const char *file, const char *mode,
412                     int line, const char *source)
413 {
414   FILE *res = fopen(file, mode);
415 
416   if(source)
417     curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
418                 source, line, file, mode, (void *)res);
419 
420   return res;
421 }
422 
curl_dbg_fdopen(int filedes,const char * mode,int line,const char * source)423 FILE *curl_dbg_fdopen(int filedes, const char *mode,
424                       int line, const char *source)
425 {
426   FILE *res = fdopen(filedes, mode);
427   if(source)
428     curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
429                  source, line, filedes, mode, (void *)res);
430   return res;
431 }
432 
curl_dbg_fclose(FILE * file,int line,const char * source)433 int curl_dbg_fclose(FILE *file, int line, const char *source)
434 {
435   int res;
436 
437   DEBUGASSERT(file != NULL);
438 
439   if(source)
440     curl_dbg_log("FILE %s:%d fclose(%p)\n",
441                  source, line, (void *)file);
442 
443   res = fclose(file);
444 
445   return res;
446 }
447 
448 #define LOGLINE_BUFSIZE  1024
449 
450 /* this does the writing to the memory tracking log file */
curl_dbg_log(const char * format,...)451 void curl_dbg_log(const char *format, ...)
452 {
453   char *buf;
454   int nchars;
455   va_list ap;
456 
457   if(!curl_dbg_logfile)
458     return;
459 
460   buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
461   if(!buf)
462     return;
463 
464   va_start(ap, format);
465   nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
466   va_end(ap);
467 
468   if(nchars > LOGLINE_BUFSIZE - 1)
469     nchars = LOGLINE_BUFSIZE - 1;
470 
471   if(nchars > 0)
472     fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
473 
474   (Curl_cfree)(buf);
475 }
476 
477 #endif /* CURLDEBUG */
478