]> granicus.if.org Git - linux-pam/blob - libpam/pam_malloc.c
Relevant BUGIDs: 490938
[linux-pam] / libpam / pam_malloc.c
1 /*
2  * $Id$
3  */
4
5 /*
6  * This pair of files helps to locate memory leaks. It is a wrapper for
7  * the malloc family of calls. (Actutally, it currently only deals
8  * with calloc, malloc, realloc, free, strdup and exit)
9  *
10  * To use these functions the header "pam_malloc.h" must be included
11  * in all parts of the code (that use the malloc functions) and this
12  * file must be linked with the result. The pam_malloc_flags can be
13  * set from another function and determine the level of logging.
14  *
15  * The output is via the macros defined in _pam_macros.h
16  *
17  * It is a debugging tool and should be turned off in released code.
18  *
19  * This suite was written by Andrew Morgan <morgan@kernel.org> for
20  * Linux-PAM.
21  */
22
23 #ifndef DEBUG
24 #define DEBUG
25 #endif
26 #include "pam_private.h"
27
28 #include <security/pam_malloc.h>
29 #include <security/_pam_macros.h>
30
31 /* this must be done to stop infinite recursion! */
32 #undef malloc
33 #undef calloc
34 #undef free
35 #undef realloc
36 #undef exit
37 #undef strdup
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42
43 /*
44  * default debugging level
45  */
46
47 int pam_malloc_flags = PAM_MALLOC_ALL;
48 int pam_malloc_delay_length = 4;
49
50 #define on(x) ((pam_malloc_flags&(x))==(x))
51
52 /*
53  * the implementation
54  */
55
56 static const char *last_fn=NULL;
57 static const char *last_file=NULL;
58 static const char *last_call=NULL;
59 static int last_line = 1;
60
61 #define err(x) { _pam_output_xdebug_info(); _pam_output_debug x ; }
62
63 static void set_last_(const char *x, const char *f
64                       , const char *fn, const int l)
65 {
66      last_fn   = x  ? x : "error-in-pam_malloc..";
67      last_file = f  ? f : "*bad-file*";
68      last_call = fn ? fn: "*bad-fn*";
69      last_line = l;
70 }
71
72 static void _pam_output_xdebug_info(void)
73 {
74     FILE *logfile;
75     int must_close = 1, fd;
76
77 #ifdef O_NOFOLLOW
78     if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_NOFOLLOW|O_APPEND)) != -1) {
79 #else
80     if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_APPEND)) != -1) {
81 #endif
82         if (!(logfile = fdopen(fd,"a"))) {
83             logfile = stderr;
84             must_close = 0;
85             close(fd);
86         }
87     } else {
88         logfile = stderr;
89         must_close = 0;
90     }
91     fprintf(logfile, "[%s:%s(%d)->%s()] ",
92            last_file, last_call, last_line, last_fn);
93     fflush(logfile);
94     if (must_close)
95         fclose(logfile);
96 }
97
98 static void hinder(void)
99 {
100      if (on(PAM_MALLOC_PAUSE)) {
101           if (on(0)) err(("pause requested"));
102           sleep(pam_malloc_delay_length);
103      }
104
105      if (on(PAM_MALLOC_STOP)) {
106           if (on(0)) err(("stop requested"));
107           exit(1);
108      }
109 }
110
111 /*
112  * here are the memory pointer registering functions.. these actually
113  * use malloc(!) but that's ok! ;^)
114  */
115
116 struct reference {
117      void *ptr;          /* pointer */
118      int nelements;      /* number of elements */
119      int size;           /* - each of this size */
120      char *file;         /* where it was requested - filename */
121      char *function;     /*                        - function */
122      int line;           /*                        - line number */
123 /*
124  * linking info
125  */
126      struct reference *next;
127 };
128
129 static void _dump(const char *say, const struct reference *ref)
130 {
131     _pam_output_debug(" <%s: %p (#%d of %d) req. by %s(); %s line %d>"
132                       , say
133                       , ref->ptr,ref->nelements,ref->size
134                       , ref->function,ref->file,ref->line);
135 }
136
137 static struct reference *root=NULL;
138
139 static char *_strdup(const char *x)
140 {
141      char *s;
142
143      s = (char *)malloc(strlen(x)+1);
144      if (s == NULL) {
145           if (on(0)) err(("_strdup failed"));
146           exit(1);
147      }
148
149      strcpy(s,x);
150      return s;
151 }
152
153 static void add_new_ref(void *new, int n, int size)
154 {
155      struct reference *ref=NULL;
156
157      ref = (struct reference *) malloc( sizeof(struct reference) );
158      if (new == NULL || ref == NULL) {
159           if (on(0)) err(("internal error {add_new_ref}"));
160           exit(1);
161      }
162
163      ref->ptr = new;
164      ref->nelements = n;
165      ref->size = size;
166
167      ref->file = _strdup(last_file);
168      ref->function = _strdup(last_call);
169      ref->line = last_line;
170
171      ref->next = root;
172
173      if (on(PAM_MALLOC_REQUEST)) {
174           _dump("new_ptr", ref);
175      }
176
177      root = ref;
178 }
179
180 static void del_old_ref(void *old)
181 {
182      struct reference *this,*last;
183
184      if (old == NULL) {
185           if (on(0)) err(("internal error {del_old_ref}"));
186           exit(1);
187      }
188
189      /* locate old pointer */
190
191      last = NULL;
192      this = root;
193      while (this) {
194           if (this->ptr == old)
195                break;
196           last = this;
197           this = this->next;
198      }
199
200      /* Did we find a reference ? */
201
202      if (this) {
203           if (on(PAM_MALLOC_FREE)) {
204                _dump("free old_ptr", this);
205           }
206           if (last == NULL) {
207                root = this->next;
208           } else {
209                last->next = this->next;
210           }
211           free(this->file);
212           free(this->function);
213           free(this);
214      } else {
215           if (on(0)) err(("ERROR!: bad memory"));
216           hinder();
217      }
218 }
219
220 static void verify_old_ref(void *old)
221 {
222      struct reference *this;
223
224      if (old == NULL) {
225           if (on(0)) err(("internal error {verify_old_ref}"));
226           exit(1);
227      }
228
229      /* locate old pointer */
230
231      this = root;
232      while (this) {
233           if (this->ptr == old)
234                break;
235           this = this->next;
236      }
237
238      /* Did we find a reference ? */
239
240      if (this) {
241           if (on(PAM_MALLOC_VERIFY)) {
242                _dump("verify_ptr", this);
243           }
244      } else {
245           if (on(0)) err(("ERROR!: bad request"));
246           hinder();
247      }
248 }
249
250 static void dump_memory_list(const char *dump)
251 {
252      struct reference *this;
253
254      this = root;
255      if (this) {
256           if (on(0)) err(("un-free()'d memory"));
257           while (this) {
258                _dump(dump, this);
259                this = this->next;
260           }
261      } else {
262           if (on(0)) err(("no memory allocated"));
263      }
264 }
265
266 /* now for the wrappers */
267
268 #define _fn(x)  set_last_(x,file,fn,line)
269
270 void *pam_malloc(size_t size, const char *file, const char *fn, const int line)
271 {
272      void *new;
273
274      _fn("malloc");
275
276      if (on(PAM_MALLOC_FUNC)) err(("request for %d", size));
277
278      new = malloc(size);
279      if (new == NULL) {
280           if (on(PAM_MALLOC_FAIL)) err(("returned NULL"));
281      } else {
282           if (on(PAM_MALLOC_REQUEST)) err(("request new"));
283           add_new_ref(new, 1, size);
284      }
285
286      return new;
287 }
288
289 void *pam_calloc(size_t nelm, size_t size
290                 , const char *file, const char *fn, const int line)
291 {
292      void *new;
293
294      _fn("calloc");
295
296      if (on(PAM_MALLOC_FUNC)) err(("request for %d of %d", nelm, size));
297
298      new = calloc(nelm,size);
299      if (new == NULL) {
300           if (on(PAM_MALLOC_FAIL)) err(("returned NULL"));
301      } else {
302           if (on(PAM_MALLOC_REQUEST)) err(("request new"));
303           add_new_ref(new, nelm, size);
304      }
305
306      return new;
307 }
308
309 void  pam_free(void *ptr
310               , const char *file, const char *fn, const int line)
311 {
312      _fn("free");
313
314      if (on(PAM_MALLOC_FUNC))
315          err(("request (%s:%s():%d) to free %p", file, fn, line, ptr));
316
317      if (ptr == NULL) {
318           if (on(PAM_MALLOC_NULL)) err(("passed NULL pointer"));
319      } else {
320           if (on(PAM_MALLOC_FREE)) err(("deleted old"));
321           del_old_ref(ptr);
322           free(ptr);
323      }
324 }
325
326 void *pam_memalign(size_t ali, size_t size
327                   , const char *file, const char *fn, const int line)
328 {
329      _fn("memalign");
330      if (on(0)) err(("not implemented currently (Sorry)"));
331      exit(1);
332 }
333
334 void *pam_realloc(void *ptr, size_t size
335                 , const char *file, const char *fn, const int line)
336 {
337      void *new;
338
339      _fn("realloc");
340
341      if (on(PAM_MALLOC_FUNC)) err(("resize %p to %d", ptr, size));
342
343      if (ptr == NULL) {
344           if (on(PAM_MALLOC_NULL)) err(("passed NULL pointer"));
345      } else {
346           verify_old_ref(ptr);
347      }
348
349      new = realloc(ptr, size);
350      if (new == NULL) {
351           if (on(PAM_MALLOC_FAIL)) err(("returned NULL"));
352      } else {
353           if (ptr) {
354                if (on(PAM_MALLOC_FREE)) err(("deleted old"));
355                del_old_ref(ptr);
356           } else {
357                if (on(PAM_MALLOC_NULL)) err(("old is NULL"));
358           }
359           if (on(PAM_MALLOC_REQUEST)) err(("request new"));
360           add_new_ref(new, 1, size);
361      }
362
363      return new;
364 }
365
366 void *pam_valloc(size_t size
367                 , const char *file, const char *fn, const int line)
368 {
369      _fn("valloc");
370      if (on(0)) err(("not implemented currently (Sorry)"));
371      exit(1);
372 }
373
374 #include <alloca.h>
375
376 void *pam_alloca(size_t size
377                 , const char *file, const char *fn, const int line)
378 {
379      _fn("alloca");
380      if (on(0)) err(("not implemented currently (Sorry)"));
381      exit(1);
382 }
383
384 void pam_exit(int i
385               , const char *file, const char *fn, const int line)
386 {
387      D(("time to exit"));
388
389      _fn("exit");
390
391      if (on(0)) err(("passed (%d)", i));
392      if (on(PAM_MALLOC_LEAKED)) {
393           dump_memory_list("leaked");
394      }
395      exit(i);
396 }
397
398 char *pam_strdup(const char *orig,
399                  const char *file, const char *fn, const int line)
400 {
401     char *new;
402
403     _fn("strdup");
404
405     if (on(PAM_MALLOC_FUNC)) err(("request for dup of [%s]", orig));
406
407     new = strdup(orig);
408     if (new == NULL) {
409         if (on(PAM_MALLOC_FAIL)) err(("returned NULL"));
410     } else {
411         if (on(PAM_MALLOC_REQUEST)) err(("request dup of [%s]", orig));
412         add_new_ref(new, 1, strlen(new)+1);
413     }
414
415     return new;
416 }
417
418 /* end of file */