Close "/dev/urandom" if we manage to open it.
[obnox/wireshark/wip.git] / epan / emem.c
1 /* emem.c
2  * Ethereal memory management and garbage collection functions
3  * Ronnie Sahlberg 2005
4  *
5  * $Id$
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33
34 #include <time.h>
35 #ifdef HAVE_SYS_TIME_H
36 #include <sys/time.h>
37 #endif
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #ifdef _WIN32
44 #include <process.h>    /* getpid */
45 #endif
46
47 #include <glib.h>
48 #include <proto.h>
49 #include "emem.h"
50 #include <wiretap/file_util.h>
51
52 /* When required, allocate more memory from the OS in this size chunks */
53 #define EMEM_PACKET_CHUNK_SIZE 10485760
54
55 /* The maximum number of allocations per chunk */
56 #define EMEM_ALLOCS_PER_CHUNK (EMEM_PACKET_CHUNK_SIZE / 512)
57
58 /*
59  * Tools like Valgrind and ElectricFence don't work well with memchunks.
60  * Uncomment the defines below to make {ep|se}_alloc() allocate each
61  * object individually.
62  */
63 /* #define EP_DEBUG_FREE 1 */
64 /* #define SE_DEBUG_FREE 1 */
65
66 #if GLIB_MAJOR_VERSION >= 2
67 GRand   *rand_state = NULL;
68 #endif
69
70 #define EMEM_CANARY_SIZE 8
71 #define EMEM_CANARY_DATA_SIZE (EMEM_CANARY_SIZE * 2 - 1)
72 guint8  ep_canary[EMEM_CANARY_DATA_SIZE], se_canary[EMEM_CANARY_DATA_SIZE];
73
74 typedef struct _emem_chunk_t {
75         struct _emem_chunk_t *next;
76         unsigned int    amount_free;
77         unsigned int    free_offset;
78         char *buf;
79         unsigned int    c_count;
80         void            *canary[EMEM_ALLOCS_PER_CHUNK];
81         guint8          cmp_len[EMEM_ALLOCS_PER_CHUNK];
82 } emem_chunk_t;
83
84 typedef struct _emem_header_t {
85   emem_chunk_t *free_list;
86   emem_chunk_t *used_list;
87 } emem_header_t;
88
89 static emem_header_t ep_packet_mem;
90 static emem_header_t se_packet_mem;
91
92 /*
93  * Set a canary value to be placed between memchunks.
94  */
95
96 void
97 emem_canary(guint8 *canary) {
98         int i;
99
100         /* First, use GLib's random function if we have it */
101 #if GLIB_MAJOR_VERSION >= 2
102         if (rand_state == NULL) {
103                 rand_state = g_rand_new();
104         }
105         for (i = 0; i < EMEM_CANARY_DATA_SIZE; i ++) {
106                 canary[i] = (guint8) g_rand_int(rand_state);
107         }
108         return;
109 #else
110         FILE *fp;
111         size_t sz;
112         /* Try /dev/urandom */
113         if (fp = eth_fopen("/dev/urandom", 0)) {
114                 sz = fread(canary, EMEM_CANARY_DATA_SIZE, 1, fd);
115                 fclose(fp);
116                 if (sz == EMEM_CANARY_SIZE) {
117                         return;
118                 }
119         }
120
121         /* Our last resort */
122         srandom(time(NULL) | getpid());
123         for (i = 0; i < EMEM_CANARY_DATA_SIZE; i ++) {
124                 canary[i] = (guint8) random();
125         }
126         return;
127 #endif /* GLIB_MAJOR_VERSION >= 2 */
128 }
129
130 /*
131  * Given an allocation size, return the amount of padding needed for
132  * the canary value.
133  */
134 guint8
135 emem_canary_pad (size_t allocation) {
136         guint8 pad;
137
138         pad = EMEM_CANARY_SIZE - (allocation % EMEM_CANARY_SIZE);
139         if (pad < EMEM_CANARY_SIZE)
140                 pad += EMEM_CANARY_SIZE;
141
142         return pad;
143 }
144
145 /* Initialize the packet-lifetime memory allocation pool.
146  * This function should be called only once when Ethereal or Tethereal starts
147  * up.
148  */
149 void
150 ep_init_chunk(void)
151 {
152         ep_packet_mem.free_list=NULL;
153         ep_packet_mem.used_list=NULL;
154
155         emem_canary(ep_canary);
156 }
157 /* Initialize the capture-lifetime memory allocation pool.
158  * This function should be called only once when Ethereal or Tethereal starts
159  * up.
160  */
161 void
162 se_init_chunk(void)
163 {
164         se_packet_mem.free_list=NULL;
165         se_packet_mem.used_list=NULL;
166
167         emem_canary(se_canary);
168 }
169
170 #define EMEM_CREATE_CHUNK(FREE_LIST) \
171         /* we dont have any free data, so we must allocate a new one */ \
172         if(!FREE_LIST){ \
173                 emem_chunk_t *npc; \
174                 npc=g_malloc(sizeof(emem_chunk_t)); \
175                 npc->next=NULL; \
176                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE; \
177                 npc->free_offset=0; \
178                 npc->buf=g_malloc(EMEM_PACKET_CHUNK_SIZE); \
179                 npc->c_count = 0; \
180                 FREE_LIST=npc; \
181         }
182
183 /* allocate 'size' amount of memory with an allocation lifetime until the
184  * next packet.
185  */
186 void *
187 ep_alloc(size_t size)
188 {
189         void *buf, *cptr;
190         guint8 pad = emem_canary_pad(size);
191         emem_chunk_t *free_list;
192
193 #ifndef EP_DEBUG_FREE
194         /* Round up to an 8 byte boundary.  Make sure we have at least
195          * 8 pad bytes for our canary.
196          */
197         size += pad;
198
199         /* make sure we dont try to allocate too much (arbitrary limit) */
200         DISSECTOR_ASSERT(size<(EMEM_PACKET_CHUNK_SIZE>>2));
201
202         EMEM_CREATE_CHUNK(ep_packet_mem.free_list);
203
204         /* oops, we need to allocate more memory to serve this request
205          * than we have free. move this node to the used list and try again
206          */
207         if(size>ep_packet_mem.free_list->amount_free || ep_packet_mem.free_list->c_count >= EMEM_ALLOCS_PER_CHUNK){
208                 emem_chunk_t *npc;
209                 npc=ep_packet_mem.free_list;
210                 ep_packet_mem.free_list=ep_packet_mem.free_list->next;
211                 npc->next=ep_packet_mem.used_list;
212                 ep_packet_mem.used_list=npc;
213         }
214
215         EMEM_CREATE_CHUNK(ep_packet_mem.free_list);
216
217         free_list = ep_packet_mem.free_list;
218
219         buf = free_list->buf + free_list->free_offset;
220
221         free_list->amount_free -= size;
222         free_list->free_offset += size;
223
224         cptr = (char *)buf + size - pad;
225         memcpy(cptr, &ep_canary, pad);
226         free_list->canary[free_list->c_count] = cptr;
227         free_list->cmp_len[free_list->c_count] = pad;
228         free_list->c_count++;
229
230 #else /* EP_DEBUG_FREE */
231         emem_chunk_t *npc;
232
233         npc=g_malloc(sizeof(emem_chunk_t));
234         npc->next=ep_packet_mem.used_list;
235         npc->amount_free=size;
236         npc->free_offset=0;
237         npc->buf=g_malloc(size);
238         buf = npc->buf;
239         ep_packet_mem.used_list=npc;
240 #endif /* EP_DEBUG_FREE */
241
242         return buf;
243 }
244 /* allocate 'size' amount of memory with an allocation lifetime until the
245  * next capture.
246  */
247 void *
248 se_alloc(size_t size)
249 {
250         void *buf, *cptr;
251         guint8 pad = emem_canary_pad(size);
252         emem_chunk_t *free_list;
253
254 #ifndef SE_DEBUG_FREE
255         /* Round up to an 8 byte boundary.  Make sure we have at least
256          * 8 pad bytes for our canary.
257          */
258         size += pad;
259
260         /* make sure we dont try to allocate too much (arbitrary limit) */
261         DISSECTOR_ASSERT(size<(EMEM_PACKET_CHUNK_SIZE>>2));
262
263         EMEM_CREATE_CHUNK(se_packet_mem.free_list);
264
265         /* oops, we need to allocate more memory to serve this request
266          * than we have free. move this node to the used list and try again
267          */
268         if(size>se_packet_mem.free_list->amount_free || se_packet_mem.free_list->c_count >= EMEM_ALLOCS_PER_CHUNK){
269                 emem_chunk_t *npc;
270                 npc=se_packet_mem.free_list;
271                 se_packet_mem.free_list=se_packet_mem.free_list->next;
272                 npc->next=se_packet_mem.used_list;
273                 se_packet_mem.used_list=npc;
274         }
275
276         EMEM_CREATE_CHUNK(se_packet_mem.free_list);
277
278         free_list = se_packet_mem.free_list;
279
280         buf = free_list->buf + free_list->free_offset;
281
282         free_list->amount_free -= size;
283         free_list->free_offset += size;
284
285         cptr = (char *)buf + size - pad;
286         memcpy(cptr, &se_canary, pad);
287         free_list->canary[free_list->c_count] = cptr;
288         free_list->cmp_len[free_list->c_count] = pad;
289         free_list->c_count++;
290
291 #else /* SE_DEBUG_FREE */
292         emem_chunk_t *npc;
293
294         npc=g_malloc(sizeof(emem_chunk_t));
295         npc->next=se_packet_mem.used_list;
296         npc->amount_free=size;
297         npc->free_offset=0;
298         npc->buf=g_malloc(size);
299         buf = npc->buf;
300         se_packet_mem.used_list=npc;
301 #endif /* SE_DEBUG_FREE */
302
303         return buf;
304 }
305
306
307 void* ep_alloc0(size_t size) {
308         return memset(ep_alloc(size),'\0',size);
309 }
310
311 gchar* ep_strdup(const gchar* src) {
312         guint len = strlen(src);
313         gchar* dst;
314
315         dst = strncpy(ep_alloc(len+1), src, len);
316
317         dst[len] = '\0';
318
319         return dst;
320 }
321
322 gchar* ep_strndup(const gchar* src, size_t len) {
323         gchar* dst = ep_alloc(len+1);
324         guint i;
325
326         for (i = 0; src[i] && i < len; i++)
327                 dst[i] = src[i];
328
329         dst[i] = '\0';
330
331         return dst;
332 }
333
334 guint8* ep_memdup(const guint8* src, size_t len) {
335         return memcpy(ep_alloc(len), src, len);
336 }
337
338 gchar* ep_strdup_vprintf(const gchar* fmt, va_list ap) {
339         va_list ap2;
340         guint len;
341         gchar* dst;
342
343         G_VA_COPY(ap2, ap);
344
345         len = g_printf_string_upper_bound(fmt, ap);
346
347         dst = ep_alloc(len+1);
348         g_vsnprintf (dst, len, fmt, ap2);
349         va_end(ap2);
350
351         return dst;
352 }
353
354 gchar* ep_strdup_printf(const gchar* fmt, ...) {
355         va_list ap;
356         gchar* dst;
357
358         va_start(ap,fmt);
359         dst = ep_strdup_vprintf(fmt, ap);
360         va_end(ap);
361         return dst;
362 }
363
364 gchar** ep_strsplit(const gchar* string, const gchar* sep, int max_tokens) {
365         gchar* splitted;
366         gchar* s;
367         guint tokens;
368         guint str_len;
369         guint sep_len;
370         guint i;
371         gchar** vec;
372         enum { AT_START, IN_PAD, IN_TOKEN } state;
373         guint curr_tok = 0;
374
375         if ( ! string
376                  || ! sep
377                  || ! sep[0])
378                 return NULL;
379
380         s = splitted = ep_strdup(string);
381         str_len = strlen(splitted);
382         sep_len = strlen(sep);
383
384         if (max_tokens < 1) max_tokens = INT_MAX;
385
386         tokens = 1;
387
388
389         while (tokens <= (guint)max_tokens && ( s = strstr(s,sep) )) {
390                 tokens++;
391
392                 for(i=0; i < sep_len; i++ )
393                         s[i] = '\0';
394
395                 s += sep_len;
396
397         }
398
399         vec = ep_alloc_array(gchar*,tokens+1);
400         state = AT_START;
401
402         for (i=0; i< str_len; i++) {
403                 switch(state) {
404                         case AT_START:
405                                 switch(splitted[i]) {
406                                         case '\0':
407                                                 state  = IN_PAD;
408                                                 continue;
409                                         default:
410                                                 vec[curr_tok] = &(splitted[i]);
411                                                 curr_tok++;
412                                                 state = IN_TOKEN;
413                                                 continue;
414                                 }
415                         case IN_TOKEN:
416                                 switch(splitted[i]) {
417                                         case '\0':
418                                                 state = IN_PAD;
419                                         default:
420                                                 continue;
421                                 }
422                         case IN_PAD:
423                                 switch(splitted[i]) {
424                                         default:
425                                                 vec[curr_tok] = &(splitted[i]);
426                                                 curr_tok++;
427                                                 state = IN_TOKEN;
428                                         case '\0':
429                                                 continue;
430                                 }
431                 }
432         }
433
434         vec[curr_tok] = NULL;
435
436         return vec;
437 }
438
439
440
441 void* se_alloc0(size_t size) {
442         return memset(se_alloc(size),'\0',size);
443 }
444
445 /* If str is NULL, just return the string "<NULL>" so that the callers dont
446  * have to bother checking it.
447  */
448 gchar* se_strdup(const gchar* src) {
449         guint len;
450         gchar* dst;
451
452         if(!src){
453                 return "<NULL>";
454         }
455
456         len = strlen(src);
457         dst = strncpy(se_alloc(len+1), src, len);
458
459         dst[len] = '\0';
460
461         return dst;
462 }
463
464 gchar* se_strndup(const gchar* src, size_t len) {
465         gchar* dst = se_alloc(len+1);
466         guint i;
467
468         for (i = 0; src[i] && i < len; i++)
469                 dst[i] = src[i];
470
471         dst[i] = '\0';
472
473         return dst;
474 }
475
476 guint8* se_memdup(const guint8* src, size_t len) {
477         return memcpy(se_alloc(len), src, len);
478 }
479
480 gchar* se_strdup_vprintf(const gchar* fmt, va_list ap) {
481         va_list ap2;
482         guint len;
483         gchar* dst;
484
485         G_VA_COPY(ap2, ap);
486
487         len = g_printf_string_upper_bound(fmt, ap);
488
489         dst = se_alloc(len+1);
490         g_vsnprintf (dst, len, fmt, ap2);
491         va_end(ap2);
492
493         return dst;
494 }
495
496 gchar* se_strdup_printf(const gchar* fmt, ...) {
497         va_list ap;
498         gchar* dst;
499
500         va_start(ap,fmt);
501         dst = se_strdup_vprintf(fmt, ap);
502         va_end(ap);
503         return dst;
504 }
505
506 /* release all allocated memory back to the pool.
507  */
508 void
509 ep_free_all(void)
510 {
511         emem_chunk_t *npc;
512         guint i;
513
514         /* move all used chunks over to the free list */
515         while(ep_packet_mem.used_list){
516                 npc=ep_packet_mem.used_list;
517                 ep_packet_mem.used_list=ep_packet_mem.used_list->next;
518                 npc->next=ep_packet_mem.free_list;
519                 ep_packet_mem.free_list=npc;
520         }
521
522         /* clear them all out */
523         npc = ep_packet_mem.free_list;
524         while (npc != NULL) {
525 #ifndef EP_DEBUG_FREE
526                 for (i = 0; i < npc->c_count; i++) {
527                         g_assert(memcmp(npc->canary[i], &ep_canary, npc->cmp_len[i]) == 0);
528                 }
529                 npc->c_count = 0;
530                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE;
531                 npc->free_offset=0;
532                 npc = npc->next;
533 #else /* EP_DEBUG_FREE */
534                 emem_chunk_t *next = npc->next;
535
536                 g_free(npc->buf);
537                 g_free(npc);
538                 npc = next;
539 #endif /* EP_DEBUG_FREE */
540         }
541
542 #ifdef EP_DEBUG_FREE
543         ep_init_chunk();
544 #endif
545 }
546 /* release all allocated memory back to the pool.
547  */
548 void
549 se_free_all(void)
550 {
551         emem_chunk_t *npc;
552         guint i;
553
554         /* move all used chunks ove to the free list */
555         while(se_packet_mem.used_list){
556                 npc=se_packet_mem.used_list;
557                 se_packet_mem.used_list=se_packet_mem.used_list->next;
558                 npc->next=se_packet_mem.free_list;
559                 se_packet_mem.free_list=npc;
560         }
561
562         /* clear them all out */
563         npc = se_packet_mem.free_list;
564         while (npc != NULL) {
565 #ifndef SE_DEBUG_FREE
566                 for (i = 0; i < npc->c_count; i++) {
567                         g_assert(memcmp(npc->canary[i], &se_canary, npc->cmp_len[i]) == 0);
568                 }
569                 npc->c_count = 0;
570                 npc->amount_free=EMEM_PACKET_CHUNK_SIZE;
571                 npc->free_offset=0;
572                 npc = npc->next;
573 #else /* SE_DEBUG_FREE */
574                 emem_chunk_t *next = npc->next;
575
576                 g_free(npc->buf);
577                 g_free(npc);
578                 npc = next;
579 #endif /* SE_DEBUG_FREE */
580         }
581
582 #ifdef SE_DEBUG_FREE
583                 se_init_chunk();
584 #endif
585 }
586
587
588 ep_stack_t ep_stack_new(void) {
589     ep_stack_t s = ep_new(struct _ep_stack_frame_t*);
590     *s = ep_new0(struct _ep_stack_frame_t);
591     return s;
592 }
593
594 /*  for ep_stack_t we'll keep the popped frames so we reuse them instead
595 of allocating new ones.
596 */
597
598
599 void* ep_stack_push(ep_stack_t stack, void* data) {
600     struct _ep_stack_frame_t* frame;
601     struct _ep_stack_frame_t* head = (*stack);
602
603     if (head->above) {
604         frame = head->above;
605     } else {
606        frame = ep_new(struct _ep_stack_frame_t);
607        head->above = frame;
608        frame->below = head;
609        frame->above = NULL;
610     }
611
612     frame->payload = data;
613     (*stack) = frame;
614
615     return data;
616 }
617
618 void* ep_stack_pop(ep_stack_t stack) {
619
620     if ((*stack)->below) {
621         (*stack) = (*stack)->below;
622         return (*stack)->above->payload;
623     } else {
624         return NULL;
625     }
626 }
627