1 /* Make sure that, on error, an opened dict is properly freed. */ 2 3 #define _GNU_SOURCE 1 4 #include <dlfcn.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <ctf-api.h> 9 #include <ctf.h> 10 11 static unsigned long long malloc_count; 12 static unsigned long long free_count; 13 14 static void *(*real_malloc) (size_t size); 15 static void (*real_free) (void *ptr); 16 static void *(*real_realloc) (void *ptr, size_t size); 17 static void *(*real_calloc) (size_t nmemb, size_t size); 18 19 /* Interpose malloc/free functionss and count calls to spot unbalanced ones. 20 Extra complexity to deal with dlsym() calling dlerror() and thus calloc() -- 21 luckily it handles malloc failure fine, so we can just always fail before the 22 hooks are installed. */ 23 24 static int in_hooks; 25 26 static void hook_init (void) 27 { 28 if (!real_calloc) 29 { 30 in_hooks = 1; 31 real_calloc = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc"); 32 real_malloc = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc"); 33 real_free = (void (*) (void *)) dlsym (RTLD_NEXT, "free"); 34 real_realloc = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc"); 35 if (!real_malloc || !real_free || !real_realloc || !real_calloc) 36 { 37 fprintf (stderr, "Cannot hook malloc\n"); 38 exit(1); 39 } 40 in_hooks = 0; 41 } 42 } 43 44 void *malloc (size_t size) 45 { 46 if (in_hooks) 47 return NULL; 48 49 hook_init(); 50 malloc_count++; 51 return real_malloc (size); 52 } 53 54 void *realloc (void *ptr, size_t size) 55 { 56 void *new_ptr; 57 58 if (in_hooks) 59 return NULL; 60 61 hook_init(); 62 new_ptr = real_realloc (ptr, size); 63 64 if (!ptr) 65 malloc_count++; 66 67 if (size == 0) 68 free_count++; 69 70 return new_ptr; 71 } 72 73 void *calloc (size_t nmemb, size_t size) 74 { 75 void *ptr; 76 77 if (in_hooks) 78 return NULL; 79 80 hook_init(); 81 ptr = real_calloc (nmemb, size); 82 83 if (ptr) 84 malloc_count++; 85 return ptr; 86 } 87 88 void free (void *ptr) 89 { 90 hook_init(); 91 92 if (in_hooks) 93 return; 94 95 if (ptr != NULL) 96 free_count++; 97 98 return real_free (ptr); 99 } 100 101 int main (void) 102 { 103 ctf_dict_t *fp; 104 ctf_encoding_t e = { CTF_INT_SIGNED, 0, sizeof (long) }; 105 int err; 106 ctf_id_t type; 107 size_t i; 108 char *written; 109 size_t written_size; 110 char *foo; 111 ctf_next_t *it = NULL; 112 unsigned long long frozen_malloc_count, frozen_free_count; 113 114 if ((fp = ctf_create (&err)) == NULL) 115 goto open_err; 116 117 /* Define an integer, then a pile of unconnected pointers to it, just to 118 use up space.. */ 119 120 if ((type = ctf_add_integer (fp, CTF_ADD_ROOT, "long", &e)) == CTF_ERR) 121 goto err; 122 123 for (i = 0; i < 100; i++) 124 { 125 if (ctf_add_pointer (fp, CTF_ADD_ROOT, type) == CTF_ERR) 126 goto err; 127 } 128 129 /* Write the dict out, uncompressed (to stop it failing to open due to decompression 130 failure after we corrupt it: the leak is only observable if the dict gets 131 malloced, which only happens after that point.) */ 132 133 if ((written = ctf_write_mem (fp, &written_size, (size_t) -1)) == NULL) 134 goto write_err; 135 136 ctf_dict_close (fp); 137 138 /* Corrupt the dict. */ 139 140 memset (written + sizeof (ctf_header_t), 64, 64); 141 142 /* Reset the counters: we are interested only in leaks at open 143 time. */ 144 malloc_count = 0; 145 free_count = 0; 146 147 if ((ctf_simple_open (written, written_size, NULL, 0, 0, NULL, 0, &err)) != NULL) 148 { 149 fprintf (stderr, "wildly corrupted dict still opened OK?!\n"); 150 exit (1); 151 } 152 153 /* The error log will have accumulated errors which need to be 154 consumed and freed if they are not to appear as a spurious leak. */ 155 156 while ((foo = ctf_errwarning_next (NULL, &it, NULL, NULL)) != NULL) 157 free (foo); 158 159 frozen_malloc_count = malloc_count; 160 frozen_free_count = free_count; 161 162 if (frozen_malloc_count == 0) 163 fprintf (stderr, "Allocation count after failed open is zero: likely hook failure.\n"); 164 else if (frozen_malloc_count > frozen_free_count) 165 fprintf (stderr, "Memory leak is present: %lli allocations (%lli allocations, %lli frees).\n", 166 frozen_malloc_count - frozen_free_count, frozen_malloc_count, frozen_free_count); 167 else if (frozen_malloc_count < frozen_free_count) 168 fprintf (stderr, "Possible double-free: %lli allocations, %lli frees.\n", 169 frozen_malloc_count, frozen_free_count); 170 171 printf ("All OK.\n"); 172 exit (0); 173 174 open_err: 175 fprintf (stderr, "Cannot open/create: %s\n", ctf_errmsg (err)); 176 exit (1); 177 178 err: 179 fprintf (stderr, "Cannot add: %s\n", ctf_errmsg (ctf_errno (fp))); 180 exit (1); 181 182 write_err: 183 fprintf (stderr, "Cannot write: %s\n", ctf_errmsg (ctf_errno (fp))); 184 exit (1); 185 } 186