1238106Sdes/*
2238106Sdes * util/alloc.c - memory allocation service.
3238106Sdes *
4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved.
5238106Sdes *
6238106Sdes * This software is open source.
7238106Sdes *
8238106Sdes * Redistribution and use in source and binary forms, with or without
9238106Sdes * modification, are permitted provided that the following conditions
10238106Sdes * are met:
11238106Sdes *
12238106Sdes * Redistributions of source code must retain the above copyright notice,
13238106Sdes * this list of conditions and the following disclaimer.
14238106Sdes *
15238106Sdes * Redistributions in binary form must reproduce the above copyright notice,
16238106Sdes * this list of conditions and the following disclaimer in the documentation
17238106Sdes * and/or other materials provided with the distribution.
18238106Sdes *
19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may
20238106Sdes * be used to endorse or promote products derived from this software without
21238106Sdes * specific prior written permission.
22238106Sdes *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes *
39238106Sdes * This file contains memory allocation functions.
40238106Sdes */
41238106Sdes
42238106Sdes#include "config.h"
43238106Sdes#include "util/alloc.h"
44238106Sdes#include "util/regional.h"
45238106Sdes#include "util/data/packed_rrset.h"
46238106Sdes#include "util/fptr_wlist.h"
47238106Sdes
48238106Sdes/** custom size of cached regional blocks */
49238106Sdes#define ALLOC_REG_SIZE	16384
50238106Sdes/** number of bits for ID part of uint64, rest for number of threads. */
51238106Sdes#define THRNUM_SHIFT	48	/* for 65k threads, 2^48 rrsets per thr. */
52238106Sdes
53238106Sdes/** setup new special type */
54238106Sdesstatic void
55238106Sdesalloc_setup_special(alloc_special_t* t)
56238106Sdes{
57238106Sdes	memset(t, 0, sizeof(*t));
58238106Sdes	lock_rw_init(&t->entry.lock);
59238106Sdes	t->entry.key = t;
60238106Sdes}
61238106Sdes
62238106Sdes/** prealloc some entries in the cache. To minimize contention.
63238106Sdes * Result is 1 lock per alloc_max newly created entries.
64238106Sdes * @param alloc: the structure to fill up.
65238106Sdes */
66238106Sdesstatic void
67238106Sdesprealloc(struct alloc_cache* alloc)
68238106Sdes{
69238106Sdes	alloc_special_t* p;
70238106Sdes	int i;
71238106Sdes	for(i=0; i<ALLOC_SPECIAL_MAX; i++) {
72238106Sdes		if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t)))) {
73238106Sdes			log_err("prealloc: out of memory");
74238106Sdes			return;
75238106Sdes		}
76238106Sdes		alloc_setup_special(p);
77238106Sdes		alloc_set_special_next(p, alloc->quar);
78238106Sdes		alloc->quar = p;
79238106Sdes		alloc->num_quar++;
80238106Sdes	}
81238106Sdes}
82238106Sdes
83238106Sdes/** prealloc region blocks */
84238106Sdesstatic void
85238106Sdesprealloc_blocks(struct alloc_cache* alloc, size_t num)
86238106Sdes{
87238106Sdes	size_t i;
88238106Sdes	struct regional* r;
89238106Sdes	for(i=0; i<num; i++) {
90238106Sdes		r = regional_create_custom(ALLOC_REG_SIZE);
91238106Sdes		if(!r) {
92238106Sdes			log_err("prealloc blocks: out of memory");
93238106Sdes			return;
94238106Sdes		}
95238106Sdes		r->next = (char*)alloc->reg_list;
96238106Sdes		alloc->reg_list = r;
97238106Sdes		alloc->num_reg_blocks ++;
98238106Sdes	}
99238106Sdes}
100238106Sdes
101238106Sdesvoid
102238106Sdesalloc_init(struct alloc_cache* alloc, struct alloc_cache* super,
103238106Sdes	int thread_num)
104238106Sdes{
105238106Sdes	memset(alloc, 0, sizeof(*alloc));
106238106Sdes	alloc->super = super;
107238106Sdes	alloc->thread_num = thread_num;
108238106Sdes	alloc->next_id = (uint64_t)thread_num; 	/* in steps, so that type */
109238106Sdes	alloc->next_id <<= THRNUM_SHIFT; 	/* of *_id is used. */
110238106Sdes	alloc->last_id = 1; 			/* so no 64bit constants, */
111238106Sdes	alloc->last_id <<= THRNUM_SHIFT; 	/* or implicit 'int' ops. */
112238106Sdes	alloc->last_id -= 1; 			/* for compiler portability. */
113238106Sdes	alloc->last_id |= alloc->next_id;
114238106Sdes	alloc->next_id += 1;			/* because id=0 is special. */
115238106Sdes	alloc->max_reg_blocks = 100;
116238106Sdes	alloc->num_reg_blocks = 0;
117238106Sdes	alloc->reg_list = NULL;
118238106Sdes	alloc->cleanup = NULL;
119238106Sdes	alloc->cleanup_arg = NULL;
120238106Sdes	if(alloc->super)
121238106Sdes		prealloc_blocks(alloc, alloc->max_reg_blocks);
122238106Sdes	if(!alloc->super) {
123238106Sdes		lock_quick_init(&alloc->lock);
124238106Sdes		lock_protect(&alloc->lock, alloc, sizeof(*alloc));
125238106Sdes	}
126238106Sdes}
127238106Sdes
128238106Sdesvoid
129238106Sdesalloc_clear(struct alloc_cache* alloc)
130238106Sdes{
131238106Sdes	alloc_special_t* p, *np;
132238106Sdes	struct regional* r, *nr;
133238106Sdes	if(!alloc)
134238106Sdes		return;
135238106Sdes	if(!alloc->super) {
136238106Sdes		lock_quick_destroy(&alloc->lock);
137238106Sdes	}
138238106Sdes	if(alloc->super && alloc->quar) {
139238106Sdes		/* push entire list into super */
140238106Sdes		p = alloc->quar;
141238106Sdes		while(alloc_special_next(p)) /* find last */
142238106Sdes			p = alloc_special_next(p);
143238106Sdes		lock_quick_lock(&alloc->super->lock);
144238106Sdes		alloc_set_special_next(p, alloc->super->quar);
145238106Sdes		alloc->super->quar = alloc->quar;
146238106Sdes		alloc->super->num_quar += alloc->num_quar;
147238106Sdes		lock_quick_unlock(&alloc->super->lock);
148238106Sdes	} else {
149238106Sdes		/* free */
150238106Sdes		p = alloc->quar;
151238106Sdes		while(p) {
152238106Sdes			np = alloc_special_next(p);
153238106Sdes			/* deinit special type */
154238106Sdes			lock_rw_destroy(&p->entry.lock);
155238106Sdes			free(p);
156238106Sdes			p = np;
157238106Sdes		}
158238106Sdes	}
159238106Sdes	alloc->quar = 0;
160238106Sdes	alloc->num_quar = 0;
161238106Sdes	r = alloc->reg_list;
162238106Sdes	while(r) {
163238106Sdes		nr = (struct regional*)r->next;
164238106Sdes		free(r);
165238106Sdes		r = nr;
166238106Sdes	}
167238106Sdes	alloc->reg_list = NULL;
168238106Sdes	alloc->num_reg_blocks = 0;
169238106Sdes}
170238106Sdes
171238106Sdesuint64_t
172238106Sdesalloc_get_id(struct alloc_cache* alloc)
173238106Sdes{
174238106Sdes	uint64_t id = alloc->next_id++;
175238106Sdes	if(id == alloc->last_id) {
176238106Sdes		log_warn("rrset alloc: out of 64bit ids. Clearing cache.");
177238106Sdes		fptr_ok(fptr_whitelist_alloc_cleanup(alloc->cleanup));
178238106Sdes		(*alloc->cleanup)(alloc->cleanup_arg);
179238106Sdes
180238106Sdes		/* start back at first number */   	/* like in alloc_init*/
181238106Sdes		alloc->next_id = (uint64_t)alloc->thread_num;
182238106Sdes		alloc->next_id <<= THRNUM_SHIFT; 	/* in steps for comp. */
183238106Sdes		alloc->next_id += 1;			/* portability. */
184238106Sdes		/* and generate new and safe id */
185238106Sdes		id = alloc->next_id++;
186238106Sdes	}
187238106Sdes	return id;
188238106Sdes}
189238106Sdes
190238106Sdesalloc_special_t*
191238106Sdesalloc_special_obtain(struct alloc_cache* alloc)
192238106Sdes{
193238106Sdes	alloc_special_t* p;
194238106Sdes	log_assert(alloc);
195238106Sdes	/* see if in local cache */
196238106Sdes	if(alloc->quar) {
197238106Sdes		p = alloc->quar;
198238106Sdes		alloc->quar = alloc_special_next(p);
199238106Sdes		alloc->num_quar--;
200238106Sdes		p->id = alloc_get_id(alloc);
201238106Sdes		return p;
202238106Sdes	}
203238106Sdes	/* see if in global cache */
204238106Sdes	if(alloc->super) {
205238106Sdes		/* could maybe grab alloc_max/2 entries in one go,
206238106Sdes		 * but really, isn't that just as fast as this code? */
207238106Sdes		lock_quick_lock(&alloc->super->lock);
208238106Sdes		if((p = alloc->super->quar)) {
209238106Sdes			alloc->super->quar = alloc_special_next(p);
210238106Sdes			alloc->super->num_quar--;
211238106Sdes		}
212238106Sdes		lock_quick_unlock(&alloc->super->lock);
213238106Sdes		if(p) {
214238106Sdes			p->id = alloc_get_id(alloc);
215238106Sdes			return p;
216238106Sdes		}
217238106Sdes	}
218238106Sdes	/* allocate new */
219238106Sdes	prealloc(alloc);
220238106Sdes	if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t)))) {
221238106Sdes		log_err("alloc_special_obtain: out of memory");
222238106Sdes		return NULL;
223238106Sdes	}
224238106Sdes	alloc_setup_special(p);
225238106Sdes	p->id = alloc_get_id(alloc);
226238106Sdes	return p;
227238106Sdes}
228238106Sdes
229238106Sdes/** push mem and some more items to the super */
230238106Sdesstatic void
231238106Sdespushintosuper(struct alloc_cache* alloc, alloc_special_t* mem)
232238106Sdes{
233238106Sdes	int i;
234238106Sdes	alloc_special_t *p = alloc->quar;
235238106Sdes	log_assert(p);
236238106Sdes	log_assert(alloc && alloc->super &&
237238106Sdes		alloc->num_quar >= ALLOC_SPECIAL_MAX);
238238106Sdes	/* push ALLOC_SPECIAL_MAX/2 after mem */
239238106Sdes	alloc_set_special_next(mem, alloc->quar);
240238106Sdes	for(i=1; i<ALLOC_SPECIAL_MAX/2; i++) {
241238106Sdes		p = alloc_special_next(p);
242238106Sdes	}
243238106Sdes	alloc->quar = alloc_special_next(p);
244238106Sdes	alloc->num_quar -= ALLOC_SPECIAL_MAX/2;
245238106Sdes
246238106Sdes	/* dump mem+list into the super quar list */
247238106Sdes	lock_quick_lock(&alloc->super->lock);
248238106Sdes	alloc_set_special_next(p, alloc->super->quar);
249238106Sdes	alloc->super->quar = mem;
250238106Sdes	alloc->super->num_quar += ALLOC_SPECIAL_MAX/2 + 1;
251238106Sdes	lock_quick_unlock(&alloc->super->lock);
252238106Sdes	/* so 1 lock per mem+alloc/2 deletes */
253238106Sdes}
254238106Sdes
255238106Sdesvoid
256238106Sdesalloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem)
257238106Sdes{
258238106Sdes	log_assert(alloc);
259238106Sdes	if(!mem)
260238106Sdes		return;
261238106Sdes	if(!alloc->super) {
262238106Sdes		lock_quick_lock(&alloc->lock); /* superalloc needs locking */
263238106Sdes	}
264238106Sdes
265238106Sdes	alloc_special_clean(mem);
266238106Sdes	if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) {
267238106Sdes		/* push it to the super structure */
268238106Sdes		pushintosuper(alloc, mem);
269238106Sdes		return;
270238106Sdes	}
271238106Sdes
272238106Sdes	alloc_set_special_next(mem, alloc->quar);
273238106Sdes	alloc->quar = mem;
274238106Sdes	alloc->num_quar++;
275238106Sdes	if(!alloc->super) {
276238106Sdes		lock_quick_unlock(&alloc->lock);
277238106Sdes	}
278238106Sdes}
279238106Sdes
280238106Sdesvoid
281238106Sdesalloc_stats(struct alloc_cache* alloc)
282238106Sdes{
283238106Sdes	log_info("%salloc: %d in cache, %d blocks.", alloc->super?"":"sup",
284238106Sdes		(int)alloc->num_quar, (int)alloc->num_reg_blocks);
285238106Sdes}
286238106Sdes
287238106Sdessize_t alloc_get_mem(struct alloc_cache* alloc)
288238106Sdes{
289238106Sdes	alloc_special_t* p;
290238106Sdes	size_t s = sizeof(*alloc);
291238106Sdes	if(!alloc->super) {
292238106Sdes		lock_quick_lock(&alloc->lock); /* superalloc needs locking */
293238106Sdes	}
294238106Sdes	s += sizeof(alloc_special_t) * alloc->num_quar;
295238106Sdes	for(p = alloc->quar; p; p = alloc_special_next(p)) {
296238106Sdes		s += lock_get_mem(&p->entry.lock);
297238106Sdes	}
298238106Sdes	s += alloc->num_reg_blocks * ALLOC_REG_SIZE;
299238106Sdes	if(!alloc->super) {
300238106Sdes		lock_quick_unlock(&alloc->lock);
301238106Sdes	}
302238106Sdes	return s;
303238106Sdes}
304238106Sdes
305238106Sdesstruct regional*
306238106Sdesalloc_reg_obtain(struct alloc_cache* alloc)
307238106Sdes{
308238106Sdes	if(alloc->num_reg_blocks > 0) {
309238106Sdes		struct regional* r = alloc->reg_list;
310238106Sdes		alloc->reg_list = (struct regional*)r->next;
311238106Sdes		r->next = NULL;
312238106Sdes		alloc->num_reg_blocks--;
313238106Sdes		return r;
314238106Sdes	}
315238106Sdes	return regional_create_custom(ALLOC_REG_SIZE);
316238106Sdes}
317238106Sdes
318238106Sdesvoid
319238106Sdesalloc_reg_release(struct alloc_cache* alloc, struct regional* r)
320238106Sdes{
321238106Sdes	if(alloc->num_reg_blocks >= alloc->max_reg_blocks) {
322238106Sdes		regional_destroy(r);
323238106Sdes		return;
324238106Sdes	}
325238106Sdes	if(!r) return;
326238106Sdes	regional_free_all(r);
327238106Sdes	log_assert(r->next == NULL);
328238106Sdes	r->next = (char*)alloc->reg_list;
329238106Sdes	alloc->reg_list = r;
330238106Sdes	alloc->num_reg_blocks++;
331238106Sdes}
332238106Sdes
333238106Sdesvoid
334238106Sdesalloc_set_id_cleanup(struct alloc_cache* alloc, void (*cleanup)(void*),
335238106Sdes        void* arg)
336238106Sdes{
337238106Sdes	alloc->cleanup = cleanup;
338238106Sdes	alloc->cleanup_arg = arg;
339238106Sdes}
340238106Sdes
341238106Sdes/** global debug value to keep track of total memory mallocs */
342238106Sdessize_t unbound_mem_alloc = 0;
343238106Sdes/** global debug value to keep track of total memory frees */
344238106Sdessize_t unbound_mem_freed = 0;
345238106Sdes#ifdef UNBOUND_ALLOC_STATS
346238106Sdes/** special value to know if the memory is being tracked */
347238106Sdesuint64_t mem_special = (uint64_t)0xfeed43327766abcdLL;
348238106Sdes#ifdef malloc
349238106Sdes#undef malloc
350238106Sdes#endif
351238106Sdes/** malloc with stats */
352238106Sdesvoid *unbound_stat_malloc(size_t size)
353238106Sdes{
354238106Sdes	void* res;
355238106Sdes	if(size == 0) size = 1;
356238106Sdes	res = malloc(size+16);
357238106Sdes	if(!res) return NULL;
358238106Sdes	unbound_mem_alloc += size;
359238106Sdes	log_info("stat %p=malloc(%u)", res+16, (unsigned)size);
360238106Sdes	memcpy(res, &size, sizeof(size));
361238106Sdes	memcpy(res+8, &mem_special, sizeof(mem_special));
362238106Sdes	return res+16;
363238106Sdes}
364238106Sdes#ifdef calloc
365238106Sdes#undef calloc
366238106Sdes#endif
367291767Sdes#ifndef INT_MAX
368291767Sdes#define INT_MAX (((int)-1)>>1)
369291767Sdes#endif
370238106Sdes/** calloc with stats */
371238106Sdesvoid *unbound_stat_calloc(size_t nmemb, size_t size)
372238106Sdes{
373291767Sdes	size_t s;
374291767Sdes	void* res;
375291767Sdes	if(nmemb != 0 && INT_MAX/nmemb < size)
376291767Sdes		return NULL; /* integer overflow check */
377291767Sdes	s = (nmemb*size==0)?(size_t)1:nmemb*size;
378291767Sdes	res = calloc(1, s+16);
379238106Sdes	if(!res) return NULL;
380238106Sdes	log_info("stat %p=calloc(%u, %u)", res+16, (unsigned)nmemb, (unsigned)size);
381238106Sdes	unbound_mem_alloc += s;
382238106Sdes	memcpy(res, &s, sizeof(s));
383238106Sdes	memcpy(res+8, &mem_special, sizeof(mem_special));
384238106Sdes	return res+16;
385238106Sdes}
386238106Sdes#ifdef free
387238106Sdes#undef free
388238106Sdes#endif
389238106Sdes/** free with stats */
390238106Sdesvoid unbound_stat_free(void *ptr)
391238106Sdes{
392238106Sdes	size_t s;
393238106Sdes	if(!ptr) return;
394238106Sdes	if(memcmp(ptr-8, &mem_special, sizeof(mem_special)) != 0) {
395238106Sdes		free(ptr);
396238106Sdes		return;
397238106Sdes	}
398238106Sdes	ptr-=16;
399238106Sdes	memcpy(&s, ptr, sizeof(s));
400238106Sdes	log_info("stat free(%p) size %u", ptr+16, (unsigned)s);
401238106Sdes	memset(ptr+8, 0, 8);
402238106Sdes	unbound_mem_freed += s;
403238106Sdes	free(ptr);
404238106Sdes}
405238106Sdes#ifdef realloc
406238106Sdes#undef realloc
407238106Sdes#endif
408238106Sdes/** realloc with stats */
409238106Sdesvoid *unbound_stat_realloc(void *ptr, size_t size)
410238106Sdes{
411238106Sdes	size_t cursz;
412238106Sdes	void* res;
413238106Sdes	if(!ptr) return unbound_stat_malloc(size);
414238106Sdes	if(memcmp(ptr-8, &mem_special, sizeof(mem_special)) != 0) {
415238106Sdes		return realloc(ptr, size);
416238106Sdes	}
417238106Sdes	if(size==0) {
418238106Sdes		unbound_stat_free(ptr);
419238106Sdes		return NULL;
420238106Sdes	}
421238106Sdes	ptr -= 16;
422238106Sdes	memcpy(&cursz, ptr, sizeof(cursz));
423238106Sdes	if(cursz == size) {
424238106Sdes		/* nothing changes */
425238106Sdes		return ptr;
426238106Sdes	}
427238106Sdes	res = malloc(size+16);
428238106Sdes	if(!res) return NULL;
429238106Sdes	unbound_mem_alloc += size;
430238106Sdes	unbound_mem_freed += cursz;
431238106Sdes	log_info("stat realloc(%p, %u) from %u", ptr+16, (unsigned)size, (unsigned)cursz);
432238106Sdes	if(cursz > size) {
433238106Sdes		memcpy(res+16, ptr+16, size);
434238106Sdes	} else if(size > cursz) {
435238106Sdes		memcpy(res+16, ptr+16, cursz);
436238106Sdes	}
437238106Sdes	memset(ptr+8, 0, 8);
438238106Sdes	free(ptr);
439238106Sdes	memcpy(res, &size, sizeof(size));
440238106Sdes	memcpy(res+8, &mem_special, sizeof(mem_special));
441238106Sdes	return res+16;
442238106Sdes}
443238106Sdes
444238106Sdes/** log to file where alloc was done */
445238106Sdesvoid *unbound_stat_malloc_log(size_t size, const char* file, int line,
446238106Sdes        const char* func)
447238106Sdes{
448238106Sdes	log_info("%s:%d %s malloc(%u)", file, line, func, (unsigned)size);
449238106Sdes	return unbound_stat_malloc(size);
450238106Sdes}
451238106Sdes
452238106Sdes/** log to file where alloc was done */
453238106Sdesvoid *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
454238106Sdes        int line, const char* func)
455238106Sdes{
456238106Sdes	log_info("%s:%d %s calloc(%u, %u)", file, line, func,
457238106Sdes		(unsigned) nmemb, (unsigned)size);
458238106Sdes	return unbound_stat_calloc(nmemb, size);
459238106Sdes}
460238106Sdes
461238106Sdes/** log to file where free was done */
462238106Sdesvoid unbound_stat_free_log(void *ptr, const char* file, int line,
463238106Sdes        const char* func)
464238106Sdes{
465238106Sdes	if(ptr && memcmp(ptr-8, &mem_special, sizeof(mem_special)) == 0) {
466238106Sdes		size_t s;
467238106Sdes		memcpy(&s, ptr-16, sizeof(s));
468238106Sdes		log_info("%s:%d %s free(%p) size %u",
469238106Sdes			file, line, func, ptr, (unsigned)s);
470238106Sdes	} else
471238106Sdes		log_info("%s:%d %s unmatched free(%p)", file, line, func, ptr);
472238106Sdes	unbound_stat_free(ptr);
473238106Sdes}
474238106Sdes
475238106Sdes/** log to file where alloc was done */
476238106Sdesvoid *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
477238106Sdes        int line, const char* func)
478238106Sdes{
479238106Sdes	log_info("%s:%d %s realloc(%p, %u)", file, line, func,
480238106Sdes		ptr, (unsigned)size);
481238106Sdes	return unbound_stat_realloc(ptr, size);
482238106Sdes}
483238106Sdes
484238106Sdes#endif /* UNBOUND_ALLOC_STATS */
485238106Sdes#ifdef UNBOUND_ALLOC_LITE
486238106Sdes#undef malloc
487238106Sdes#undef calloc
488238106Sdes#undef free
489238106Sdes#undef realloc
490238106Sdes/** length of prefix and suffix */
491238106Sdesstatic size_t lite_pad = 16;
492238106Sdes/** prefix value to check */
493238106Sdesstatic char* lite_pre = "checkfront123456";
494238106Sdes/** suffix value to check */
495238106Sdesstatic char* lite_post= "checkafter123456";
496238106Sdes
497238106Sdesvoid *unbound_stat_malloc_lite(size_t size, const char* file, int line,
498238106Sdes        const char* func)
499238106Sdes{
500238106Sdes	/*  [prefix .. len .. actual data .. suffix] */
501238106Sdes	void* res = malloc(size+lite_pad*2+sizeof(size_t));
502238106Sdes	if(!res) return NULL;
503238106Sdes	memmove(res, lite_pre, lite_pad);
504238106Sdes	memmove(res+lite_pad, &size, sizeof(size_t));
505238106Sdes	memset(res+lite_pad+sizeof(size_t), 0x1a, size); /* init the memory */
506238106Sdes	memmove(res+lite_pad+size+sizeof(size_t), lite_post, lite_pad);
507238106Sdes	return res+lite_pad+sizeof(size_t);
508238106Sdes}
509238106Sdes
510238106Sdesvoid *unbound_stat_calloc_lite(size_t nmemb, size_t size, const char* file,
511238106Sdes        int line, const char* func)
512238106Sdes{
513291767Sdes	size_t req;
514291767Sdes	void* res;
515291767Sdes	if(nmemb != 0 && INT_MAX/nmemb < size)
516291767Sdes		return NULL; /* integer overflow check */
517291767Sdes	req = nmemb * size;
518291767Sdes	res = malloc(req+lite_pad*2+sizeof(size_t));
519238106Sdes	if(!res) return NULL;
520238106Sdes	memmove(res, lite_pre, lite_pad);
521238106Sdes	memmove(res+lite_pad, &req, sizeof(size_t));
522238106Sdes	memset(res+lite_pad+sizeof(size_t), 0, req);
523238106Sdes	memmove(res+lite_pad+req+sizeof(size_t), lite_post, lite_pad);
524238106Sdes	return res+lite_pad+sizeof(size_t);
525238106Sdes}
526238106Sdes
527238106Sdesvoid unbound_stat_free_lite(void *ptr, const char* file, int line,
528238106Sdes        const char* func)
529238106Sdes{
530238106Sdes	void* real;
531238106Sdes	size_t orig = 0;
532238106Sdes	if(!ptr) return;
533238106Sdes	real = ptr-lite_pad-sizeof(size_t);
534238106Sdes	if(memcmp(real, lite_pre, lite_pad) != 0) {
535238106Sdes		log_err("free(): prefix failed %s:%d %s", file, line, func);
536238106Sdes		log_hex("prefix here", real, lite_pad);
537238106Sdes		log_hex("  should be", lite_pre, lite_pad);
538238106Sdes		fatal_exit("alloc assertion failed");
539238106Sdes	}
540238106Sdes	memmove(&orig, real+lite_pad, sizeof(size_t));
541238106Sdes	if(memcmp(real+lite_pad+orig+sizeof(size_t), lite_post, lite_pad)!=0){
542238106Sdes		log_err("free(): suffix failed %s:%d %s", file, line, func);
543238106Sdes		log_err("alloc size is %d", (int)orig);
544238106Sdes		log_hex("suffix here", real+lite_pad+orig+sizeof(size_t),
545238106Sdes			lite_pad);
546238106Sdes		log_hex("  should be", lite_post, lite_pad);
547238106Sdes		fatal_exit("alloc assertion failed");
548238106Sdes	}
549238106Sdes	memset(real, 0xdd, orig+lite_pad*2+sizeof(size_t)); /* mark it */
550238106Sdes	free(real);
551238106Sdes}
552238106Sdes
553238106Sdesvoid *unbound_stat_realloc_lite(void *ptr, size_t size, const char* file,
554238106Sdes        int line, const char* func)
555238106Sdes{
556238106Sdes	/* always free and realloc (no growing) */
557238106Sdes	void* real, *newa;
558238106Sdes	size_t orig = 0;
559238106Sdes	if(!ptr) {
560238106Sdes		/* like malloc() */
561238106Sdes		return unbound_stat_malloc_lite(size, file, line, func);
562238106Sdes	}
563238106Sdes	if(!size) {
564238106Sdes		/* like free() */
565238106Sdes		unbound_stat_free_lite(ptr, file, line, func);
566238106Sdes		return NULL;
567238106Sdes	}
568238106Sdes	/* change allocation size and copy */
569238106Sdes	real = ptr-lite_pad-sizeof(size_t);
570238106Sdes	if(memcmp(real, lite_pre, lite_pad) != 0) {
571238106Sdes		log_err("realloc(): prefix failed %s:%d %s", file, line, func);
572238106Sdes		log_hex("prefix here", real, lite_pad);
573238106Sdes		log_hex("  should be", lite_pre, lite_pad);
574238106Sdes		fatal_exit("alloc assertion failed");
575238106Sdes	}
576238106Sdes	memmove(&orig, real+lite_pad, sizeof(size_t));
577238106Sdes	if(memcmp(real+lite_pad+orig+sizeof(size_t), lite_post, lite_pad)!=0){
578238106Sdes		log_err("realloc(): suffix failed %s:%d %s", file, line, func);
579238106Sdes		log_err("alloc size is %d", (int)orig);
580238106Sdes		log_hex("suffix here", real+lite_pad+orig+sizeof(size_t),
581238106Sdes			lite_pad);
582238106Sdes		log_hex("  should be", lite_post, lite_pad);
583238106Sdes		fatal_exit("alloc assertion failed");
584238106Sdes	}
585238106Sdes	/* new alloc and copy over */
586238106Sdes	newa = unbound_stat_malloc_lite(size, file, line, func);
587238106Sdes	if(!newa)
588238106Sdes		return NULL;
589238106Sdes	if(orig < size)
590238106Sdes		memmove(newa, ptr, orig);
591238106Sdes	else	memmove(newa, ptr, size);
592238106Sdes	memset(real, 0xdd, orig+lite_pad*2+sizeof(size_t)); /* mark it */
593238106Sdes	free(real);
594238106Sdes	return newa;
595238106Sdes}
596238106Sdes
597238106Sdeschar* unbound_strdup_lite(const char* s, const char* file, int line,
598238106Sdes        const char* func)
599238106Sdes{
600238106Sdes	/* this routine is made to make sure strdup() uses the malloc_lite */
601238106Sdes	size_t l = strlen(s)+1;
602238106Sdes	char* n = (char*)unbound_stat_malloc_lite(l, file, line, func);
603238106Sdes	if(!n) return NULL;
604238106Sdes	memmove(n, s, l);
605238106Sdes	return n;
606238106Sdes}
607238106Sdes
608238106Sdeschar* unbound_lite_wrapstr(char* s)
609238106Sdes{
610238106Sdes	char* n = unbound_strdup_lite(s, __FILE__, __LINE__, __func__);
611238106Sdes	free(s);
612238106Sdes	return n;
613238106Sdes}
614238106Sdes
615269257Sdes#undef sldns_pkt2wire
616269257Sdessldns_status unbound_lite_pkt2wire(uint8_t **dest, const sldns_pkt *p,
617238106Sdes	size_t *size)
618238106Sdes{
619238106Sdes	uint8_t* md = NULL;
620238106Sdes	size_t ms = 0;
621269257Sdes	sldns_status s = sldns_pkt2wire(&md, p, &ms);
622238106Sdes	if(md) {
623238106Sdes		*dest = unbound_stat_malloc_lite(ms, __FILE__, __LINE__,
624238106Sdes			__func__);
625238106Sdes		*size = ms;
626238106Sdes		if(!*dest) { free(md); return LDNS_STATUS_MEM_ERR; }
627238106Sdes		memcpy(*dest, md, ms);
628238106Sdes		free(md);
629238106Sdes	} else {
630238106Sdes		*dest = NULL;
631238106Sdes		*size = 0;
632238106Sdes	}
633238106Sdes	return s;
634238106Sdes}
635238106Sdes
636238106Sdes#undef i2d_DSA_SIG
637238106Sdesint unbound_lite_i2d_DSA_SIG(DSA_SIG* dsasig, unsigned char** sig)
638238106Sdes{
639238106Sdes	unsigned char* n = NULL;
640238106Sdes	int r= i2d_DSA_SIG(dsasig, &n);
641238106Sdes	if(n) {
642238106Sdes		*sig = unbound_stat_malloc_lite((size_t)r, __FILE__, __LINE__,
643238106Sdes			__func__);
644238106Sdes		if(!*sig) return -1;
645238106Sdes		memcpy(*sig, n, (size_t)r);
646238106Sdes		free(n);
647238106Sdes		return r;
648238106Sdes	}
649238106Sdes	*sig = NULL;
650238106Sdes	return r;
651238106Sdes}
652238106Sdes
653238106Sdes#endif /* UNBOUND_ALLOC_LITE */
654