1933707f3Ssthen /* 2933707f3Ssthen * util/alloc.c - memory allocation service. 3933707f3Ssthen * 4933707f3Ssthen * Copyright (c) 2007, NLnet Labs. All rights reserved. 5933707f3Ssthen * 6933707f3Ssthen * This software is open source. 7933707f3Ssthen * 8933707f3Ssthen * Redistribution and use in source and binary forms, with or without 9933707f3Ssthen * modification, are permitted provided that the following conditions 10933707f3Ssthen * are met: 11933707f3Ssthen * 12933707f3Ssthen * Redistributions of source code must retain the above copyright notice, 13933707f3Ssthen * this list of conditions and the following disclaimer. 14933707f3Ssthen * 15933707f3Ssthen * Redistributions in binary form must reproduce the above copyright notice, 16933707f3Ssthen * this list of conditions and the following disclaimer in the documentation 17933707f3Ssthen * and/or other materials provided with the distribution. 18933707f3Ssthen * 19933707f3Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may 20933707f3Ssthen * be used to endorse or promote products derived from this software without 21933707f3Ssthen * specific prior written permission. 22933707f3Ssthen * 23933707f3Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 245d76a658Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 255d76a658Ssthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 265d76a658Ssthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 275d76a658Ssthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 285d76a658Ssthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 295d76a658Ssthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 305d76a658Ssthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 315d76a658Ssthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 325d76a658Ssthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 335d76a658Ssthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34933707f3Ssthen */ 35933707f3Ssthen 36933707f3Ssthen /** 37933707f3Ssthen * \file 38933707f3Ssthen * 39933707f3Ssthen * This file contains memory allocation functions. 40933707f3Ssthen */ 41933707f3Ssthen 42933707f3Ssthen #include "config.h" 43933707f3Ssthen #include "util/alloc.h" 44933707f3Ssthen #include "util/regional.h" 45933707f3Ssthen #include "util/data/packed_rrset.h" 46933707f3Ssthen #include "util/fptr_wlist.h" 47933707f3Ssthen 48933707f3Ssthen /** custom size of cached regional blocks */ 49933707f3Ssthen #define ALLOC_REG_SIZE 16384 50933707f3Ssthen /** number of bits for ID part of uint64, rest for number of threads. */ 51933707f3Ssthen #define THRNUM_SHIFT 48 /* for 65k threads, 2^48 rrsets per thr. */ 52933707f3Ssthen 53933707f3Ssthen /** setup new special type */ 54933707f3Ssthen static void 5577079be7Ssthen alloc_setup_special(alloc_special_type* t) 56933707f3Ssthen { 57933707f3Ssthen memset(t, 0, sizeof(*t)); 58933707f3Ssthen lock_rw_init(&t->entry.lock); 59933707f3Ssthen t->entry.key = t; 60933707f3Ssthen } 61933707f3Ssthen 62933707f3Ssthen /** prealloc some entries in the cache. To minimize contention. 63933707f3Ssthen * Result is 1 lock per alloc_max newly created entries. 64933707f3Ssthen * @param alloc: the structure to fill up. 65933707f3Ssthen */ 66933707f3Ssthen static void 6777079be7Ssthen prealloc_setup(struct alloc_cache* alloc) 68933707f3Ssthen { 6977079be7Ssthen alloc_special_type* p; 70933707f3Ssthen int i; 71933707f3Ssthen for(i=0; i<ALLOC_SPECIAL_MAX; i++) { 7277079be7Ssthen if(!(p = (alloc_special_type*)malloc( 7377079be7Ssthen sizeof(alloc_special_type)))) { 74933707f3Ssthen log_err("prealloc: out of memory"); 75933707f3Ssthen return; 76933707f3Ssthen } 77933707f3Ssthen alloc_setup_special(p); 78933707f3Ssthen alloc_set_special_next(p, alloc->quar); 79933707f3Ssthen alloc->quar = p; 80933707f3Ssthen alloc->num_quar++; 81933707f3Ssthen } 82933707f3Ssthen } 83933707f3Ssthen 84933707f3Ssthen /** prealloc region blocks */ 85933707f3Ssthen static void 86933707f3Ssthen prealloc_blocks(struct alloc_cache* alloc, size_t num) 87933707f3Ssthen { 88933707f3Ssthen size_t i; 89933707f3Ssthen struct regional* r; 90933707f3Ssthen for(i=0; i<num; i++) { 91933707f3Ssthen r = regional_create_custom(ALLOC_REG_SIZE); 92933707f3Ssthen if(!r) { 93933707f3Ssthen log_err("prealloc blocks: out of memory"); 94933707f3Ssthen return; 95933707f3Ssthen } 96933707f3Ssthen r->next = (char*)alloc->reg_list; 97933707f3Ssthen alloc->reg_list = r; 98933707f3Ssthen alloc->num_reg_blocks ++; 99933707f3Ssthen } 100933707f3Ssthen } 101933707f3Ssthen 102933707f3Ssthen void 103933707f3Ssthen alloc_init(struct alloc_cache* alloc, struct alloc_cache* super, 104933707f3Ssthen int thread_num) 105933707f3Ssthen { 106933707f3Ssthen memset(alloc, 0, sizeof(*alloc)); 107933707f3Ssthen alloc->super = super; 108933707f3Ssthen alloc->thread_num = thread_num; 109933707f3Ssthen alloc->next_id = (uint64_t)thread_num; /* in steps, so that type */ 110933707f3Ssthen alloc->next_id <<= THRNUM_SHIFT; /* of *_id is used. */ 111933707f3Ssthen alloc->last_id = 1; /* so no 64bit constants, */ 112933707f3Ssthen alloc->last_id <<= THRNUM_SHIFT; /* or implicit 'int' ops. */ 113933707f3Ssthen alloc->last_id -= 1; /* for compiler portability. */ 114933707f3Ssthen alloc->last_id |= alloc->next_id; 115933707f3Ssthen alloc->next_id += 1; /* because id=0 is special. */ 116933707f3Ssthen alloc->max_reg_blocks = 100; 117933707f3Ssthen alloc->num_reg_blocks = 0; 118933707f3Ssthen alloc->reg_list = NULL; 119933707f3Ssthen alloc->cleanup = NULL; 120933707f3Ssthen alloc->cleanup_arg = NULL; 121933707f3Ssthen if(alloc->super) 122933707f3Ssthen prealloc_blocks(alloc, alloc->max_reg_blocks); 123933707f3Ssthen if(!alloc->super) { 124933707f3Ssthen lock_quick_init(&alloc->lock); 125933707f3Ssthen lock_protect(&alloc->lock, alloc, sizeof(*alloc)); 126933707f3Ssthen } 127933707f3Ssthen } 128933707f3Ssthen 12920237c55Ssthen /** free the special list */ 13020237c55Ssthen static void 13120237c55Ssthen alloc_clear_special_list(struct alloc_cache* alloc) 13220237c55Ssthen { 13320237c55Ssthen alloc_special_type* p, *np; 13420237c55Ssthen /* free */ 13520237c55Ssthen p = alloc->quar; 13620237c55Ssthen while(p) { 13720237c55Ssthen np = alloc_special_next(p); 13820237c55Ssthen /* deinit special type */ 13920237c55Ssthen lock_rw_destroy(&p->entry.lock); 14020237c55Ssthen free(p); 14120237c55Ssthen p = np; 14220237c55Ssthen } 14320237c55Ssthen } 14420237c55Ssthen 14520237c55Ssthen void 14620237c55Ssthen alloc_clear_special(struct alloc_cache* alloc) 14720237c55Ssthen { 14820237c55Ssthen if(!alloc->super) { 14920237c55Ssthen lock_quick_lock(&alloc->lock); 15020237c55Ssthen } 15120237c55Ssthen alloc_clear_special_list(alloc); 15220237c55Ssthen alloc->quar = 0; 15320237c55Ssthen alloc->num_quar = 0; 15420237c55Ssthen if(!alloc->super) { 15520237c55Ssthen lock_quick_unlock(&alloc->lock); 15620237c55Ssthen } 15720237c55Ssthen } 15820237c55Ssthen 159933707f3Ssthen void 160933707f3Ssthen alloc_clear(struct alloc_cache* alloc) 161933707f3Ssthen { 16220237c55Ssthen alloc_special_type* p; 163933707f3Ssthen struct regional* r, *nr; 164933707f3Ssthen if(!alloc) 165933707f3Ssthen return; 166933707f3Ssthen if(!alloc->super) { 167933707f3Ssthen lock_quick_destroy(&alloc->lock); 168933707f3Ssthen } 169933707f3Ssthen if(alloc->super && alloc->quar) { 170933707f3Ssthen /* push entire list into super */ 171933707f3Ssthen p = alloc->quar; 172933707f3Ssthen while(alloc_special_next(p)) /* find last */ 173933707f3Ssthen p = alloc_special_next(p); 174933707f3Ssthen lock_quick_lock(&alloc->super->lock); 175933707f3Ssthen alloc_set_special_next(p, alloc->super->quar); 176933707f3Ssthen alloc->super->quar = alloc->quar; 177933707f3Ssthen alloc->super->num_quar += alloc->num_quar; 178933707f3Ssthen lock_quick_unlock(&alloc->super->lock); 179933707f3Ssthen } else { 18020237c55Ssthen alloc_clear_special_list(alloc); 181933707f3Ssthen } 182933707f3Ssthen alloc->quar = 0; 183933707f3Ssthen alloc->num_quar = 0; 184933707f3Ssthen r = alloc->reg_list; 185933707f3Ssthen while(r) { 186933707f3Ssthen nr = (struct regional*)r->next; 187933707f3Ssthen free(r); 188933707f3Ssthen r = nr; 189933707f3Ssthen } 190933707f3Ssthen alloc->reg_list = NULL; 191933707f3Ssthen alloc->num_reg_blocks = 0; 192933707f3Ssthen } 193933707f3Ssthen 194933707f3Ssthen uint64_t 195933707f3Ssthen alloc_get_id(struct alloc_cache* alloc) 196933707f3Ssthen { 197933707f3Ssthen uint64_t id = alloc->next_id++; 198933707f3Ssthen if(id == alloc->last_id) { 199933707f3Ssthen log_warn("rrset alloc: out of 64bit ids. Clearing cache."); 200933707f3Ssthen fptr_ok(fptr_whitelist_alloc_cleanup(alloc->cleanup)); 201933707f3Ssthen (*alloc->cleanup)(alloc->cleanup_arg); 202933707f3Ssthen 203933707f3Ssthen /* start back at first number */ /* like in alloc_init*/ 204933707f3Ssthen alloc->next_id = (uint64_t)alloc->thread_num; 205933707f3Ssthen alloc->next_id <<= THRNUM_SHIFT; /* in steps for comp. */ 206933707f3Ssthen alloc->next_id += 1; /* portability. */ 207933707f3Ssthen /* and generate new and safe id */ 208933707f3Ssthen id = alloc->next_id++; 209933707f3Ssthen } 210933707f3Ssthen return id; 211933707f3Ssthen } 212933707f3Ssthen 21377079be7Ssthen alloc_special_type* 214933707f3Ssthen alloc_special_obtain(struct alloc_cache* alloc) 215933707f3Ssthen { 21677079be7Ssthen alloc_special_type* p; 217933707f3Ssthen log_assert(alloc); 218933707f3Ssthen /* see if in local cache */ 219933707f3Ssthen if(alloc->quar) { 220933707f3Ssthen p = alloc->quar; 221933707f3Ssthen alloc->quar = alloc_special_next(p); 222933707f3Ssthen alloc->num_quar--; 223933707f3Ssthen p->id = alloc_get_id(alloc); 224933707f3Ssthen return p; 225933707f3Ssthen } 226933707f3Ssthen /* see if in global cache */ 227933707f3Ssthen if(alloc->super) { 228933707f3Ssthen /* could maybe grab alloc_max/2 entries in one go, 229933707f3Ssthen * but really, isn't that just as fast as this code? */ 230933707f3Ssthen lock_quick_lock(&alloc->super->lock); 231933707f3Ssthen if((p = alloc->super->quar)) { 232933707f3Ssthen alloc->super->quar = alloc_special_next(p); 233933707f3Ssthen alloc->super->num_quar--; 234933707f3Ssthen } 235933707f3Ssthen lock_quick_unlock(&alloc->super->lock); 236933707f3Ssthen if(p) { 237933707f3Ssthen p->id = alloc_get_id(alloc); 238933707f3Ssthen return p; 239933707f3Ssthen } 240933707f3Ssthen } 241933707f3Ssthen /* allocate new */ 24277079be7Ssthen prealloc_setup(alloc); 24377079be7Ssthen if(!(p = (alloc_special_type*)malloc(sizeof(alloc_special_type)))) { 244933707f3Ssthen log_err("alloc_special_obtain: out of memory"); 245933707f3Ssthen return NULL; 246933707f3Ssthen } 247933707f3Ssthen alloc_setup_special(p); 248933707f3Ssthen p->id = alloc_get_id(alloc); 249933707f3Ssthen return p; 250933707f3Ssthen } 251933707f3Ssthen 252933707f3Ssthen /** push mem and some more items to the super */ 253933707f3Ssthen static void 25477079be7Ssthen pushintosuper(struct alloc_cache* alloc, alloc_special_type* mem) 255933707f3Ssthen { 256933707f3Ssthen int i; 25777079be7Ssthen alloc_special_type *p = alloc->quar; 258933707f3Ssthen log_assert(p); 259933707f3Ssthen log_assert(alloc && alloc->super && 260933707f3Ssthen alloc->num_quar >= ALLOC_SPECIAL_MAX); 261933707f3Ssthen /* push ALLOC_SPECIAL_MAX/2 after mem */ 262933707f3Ssthen alloc_set_special_next(mem, alloc->quar); 263933707f3Ssthen for(i=1; i<ALLOC_SPECIAL_MAX/2; i++) { 264933707f3Ssthen p = alloc_special_next(p); 265933707f3Ssthen } 266933707f3Ssthen alloc->quar = alloc_special_next(p); 267933707f3Ssthen alloc->num_quar -= ALLOC_SPECIAL_MAX/2; 268933707f3Ssthen 269933707f3Ssthen /* dump mem+list into the super quar list */ 270933707f3Ssthen lock_quick_lock(&alloc->super->lock); 271933707f3Ssthen alloc_set_special_next(p, alloc->super->quar); 272933707f3Ssthen alloc->super->quar = mem; 273933707f3Ssthen alloc->super->num_quar += ALLOC_SPECIAL_MAX/2 + 1; 274933707f3Ssthen lock_quick_unlock(&alloc->super->lock); 275933707f3Ssthen /* so 1 lock per mem+alloc/2 deletes */ 276933707f3Ssthen } 277933707f3Ssthen 278933707f3Ssthen void 27977079be7Ssthen alloc_special_release(struct alloc_cache* alloc, alloc_special_type* mem) 280933707f3Ssthen { 281933707f3Ssthen log_assert(alloc); 282933707f3Ssthen if(!mem) 283933707f3Ssthen return; 284933707f3Ssthen if(!alloc->super) { 285933707f3Ssthen lock_quick_lock(&alloc->lock); /* superalloc needs locking */ 286933707f3Ssthen } 287933707f3Ssthen 288933707f3Ssthen alloc_special_clean(mem); 289933707f3Ssthen if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) { 290933707f3Ssthen /* push it to the super structure */ 291933707f3Ssthen pushintosuper(alloc, mem); 292933707f3Ssthen return; 293933707f3Ssthen } 294933707f3Ssthen 295933707f3Ssthen alloc_set_special_next(mem, alloc->quar); 296933707f3Ssthen alloc->quar = mem; 297933707f3Ssthen alloc->num_quar++; 298933707f3Ssthen if(!alloc->super) { 299933707f3Ssthen lock_quick_unlock(&alloc->lock); 300933707f3Ssthen } 301933707f3Ssthen } 302933707f3Ssthen 303933707f3Ssthen void 304933707f3Ssthen alloc_stats(struct alloc_cache* alloc) 305933707f3Ssthen { 306933707f3Ssthen log_info("%salloc: %d in cache, %d blocks.", alloc->super?"":"sup", 307933707f3Ssthen (int)alloc->num_quar, (int)alloc->num_reg_blocks); 308933707f3Ssthen } 309933707f3Ssthen 310933707f3Ssthen size_t alloc_get_mem(struct alloc_cache* alloc) 311933707f3Ssthen { 31277079be7Ssthen alloc_special_type* p; 313933707f3Ssthen size_t s = sizeof(*alloc); 314933707f3Ssthen if(!alloc->super) { 315933707f3Ssthen lock_quick_lock(&alloc->lock); /* superalloc needs locking */ 316933707f3Ssthen } 31777079be7Ssthen s += sizeof(alloc_special_type) * alloc->num_quar; 318933707f3Ssthen for(p = alloc->quar; p; p = alloc_special_next(p)) { 319933707f3Ssthen s += lock_get_mem(&p->entry.lock); 320933707f3Ssthen } 321933707f3Ssthen s += alloc->num_reg_blocks * ALLOC_REG_SIZE; 322933707f3Ssthen if(!alloc->super) { 323933707f3Ssthen lock_quick_unlock(&alloc->lock); 324933707f3Ssthen } 325933707f3Ssthen return s; 326933707f3Ssthen } 327933707f3Ssthen 328933707f3Ssthen struct regional* 329933707f3Ssthen alloc_reg_obtain(struct alloc_cache* alloc) 330933707f3Ssthen { 331933707f3Ssthen if(alloc->num_reg_blocks > 0) { 332933707f3Ssthen struct regional* r = alloc->reg_list; 333933707f3Ssthen alloc->reg_list = (struct regional*)r->next; 334933707f3Ssthen r->next = NULL; 335933707f3Ssthen alloc->num_reg_blocks--; 336933707f3Ssthen return r; 337933707f3Ssthen } 338933707f3Ssthen return regional_create_custom(ALLOC_REG_SIZE); 339933707f3Ssthen } 340933707f3Ssthen 341933707f3Ssthen void 342933707f3Ssthen alloc_reg_release(struct alloc_cache* alloc, struct regional* r) 343933707f3Ssthen { 344933707f3Ssthen if(alloc->num_reg_blocks >= alloc->max_reg_blocks) { 345933707f3Ssthen regional_destroy(r); 346933707f3Ssthen return; 347933707f3Ssthen } 348933707f3Ssthen if(!r) return; 349933707f3Ssthen regional_free_all(r); 350933707f3Ssthen log_assert(r->next == NULL); 351933707f3Ssthen r->next = (char*)alloc->reg_list; 352933707f3Ssthen alloc->reg_list = r; 353933707f3Ssthen alloc->num_reg_blocks++; 354933707f3Ssthen } 355933707f3Ssthen 356933707f3Ssthen void 357933707f3Ssthen alloc_set_id_cleanup(struct alloc_cache* alloc, void (*cleanup)(void*), 358933707f3Ssthen void* arg) 359933707f3Ssthen { 360933707f3Ssthen alloc->cleanup = cleanup; 361933707f3Ssthen alloc->cleanup_arg = arg; 362933707f3Ssthen } 363933707f3Ssthen 364933707f3Ssthen /** global debug value to keep track of total memory mallocs */ 365933707f3Ssthen size_t unbound_mem_alloc = 0; 366933707f3Ssthen /** global debug value to keep track of total memory frees */ 367933707f3Ssthen size_t unbound_mem_freed = 0; 368933707f3Ssthen #ifdef UNBOUND_ALLOC_STATS 369933707f3Ssthen /** special value to know if the memory is being tracked */ 370933707f3Ssthen uint64_t mem_special = (uint64_t)0xfeed43327766abcdLL; 371933707f3Ssthen #ifdef malloc 372933707f3Ssthen #undef malloc 373933707f3Ssthen #endif 374933707f3Ssthen /** malloc with stats */ 375933707f3Ssthen void *unbound_stat_malloc(size_t size) 376933707f3Ssthen { 377933707f3Ssthen void* res; 378933707f3Ssthen if(size == 0) size = 1; 379550cf4a9Ssthen log_assert(size <= SIZE_MAX-16); 380933707f3Ssthen res = malloc(size+16); 381933707f3Ssthen if(!res) return NULL; 382933707f3Ssthen unbound_mem_alloc += size; 383933707f3Ssthen log_info("stat %p=malloc(%u)", res+16, (unsigned)size); 384933707f3Ssthen memcpy(res, &size, sizeof(size)); 385933707f3Ssthen memcpy(res+8, &mem_special, sizeof(mem_special)); 386933707f3Ssthen return res+16; 387933707f3Ssthen } 388933707f3Ssthen #ifdef calloc 389933707f3Ssthen #undef calloc 390933707f3Ssthen #endif 391fdfb4ba6Ssthen #ifndef INT_MAX 392fdfb4ba6Ssthen #define INT_MAX (((int)-1)>>1) 393fdfb4ba6Ssthen #endif 394933707f3Ssthen /** calloc with stats */ 395933707f3Ssthen void *unbound_stat_calloc(size_t nmemb, size_t size) 396933707f3Ssthen { 397fdfb4ba6Ssthen size_t s; 398fdfb4ba6Ssthen void* res; 399fdfb4ba6Ssthen if(nmemb != 0 && INT_MAX/nmemb < size) 400fdfb4ba6Ssthen return NULL; /* integer overflow check */ 401fdfb4ba6Ssthen s = (nmemb*size==0)?(size_t)1:nmemb*size; 402550cf4a9Ssthen log_assert(s <= SIZE_MAX-16); 403fdfb4ba6Ssthen res = calloc(1, s+16); 404933707f3Ssthen if(!res) return NULL; 405933707f3Ssthen log_info("stat %p=calloc(%u, %u)", res+16, (unsigned)nmemb, (unsigned)size); 406933707f3Ssthen unbound_mem_alloc += s; 407933707f3Ssthen memcpy(res, &s, sizeof(s)); 408933707f3Ssthen memcpy(res+8, &mem_special, sizeof(mem_special)); 409933707f3Ssthen return res+16; 410933707f3Ssthen } 411933707f3Ssthen #ifdef free 412933707f3Ssthen #undef free 413933707f3Ssthen #endif 414933707f3Ssthen /** free with stats */ 415933707f3Ssthen void unbound_stat_free(void *ptr) 416933707f3Ssthen { 417933707f3Ssthen size_t s; 418933707f3Ssthen if(!ptr) return; 419933707f3Ssthen if(memcmp(ptr-8, &mem_special, sizeof(mem_special)) != 0) { 420933707f3Ssthen free(ptr); 421933707f3Ssthen return; 422933707f3Ssthen } 423933707f3Ssthen ptr-=16; 424933707f3Ssthen memcpy(&s, ptr, sizeof(s)); 425933707f3Ssthen log_info("stat free(%p) size %u", ptr+16, (unsigned)s); 426933707f3Ssthen memset(ptr+8, 0, 8); 427933707f3Ssthen unbound_mem_freed += s; 428933707f3Ssthen free(ptr); 429933707f3Ssthen } 430933707f3Ssthen #ifdef realloc 431933707f3Ssthen #undef realloc 432933707f3Ssthen #endif 433933707f3Ssthen /** realloc with stats */ 434933707f3Ssthen void *unbound_stat_realloc(void *ptr, size_t size) 435933707f3Ssthen { 436933707f3Ssthen size_t cursz; 437933707f3Ssthen void* res; 438933707f3Ssthen if(!ptr) return unbound_stat_malloc(size); 439933707f3Ssthen if(memcmp(ptr-8, &mem_special, sizeof(mem_special)) != 0) { 440933707f3Ssthen return realloc(ptr, size); 441933707f3Ssthen } 442933707f3Ssthen if(size==0) { 443933707f3Ssthen unbound_stat_free(ptr); 444933707f3Ssthen return NULL; 445933707f3Ssthen } 446933707f3Ssthen ptr -= 16; 447933707f3Ssthen memcpy(&cursz, ptr, sizeof(cursz)); 448933707f3Ssthen if(cursz == size) { 449933707f3Ssthen /* nothing changes */ 450933707f3Ssthen return ptr; 451933707f3Ssthen } 452550cf4a9Ssthen log_assert(size <= SIZE_MAX-16); 453933707f3Ssthen res = malloc(size+16); 454933707f3Ssthen if(!res) return NULL; 455933707f3Ssthen unbound_mem_alloc += size; 456933707f3Ssthen unbound_mem_freed += cursz; 457933707f3Ssthen log_info("stat realloc(%p, %u) from %u", ptr+16, (unsigned)size, (unsigned)cursz); 458933707f3Ssthen if(cursz > size) { 459933707f3Ssthen memcpy(res+16, ptr+16, size); 460933707f3Ssthen } else if(size > cursz) { 461933707f3Ssthen memcpy(res+16, ptr+16, cursz); 462933707f3Ssthen } 463933707f3Ssthen memset(ptr+8, 0, 8); 464933707f3Ssthen free(ptr); 465933707f3Ssthen memcpy(res, &size, sizeof(size)); 466933707f3Ssthen memcpy(res+8, &mem_special, sizeof(mem_special)); 467933707f3Ssthen return res+16; 468933707f3Ssthen } 469*98bc733bSsthen /** strdup with stats */ 470*98bc733bSsthen char *unbound_stat_strdup(const char* s) 471*98bc733bSsthen { 472*98bc733bSsthen size_t len; 473*98bc733bSsthen char* res; 474*98bc733bSsthen if(!s) return NULL; 475*98bc733bSsthen len = strlen(s); 476*98bc733bSsthen res = unbound_stat_malloc(len+1); 477*98bc733bSsthen if(!res) return NULL; 478*98bc733bSsthen memmove(res, s, len+1); 479*98bc733bSsthen return res; 480*98bc733bSsthen } 481933707f3Ssthen 482933707f3Ssthen /** log to file where alloc was done */ 483933707f3Ssthen void *unbound_stat_malloc_log(size_t size, const char* file, int line, 484933707f3Ssthen const char* func) 485933707f3Ssthen { 486933707f3Ssthen log_info("%s:%d %s malloc(%u)", file, line, func, (unsigned)size); 487933707f3Ssthen return unbound_stat_malloc(size); 488933707f3Ssthen } 489933707f3Ssthen 490933707f3Ssthen /** log to file where alloc was done */ 491933707f3Ssthen void *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file, 492933707f3Ssthen int line, const char* func) 493933707f3Ssthen { 494933707f3Ssthen log_info("%s:%d %s calloc(%u, %u)", file, line, func, 495933707f3Ssthen (unsigned) nmemb, (unsigned)size); 496933707f3Ssthen return unbound_stat_calloc(nmemb, size); 497933707f3Ssthen } 498933707f3Ssthen 499933707f3Ssthen /** log to file where free was done */ 500933707f3Ssthen void unbound_stat_free_log(void *ptr, const char* file, int line, 501933707f3Ssthen const char* func) 502933707f3Ssthen { 503933707f3Ssthen if(ptr && memcmp(ptr-8, &mem_special, sizeof(mem_special)) == 0) { 504933707f3Ssthen size_t s; 505933707f3Ssthen memcpy(&s, ptr-16, sizeof(s)); 506933707f3Ssthen log_info("%s:%d %s free(%p) size %u", 507933707f3Ssthen file, line, func, ptr, (unsigned)s); 508933707f3Ssthen } else 509933707f3Ssthen log_info("%s:%d %s unmatched free(%p)", file, line, func, ptr); 510933707f3Ssthen unbound_stat_free(ptr); 511933707f3Ssthen } 512933707f3Ssthen 513933707f3Ssthen /** log to file where alloc was done */ 514933707f3Ssthen void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file, 515933707f3Ssthen int line, const char* func) 516933707f3Ssthen { 517933707f3Ssthen log_info("%s:%d %s realloc(%p, %u)", file, line, func, 518933707f3Ssthen ptr, (unsigned)size); 519933707f3Ssthen return unbound_stat_realloc(ptr, size); 520933707f3Ssthen } 521933707f3Ssthen 522*98bc733bSsthen /** log to file where strdup was done */ 523*98bc733bSsthen char *unbound_stat_strdup_log(const char *s, const char* file, int line, 524*98bc733bSsthen const char* func) 525*98bc733bSsthen { 526*98bc733bSsthen log_info("%s:%d %s strdup size %u", file, line, func, 527*98bc733bSsthen (s?(unsigned)strlen(s)+1:0)); 528*98bc733bSsthen return unbound_stat_strdup(s); 529*98bc733bSsthen } 530*98bc733bSsthen 531933707f3Ssthen #endif /* UNBOUND_ALLOC_STATS */ 532933707f3Ssthen #ifdef UNBOUND_ALLOC_LITE 533933707f3Ssthen #undef malloc 534933707f3Ssthen #undef calloc 535933707f3Ssthen #undef free 536933707f3Ssthen #undef realloc 537933707f3Ssthen /** length of prefix and suffix */ 538933707f3Ssthen static size_t lite_pad = 16; 539933707f3Ssthen /** prefix value to check */ 540933707f3Ssthen static char* lite_pre = "checkfront123456"; 541933707f3Ssthen /** suffix value to check */ 542933707f3Ssthen static char* lite_post= "checkafter123456"; 543933707f3Ssthen 544933707f3Ssthen void *unbound_stat_malloc_lite(size_t size, const char* file, int line, 545933707f3Ssthen const char* func) 546933707f3Ssthen { 547933707f3Ssthen /* [prefix .. len .. actual data .. suffix] */ 548550cf4a9Ssthen void* res; 549550cf4a9Ssthen log_assert(size <= SIZE_MAX-(lite_pad*2+sizeof(size_t))); 550550cf4a9Ssthen res = malloc(size+lite_pad*2+sizeof(size_t)); 551933707f3Ssthen if(!res) return NULL; 552933707f3Ssthen memmove(res, lite_pre, lite_pad); 553933707f3Ssthen memmove(res+lite_pad, &size, sizeof(size_t)); 554933707f3Ssthen memset(res+lite_pad+sizeof(size_t), 0x1a, size); /* init the memory */ 555933707f3Ssthen memmove(res+lite_pad+size+sizeof(size_t), lite_post, lite_pad); 556933707f3Ssthen return res+lite_pad+sizeof(size_t); 557933707f3Ssthen } 558933707f3Ssthen 559933707f3Ssthen void *unbound_stat_calloc_lite(size_t nmemb, size_t size, const char* file, 560933707f3Ssthen int line, const char* func) 561933707f3Ssthen { 562fdfb4ba6Ssthen size_t req; 563fdfb4ba6Ssthen void* res; 564fdfb4ba6Ssthen if(nmemb != 0 && INT_MAX/nmemb < size) 565fdfb4ba6Ssthen return NULL; /* integer overflow check */ 566fdfb4ba6Ssthen req = nmemb * size; 567550cf4a9Ssthen log_assert(req <= SIZE_MAX-(lite_pad*2+sizeof(size_t))); 568fdfb4ba6Ssthen res = malloc(req+lite_pad*2+sizeof(size_t)); 569933707f3Ssthen if(!res) return NULL; 570933707f3Ssthen memmove(res, lite_pre, lite_pad); 571933707f3Ssthen memmove(res+lite_pad, &req, sizeof(size_t)); 572933707f3Ssthen memset(res+lite_pad+sizeof(size_t), 0, req); 573933707f3Ssthen memmove(res+lite_pad+req+sizeof(size_t), lite_post, lite_pad); 574933707f3Ssthen return res+lite_pad+sizeof(size_t); 575933707f3Ssthen } 576933707f3Ssthen 577933707f3Ssthen void unbound_stat_free_lite(void *ptr, const char* file, int line, 578933707f3Ssthen const char* func) 579933707f3Ssthen { 580933707f3Ssthen void* real; 581933707f3Ssthen size_t orig = 0; 582933707f3Ssthen if(!ptr) return; 583933707f3Ssthen real = ptr-lite_pad-sizeof(size_t); 584933707f3Ssthen if(memcmp(real, lite_pre, lite_pad) != 0) { 585933707f3Ssthen log_err("free(): prefix failed %s:%d %s", file, line, func); 586933707f3Ssthen log_hex("prefix here", real, lite_pad); 587933707f3Ssthen log_hex(" should be", lite_pre, lite_pad); 588933707f3Ssthen fatal_exit("alloc assertion failed"); 589933707f3Ssthen } 590933707f3Ssthen memmove(&orig, real+lite_pad, sizeof(size_t)); 591933707f3Ssthen if(memcmp(real+lite_pad+orig+sizeof(size_t), lite_post, lite_pad)!=0){ 592933707f3Ssthen log_err("free(): suffix failed %s:%d %s", file, line, func); 593933707f3Ssthen log_err("alloc size is %d", (int)orig); 594933707f3Ssthen log_hex("suffix here", real+lite_pad+orig+sizeof(size_t), 595933707f3Ssthen lite_pad); 596933707f3Ssthen log_hex(" should be", lite_post, lite_pad); 597933707f3Ssthen fatal_exit("alloc assertion failed"); 598933707f3Ssthen } 599933707f3Ssthen memset(real, 0xdd, orig+lite_pad*2+sizeof(size_t)); /* mark it */ 600933707f3Ssthen free(real); 601933707f3Ssthen } 602933707f3Ssthen 603933707f3Ssthen void *unbound_stat_realloc_lite(void *ptr, size_t size, const char* file, 604933707f3Ssthen int line, const char* func) 605933707f3Ssthen { 606933707f3Ssthen /* always free and realloc (no growing) */ 607933707f3Ssthen void* real, *newa; 608933707f3Ssthen size_t orig = 0; 609933707f3Ssthen if(!ptr) { 610933707f3Ssthen /* like malloc() */ 611933707f3Ssthen return unbound_stat_malloc_lite(size, file, line, func); 612933707f3Ssthen } 613933707f3Ssthen if(!size) { 614933707f3Ssthen /* like free() */ 615933707f3Ssthen unbound_stat_free_lite(ptr, file, line, func); 616933707f3Ssthen return NULL; 617933707f3Ssthen } 618933707f3Ssthen /* change allocation size and copy */ 619933707f3Ssthen real = ptr-lite_pad-sizeof(size_t); 620933707f3Ssthen if(memcmp(real, lite_pre, lite_pad) != 0) { 621933707f3Ssthen log_err("realloc(): prefix failed %s:%d %s", file, line, func); 622933707f3Ssthen log_hex("prefix here", real, lite_pad); 623933707f3Ssthen log_hex(" should be", lite_pre, lite_pad); 624933707f3Ssthen fatal_exit("alloc assertion failed"); 625933707f3Ssthen } 626933707f3Ssthen memmove(&orig, real+lite_pad, sizeof(size_t)); 627933707f3Ssthen if(memcmp(real+lite_pad+orig+sizeof(size_t), lite_post, lite_pad)!=0){ 628933707f3Ssthen log_err("realloc(): suffix failed %s:%d %s", file, line, func); 629933707f3Ssthen log_err("alloc size is %d", (int)orig); 630933707f3Ssthen log_hex("suffix here", real+lite_pad+orig+sizeof(size_t), 631933707f3Ssthen lite_pad); 632933707f3Ssthen log_hex(" should be", lite_post, lite_pad); 633933707f3Ssthen fatal_exit("alloc assertion failed"); 634933707f3Ssthen } 635933707f3Ssthen /* new alloc and copy over */ 636933707f3Ssthen newa = unbound_stat_malloc_lite(size, file, line, func); 637933707f3Ssthen if(!newa) 638933707f3Ssthen return NULL; 639933707f3Ssthen if(orig < size) 640933707f3Ssthen memmove(newa, ptr, orig); 641933707f3Ssthen else memmove(newa, ptr, size); 642933707f3Ssthen memset(real, 0xdd, orig+lite_pad*2+sizeof(size_t)); /* mark it */ 643933707f3Ssthen free(real); 644933707f3Ssthen return newa; 645933707f3Ssthen } 646933707f3Ssthen 647933707f3Ssthen char* unbound_strdup_lite(const char* s, const char* file, int line, 648933707f3Ssthen const char* func) 649933707f3Ssthen { 650933707f3Ssthen /* this routine is made to make sure strdup() uses the malloc_lite */ 651933707f3Ssthen size_t l = strlen(s)+1; 652933707f3Ssthen char* n = (char*)unbound_stat_malloc_lite(l, file, line, func); 653933707f3Ssthen if(!n) return NULL; 654933707f3Ssthen memmove(n, s, l); 655933707f3Ssthen return n; 656933707f3Ssthen } 657933707f3Ssthen 658933707f3Ssthen char* unbound_lite_wrapstr(char* s) 659933707f3Ssthen { 660933707f3Ssthen char* n = unbound_strdup_lite(s, __FILE__, __LINE__, __func__); 661933707f3Ssthen free(s); 662933707f3Ssthen return n; 663933707f3Ssthen } 664933707f3Ssthen 6655d76a658Ssthen #undef sldns_pkt2wire 6665d76a658Ssthen sldns_status unbound_lite_pkt2wire(uint8_t **dest, const sldns_pkt *p, 667933707f3Ssthen size_t *size) 668933707f3Ssthen { 669933707f3Ssthen uint8_t* md = NULL; 670933707f3Ssthen size_t ms = 0; 6715d76a658Ssthen sldns_status s = sldns_pkt2wire(&md, p, &ms); 672933707f3Ssthen if(md) { 673933707f3Ssthen *dest = unbound_stat_malloc_lite(ms, __FILE__, __LINE__, 674933707f3Ssthen __func__); 675933707f3Ssthen *size = ms; 676933707f3Ssthen if(!*dest) { free(md); return LDNS_STATUS_MEM_ERR; } 677933707f3Ssthen memcpy(*dest, md, ms); 678933707f3Ssthen free(md); 679933707f3Ssthen } else { 680933707f3Ssthen *dest = NULL; 681933707f3Ssthen *size = 0; 682933707f3Ssthen } 683933707f3Ssthen return s; 684933707f3Ssthen } 685933707f3Ssthen 686933707f3Ssthen #undef i2d_DSA_SIG 687933707f3Ssthen int unbound_lite_i2d_DSA_SIG(DSA_SIG* dsasig, unsigned char** sig) 688933707f3Ssthen { 689933707f3Ssthen unsigned char* n = NULL; 690933707f3Ssthen int r= i2d_DSA_SIG(dsasig, &n); 691933707f3Ssthen if(n) { 692933707f3Ssthen *sig = unbound_stat_malloc_lite((size_t)r, __FILE__, __LINE__, 693933707f3Ssthen __func__); 694933707f3Ssthen if(!*sig) return -1; 695933707f3Ssthen memcpy(*sig, n, (size_t)r); 696933707f3Ssthen free(n); 697933707f3Ssthen return r; 698933707f3Ssthen } 699933707f3Ssthen *sig = NULL; 700933707f3Ssthen return r; 701933707f3Ssthen } 702933707f3Ssthen 703933707f3Ssthen #endif /* UNBOUND_ALLOC_LITE */ 704