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