1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2015, 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 http://curl.haxx.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 "curl_printf.h"
30 #include "urldata.h"
31 
32 #define MEMDEBUG_NODEFINES /* don't redefine the standard functions */
33 #include "curl_memory.h"
34 #include "memdebug.h"
35 
36 #ifndef HAVE_ASSERT_H
37 #  define assert(x) Curl_nop_stmt
38 #endif
39 
40 /*
41  * Until 2011-08-17 libcurl's Memory Tracking feature also performed
42  * automatic malloc and free filling operations using 0xA5 and 0x13
43  * values. Our own preinitialization of dynamically allocated memory
44  * might be useful when not using third party memory debuggers, but
45  * on the other hand this would fool memory debuggers into thinking
46  * that all dynamically allocated memory is properly initialized.
47  *
48  * As a default setting, libcurl's Memory Tracking feature no longer
49  * performs preinitialization of dynamically allocated memory on its
50  * own. If you know what you are doing, and really want to retain old
51  * behavior, you can achieve this compiling with preprocessor symbols
52  * CURL_MT_MALLOC_FILL and CURL_MT_FREE_FILL defined with appropriate
53  * values.
54  */
55 
56 #ifdef CURL_MT_MALLOC_FILL
57 # if (CURL_MT_MALLOC_FILL < 0) || (CURL_MT_MALLOC_FILL > 0xff)
58 #   error "invalid CURL_MT_MALLOC_FILL or out of range"
59 # endif
60 #endif
61 
62 #ifdef CURL_MT_FREE_FILL
63 # if (CURL_MT_FREE_FILL < 0) || (CURL_MT_FREE_FILL > 0xff)
64 #   error "invalid CURL_MT_FREE_FILL or out of range"
65 # endif
66 #endif
67 
68 #if defined(CURL_MT_MALLOC_FILL) && defined(CURL_MT_FREE_FILL)
69 # if (CURL_MT_MALLOC_FILL == CURL_MT_FREE_FILL)
70 #   error "CURL_MT_MALLOC_FILL same as CURL_MT_FREE_FILL"
71 # endif
72 #endif
73 
74 #ifdef CURL_MT_MALLOC_FILL
75 #  define mt_malloc_fill(buf,len) memset((buf), CURL_MT_MALLOC_FILL, (len))
76 #else
77 #  define mt_malloc_fill(buf,len) Curl_nop_stmt
78 #endif
79 
80 #ifdef CURL_MT_FREE_FILL
81 #  define mt_free_fill(buf,len) memset((buf), CURL_MT_FREE_FILL, (len))
82 #else
83 #  define mt_free_fill(buf,len) Curl_nop_stmt
84 #endif
85 
86 struct memdebug {
87   size_t size;
88   union {
89     curl_off_t o;
90     double d;
91     void * p;
92   } mem[1];
93   /* I'm hoping this is the thing with the strictest alignment
94    * requirements.  That also means we waste some space :-( */
95 };
96 
97 /*
98  * Note that these debug functions are very simple and they are meant to
99  * remain so. For advanced analysis, record a log file and write perl scripts
100  * to analyze them!
101  *
102  * Don't use these with multithreaded test programs!
103  */
104 
105 #define logfile curl_debuglogfile
106 FILE *curl_debuglogfile = NULL;
107 static bool memlimit = FALSE; /* enable memory limit */
108 static long memsize = 0;  /* set number of mallocs allowed */
109 
110 /* this sets the log file name */
curl_memdebug(const char * logname)111 void curl_memdebug(const char *logname)
112 {
113   if(!logfile) {
114     if(logname && *logname)
115       logfile = fopen(logname, FOPEN_WRITETEXT);
116     else
117       logfile = stderr;
118 #ifdef MEMDEBUG_LOG_SYNC
119     /* Flush the log file after every line so the log isn't lost in a crash */
120     setvbuf(logfile, (char *)NULL, _IOLBF, 0);
121 #endif
122   }
123 }
124 
125 /* This function sets the number of malloc() calls that should return
126    successfully! */
curl_memlimit(long limit)127 void curl_memlimit(long limit)
128 {
129   if(!memlimit) {
130     memlimit = TRUE;
131     memsize = limit;
132   }
133 }
134 
135 /* returns TRUE if this isn't allowed! */
countcheck(const char * func,int line,const char * source)136 static bool countcheck(const char *func, int line, const char *source)
137 {
138   /* if source is NULL, then the call is made internally and this check
139      should not be made */
140   if(memlimit && source) {
141     if(!memsize) {
142       if(source) {
143         /* log to file */
144         curl_memlog("LIMIT %s:%d %s reached memlimit\n",
145                     source, line, func);
146         /* log to stderr also */
147         fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
148                 source, line, func);
149       }
150       SET_ERRNO(ENOMEM);
151       return TRUE; /* RETURN ERROR! */
152     }
153     else
154       memsize--; /* countdown */
155 
156     /* log the countdown */
157     if(source)
158       curl_memlog("LIMIT %s:%d %ld ALLOCS left\n",
159                   source, line, memsize);
160 
161   }
162 
163   return FALSE; /* allow this */
164 }
165 
curl_domalloc(size_t wantedsize,int line,const char * source)166 void *curl_domalloc(size_t wantedsize, int line, const char *source)
167 {
168   struct memdebug *mem;
169   size_t size;
170 
171   assert(wantedsize != 0);
172 
173   if(countcheck("malloc", line, source))
174     return NULL;
175 
176   /* alloc at least 64 bytes */
177   size = sizeof(struct memdebug)+wantedsize;
178 
179   mem = (Curl_cmalloc)(size);
180   if(mem) {
181     /* fill memory with junk */
182     mt_malloc_fill(mem->mem, wantedsize);
183     mem->size = wantedsize;
184   }
185 
186   if(source)
187     curl_memlog("MEM %s:%d malloc(%zu) = %p\n",
188                 source, line, wantedsize,
189                 mem ? (void *)mem->mem : (void *)0);
190 
191   return (mem ? mem->mem : NULL);
192 }
193 
curl_docalloc(size_t wanted_elements,size_t wanted_size,int line,const char * source)194 void *curl_docalloc(size_t wanted_elements, size_t wanted_size,
195                     int line, const char *source)
196 {
197   struct memdebug *mem;
198   size_t size, user_size;
199 
200   assert(wanted_elements != 0);
201   assert(wanted_size != 0);
202 
203   if(countcheck("calloc", line, source))
204     return NULL;
205 
206   /* alloc at least 64 bytes */
207   user_size = wanted_size * wanted_elements;
208   size = sizeof(struct memdebug) + user_size;
209 
210   mem = (Curl_ccalloc)(1, size);
211   if(mem)
212     mem->size = user_size;
213 
214   if(source)
215     curl_memlog("MEM %s:%d calloc(%zu,%zu) = %p\n",
216                 source, line, wanted_elements, wanted_size,
217                 mem ? (void *)mem->mem : (void *)0);
218 
219   return (mem ? mem->mem : NULL);
220 }
221 
curl_dostrdup(const char * str,int line,const char * source)222 char *curl_dostrdup(const char *str, int line, const char *source)
223 {
224   char *mem;
225   size_t len;
226 
227   assert(str != NULL);
228 
229   if(countcheck("strdup", line, source))
230     return NULL;
231 
232   len=strlen(str)+1;
233 
234   mem=curl_domalloc(len, 0, NULL); /* NULL prevents logging */
235   if(mem)
236     memcpy(mem, str, len);
237 
238   if(source)
239     curl_memlog("MEM %s:%d strdup(%p) (%zu) = %p\n",
240                 source, line, (void *)str, len, (void *)mem);
241 
242   return mem;
243 }
244 
245 #if defined(WIN32) && defined(UNICODE)
curl_dowcsdup(const wchar_t * str,int line,const char * source)246 wchar_t *curl_dowcsdup(const wchar_t *str, int line, const char *source)
247 {
248   wchar_t *mem;
249   size_t wsiz, bsiz;
250 
251   assert(str != NULL);
252 
253   if(countcheck("wcsdup", line, source))
254     return NULL;
255 
256   wsiz = wcslen(str) + 1;
257   bsiz = wsiz * sizeof(wchar_t);
258 
259   mem = curl_domalloc(bsiz, 0, NULL); /* NULL prevents logging */
260   if(mem)
261     memcpy(mem, str, bsiz);
262 
263   if(source)
264     curl_memlog("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
265                 source, line, (void *)str, bsiz, (void *)mem);
266 
267   return mem;
268 }
269 #endif
270 
271 /* We provide a realloc() that accepts a NULL as pointer, which then
272    performs a malloc(). In order to work with ares. */
curl_dorealloc(void * ptr,size_t wantedsize,int line,const char * source)273 void *curl_dorealloc(void *ptr, size_t wantedsize,
274                      int line, const char *source)
275 {
276   struct memdebug *mem=NULL;
277 
278   size_t size = sizeof(struct memdebug)+wantedsize;
279 
280   assert(wantedsize != 0);
281 
282   if(countcheck("realloc", line, source))
283     return NULL;
284 
285 #ifdef __INTEL_COMPILER
286 #  pragma warning(push)
287 #  pragma warning(disable:1684)
288    /* 1684: conversion from pointer to same-sized integral type */
289 #endif
290 
291   if(ptr)
292     mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
293 
294 #ifdef __INTEL_COMPILER
295 #  pragma warning(pop)
296 #endif
297 
298   mem = (Curl_crealloc)(mem, size);
299   if(source)
300     curl_memlog("MEM %s:%d realloc(%p, %zu) = %p\n",
301                 source, line, (void *)ptr, wantedsize,
302                 mem ? (void *)mem->mem : (void *)0);
303 
304   if(mem) {
305     mem->size = wantedsize;
306     return mem->mem;
307   }
308 
309   return NULL;
310 }
311 
curl_dofree(void * ptr,int line,const char * source)312 void curl_dofree(void *ptr, int line, const char *source)
313 {
314   struct memdebug *mem;
315 
316   if(ptr) {
317 
318 #ifdef __INTEL_COMPILER
319 #  pragma warning(push)
320 #  pragma warning(disable:1684)
321    /* 1684: conversion from pointer to same-sized integral type */
322 #endif
323 
324     mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
325 
326 #ifdef __INTEL_COMPILER
327 #  pragma warning(pop)
328 #endif
329 
330     /* destroy */
331     mt_free_fill(mem->mem, mem->size);
332 
333     /* free for real */
334     (Curl_cfree)(mem);
335   }
336 
337   if(source)
338     curl_memlog("MEM %s:%d free(%p)\n", source, line, (void *)ptr);
339 }
340 
curl_socket(int domain,int type,int protocol,int line,const char * source)341 curl_socket_t curl_socket(int domain, int type, int protocol,
342                           int line, const char *source)
343 {
344   const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
345     "FD %s:%d socket() = %d\n" :
346     (sizeof(curl_socket_t) == sizeof(long)) ?
347     "FD %s:%d socket() = %ld\n" :
348     "FD %s:%d socket() = %zd\n";
349 
350   curl_socket_t sockfd = socket(domain, type, protocol);
351 
352   if(source && (sockfd != CURL_SOCKET_BAD))
353     curl_memlog(fmt, source, line, sockfd);
354 
355   return sockfd;
356 }
357 
358 #ifdef HAVE_SOCKETPAIR
curl_socketpair(int domain,int type,int protocol,curl_socket_t socket_vector[2],int line,const char * source)359 int curl_socketpair(int domain, int type, int protocol,
360                     curl_socket_t socket_vector[2],
361                     int line, const char *source)
362 {
363   const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
364     "FD %s:%d socketpair() = %d %d\n" :
365     (sizeof(curl_socket_t) == sizeof(long)) ?
366     "FD %s:%d socketpair() = %ld %ld\n" :
367     "FD %s:%d socketpair() = %zd %zd\n";
368 
369   int res = socketpair(domain, type, protocol, socket_vector);
370 
371   if(source && (0 == res))
372     curl_memlog(fmt, source, line, socket_vector[0], socket_vector[1]);
373 
374   return res;
375 }
376 #endif
377 
curl_accept(curl_socket_t s,void * saddr,void * saddrlen,int line,const char * source)378 curl_socket_t curl_accept(curl_socket_t s, void *saddr, void *saddrlen,
379                           int line, const char *source)
380 {
381   const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
382     "FD %s:%d accept() = %d\n" :
383     (sizeof(curl_socket_t) == sizeof(long)) ?
384     "FD %s:%d accept() = %ld\n" :
385     "FD %s:%d accept() = %zd\n";
386 
387   struct sockaddr *addr = (struct sockaddr *)saddr;
388   curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
389 
390   curl_socket_t sockfd = accept(s, addr, addrlen);
391 
392   if(source && (sockfd != CURL_SOCKET_BAD))
393     curl_memlog(fmt, source, line, sockfd);
394 
395   return sockfd;
396 }
397 
398 /* separate function to allow libcurl to mark a "faked" close */
curl_mark_sclose(curl_socket_t sockfd,int line,const char * source)399 void curl_mark_sclose(curl_socket_t sockfd, int line, const char *source)
400 {
401   const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
402     "FD %s:%d sclose(%d)\n":
403     (sizeof(curl_socket_t) == sizeof(long)) ?
404     "FD %s:%d sclose(%ld)\n":
405     "FD %s:%d sclose(%zd)\n";
406 
407   if(source)
408     curl_memlog(fmt, source, line, sockfd);
409 }
410 
411 /* this is our own defined way to close sockets on *ALL* platforms */
curl_sclose(curl_socket_t sockfd,int line,const char * source)412 int curl_sclose(curl_socket_t sockfd, int line, const char *source)
413 {
414   int res=sclose(sockfd);
415   curl_mark_sclose(sockfd, line, source);
416   return res;
417 }
418 
curl_fopen(const char * file,const char * mode,int line,const char * source)419 FILE *curl_fopen(const char *file, const char *mode,
420                  int line, const char *source)
421 {
422   FILE *res=fopen(file, mode);
423 
424   if(source)
425     curl_memlog("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
426                 source, line, file, mode, (void *)res);
427 
428   return res;
429 }
430 
431 #ifdef HAVE_FDOPEN
curl_fdopen(int filedes,const char * mode,int line,const char * source)432 FILE *curl_fdopen(int filedes, const char *mode,
433                   int line, const char *source)
434 {
435   FILE *res=fdopen(filedes, mode);
436 
437   if(source)
438     curl_memlog("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
439                 source, line, filedes, mode, (void *)res);
440 
441   return res;
442 }
443 #endif
444 
curl_fclose(FILE * file,int line,const char * source)445 int curl_fclose(FILE *file, int line, const char *source)
446 {
447   int res;
448 
449   assert(file != NULL);
450 
451   res=fclose(file);
452 
453   if(source)
454     curl_memlog("FILE %s:%d fclose(%p)\n",
455                 source, line, (void *)file);
456 
457   return res;
458 }
459 
460 #define LOGLINE_BUFSIZE  1024
461 
462 /* this does the writting to the memory tracking log file */
curl_memlog(const char * format,...)463 void curl_memlog(const char *format, ...)
464 {
465   char *buf;
466   int nchars;
467   va_list ap;
468 
469   if(!logfile)
470     return;
471 
472   buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
473   if(!buf)
474     return;
475 
476   va_start(ap, format);
477   nchars = vsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
478   va_end(ap);
479 
480   if(nchars > LOGLINE_BUFSIZE - 1)
481     nchars = LOGLINE_BUFSIZE - 1;
482 
483   if(nchars > 0)
484     fwrite(buf, 1, nchars, logfile);
485 
486   (Curl_cfree)(buf);
487 }
488 
489 #endif /* CURLDEBUG */
490