alloc.c revision 287917
1186681Sed/*
2186681Sed * util/alloc.c - memory allocation service.
3186681Sed *
4186681Sed * Copyright (c) 2007, NLnet Labs. All rights reserved.
5186681Sed *
6186681Sed * This software is open source.
7186681Sed *
8186681Sed * Redistribution and use in source and binary forms, with or without
9186681Sed * modification, are permitted provided that the following conditions
10186681Sed * are met:
11186681Sed *
12186681Sed * Redistributions of source code must retain the above copyright notice,
13186681Sed * this list of conditions and the following disclaimer.
14186681Sed *
15186681Sed * Redistributions in binary form must reproduce the above copyright notice,
16186681Sed * this list of conditions and the following disclaimer in the documentation
17186681Sed * and/or other materials provided with the distribution.
18186681Sed *
19186681Sed * Neither the name of the NLNET LABS nor the names of its contributors may
20186681Sed * be used to endorse or promote products derived from this software without
21186681Sed * specific prior written permission.
22186681Sed *
23186681Sed * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24186681Sed * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25186681Sed * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26186681Sed * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27186681Sed * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28186681Sed * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29186681Sed * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30186681Sed * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31186681Sed * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32286798Sed * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33186681Sed * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34186681Sed */
35186681Sed
36186681Sed/**
37186681Sed * \file
38186681Sed *
39286798Sed * This file contains memory allocation functions.
40206141Sed */
41186681Sed
42186681Sed#include "config.h"
43186681Sed#include "util/alloc.h"
44186681Sed#include "util/regional.h"
45186681Sed#include "util/data/packed_rrset.h"
46221698Sed#include "util/fptr_wlist.h"
47221698Sed
48221698Sed/** custom size of cached regional blocks */
49186681Sed#define ALLOC_REG_SIZE	16384
50199171Sed/** number of bits for ID part of uint64, rest for number of threads. */
51199171Sed#define THRNUM_SHIFT	48	/* for 65k threads, 2^48 rrsets per thr. */
52199171Sed
53199171Sed/** setup new special type */
54199171Sedstatic void
55199171Sedalloc_setup_special(alloc_special_t* t)
56199171Sed{
57199171Sed	memset(t, 0, sizeof(*t));
58199171Sed	lock_rw_init(&t->entry.lock);
59186681Sed	t->entry.key = t;
60186681Sed}
61186681Sed
62186681Sed/** prealloc some entries in the cache. To minimize contention.
63197470Sed * Result is 1 lock per alloc_max newly created entries.
64197470Sed * @param alloc: the structure to fill up.
65197470Sed */
66197470Sedstatic void
67186681Sedprealloc(struct alloc_cache* alloc)
68186681Sed{
69186681Sed	alloc_special_t* p;
70186681Sed	int i;
71186681Sed	for(i=0; i<ALLOC_SPECIAL_MAX; i++) {
72186681Sed		if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t)))) {
73186681Sed			log_err("prealloc: out of memory");
74186681Sed			return;
75186681Sed		}
76186681Sed		alloc_setup_special(p);
77186681Sed		alloc_set_special_next(p, alloc->quar);
78186681Sed		alloc->quar = p;
79186681Sed		alloc->num_quar++;
80186681Sed	}
81186681Sed}
82186681Sed
83186681Sed/** prealloc region blocks */
84186681Sedstatic void
85186681Sedprealloc_blocks(struct alloc_cache* alloc, size_t num)
86186681Sed{
87186681Sed	size_t i;
88186681Sed	struct regional* r;
89186681Sed	for(i=0; i<num; i++) {
90186681Sed		r = regional_create_custom(ALLOC_REG_SIZE);
91186681Sed		if(!r) {
92186681Sed			log_err("prealloc blocks: out of memory");
93186681Sed			return;
94186681Sed		}
95186681Sed		r->next = (char*)alloc->reg_list;
96186681Sed		alloc->reg_list = r;
97186681Sed		alloc->num_reg_blocks ++;
98186681Sed	}
99186681Sed}
100186681Sed
101186681Sedvoid
102186681Sedalloc_init(struct alloc_cache* alloc, struct alloc_cache* super,
103186681Sed	int thread_num)
104186681Sed{
105186681Sed	memset(alloc, 0, sizeof(*alloc));
106186681Sed	alloc->super = super;
107186681Sed	alloc->thread_num = thread_num;
108186681Sed	alloc->next_id = (uint64_t)thread_num; 	/* in steps, so that type */
109186681Sed	alloc->next_id <<= THRNUM_SHIFT; 	/* of *_id is used. */
110186681Sed	alloc->last_id = 1; 			/* so no 64bit constants, */
111186681Sed	alloc->last_id <<= THRNUM_SHIFT; 	/* or implicit 'int' ops. */
112186681Sed	alloc->last_id -= 1; 			/* for compiler portability. */
113186681Sed	alloc->last_id |= alloc->next_id;
114186681Sed	alloc->next_id += 1;			/* because id=0 is special. */
115186681Sed	alloc->max_reg_blocks = 100;
116186681Sed	alloc->num_reg_blocks = 0;
117186681Sed	alloc->reg_list = NULL;
118186681Sed	alloc->cleanup = NULL;
119186681Sed	alloc->cleanup_arg = NULL;
120186681Sed	if(alloc->super)
121186681Sed		prealloc_blocks(alloc, alloc->max_reg_blocks);
122186681Sed	if(!alloc->super) {
123186681Sed		lock_quick_init(&alloc->lock);
124186681Sed		lock_protect(&alloc->lock, alloc, sizeof(*alloc));
125186681Sed	}
126186681Sed}
127186681Sed
128186681Sedvoid
129193184Sedalloc_clear(struct alloc_cache* alloc)
130186681Sed{
131186681Sed	alloc_special_t* p, *np;
132186681Sed	struct regional* r, *nr;
133186681Sed	if(!alloc)
134186681Sed		return;
135186681Sed	if(!alloc->super) {
136186681Sed		lock_quick_destroy(&alloc->lock);
137186681Sed	}
138186681Sed	if(alloc->super && alloc->quar) {
139186681Sed		/* push entire list into super */
140186681Sed		p = alloc->quar;
141186681Sed		while(alloc_special_next(p)) /* find last */
142186681Sed			p = alloc_special_next(p);
143186681Sed		lock_quick_lock(&alloc->super->lock);
144186681Sed		alloc_set_special_next(p, alloc->super->quar);
145186681Sed		alloc->super->quar = alloc->quar;
146186681Sed		alloc->super->num_quar += alloc->num_quar;
147186681Sed		lock_quick_unlock(&alloc->super->lock);
148186681Sed	} else {
149186681Sed		/* free */
150186681Sed		p = alloc->quar;
151186681Sed		while(p) {
152186681Sed			np = alloc_special_next(p);
153186681Sed			/* deinit special type */
154186681Sed			lock_rw_destroy(&p->entry.lock);
155186681Sed			free(p);
156186681Sed			p = np;
157186681Sed		}
158197117Sed	}
159197117Sed	alloc->quar = 0;
160186681Sed	alloc->num_quar = 0;
161186681Sed	r = alloc->reg_list;
162186681Sed	while(r) {
163186681Sed		nr = (struct regional*)r->next;
164186681Sed		free(r);
165186681Sed		r = nr;
166186681Sed	}
167186681Sed	alloc->reg_list = NULL;
168186681Sed	alloc->num_reg_blocks = 0;
169186681Sed}
170186681Sed
171186681Seduint64_t
172186681Sedalloc_get_id(struct alloc_cache* alloc)
173197853Sed{
174197853Sed	uint64_t id = alloc->next_id++;
175197853Sed	if(id == alloc->last_id) {
176197853Sed		log_warn("rrset alloc: out of 64bit ids. Clearing cache.");
177197853Sed		fptr_ok(fptr_whitelist_alloc_cleanup(alloc->cleanup));
178197853Sed		(*alloc->cleanup)(alloc->cleanup_arg);
179197853Sed
180197853Sed		/* start back at first number */   	/* like in alloc_init*/
181197853Sed		alloc->next_id = (uint64_t)alloc->thread_num;
182197853Sed		alloc->next_id <<= THRNUM_SHIFT; 	/* in steps for comp. */
183197853Sed		alloc->next_id += 1;			/* portability. */
184197853Sed		/* and generate new and safe id */
185197853Sed		id = alloc->next_id++;
186197853Sed	}
187197853Sed	return id;
188197853Sed}
189197853Sed
190197853Sedalloc_special_t*
191186681Sedalloc_special_obtain(struct alloc_cache* alloc)
192186681Sed{
193186681Sed	alloc_special_t* p;
194186681Sed	log_assert(alloc);
195186681Sed	/* see if in local cache */
196186681Sed	if(alloc->quar) {
197186681Sed		p = alloc->quar;
198186681Sed		alloc->quar = alloc_special_next(p);
199186681Sed		alloc->num_quar--;
200186681Sed		p->id = alloc_get_id(alloc);
201186681Sed		return p;
202186681Sed	}
203186681Sed	/* see if in global cache */
204186798Sed	if(alloc->super) {
205186798Sed		/* could maybe grab alloc_max/2 entries in one go,
206186798Sed		 * but really, isn't that just as fast as this code? */
207187469Sed		lock_quick_lock(&alloc->super->lock);
208197117Sed		if((p = alloc->super->quar)) {
209197117Sed			alloc->super->quar = alloc_special_next(p);
210197117Sed			alloc->super->num_quar--;
211197520Sed		}
212187469Sed		lock_quick_unlock(&alloc->super->lock);
213187469Sed		if(p) {
214197117Sed			p->id = alloc_get_id(alloc);
215197117Sed			return p;
216197117Sed		}
217197520Sed	}
218187469Sed	/* allocate new */
219186681Sed	prealloc(alloc);
220186681Sed	if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t)))) {
221186681Sed		log_err("alloc_special_obtain: out of memory");
222186681Sed		return NULL;
223186681Sed	}
224186681Sed	alloc_setup_special(p);
225186681Sed	p->id = alloc_get_id(alloc);
226186681Sed	return p;
227186681Sed}
228186681Sed
229186681Sed/** push mem and some more items to the super */
230186681Sedstatic void
231186681Sedpushintosuper(struct alloc_cache* alloc, alloc_special_t* mem)
232186681Sed{
233186681Sed	int i;
234186681Sed	alloc_special_t *p = alloc->quar;
235186681Sed	log_assert(p);
236186681Sed	log_assert(alloc && alloc->super &&
237186681Sed		alloc->num_quar >= ALLOC_SPECIAL_MAX);
238186681Sed	/* push ALLOC_SPECIAL_MAX/2 after mem */
239186681Sed	alloc_set_special_next(mem, alloc->quar);
240186681Sed	for(i=1; i<ALLOC_SPECIAL_MAX/2; i++) {
241186681Sed		p = alloc_special_next(p);
242186681Sed	}
243186681Sed	alloc->quar = alloc_special_next(p);
244186681Sed	alloc->num_quar -= ALLOC_SPECIAL_MAX/2;
245186681Sed
246186681Sed	/* dump mem+list into the super quar list */
247186681Sed	lock_quick_lock(&alloc->super->lock);
248186681Sed	alloc_set_special_next(p, alloc->super->quar);
249186681Sed	alloc->super->quar = mem;
250186681Sed	alloc->super->num_quar += ALLOC_SPECIAL_MAX/2 + 1;
251186681Sed	lock_quick_unlock(&alloc->super->lock);
252186681Sed	/* so 1 lock per mem+alloc/2 deletes */
253197117Sed}
254186681Sed
255186681Sedvoid
256186681Sedalloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem)
257186681Sed{
258186681Sed	log_assert(alloc);
259186681Sed	if(!mem)
260186681Sed		return;
261186681Sed	if(!alloc->super) {
262186681Sed		lock_quick_lock(&alloc->lock); /* superalloc needs locking */
263186681Sed	}
264186681Sed
265186681Sed	alloc_special_clean(mem);
266186681Sed	if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) {
267186681Sed		/* push it to the super structure */
268186681Sed		pushintosuper(alloc, mem);
269186681Sed		return;
270186681Sed	}
271186681Sed
272186681Sed	alloc_set_special_next(mem, alloc->quar);
273186681Sed	alloc->quar = mem;
274186681Sed	alloc->num_quar++;
275194293Sed	if(!alloc->super) {
276186681Sed		lock_quick_unlock(&alloc->lock);
277186681Sed	}
278186681Sed}
279186681Sed
280186681Sedvoid
281186681Sedalloc_stats(struct alloc_cache* alloc)
282186681Sed{
283186681Sed	log_info("%salloc: %d in cache, %d blocks.", alloc->super?"":"sup",
284186681Sed		(int)alloc->num_quar, (int)alloc->num_reg_blocks);
285186681Sed}
286186681Sed
287186681Sedsize_t alloc_get_mem(struct alloc_cache* alloc)
288186681Sed{
289186681Sed	alloc_special_t* p;
290197117Sed	size_t s = sizeof(*alloc);
291197117Sed	if(!alloc->super) {
292197117Sed		lock_quick_lock(&alloc->lock); /* superalloc needs locking */
293197117Sed	}
294197117Sed	s += sizeof(alloc_special_t) * alloc->num_quar;
295197117Sed	for(p = alloc->quar; p; p = alloc_special_next(p)) {
296197117Sed		s += lock_get_mem(&p->entry.lock);
297186681Sed	}
298186681Sed	s += alloc->num_reg_blocks * ALLOC_REG_SIZE;
299186681Sed	if(!alloc->super) {
300186681Sed		lock_quick_unlock(&alloc->lock);
301186681Sed	}
302186681Sed	return s;
303186681Sed}
304186681Sed
305186681Sedstruct regional*
306186681Sedalloc_reg_obtain(struct alloc_cache* alloc)
307186681Sed{
308188391Sed	if(alloc->num_reg_blocks > 0) {
309188391Sed		struct regional* r = alloc->reg_list;
310188391Sed		alloc->reg_list = (struct regional*)r->next;
311188391Sed		r->next = NULL;
312188391Sed		alloc->num_reg_blocks--;
313188391Sed		return r;
314188391Sed	}
315189617Sed	return regional_create_custom(ALLOC_REG_SIZE);
316189617Sed}
317189617Sed
318189617Sedvoid
319189617Sedalloc_reg_release(struct alloc_cache* alloc, struct regional* r)
320189617Sed{
321189617Sed	if(alloc->num_reg_blocks >= alloc->max_reg_blocks) {
322188391Sed		regional_destroy(r);
323188391Sed		return;
324188391Sed	}
325188391Sed	if(!r) return;
326188391Sed	regional_free_all(r);
327188391Sed	log_assert(r->next == NULL);
328188391Sed	r->next = (char*)alloc->reg_list;
329186681Sed	alloc->reg_list = r;
330186681Sed	alloc->num_reg_blocks++;
331186681Sed}
332186681Sed
333186681Sedvoid
334186681Sedalloc_set_id_cleanup(struct alloc_cache* alloc, void (*cleanup)(void*),
335186681Sed        void* arg)
336197117Sed{
337197117Sed	alloc->cleanup = cleanup;
338197117Sed	alloc->cleanup_arg = arg;
339197117Sed}
340197117Sed
341197117Sed/** global debug value to keep track of total memory mallocs */
342197117Sedsize_t unbound_mem_alloc = 0;
343261547Sray/** global debug value to keep track of total memory frees */
344261551Sraysize_t unbound_mem_freed = 0;
345261547Sray#ifdef UNBOUND_ALLOC_STATS
346261547Sray/** special value to know if the memory is being tracked */
347261547Srayuint64_t mem_special = (uint64_t)0xfeed43327766abcdLL;
348261547Sray#ifdef malloc
349261547Sray#undef malloc
350261547Sray#endif
351261547Sray/** malloc with stats */
352261547Srayvoid *unbound_stat_malloc(size_t size)
353261547Sray{
354261547Sray	void* res;
355261547Sray	if(size == 0) size = 1;
356261547Sray	res = malloc(size+16);
357261547Sray	if(!res) return NULL;
358186681Sed	unbound_mem_alloc += size;
359186681Sed	log_info("stat %p=malloc(%u)", res+16, (unsigned)size);
360186681Sed	memcpy(res, &size, sizeof(size));
361186681Sed	memcpy(res+8, &mem_special, sizeof(mem_special));
362261551Sray	return res+16;
363186681Sed}
364197114Sed#ifdef calloc
365186681Sed#undef calloc
366186681Sed#endif
367197115Sed#ifndef INT_MAX
368259016Sray#define INT_MAX (((int)-1)>>1)
369259016Sray#endif
370259016Sray/** calloc with stats */
371261551Srayvoid *unbound_stat_calloc(size_t nmemb, size_t size)
372259016Sray{
373259016Sray	size_t s;
374259016Sray	void* res;
375259016Sray	if(nmemb != 0 && INT_MAX/nmemb < size)
376259016Sray		return NULL; /* integer overflow check */
377197115Sed	s = (nmemb*size==0)?(size_t)1:nmemb*size;
378197115Sed	res = calloc(1, s+16);
379197115Sed	if(!res) return NULL;
380197117Sed	log_info("stat %p=calloc(%u, %u)", res+16, (unsigned)nmemb, (unsigned)size);
381197115Sed	unbound_mem_alloc += s;
382197115Sed	memcpy(res, &s, sizeof(s));
383197117Sed	memcpy(res+8, &mem_special, sizeof(mem_special));
384197117Sed	return res+16;
385197117Sed}
386197117Sed#ifdef free
387197117Sed#undef free
388197117Sed#endif
389197117Sed/** free with stats */
390186681Sedvoid unbound_stat_free(void *ptr)
391186681Sed{
392186681Sed	size_t s;
393186681Sed	if(!ptr) return;
394186681Sed	if(memcmp(ptr-8, &mem_special, sizeof(mem_special)) != 0) {
395186681Sed		free(ptr);
396186681Sed		return;
397186681Sed	}
398186681Sed	ptr-=16;
399186681Sed	memcpy(&s, ptr, sizeof(s));
400186681Sed	log_info("stat free(%p) size %u", ptr+16, (unsigned)s);
401186681Sed	memset(ptr+8, 0, 8);
402186681Sed	unbound_mem_freed += s;
403186681Sed	free(ptr);
404186681Sed}
405186681Sed#ifdef realloc
406186681Sed#undef realloc
407186681Sed#endif
408186681Sed/** realloc with stats */
409186681Sedvoid *unbound_stat_realloc(void *ptr, size_t size)
410186681Sed{
411286798Sed	size_t cursz;
412186681Sed	void* res;
413286798Sed	if(!ptr) return unbound_stat_malloc(size);
414286798Sed	if(memcmp(ptr-8, &mem_special, sizeof(mem_special)) != 0) {
415286798Sed		return realloc(ptr, size);
416286798Sed	}
417286798Sed	if(size==0) {
418286798Sed		unbound_stat_free(ptr);
419286798Sed		return NULL;
420286798Sed	}
421286798Sed	ptr -= 16;
422286798Sed	memcpy(&cursz, ptr, sizeof(cursz));
423286798Sed	if(cursz == size) {
424186681Sed		/* nothing changes */
425186681Sed		return ptr;
426186681Sed	}
427186681Sed	res = malloc(size+16);
428186681Sed	if(!res) return NULL;
429186681Sed	unbound_mem_alloc += size;
430186681Sed	unbound_mem_freed += cursz;
431186681Sed	log_info("stat realloc(%p, %u) from %u", ptr+16, (unsigned)size, (unsigned)cursz);
432186681Sed	if(cursz > size) {
433186681Sed		memcpy(res+16, ptr+16, size);
434186681Sed	} else if(size > cursz) {
435186681Sed		memcpy(res+16, ptr+16, cursz);
436186681Sed	}
437186681Sed	memset(ptr+8, 0, 8);
438186681Sed	free(ptr);
439186681Sed	memcpy(res, &size, sizeof(size));
440186681Sed	memcpy(res+8, &mem_special, sizeof(mem_special));
441186681Sed	return res+16;
442186681Sed}
443186681Sed
444186681Sed/** log to file where alloc was done */
445186681Sedvoid *unbound_stat_malloc_log(size_t size, const char* file, int line,
446186681Sed        const char* func)
447186681Sed{
448186681Sed	log_info("%s:%d %s malloc(%u)", file, line, func, (unsigned)size);
449186681Sed	return unbound_stat_malloc(size);
450186681Sed}
451186681Sed
452197522Sed/** log to file where alloc was done */
453197522Sedvoid *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
454197522Sed        int line, const char* func)
455197522Sed{
456197522Sed	log_info("%s:%d %s calloc(%u, %u)", file, line, func,
457197522Sed		(unsigned) nmemb, (unsigned)size);
458197522Sed	return unbound_stat_calloc(nmemb, size);
459197522Sed}
460197522Sed
461197522Sed/** log to file where free was done */
462197522Sedvoid unbound_stat_free_log(void *ptr, const char* file, int line,
463197522Sed        const char* func)
464197522Sed{
465197522Sed	if(ptr && memcmp(ptr-8, &mem_special, sizeof(mem_special)) == 0) {
466197522Sed		size_t s;
467197522Sed		memcpy(&s, ptr-16, sizeof(s));
468197522Sed		log_info("%s:%d %s free(%p) size %u",
469197522Sed			file, line, func, ptr, (unsigned)s);
470197522Sed	} else
471197522Sed		log_info("%s:%d %s unmatched free(%p)", file, line, func, ptr);
472197522Sed	unbound_stat_free(ptr);
473197522Sed}
474197522Sed
475197522Sed/** log to file where alloc was done */
476197522Sedvoid *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
477197522Sed        int line, const char* func)
478197522Sed{
479197522Sed	log_info("%s:%d %s realloc(%p, %u)", file, line, func,
480197522Sed		ptr, (unsigned)size);
481197522Sed	return unbound_stat_realloc(ptr, size);
482197522Sed}
483197522Sed
484197522Sed#endif /* UNBOUND_ALLOC_STATS */
485197522Sed#ifdef UNBOUND_ALLOC_LITE
486197522Sed#undef malloc
487197522Sed#undef calloc
488197522Sed#undef free
489197522Sed#undef realloc
490197522Sed/** length of prefix and suffix */
491197522Sedstatic size_t lite_pad = 16;
492197522Sed/** prefix value to check */
493197522Sedstatic char* lite_pre = "checkfront123456";
494197522Sed/** suffix value to check */
495197522Sedstatic char* lite_post= "checkafter123456";
496197522Sed
497197522Sedvoid *unbound_stat_malloc_lite(size_t size, const char* file, int line,
498197522Sed        const char* func)
499197522Sed{
500197522Sed	/*  [prefix .. len .. actual data .. suffix] */
501197522Sed	void* res = malloc(size+lite_pad*2+sizeof(size_t));
502197522Sed	if(!res) return NULL;
503199171Sed	memmove(res, lite_pre, lite_pad);
504199171Sed	memmove(res+lite_pad, &size, sizeof(size_t));
505199171Sed	memset(res+lite_pad+sizeof(size_t), 0x1a, size); /* init the memory */
506199171Sed	memmove(res+lite_pad+size+sizeof(size_t), lite_post, lite_pad);
507199175Sed	return res+lite_pad+sizeof(size_t);
508199171Sed}
509199171Sed
510199171Sedvoid *unbound_stat_calloc_lite(size_t nmemb, size_t size, const char* file,
511199171Sed        int line, const char* func)
512199171Sed{
513199171Sed	size_t req;
514199171Sed	void* res;
515199171Sed	if(nmemb != 0 && INT_MAX/nmemb < size)
516199171Sed		return NULL; /* integer overflow check */
517199171Sed	req = nmemb * size;
518199171Sed	res = malloc(req+lite_pad*2+sizeof(size_t));
519199171Sed	if(!res) return NULL;
520199171Sed	memmove(res, lite_pre, lite_pad);
521199171Sed	memmove(res+lite_pad, &req, sizeof(size_t));
522199171Sed	memset(res+lite_pad+sizeof(size_t), 0, req);
523199171Sed	memmove(res+lite_pad+req+sizeof(size_t), lite_post, lite_pad);
524199171Sed	return res+lite_pad+sizeof(size_t);
525199171Sed}
526199171Sed
527199171Sedvoid unbound_stat_free_lite(void *ptr, const char* file, int line,
528199171Sed        const char* func)
529199171Sed{
530199175Sed	void* real;
531199171Sed	size_t orig = 0;
532199171Sed	if(!ptr) return;
533199171Sed	real = ptr-lite_pad-sizeof(size_t);
534199171Sed	if(memcmp(real, lite_pre, lite_pad) != 0) {
535199171Sed		log_err("free(): prefix failed %s:%d %s", file, line, func);
536199171Sed		log_hex("prefix here", real, lite_pad);
537199171Sed		log_hex("  should be", lite_pre, lite_pad);
538199171Sed		fatal_exit("alloc assertion failed");
539199171Sed	}
540199171Sed	memmove(&orig, real+lite_pad, sizeof(size_t));
541199171Sed	if(memcmp(real+lite_pad+orig+sizeof(size_t), lite_post, lite_pad)!=0){
542199171Sed		log_err("free(): suffix failed %s:%d %s", file, line, func);
543199171Sed		log_err("alloc size is %d", (int)orig);
544199171Sed		log_hex("suffix here", real+lite_pad+orig+sizeof(size_t),
545199171Sed			lite_pad);
546199171Sed		log_hex("  should be", lite_post, lite_pad);
547199171Sed		fatal_exit("alloc assertion failed");
548199171Sed	}
549199171Sed	memset(real, 0xdd, orig+lite_pad*2+sizeof(size_t)); /* mark it */
550199171Sed	free(real);
551199171Sed}
552199171Sed
553199171Sedvoid *unbound_stat_realloc_lite(void *ptr, size_t size, const char* file,
554199171Sed        int line, const char* func)
555199171Sed{
556199171Sed	/* always free and realloc (no growing) */
557199171Sed	void* real, *newa;
558199171Sed	size_t orig = 0;
559223574Sed	if(!ptr) {
560199171Sed		/* like malloc() */
561199171Sed		return unbound_stat_malloc_lite(size, file, line, func);
562199171Sed	}
563186681Sed	if(!size) {
564		/* like free() */
565		unbound_stat_free_lite(ptr, file, line, func);
566		return NULL;
567	}
568	/* change allocation size and copy */
569	real = ptr-lite_pad-sizeof(size_t);
570	if(memcmp(real, lite_pre, lite_pad) != 0) {
571		log_err("realloc(): prefix failed %s:%d %s", file, line, func);
572		log_hex("prefix here", real, lite_pad);
573		log_hex("  should be", lite_pre, lite_pad);
574		fatal_exit("alloc assertion failed");
575	}
576	memmove(&orig, real+lite_pad, sizeof(size_t));
577	if(memcmp(real+lite_pad+orig+sizeof(size_t), lite_post, lite_pad)!=0){
578		log_err("realloc(): suffix failed %s:%d %s", file, line, func);
579		log_err("alloc size is %d", (int)orig);
580		log_hex("suffix here", real+lite_pad+orig+sizeof(size_t),
581			lite_pad);
582		log_hex("  should be", lite_post, lite_pad);
583		fatal_exit("alloc assertion failed");
584	}
585	/* new alloc and copy over */
586	newa = unbound_stat_malloc_lite(size, file, line, func);
587	if(!newa)
588		return NULL;
589	if(orig < size)
590		memmove(newa, ptr, orig);
591	else	memmove(newa, ptr, size);
592	memset(real, 0xdd, orig+lite_pad*2+sizeof(size_t)); /* mark it */
593	free(real);
594	return newa;
595}
596
597char* unbound_strdup_lite(const char* s, const char* file, int line,
598        const char* func)
599{
600	/* this routine is made to make sure strdup() uses the malloc_lite */
601	size_t l = strlen(s)+1;
602	char* n = (char*)unbound_stat_malloc_lite(l, file, line, func);
603	if(!n) return NULL;
604	memmove(n, s, l);
605	return n;
606}
607
608char* unbound_lite_wrapstr(char* s)
609{
610	char* n = unbound_strdup_lite(s, __FILE__, __LINE__, __func__);
611	free(s);
612	return n;
613}
614
615#undef sldns_pkt2wire
616sldns_status unbound_lite_pkt2wire(uint8_t **dest, const sldns_pkt *p,
617	size_t *size)
618{
619	uint8_t* md = NULL;
620	size_t ms = 0;
621	sldns_status s = sldns_pkt2wire(&md, p, &ms);
622	if(md) {
623		*dest = unbound_stat_malloc_lite(ms, __FILE__, __LINE__,
624			__func__);
625		*size = ms;
626		if(!*dest) { free(md); return LDNS_STATUS_MEM_ERR; }
627		memcpy(*dest, md, ms);
628		free(md);
629	} else {
630		*dest = NULL;
631		*size = 0;
632	}
633	return s;
634}
635
636#undef i2d_DSA_SIG
637int unbound_lite_i2d_DSA_SIG(DSA_SIG* dsasig, unsigned char** sig)
638{
639	unsigned char* n = NULL;
640	int r= i2d_DSA_SIG(dsasig, &n);
641	if(n) {
642		*sig = unbound_stat_malloc_lite((size_t)r, __FILE__, __LINE__,
643			__func__);
644		if(!*sig) return -1;
645		memcpy(*sig, n, (size_t)r);
646		free(n);
647		return r;
648	}
649	*sig = NULL;
650	return r;
651}
652
653#endif /* UNBOUND_ALLOC_LITE */
654