1 /* $NetBSD: mem.c,v 1.14 2023/06/26 22:03:01 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16 /*! \file */
17
18 #include <errno.h>
19 #include <inttypes.h>
20 #include <limits.h>
21 #include <stdbool.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include <isc/bind9.h>
27 #include <isc/hash.h>
28 #include <isc/lib.h>
29 #include <isc/magic.h>
30 #include <isc/mem.h>
31 #include <isc/mutex.h>
32 #include <isc/once.h>
33 #include <isc/print.h>
34 #include <isc/refcount.h>
35 #include <isc/strerr.h>
36 #include <isc/string.h>
37 #include <isc/util.h>
38
39 #ifdef HAVE_LIBXML2
40 #include <libxml/xmlwriter.h>
41 #define ISC_XMLCHAR (const xmlChar *)
42 #endif /* HAVE_LIBXML2 */
43
44 #ifdef HAVE_JSON_C
45 #include <json_object.h>
46 #endif /* HAVE_JSON_C */
47
48 #include "mem_p.h"
49
50 #define MCTXLOCK(m) LOCK(&m->lock)
51 #define MCTXUNLOCK(m) UNLOCK(&m->lock)
52
53 #ifndef ISC_MEM_DEBUGGING
54 #define ISC_MEM_DEBUGGING 0
55 #endif /* ifndef ISC_MEM_DEBUGGING */
56 LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
57 LIBISC_EXTERNAL_DATA unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT;
58
59 /*
60 * Constants.
61 */
62
63 #define DEF_MAX_SIZE 1100
64 #define DEF_MEM_TARGET 4096
65 #define ALIGNMENT_SIZE \
66 8U /*%< must be a power of 2, also update lib/dns/rbt.c */
67 #define NUM_BASIC_BLOCKS 64 /*%< must be > 1 */
68 #define TABLE_INCREMENT 1024
69 #define DEBUG_TABLE_COUNT 512U
70
71 /*
72 * Types.
73 */
74 typedef struct isc__mem isc__mem_t;
75 typedef struct isc__mempool isc__mempool_t;
76
77 #if ISC_MEM_TRACKLINES
78 typedef struct debuglink debuglink_t;
79 struct debuglink {
80 ISC_LINK(debuglink_t) link;
81 const void *ptr;
82 size_t size;
83 const char *file;
84 unsigned int line;
85 };
86
87 typedef ISC_LIST(debuglink_t) debuglist_t;
88
89 #define FLARG_PASS , file, line
90 #define FLARG , const char *file, unsigned int line
91 #else /* if ISC_MEM_TRACKLINES */
92 #define FLARG_PASS
93 #define FLARG
94 #endif /* if ISC_MEM_TRACKLINES */
95
96 typedef struct element element;
97 struct element {
98 element *next;
99 };
100
101 typedef struct {
102 /*!
103 * This structure must be ALIGNMENT_SIZE bytes.
104 */
105 union {
106 size_t size;
107 isc__mem_t *ctx;
108 char bytes[ALIGNMENT_SIZE];
109 } u;
110 } size_info;
111
112 struct stats {
113 unsigned long gets;
114 unsigned long totalgets;
115 unsigned long blocks;
116 unsigned long freefrags;
117 };
118
119 #define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C')
120 #define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)
121
122 /* List of all active memory contexts. */
123
124 static ISC_LIST(isc__mem_t) contexts;
125
126 static isc_once_t init_once = ISC_ONCE_INIT;
127 static isc_once_t shut_once = ISC_ONCE_INIT;
128 static isc_mutex_t contextslock;
129
130 /*%
131 * Total size of lost memory due to a bug of external library.
132 * Locked by the global lock.
133 */
134 static uint64_t totallost;
135
136 /*%
137 * Memory allocation and free function definitions.
138 * isc__memalloc_t must deal with memory allocation failure
139 * and must never return NULL.
140 */
141 typedef void *(*isc__memalloc_t)(size_t);
142 typedef void (*isc__memfree_t)(void *);
143
144 struct isc__mem {
145 isc_mem_t common;
146 unsigned int flags;
147 isc_mutex_t lock;
148 isc__memalloc_t memalloc;
149 isc__memfree_t memfree;
150 size_t max_size;
151 bool checkfree;
152 struct stats *stats;
153 isc_refcount_t references;
154 char name[16];
155 void *tag;
156 size_t total;
157 size_t inuse;
158 size_t maxinuse;
159 size_t malloced;
160 size_t maxmalloced;
161 size_t hi_water;
162 size_t lo_water;
163 bool hi_called;
164 bool is_overmem;
165 isc_mem_water_t water;
166 void *water_arg;
167 ISC_LIST(isc__mempool_t) pools;
168 unsigned int poolcnt;
169
170 /* ISC_MEMFLAG_INTERNAL */
171 size_t mem_target;
172 element **freelists;
173 element *basic_blocks;
174 unsigned char **basic_table;
175 unsigned int basic_table_count;
176 unsigned int basic_table_size;
177 unsigned char *lowest;
178 unsigned char *highest;
179
180 #if ISC_MEM_TRACKLINES
181 debuglist_t *debuglist;
182 size_t debuglistcnt;
183 #endif /* if ISC_MEM_TRACKLINES */
184
185 ISC_LINK(isc__mem_t) link;
186 };
187
188 #define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p')
189 #define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
190
191 struct isc__mempool {
192 /* always unlocked */
193 isc_mempool_t common; /*%< common header of mempool's */
194 isc__mem_t *mctx; /*%< our memory context */
195 ISC_LINK(isc__mempool_t) link; /*%< next pool in this mem context */
196 element *items; /*%< low water item list */
197 size_t size; /*%< size of each item on this pool */
198 unsigned int maxalloc; /*%< max number of items allowed */
199 unsigned int allocated; /*%< # of items currently given out */
200 unsigned int freecount; /*%< # of items on reserved list */
201 unsigned int freemax; /*%< # of items allowed on free list */
202 unsigned int fillcount; /*%< # of items to fetch on each fill */
203 /*%< Stats only. */
204 unsigned int gets; /*%< # of requests to this pool */
205 /*%< Debugging only. */
206 #if ISC_MEMPOOL_NAMES
207 char name[16]; /*%< printed name in stats reports */
208 #endif /* if ISC_MEMPOOL_NAMES */
209 };
210
211 /*
212 * Private Inline-able.
213 */
214
215 #if !ISC_MEM_TRACKLINES
216 #define ADD_TRACE(a, b, c, d, e)
217 #define DELETE_TRACE(a, b, c, d, e)
218 #define ISC_MEMFUNC_SCOPE
219 #else /* if !ISC_MEM_TRACKLINES */
220 #define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD)
221 #define ADD_TRACE(a, b, c, d, e) \
222 do { \
223 if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \
224 b != NULL)) \
225 add_trace_entry(a, b, c, d, e); \
226 } while (0)
227 #define DELETE_TRACE(a, b, c, d, e) \
228 do { \
229 if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \
230 b != NULL)) \
231 delete_trace_entry(a, b, c, d, e); \
232 } while (0)
233
234 static void
235 print_active(isc__mem_t *ctx, FILE *out);
236
237 #endif /* ISC_MEM_TRACKLINES */
238
239 static void *
240 isc___mem_get(isc_mem_t *ctx, size_t size FLARG);
241 static void
242 isc___mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG);
243 static void
244 isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG);
245 static void *
246 isc___mem_allocate(isc_mem_t *ctx, size_t size FLARG);
247 static void *
248 isc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG);
249 static char *
250 isc___mem_strdup(isc_mem_t *mctx, const char *s FLARG);
251 static char *
252 isc___mem_strndup(isc_mem_t *mctx0, const char *s, size_t size FLARG);
253 static void
254 isc___mem_free(isc_mem_t *ctx, void *ptr FLARG);
255
256 static isc_memmethods_t memmethods = {
257 isc___mem_get, isc___mem_put, isc___mem_putanddetach,
258 isc___mem_allocate, isc___mem_reallocate, isc___mem_strdup,
259 isc___mem_strndup, isc___mem_free,
260 };
261
262 #if ISC_MEM_TRACKLINES
263 /*!
264 * mctx must be locked.
265 */
266 static void
add_trace_entry(isc__mem_t * mctx,const void * ptr,size_t size FLARG)267 add_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) {
268 debuglink_t *dl;
269 uint32_t hash;
270 uint32_t idx;
271
272 if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
273 fprintf(stderr, "add %p size %zu file %s line %u mctx %p\n",
274 ptr, size, file, line, mctx);
275 }
276
277 if (mctx->debuglist == NULL) {
278 return;
279 }
280
281 #ifdef __COVERITY__
282 /*
283 * Use simple conversion from pointer to hash to avoid
284 * tainting 'ptr' due to byte swap in isc_hash_function.
285 */
286 hash = (uintptr_t)ptr >> 3;
287 #else
288 hash = isc_hash_function(&ptr, sizeof(ptr), true);
289 #endif
290 idx = hash % DEBUG_TABLE_COUNT;
291
292 dl = malloc(sizeof(debuglink_t));
293 INSIST(dl != NULL);
294 mctx->malloced += sizeof(debuglink_t);
295 if (mctx->malloced > mctx->maxmalloced) {
296 mctx->maxmalloced = mctx->malloced;
297 }
298
299 ISC_LINK_INIT(dl, link);
300 dl->ptr = ptr;
301 dl->size = size;
302 dl->file = file;
303 dl->line = line;
304
305 ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link);
306 mctx->debuglistcnt++;
307 }
308
309 static void
delete_trace_entry(isc__mem_t * mctx,const void * ptr,size_t size,const char * file,unsigned int line)310 delete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size,
311 const char *file, unsigned int line) {
312 debuglink_t *dl;
313 uint32_t hash;
314 uint32_t idx;
315
316 if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
317 fprintf(stderr, "del %p size %zu file %s line %u mctx %p\n",
318 ptr, size, file, line, mctx);
319 }
320
321 if (mctx->debuglist == NULL) {
322 return;
323 }
324
325 #ifdef __COVERITY__
326 /*
327 * Use simple conversion from pointer to hash to avoid
328 * tainting 'ptr' due to byte swap in isc_hash_function.
329 */
330 hash = (uintptr_t)ptr >> 3;
331 #else
332 hash = isc_hash_function(&ptr, sizeof(ptr), true);
333 #endif
334 idx = hash % DEBUG_TABLE_COUNT;
335
336 dl = ISC_LIST_HEAD(mctx->debuglist[idx]);
337 while (ISC_LIKELY(dl != NULL)) {
338 if (ISC_UNLIKELY(dl->ptr == ptr)) {
339 ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link);
340 mctx->malloced -= sizeof(*dl);
341 free(dl);
342 return;
343 }
344 dl = ISC_LIST_NEXT(dl, link);
345 }
346
347 /*
348 * If we get here, we didn't find the item on the list. We're
349 * screwed.
350 */
351 UNREACHABLE();
352 }
353 #endif /* ISC_MEM_TRACKLINES */
354
355 static size_t
rmsize(size_t size)356 rmsize(size_t size) {
357 /*
358 * round down to ALIGNMENT_SIZE
359 */
360 return (size & (~(ALIGNMENT_SIZE - 1)));
361 }
362
363 static size_t
quantize(size_t size)364 quantize(size_t size) {
365 /*!
366 * Round up the result in order to get a size big
367 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
368 * byte boundaries.
369 */
370
371 if (size == 0U) {
372 return (ALIGNMENT_SIZE);
373 }
374 return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
375 }
376
377 static void
more_basic_blocks(isc__mem_t * ctx)378 more_basic_blocks(isc__mem_t *ctx) {
379 void *tmp;
380 unsigned char *curr, *next;
381 unsigned char *first, *last;
382 unsigned char **table;
383 unsigned int table_size;
384
385 /* Require: we hold the context lock. */
386
387 INSIST(ctx->basic_table_count <= ctx->basic_table_size);
388 if (ctx->basic_table_count == ctx->basic_table_size) {
389 table_size = ctx->basic_table_size + TABLE_INCREMENT;
390 table = (ctx->memalloc)(table_size * sizeof(unsigned char *));
391 ctx->malloced += table_size * sizeof(unsigned char *);
392 if (ctx->malloced > ctx->maxmalloced) {
393 ctx->maxmalloced = ctx->malloced;
394 }
395 if (ctx->basic_table_size != 0) {
396 memmove(table, ctx->basic_table,
397 ctx->basic_table_size *
398 sizeof(unsigned char *));
399 (ctx->memfree)(ctx->basic_table);
400 ctx->malloced -= ctx->basic_table_size *
401 sizeof(unsigned char *);
402 }
403 ctx->basic_table = table;
404 ctx->basic_table_size = table_size;
405 }
406
407 tmp = (ctx->memalloc)(NUM_BASIC_BLOCKS * ctx->mem_target);
408 ctx->total += NUM_BASIC_BLOCKS * ctx->mem_target;
409 ctx->basic_table[ctx->basic_table_count] = tmp;
410 ctx->basic_table_count++;
411 ctx->malloced += NUM_BASIC_BLOCKS * ctx->mem_target;
412 if (ctx->malloced > ctx->maxmalloced) {
413 ctx->maxmalloced = ctx->malloced;
414 }
415
416 curr = tmp;
417 next = curr + ctx->mem_target;
418 for (int i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
419 ((element *)curr)->next = (element *)next;
420 curr = next;
421 next += ctx->mem_target;
422 }
423 /*
424 * curr is now pointing at the last block in the
425 * array.
426 */
427 ((element *)curr)->next = NULL;
428 first = tmp;
429 last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
430 if (first < ctx->lowest || ctx->lowest == NULL) {
431 ctx->lowest = first;
432 }
433 if (last > ctx->highest) {
434 ctx->highest = last;
435 }
436 ctx->basic_blocks = tmp;
437 }
438
439 static void
more_frags(isc__mem_t * ctx,size_t new_size)440 more_frags(isc__mem_t *ctx, size_t new_size) {
441 int frags;
442 size_t total_size;
443 void *tmp;
444 unsigned char *curr, *next;
445
446 /*!
447 * Try to get more fragments by chopping up a basic block.
448 */
449
450 if (ctx->basic_blocks == NULL) {
451 more_basic_blocks(ctx);
452 }
453 INSIST(ctx->basic_blocks != NULL);
454
455 total_size = ctx->mem_target;
456 tmp = ctx->basic_blocks;
457 ctx->basic_blocks = ctx->basic_blocks->next;
458 frags = (int)(total_size / new_size);
459 ctx->stats[new_size].blocks++;
460 ctx->stats[new_size].freefrags += frags;
461 /*
462 * Set up a linked-list of blocks of size
463 * "new_size".
464 */
465 curr = tmp;
466 next = curr + new_size;
467 total_size -= new_size;
468 for (int i = 0; i < (frags - 1); i++) {
469 ((element *)curr)->next = (element *)next;
470 curr = next;
471 next += new_size;
472 total_size -= new_size;
473 }
474 /*
475 * Add the remaining fragment of the basic block to a free list.
476 */
477 total_size = rmsize(total_size);
478 if (total_size > 0U) {
479 ((element *)next)->next = ctx->freelists[total_size];
480 ctx->freelists[total_size] = (element *)next;
481 ctx->stats[total_size].freefrags++;
482 }
483 /*
484 * curr is now pointing at the last block in the
485 * array.
486 */
487 ((element *)curr)->next = NULL;
488 ctx->freelists[new_size] = tmp;
489 }
490
491 static void *
mem_getunlocked(isc__mem_t * ctx,size_t size)492 mem_getunlocked(isc__mem_t *ctx, size_t size) {
493 size_t new_size = quantize(size);
494 void *ret;
495
496 if (new_size >= ctx->max_size) {
497 /*
498 * memget() was called on something beyond our upper limit.
499 */
500 ret = (ctx->memalloc)(size);
501 ctx->total += size;
502 ctx->inuse += size;
503 ctx->stats[ctx->max_size].gets++;
504 ctx->stats[ctx->max_size].totalgets++;
505 ctx->malloced += size;
506 if (ctx->malloced > ctx->maxmalloced) {
507 ctx->maxmalloced = ctx->malloced;
508 }
509 /*
510 * If we don't set new_size to size, then the
511 * ISC_MEMFLAG_FILL code might write over bytes we don't
512 * own.
513 */
514 new_size = size;
515 goto done;
516 }
517 /*
518 * If there are no blocks in the free list for this size, get a chunk
519 * of memory and then break it up into "new_size"-sized blocks, adding
520 * them to the free list.
521 */
522 if (ctx->freelists[new_size] == NULL) {
523 more_frags(ctx, new_size);
524 }
525 INSIST(ctx->freelists[new_size] != NULL);
526
527 /*
528 * The free list uses the "rounded-up" size "new_size".
529 */
530
531 ret = ctx->freelists[new_size];
532 ctx->freelists[new_size] = ctx->freelists[new_size]->next;
533
534 /*
535 * The stats[] uses the _actual_ "size" requested by the
536 * caller, with the caveat (in the code above) that "size" >= the
537 * max. size (max_size) ends up getting recorded as a call to
538 * max_size.
539 */
540 ctx->stats[size].gets++;
541 ctx->stats[size].totalgets++;
542 ctx->stats[new_size].freefrags--;
543 ctx->inuse += new_size;
544
545 done:
546 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0) &&
547 ISC_LIKELY(ret != NULL))
548 {
549 memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
550 }
551
552 return (ret);
553 }
554
555 #if ISC_MEM_CHECKOVERRUN
556 static void
check_overrun(void * mem,size_t size,size_t new_size)557 check_overrun(void *mem, size_t size, size_t new_size) {
558 unsigned char *cp;
559
560 cp = (unsigned char *)mem;
561 cp += size;
562 while (size < new_size) {
563 INSIST(*cp == 0xbe);
564 cp++;
565 size++;
566 }
567 }
568 #endif /* if ISC_MEM_CHECKOVERRUN */
569
570 /* coverity[+free : arg-1] */
571 static void
mem_putunlocked(isc__mem_t * ctx,void * mem,size_t size)572 mem_putunlocked(isc__mem_t *ctx, void *mem, size_t size) {
573 size_t new_size = quantize(size);
574
575 if (new_size >= ctx->max_size) {
576 /*
577 * memput() called on something beyond our upper limit.
578 */
579 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) {
580 memset(mem, 0xde, size); /* Mnemonic for "dead". */
581 }
582
583 (ctx->memfree)(mem);
584 INSIST(ctx->stats[ctx->max_size].gets != 0U);
585 ctx->stats[ctx->max_size].gets--;
586 INSIST(size <= ctx->inuse);
587 ctx->inuse -= size;
588 ctx->malloced -= size;
589 return;
590 }
591
592 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) {
593 #if ISC_MEM_CHECKOVERRUN
594 check_overrun(mem, size, new_size);
595 #endif /* if ISC_MEM_CHECKOVERRUN */
596 memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
597 }
598
599 /*
600 * The free list uses the "rounded-up" size "new_size".
601 */
602 ((element *)mem)->next = ctx->freelists[new_size];
603 ctx->freelists[new_size] = (element *)mem;
604
605 /*
606 * The stats[] uses the _actual_ "size" requested by the
607 * caller, with the caveat (in the code above) that "size" >= the
608 * max. size (max_size) ends up getting recorded as a call to
609 * max_size.
610 */
611 INSIST(ctx->stats[size].gets != 0U);
612 ctx->stats[size].gets--;
613 ctx->stats[new_size].freefrags++;
614 ctx->inuse -= new_size;
615 }
616
617 /*!
618 * Perform a malloc, doing memory filling and overrun detection as necessary.
619 */
620 static void *
mem_get(isc__mem_t * ctx,size_t size)621 mem_get(isc__mem_t *ctx, size_t size) {
622 char *ret;
623
624 #if ISC_MEM_CHECKOVERRUN
625 size += 1;
626 #endif /* if ISC_MEM_CHECKOVERRUN */
627 ret = (ctx->memalloc)(size);
628
629 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) {
630 if (ISC_LIKELY(ret != NULL)) {
631 memset(ret, 0xbe, size); /* Mnemonic for "beef". */
632 }
633 }
634 #if ISC_MEM_CHECKOVERRUN
635 else
636 {
637 if (ISC_LIKELY(ret != NULL)) {
638 ret[size - 1] = 0xbe;
639 }
640 }
641 #endif /* if ISC_MEM_CHECKOVERRUN */
642
643 return (ret);
644 }
645
646 /*!
647 * Perform a free, doing memory filling and overrun detection as necessary.
648 */
649 /* coverity[+free : arg-1] */
650 static void
mem_put(isc__mem_t * ctx,void * mem,size_t size)651 mem_put(isc__mem_t *ctx, void *mem, size_t size) {
652 #if ISC_MEM_CHECKOVERRUN
653 INSIST(((unsigned char *)mem)[size] == 0xbe);
654 size += 1;
655 #endif /* if ISC_MEM_CHECKOVERRUN */
656 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) {
657 memset(mem, 0xde, size); /* Mnemonic for "dead". */
658 }
659 (ctx->memfree)(mem);
660 }
661
662 /*!
663 * Update internal counters after a memory get.
664 */
665 static void
mem_getstats(isc__mem_t * ctx,size_t size)666 mem_getstats(isc__mem_t *ctx, size_t size) {
667 ctx->total += size;
668 ctx->inuse += size;
669
670 if (size > ctx->max_size) {
671 ctx->stats[ctx->max_size].gets++;
672 ctx->stats[ctx->max_size].totalgets++;
673 } else {
674 ctx->stats[size].gets++;
675 ctx->stats[size].totalgets++;
676 }
677
678 #if ISC_MEM_CHECKOVERRUN
679 size += 1;
680 #endif /* if ISC_MEM_CHECKOVERRUN */
681 ctx->malloced += size;
682 if (ctx->malloced > ctx->maxmalloced) {
683 ctx->maxmalloced = ctx->malloced;
684 }
685 }
686
687 /*!
688 * Update internal counters after a memory put.
689 */
690 static void
mem_putstats(isc__mem_t * ctx,void * ptr,size_t size)691 mem_putstats(isc__mem_t *ctx, void *ptr, size_t size) {
692 UNUSED(ptr);
693
694 INSIST(ctx->inuse >= size);
695 ctx->inuse -= size;
696
697 if (size > ctx->max_size) {
698 INSIST(ctx->stats[ctx->max_size].gets > 0U);
699 ctx->stats[ctx->max_size].gets--;
700 } else {
701 INSIST(ctx->stats[size].gets > 0U);
702 ctx->stats[size].gets--;
703 }
704 #if ISC_MEM_CHECKOVERRUN
705 size += 1;
706 #endif /* if ISC_MEM_CHECKOVERRUN */
707 ctx->malloced -= size;
708 }
709
710 /*
711 * Private.
712 */
713
714 static void *
default_memalloc(size_t size)715 default_memalloc(size_t size) {
716 void *ptr;
717
718 ptr = malloc(size);
719
720 /*
721 * If the space cannot be allocated, a null pointer is returned. If the
722 * size of the space requested is zero, the behavior is
723 * implementation-defined: either a null pointer is returned, or the
724 * behavior is as if the size were some nonzero value, except that the
725 * returned pointer shall not be used to access an object.
726 * [ISO9899 § 7.22.3]
727 *
728 * [ISO9899]
729 * ISO/IEC WG 9899:2011: Programming languages - C.
730 * International Organization for Standardization, Geneva,
731 * Switzerland.
732 * http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf
733 */
734
735 if (ptr == NULL && size != 0) {
736 char strbuf[ISC_STRERRORSIZE];
737 strerror_r(errno, strbuf, sizeof(strbuf));
738 isc_error_fatal(__FILE__, __LINE__, "malloc failed: %s",
739 strbuf);
740 }
741
742 return (ptr);
743 }
744
745 static void
default_memfree(void * ptr)746 default_memfree(void *ptr) {
747 free(ptr);
748 }
749
750 static void
mem_initialize(void)751 mem_initialize(void) {
752 isc_mutex_init(&contextslock);
753 ISC_LIST_INIT(contexts);
754 totallost = 0;
755 }
756
757 void
isc__mem_initialize(void)758 isc__mem_initialize(void) {
759 RUNTIME_CHECK(isc_once_do(&init_once, mem_initialize) == ISC_R_SUCCESS);
760 }
761
762 static void
mem_shutdown(void)763 mem_shutdown(void) {
764 isc__mem_checkdestroyed();
765
766 isc_mutex_destroy(&contextslock);
767 }
768
769 void
isc__mem_shutdown(void)770 isc__mem_shutdown(void) {
771 RUNTIME_CHECK(isc_once_do(&shut_once, mem_shutdown) == ISC_R_SUCCESS);
772 }
773
774 static void
mem_create(isc_mem_t ** ctxp,unsigned int flags)775 mem_create(isc_mem_t **ctxp, unsigned int flags) {
776 REQUIRE(ctxp != NULL && *ctxp == NULL);
777 #if __SANITIZE_ADDRESS__
778 REQUIRE((flags & ISC_MEMFLAG_INTERNAL) == 0);
779 #endif
780
781 isc__mem_t *ctx;
782
783 isc_enable_constructors();
784
785 STATIC_ASSERT((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0,
786 "wrong alignment size");
787
788 ctx = (default_memalloc)(sizeof(*ctx));
789
790 isc_mutex_init(&ctx->lock);
791
792 ctx->max_size = DEF_MAX_SIZE;
793 ctx->flags = flags;
794 isc_refcount_init(&ctx->references, 1);
795 memset(ctx->name, 0, sizeof(ctx->name));
796 ctx->tag = NULL;
797 ctx->total = 0;
798 ctx->inuse = 0;
799 ctx->maxinuse = 0;
800 ctx->malloced = sizeof(*ctx);
801 ctx->maxmalloced = sizeof(*ctx);
802 ctx->hi_water = 0;
803 ctx->lo_water = 0;
804 ctx->hi_called = false;
805 ctx->is_overmem = false;
806 ctx->water = NULL;
807 ctx->water_arg = NULL;
808 ctx->common.impmagic = MEM_MAGIC;
809 ctx->common.magic = ISCAPI_MCTX_MAGIC;
810 ctx->common.methods = (isc_memmethods_t *)&memmethods;
811 ctx->memalloc = default_memalloc;
812 ctx->memfree = default_memfree;
813 ctx->stats = NULL;
814 ctx->checkfree = true;
815 #if ISC_MEM_TRACKLINES
816 ctx->debuglist = NULL;
817 ctx->debuglistcnt = 0;
818 #endif /* if ISC_MEM_TRACKLINES */
819 ISC_LIST_INIT(ctx->pools);
820 ctx->poolcnt = 0;
821 ctx->freelists = NULL;
822 ctx->basic_blocks = NULL;
823 ctx->basic_table = NULL;
824 ctx->basic_table_count = 0;
825 ctx->basic_table_size = 0;
826 ctx->lowest = NULL;
827 ctx->highest = NULL;
828
829 ctx->stats =
830 (ctx->memalloc)((ctx->max_size + 1) * sizeof(struct stats));
831
832 memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
833 ctx->malloced += (ctx->max_size + 1) * sizeof(struct stats);
834 ctx->maxmalloced += (ctx->max_size + 1) * sizeof(struct stats);
835
836 if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
837 ctx->mem_target = DEF_MEM_TARGET;
838 ctx->freelists =
839 (ctx->memalloc)(ctx->max_size * sizeof(element *));
840 memset(ctx->freelists, 0, ctx->max_size * sizeof(element *));
841 ctx->malloced += ctx->max_size * sizeof(element *);
842 ctx->maxmalloced += ctx->max_size * sizeof(element *);
843 }
844
845 #if ISC_MEM_TRACKLINES
846 if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0)) {
847 unsigned int i;
848
849 ctx->debuglist = (ctx->memalloc)(
850 (DEBUG_TABLE_COUNT * sizeof(debuglist_t)));
851 for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
852 ISC_LIST_INIT(ctx->debuglist[i]);
853 }
854 ctx->malloced += DEBUG_TABLE_COUNT * sizeof(debuglist_t);
855 ctx->maxmalloced += DEBUG_TABLE_COUNT * sizeof(debuglist_t);
856 }
857 #endif /* if ISC_MEM_TRACKLINES */
858
859 LOCK(&contextslock);
860 ISC_LIST_INITANDAPPEND(contexts, ctx, link);
861 UNLOCK(&contextslock);
862
863 *ctxp = (isc_mem_t *)ctx;
864 }
865
866 /*
867 * Public.
868 */
869
870 static void
destroy(isc__mem_t * ctx)871 destroy(isc__mem_t *ctx) {
872 unsigned int i;
873
874 LOCK(&contextslock);
875 ISC_LIST_UNLINK(contexts, ctx, link);
876 totallost += ctx->inuse;
877 UNLOCK(&contextslock);
878
879 ctx->common.impmagic = 0;
880 ctx->common.magic = 0;
881
882 INSIST(ISC_LIST_EMPTY(ctx->pools));
883
884 #if ISC_MEM_TRACKLINES
885 if (ISC_UNLIKELY(ctx->debuglist != NULL)) {
886 debuglink_t *dl;
887 for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
888 for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); dl != NULL;
889 dl = ISC_LIST_HEAD(ctx->debuglist[i]))
890 {
891 if (ctx->checkfree && dl->ptr != NULL) {
892 print_active(ctx, stderr);
893 }
894 INSIST(!ctx->checkfree || dl->ptr == NULL);
895
896 ISC_LIST_UNLINK(ctx->debuglist[i], dl, link);
897 free(dl);
898 ctx->malloced -= sizeof(*dl);
899 }
900 }
901
902 (ctx->memfree)(ctx->debuglist);
903 ctx->malloced -= DEBUG_TABLE_COUNT * sizeof(debuglist_t);
904 }
905 #endif /* if ISC_MEM_TRACKLINES */
906
907 if (ctx->checkfree) {
908 for (i = 0; i <= ctx->max_size; i++) {
909 if (ctx->stats[i].gets != 0U) {
910 fprintf(stderr,
911 "Failing assertion due to probable "
912 "leaked memory in context %p (\"%s\") "
913 "(stats[%u].gets == %lu).\n",
914 ctx, ctx->name, i, ctx->stats[i].gets);
915 #if ISC_MEM_TRACKLINES
916 print_active(ctx, stderr);
917 #endif /* if ISC_MEM_TRACKLINES */
918 INSIST(ctx->stats[i].gets == 0U);
919 }
920 }
921 }
922
923 (ctx->memfree)(ctx->stats);
924 ctx->malloced -= (ctx->max_size + 1) * sizeof(struct stats);
925
926 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
927 for (i = 0; i < ctx->basic_table_count; i++) {
928 (ctx->memfree)(ctx->basic_table[i]);
929 ctx->malloced -= NUM_BASIC_BLOCKS * ctx->mem_target;
930 }
931 (ctx->memfree)(ctx->freelists);
932 ctx->malloced -= ctx->max_size * sizeof(element *);
933 if (ctx->basic_table != NULL) {
934 (ctx->memfree)(ctx->basic_table);
935 ctx->malloced -= ctx->basic_table_size *
936 sizeof(unsigned char *);
937 }
938 }
939
940 isc_mutex_destroy(&ctx->lock);
941
942 ctx->malloced -= sizeof(*ctx);
943 if (ctx->checkfree) {
944 INSIST(ctx->malloced == 0);
945 }
946 (ctx->memfree)(ctx);
947 }
948
949 void
isc_mem_attach(isc_mem_t * source0,isc_mem_t ** targetp)950 isc_mem_attach(isc_mem_t *source0, isc_mem_t **targetp) {
951 REQUIRE(VALID_CONTEXT(source0));
952 REQUIRE(targetp != NULL && *targetp == NULL);
953
954 isc__mem_t *source = (isc__mem_t *)source0;
955
956 isc_refcount_increment(&source->references);
957
958 *targetp = (isc_mem_t *)source;
959 }
960
961 void
isc_mem_detach(isc_mem_t ** ctxp)962 isc_mem_detach(isc_mem_t **ctxp) {
963 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
964
965 isc__mem_t *ctx = (isc__mem_t *)*ctxp;
966 *ctxp = NULL;
967
968 if (isc_refcount_decrement(&ctx->references) == 1) {
969 isc_refcount_destroy(&ctx->references);
970 destroy(ctx);
971 }
972 }
973
974 /*
975 * isc_mem_putanddetach() is the equivalent of:
976 *
977 * mctx = NULL;
978 * isc_mem_attach(ptr->mctx, &mctx);
979 * isc_mem_detach(&ptr->mctx);
980 * isc_mem_put(mctx, ptr, sizeof(*ptr);
981 * isc_mem_detach(&mctx);
982 */
983
984 void
isc___mem_putanddetach(isc_mem_t ** ctxp,void * ptr,size_t size FLARG)985 isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
986 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
987 REQUIRE(ptr != NULL);
988
989 isc__mem_t *ctx = (isc__mem_t *)*ctxp;
990 *ctxp = NULL;
991
992 if (ISC_UNLIKELY((isc_mem_debugging &
993 (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0))
994 {
995 if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
996 size_info *si = &(((size_info *)ptr)[-1]);
997 size_t oldsize = si->u.size - ALIGNMENT_SIZE;
998 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
999 oldsize -= ALIGNMENT_SIZE;
1000 }
1001 INSIST(oldsize == size);
1002 }
1003 isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS);
1004
1005 goto destroy;
1006 }
1007
1008 MCTXLOCK(ctx);
1009
1010 DELETE_TRACE(ctx, ptr, size, file, line);
1011
1012 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1013 mem_putunlocked(ctx, ptr, size);
1014 } else {
1015 mem_putstats(ctx, ptr, size);
1016 mem_put(ctx, ptr, size);
1017 }
1018 MCTXUNLOCK(ctx);
1019
1020 destroy:
1021 if (isc_refcount_decrement(&ctx->references) == 1) {
1022 isc_refcount_destroy(&ctx->references);
1023 destroy(ctx);
1024 }
1025 }
1026
1027 void
isc_mem_destroy(isc_mem_t ** ctxp)1028 isc_mem_destroy(isc_mem_t **ctxp) {
1029 /*
1030 * This routine provides legacy support for callers who use mctxs
1031 * without attaching/detaching.
1032 */
1033
1034 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
1035
1036 isc__mem_t *ctx = (isc__mem_t *)*ctxp;
1037
1038 #if ISC_MEM_TRACKLINES
1039 if (isc_refcount_decrement(&ctx->references) > 1) {
1040 print_active(ctx, stderr);
1041 }
1042 #else /* if ISC_MEM_TRACKLINES */
1043 isc_refcount_decrementz(&ctx->references);
1044 #endif /* if ISC_MEM_TRACKLINES */
1045 isc_refcount_destroy(&ctx->references);
1046 destroy(ctx);
1047
1048 *ctxp = NULL;
1049 }
1050
1051 void *
isc___mem_get(isc_mem_t * ctx0,size_t size FLARG)1052 isc___mem_get(isc_mem_t *ctx0, size_t size FLARG) {
1053 REQUIRE(VALID_CONTEXT(ctx0));
1054
1055 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1056 void *ptr;
1057 bool call_water = false;
1058
1059 if (ISC_UNLIKELY((isc_mem_debugging &
1060 (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0))
1061 {
1062 return (isc__mem_allocate(ctx0, size FLARG_PASS));
1063 }
1064
1065 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1066 MCTXLOCK(ctx);
1067 ptr = mem_getunlocked(ctx, size);
1068 } else {
1069 ptr = mem_get(ctx, size);
1070 MCTXLOCK(ctx);
1071 if (ptr != NULL) {
1072 mem_getstats(ctx, size);
1073 }
1074 }
1075
1076 ADD_TRACE(ctx, ptr, size, file, line);
1077
1078 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water) {
1079 ctx->is_overmem = true;
1080 if (!ctx->hi_called) {
1081 call_water = true;
1082 }
1083 }
1084 if (ctx->inuse > ctx->maxinuse) {
1085 ctx->maxinuse = ctx->inuse;
1086 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1087 (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1088 {
1089 fprintf(stderr, "maxinuse = %lu\n",
1090 (unsigned long)ctx->inuse);
1091 }
1092 }
1093 MCTXUNLOCK(ctx);
1094
1095 if (call_water && (ctx->water != NULL)) {
1096 (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1097 }
1098
1099 return (ptr);
1100 }
1101
1102 void
isc___mem_put(isc_mem_t * ctx0,void * ptr,size_t size FLARG)1103 isc___mem_put(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
1104 REQUIRE(VALID_CONTEXT(ctx0));
1105 REQUIRE(ptr != NULL);
1106
1107 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1108 bool call_water = false;
1109 size_info *si;
1110 size_t oldsize;
1111
1112 if (ISC_UNLIKELY((isc_mem_debugging &
1113 (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0))
1114 {
1115 if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1116 si = &(((size_info *)ptr)[-1]);
1117 oldsize = si->u.size - ALIGNMENT_SIZE;
1118 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1119 oldsize -= ALIGNMENT_SIZE;
1120 }
1121 INSIST(oldsize == size);
1122 }
1123 isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS);
1124 return;
1125 }
1126
1127 MCTXLOCK(ctx);
1128
1129 DELETE_TRACE(ctx, ptr, size, file, line);
1130
1131 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1132 mem_putunlocked(ctx, ptr, size);
1133 } else {
1134 mem_putstats(ctx, ptr, size);
1135 mem_put(ctx, ptr, size);
1136 }
1137
1138 /*
1139 * The check against ctx->lo_water == 0 is for the condition
1140 * when the context was pushed over hi_water but then had
1141 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1142 */
1143 if ((ctx->inuse < ctx->lo_water) || (ctx->lo_water == 0U)) {
1144 ctx->is_overmem = false;
1145 if (ctx->hi_called) {
1146 call_water = true;
1147 }
1148 }
1149
1150 MCTXUNLOCK(ctx);
1151
1152 if (call_water && (ctx->water != NULL)) {
1153 (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1154 }
1155 }
1156
1157 void
isc_mem_waterack(isc_mem_t * ctx0,int flag)1158 isc_mem_waterack(isc_mem_t *ctx0, int flag) {
1159 REQUIRE(VALID_CONTEXT(ctx0));
1160
1161 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1162
1163 MCTXLOCK(ctx);
1164 if (flag == ISC_MEM_LOWATER) {
1165 ctx->hi_called = false;
1166 } else if (flag == ISC_MEM_HIWATER) {
1167 ctx->hi_called = true;
1168 }
1169 MCTXUNLOCK(ctx);
1170 }
1171
1172 #if ISC_MEM_TRACKLINES
1173 static void
print_active(isc__mem_t * mctx,FILE * out)1174 print_active(isc__mem_t *mctx, FILE *out) {
1175 if (mctx->debuglist != NULL) {
1176 debuglink_t *dl;
1177 unsigned int i;
1178 bool found;
1179
1180 fputs("Dump of all outstanding memory allocations:\n", out);
1181 found = false;
1182 for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
1183 dl = ISC_LIST_HEAD(mctx->debuglist[i]);
1184
1185 if (dl != NULL) {
1186 found = true;
1187 }
1188
1189 while (dl != NULL) {
1190 if (dl->ptr != NULL) {
1191 fprintf(out,
1192 "\tptr %p size %zu file %s "
1193 "line %u\n",
1194 dl->ptr, dl->size, dl->file,
1195 dl->line);
1196 }
1197 dl = ISC_LIST_NEXT(dl, link);
1198 }
1199 }
1200
1201 if (!found) {
1202 fputs("\tNone.\n", out);
1203 }
1204 }
1205 }
1206 #endif /* if ISC_MEM_TRACKLINES */
1207
1208 /*
1209 * Print the stats[] on the stream "out" with suitable formatting.
1210 */
1211 void
isc_mem_stats(isc_mem_t * ctx0,FILE * out)1212 isc_mem_stats(isc_mem_t *ctx0, FILE *out) {
1213 REQUIRE(VALID_CONTEXT(ctx0));
1214
1215 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1216 size_t i;
1217 const struct stats *s;
1218 const isc__mempool_t *pool;
1219
1220 MCTXLOCK(ctx);
1221
1222 for (i = 0; i <= ctx->max_size; i++) {
1223 s = &ctx->stats[i];
1224
1225 if (s->totalgets == 0U && s->gets == 0U) {
1226 continue;
1227 }
1228 fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
1229 (i == ctx->max_size) ? ">=" : " ", (unsigned long)i,
1230 s->totalgets, s->gets);
1231 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 &&
1232 (s->blocks != 0U || s->freefrags != 0U))
1233 {
1234 fprintf(out, " (%lu bl, %lu ff)", s->blocks,
1235 s->freefrags);
1236 }
1237 fputc('\n', out);
1238 }
1239
1240 /*
1241 * Note that since a pool can be locked now, these stats might be
1242 * somewhat off if the pool is in active use at the time the stats
1243 * are dumped. The link fields are protected by the isc_mem_t's
1244 * lock, however, so walking this list and extracting integers from
1245 * stats fields is always safe.
1246 */
1247 pool = ISC_LIST_HEAD(ctx->pools);
1248 if (pool != NULL) {
1249 fputs("[Pool statistics]\n", out);
1250 fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n",
1251 "name", "size", "maxalloc", "allocated", "freecount",
1252 "freemax", "fillcount", "gets", "L");
1253 }
1254 while (pool != NULL) {
1255 fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n",
1256 #if ISC_MEMPOOL_NAMES
1257 pool->name,
1258 #else /* if ISC_MEMPOOL_NAMES */
1259 "(not tracked)",
1260 #endif /* if ISC_MEMPOOL_NAMES */
1261 (unsigned long)pool->size, pool->maxalloc,
1262 pool->allocated, pool->freecount, pool->freemax,
1263 pool->fillcount, pool->gets, "N");
1264 pool = ISC_LIST_NEXT(pool, link);
1265 }
1266
1267 #if ISC_MEM_TRACKLINES
1268 print_active(ctx, out);
1269 #endif /* if ISC_MEM_TRACKLINES */
1270
1271 MCTXUNLOCK(ctx);
1272 }
1273
1274 /*
1275 * Replacements for malloc() and free() -- they implicitly remember the
1276 * size of the object allocated (with some additional overhead).
1277 */
1278
1279 static void *
mem_allocateunlocked(isc_mem_t * ctx0,size_t size)1280 mem_allocateunlocked(isc_mem_t *ctx0, size_t size) {
1281 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1282 size_info *si;
1283
1284 size += ALIGNMENT_SIZE;
1285 if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) {
1286 size += ALIGNMENT_SIZE;
1287 }
1288
1289 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1290 si = mem_getunlocked(ctx, size);
1291 } else {
1292 si = mem_get(ctx, size);
1293 }
1294
1295 if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) {
1296 si->u.ctx = ctx;
1297 si++;
1298 }
1299 si->u.size = size;
1300 return (&si[1]);
1301 }
1302
1303 void *
isc___mem_allocate(isc_mem_t * ctx0,size_t size FLARG)1304 isc___mem_allocate(isc_mem_t *ctx0, size_t size FLARG) {
1305 REQUIRE(VALID_CONTEXT(ctx0));
1306
1307 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1308 size_info *si;
1309 bool call_water = false;
1310
1311 MCTXLOCK(ctx);
1312 si = mem_allocateunlocked((isc_mem_t *)ctx, size);
1313 if (((ctx->flags & ISC_MEMFLAG_INTERNAL) == 0)) {
1314 mem_getstats(ctx, si[-1].u.size);
1315 }
1316
1317 ADD_TRACE(ctx, si, si[-1].u.size, file, line);
1318 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1319 !ctx->is_overmem)
1320 {
1321 ctx->is_overmem = true;
1322 }
1323
1324 if (ctx->hi_water != 0U && !ctx->hi_called &&
1325 ctx->inuse > ctx->hi_water)
1326 {
1327 ctx->hi_called = true;
1328 call_water = true;
1329 }
1330 if (ctx->inuse > ctx->maxinuse) {
1331 ctx->maxinuse = ctx->inuse;
1332 if (ISC_UNLIKELY(ctx->hi_water != 0U &&
1333 ctx->inuse > ctx->hi_water &&
1334 (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0))
1335 {
1336 fprintf(stderr, "maxinuse = %lu\n",
1337 (unsigned long)ctx->inuse);
1338 }
1339 }
1340 MCTXUNLOCK(ctx);
1341
1342 if (call_water) {
1343 (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1344 }
1345
1346 return (si);
1347 }
1348
1349 void *
isc___mem_reallocate(isc_mem_t * ctx0,void * ptr,size_t size FLARG)1350 isc___mem_reallocate(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
1351 REQUIRE(VALID_CONTEXT(ctx0));
1352
1353 void *new_ptr = NULL;
1354 size_t oldsize, copysize;
1355
1356 /*
1357 * This function emulates the realloc(3) standard library function:
1358 * - if size > 0, allocate new memory; and if ptr is non NULL, copy
1359 * as much of the old contents to the new buffer and free the old one.
1360 * Note that when allocation fails the original pointer is intact;
1361 * the caller must free it.
1362 * - if size is 0 and ptr is non NULL, simply free the given ptr.
1363 * - this function returns:
1364 * pointer to the newly allocated memory, or
1365 * NULL if allocation fails or doesn't happen.
1366 */
1367 if (size > 0U) {
1368 new_ptr = isc__mem_allocate(ctx0, size FLARG_PASS);
1369 if (new_ptr != NULL && ptr != NULL) {
1370 oldsize = (((size_info *)ptr)[-1]).u.size;
1371 INSIST(oldsize >= ALIGNMENT_SIZE);
1372 oldsize -= ALIGNMENT_SIZE;
1373 if (ISC_UNLIKELY((isc_mem_debugging &
1374 ISC_MEM_DEBUGCTX) != 0))
1375 {
1376 INSIST(oldsize >= ALIGNMENT_SIZE);
1377 oldsize -= ALIGNMENT_SIZE;
1378 }
1379 copysize = (oldsize > size) ? size : oldsize;
1380 memmove(new_ptr, ptr, copysize);
1381 isc__mem_free(ctx0, ptr FLARG_PASS);
1382 }
1383 } else if (ptr != NULL) {
1384 isc__mem_free(ctx0, ptr FLARG_PASS);
1385 }
1386
1387 return (new_ptr);
1388 }
1389
1390 void
isc___mem_free(isc_mem_t * ctx0,void * ptr FLARG)1391 isc___mem_free(isc_mem_t *ctx0, void *ptr FLARG) {
1392 REQUIRE(VALID_CONTEXT(ctx0));
1393 REQUIRE(ptr != NULL);
1394
1395 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1396 size_info *si;
1397 size_t size;
1398 bool call_water = false;
1399
1400 if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) {
1401 si = &(((size_info *)ptr)[-2]);
1402 REQUIRE(si->u.ctx == ctx);
1403 size = si[1].u.size;
1404 } else {
1405 si = &(((size_info *)ptr)[-1]);
1406 size = si->u.size;
1407 }
1408
1409 MCTXLOCK(ctx);
1410
1411 DELETE_TRACE(ctx, ptr, size, file, line);
1412
1413 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1414 mem_putunlocked(ctx, si, size);
1415 } else {
1416 mem_putstats(ctx, si, size);
1417 mem_put(ctx, si, size);
1418 }
1419
1420 /*
1421 * The check against ctx->lo_water == 0 is for the condition
1422 * when the context was pushed over hi_water but then had
1423 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1424 */
1425 if (ctx->is_overmem &&
1426 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U))
1427 {
1428 ctx->is_overmem = false;
1429 }
1430
1431 if (ctx->hi_called &&
1432 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U))
1433 {
1434 ctx->hi_called = false;
1435
1436 if (ctx->water != NULL) {
1437 call_water = true;
1438 }
1439 }
1440 MCTXUNLOCK(ctx);
1441
1442 if (call_water) {
1443 (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1444 }
1445 }
1446
1447 /*
1448 * Other useful things.
1449 */
1450
1451 char *
isc___mem_strdup(isc_mem_t * mctx0,const char * s FLARG)1452 isc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) {
1453 REQUIRE(VALID_CONTEXT(mctx0));
1454 REQUIRE(s != NULL);
1455
1456 isc__mem_t *mctx = (isc__mem_t *)mctx0;
1457 size_t len;
1458 char *ns;
1459
1460 len = strlen(s) + 1;
1461
1462 ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS);
1463
1464 if (ns != NULL) {
1465 strlcpy(ns, s, len);
1466 }
1467
1468 return (ns);
1469 }
1470
1471 char *
isc___mem_strndup(isc_mem_t * mctx0,const char * s,size_t size FLARG)1472 isc___mem_strndup(isc_mem_t *mctx0, const char *s, size_t size FLARG) {
1473 REQUIRE(VALID_CONTEXT(mctx0));
1474 REQUIRE(s != NULL);
1475
1476 isc__mem_t *mctx = (isc__mem_t *)mctx0;
1477 size_t len;
1478 char *ns;
1479
1480 len = strlen(s) + 1;
1481 if (len > size) {
1482 len = size;
1483 }
1484
1485 ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS);
1486
1487 if (ns != NULL) {
1488 strlcpy(ns, s, len);
1489 }
1490
1491 return (ns);
1492 }
1493
1494 void
isc_mem_setdestroycheck(isc_mem_t * ctx0,bool flag)1495 isc_mem_setdestroycheck(isc_mem_t *ctx0, bool flag) {
1496 REQUIRE(VALID_CONTEXT(ctx0));
1497
1498 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1499
1500 MCTXLOCK(ctx);
1501
1502 ctx->checkfree = flag;
1503
1504 MCTXUNLOCK(ctx);
1505 }
1506
1507 size_t
isc_mem_inuse(isc_mem_t * ctx0)1508 isc_mem_inuse(isc_mem_t *ctx0) {
1509 REQUIRE(VALID_CONTEXT(ctx0));
1510
1511 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1512 size_t inuse;
1513
1514 MCTXLOCK(ctx);
1515
1516 inuse = ctx->inuse;
1517
1518 MCTXUNLOCK(ctx);
1519
1520 return (inuse);
1521 }
1522
1523 size_t
isc_mem_maxinuse(isc_mem_t * ctx0)1524 isc_mem_maxinuse(isc_mem_t *ctx0) {
1525 REQUIRE(VALID_CONTEXT(ctx0));
1526
1527 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1528 size_t maxinuse;
1529
1530 MCTXLOCK(ctx);
1531
1532 maxinuse = ctx->maxinuse;
1533
1534 MCTXUNLOCK(ctx);
1535
1536 return (maxinuse);
1537 }
1538
1539 size_t
isc_mem_total(isc_mem_t * ctx0)1540 isc_mem_total(isc_mem_t *ctx0) {
1541 REQUIRE(VALID_CONTEXT(ctx0));
1542
1543 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1544 size_t total;
1545
1546 MCTXLOCK(ctx);
1547
1548 total = ctx->total;
1549
1550 MCTXUNLOCK(ctx);
1551
1552 return (total);
1553 }
1554
1555 void
isc_mem_setwater(isc_mem_t * ctx0,isc_mem_water_t water,void * water_arg,size_t hiwater,size_t lowater)1556 isc_mem_setwater(isc_mem_t *ctx0, isc_mem_water_t water, void *water_arg,
1557 size_t hiwater, size_t lowater) {
1558 REQUIRE(VALID_CONTEXT(ctx0));
1559 REQUIRE(hiwater >= lowater);
1560
1561 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1562 bool callwater = false;
1563 isc_mem_water_t oldwater;
1564 void *oldwater_arg;
1565
1566 MCTXLOCK(ctx);
1567 oldwater = ctx->water;
1568 oldwater_arg = ctx->water_arg;
1569 if (water == NULL) {
1570 callwater = ctx->hi_called;
1571 ctx->water = NULL;
1572 ctx->water_arg = NULL;
1573 ctx->hi_water = 0;
1574 ctx->lo_water = 0;
1575 } else {
1576 if (ctx->hi_called &&
1577 (ctx->water != water || ctx->water_arg != water_arg ||
1578 ctx->inuse < lowater || lowater == 0U))
1579 {
1580 callwater = true;
1581 }
1582 ctx->water = water;
1583 ctx->water_arg = water_arg;
1584 ctx->hi_water = hiwater;
1585 ctx->lo_water = lowater;
1586 }
1587 MCTXUNLOCK(ctx);
1588
1589 if (callwater && oldwater != NULL) {
1590 (oldwater)(oldwater_arg, ISC_MEM_LOWATER);
1591 }
1592 }
1593
1594 ISC_NO_SANITIZE_THREAD bool
isc_mem_isovermem(isc_mem_t * ctx0)1595 isc_mem_isovermem(isc_mem_t *ctx0) {
1596 REQUIRE(VALID_CONTEXT(ctx0));
1597
1598 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1599
1600 /*
1601 * We don't bother to lock the context because 100% accuracy isn't
1602 * necessary (and even if we locked the context the returned value
1603 * could be different from the actual state when it's used anyway)
1604 */
1605 return (ctx->is_overmem);
1606 }
1607
1608 void
isc_mem_setname(isc_mem_t * ctx0,const char * name,void * tag)1609 isc_mem_setname(isc_mem_t *ctx0, const char *name, void *tag) {
1610 REQUIRE(VALID_CONTEXT(ctx0));
1611
1612 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1613
1614 LOCK(&ctx->lock);
1615 strlcpy(ctx->name, name, sizeof(ctx->name));
1616 ctx->tag = tag;
1617 UNLOCK(&ctx->lock);
1618 }
1619
1620 const char *
isc_mem_getname(isc_mem_t * ctx0)1621 isc_mem_getname(isc_mem_t *ctx0) {
1622 REQUIRE(VALID_CONTEXT(ctx0));
1623
1624 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1625
1626 if (ctx->name[0] == 0) {
1627 return ("");
1628 }
1629
1630 return (ctx->name);
1631 }
1632
1633 void *
isc_mem_gettag(isc_mem_t * ctx0)1634 isc_mem_gettag(isc_mem_t *ctx0) {
1635 REQUIRE(VALID_CONTEXT(ctx0));
1636
1637 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1638
1639 return (ctx->tag);
1640 }
1641
1642 /*
1643 * Memory pool stuff
1644 */
1645
1646 void
isc_mempool_create(isc_mem_t * mctx0,size_t size,isc_mempool_t ** mpctxp)1647 isc_mempool_create(isc_mem_t *mctx0, size_t size, isc_mempool_t **mpctxp) {
1648 REQUIRE(VALID_CONTEXT(mctx0));
1649 REQUIRE(size > 0U);
1650 REQUIRE(mpctxp != NULL && *mpctxp == NULL);
1651
1652 isc__mem_t *mctx = (isc__mem_t *)mctx0;
1653 isc__mempool_t *mpctx;
1654
1655 /*
1656 * Allocate space for this pool, initialize values, and if all works
1657 * well, attach to the memory context.
1658 */
1659 mpctx = isc_mem_get((isc_mem_t *)mctx, sizeof(isc__mempool_t));
1660
1661 mpctx->common.impmagic = MEMPOOL_MAGIC;
1662 mpctx->common.magic = ISCAPI_MPOOL_MAGIC;
1663 mpctx->mctx = NULL;
1664 isc_mem_attach((isc_mem_t *)mctx, (isc_mem_t **)&mpctx->mctx);
1665 /*
1666 * Mempools are stored as a linked list of element.
1667 */
1668 if (size < sizeof(element)) {
1669 size = sizeof(element);
1670 }
1671 mpctx->size = size;
1672 mpctx->maxalloc = UINT_MAX;
1673 mpctx->allocated = 0;
1674 mpctx->freecount = 0;
1675 mpctx->freemax = 1;
1676 mpctx->fillcount = 1;
1677 mpctx->gets = 0;
1678 #if ISC_MEMPOOL_NAMES
1679 mpctx->name[0] = 0;
1680 #endif /* if ISC_MEMPOOL_NAMES */
1681 mpctx->items = NULL;
1682
1683 *mpctxp = (isc_mempool_t *)mpctx;
1684
1685 MCTXLOCK(mctx);
1686 ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
1687 mctx->poolcnt++;
1688 MCTXUNLOCK(mctx);
1689 }
1690
1691 void
isc_mempool_setname(isc_mempool_t * mpctx0,const char * name)1692 isc_mempool_setname(isc_mempool_t *mpctx0, const char *name) {
1693 REQUIRE(VALID_MEMPOOL(mpctx0));
1694 REQUIRE(name != NULL);
1695
1696 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1697
1698 #if ISC_MEMPOOL_NAMES
1699 strlcpy(mpctx->name, name, sizeof(mpctx->name));
1700 #else /* if ISC_MEMPOOL_NAMES */
1701 UNUSED(mpctx);
1702 UNUSED(name);
1703 #endif /* if ISC_MEMPOOL_NAMES */
1704 }
1705
1706 void
isc_mempool_destroy(isc_mempool_t ** mpctxp)1707 isc_mempool_destroy(isc_mempool_t **mpctxp) {
1708 REQUIRE(mpctxp != NULL);
1709 REQUIRE(VALID_MEMPOOL(*mpctxp));
1710
1711 isc__mempool_t *mpctx;
1712 isc__mem_t *mctx;
1713 element *item;
1714
1715 mpctx = (isc__mempool_t *)*mpctxp;
1716 #if ISC_MEMPOOL_NAMES
1717 if (mpctx->allocated > 0) {
1718 UNEXPECTED_ERROR(__FILE__, __LINE__,
1719 "isc_mempool_destroy(): mempool %s "
1720 "leaked memory",
1721 mpctx->name);
1722 }
1723 #endif /* if ISC_MEMPOOL_NAMES */
1724 REQUIRE(mpctx->allocated == 0);
1725
1726 mctx = mpctx->mctx;
1727
1728 /*
1729 * Return any items on the free list
1730 */
1731 MCTXLOCK(mctx);
1732 while (mpctx->items != NULL) {
1733 INSIST(mpctx->freecount > 0);
1734 mpctx->freecount--;
1735 item = mpctx->items;
1736 mpctx->items = item->next;
1737 mem_putstats(mctx, item, mpctx->size);
1738 mem_put(mctx, item, mpctx->size);
1739 }
1740 MCTXUNLOCK(mctx);
1741
1742 /*
1743 * Remove our linked list entry from the memory context.
1744 */
1745 MCTXLOCK(mctx);
1746 ISC_LIST_UNLINK(mctx->pools, mpctx, link);
1747 mctx->poolcnt--;
1748 MCTXUNLOCK(mctx);
1749
1750 mpctx->common.impmagic = 0;
1751 mpctx->common.magic = 0;
1752
1753 isc_mem_putanddetach((isc_mem_t **)&mpctx->mctx, mpctx,
1754 sizeof(isc__mempool_t));
1755
1756 *mpctxp = NULL;
1757 }
1758
1759 #if __SANITIZE_ADDRESS__
1760 void *
isc__mempool_get(isc_mempool_t * mpctx0 FLARG)1761 isc__mempool_get(isc_mempool_t *mpctx0 FLARG) {
1762 void *item = NULL;
1763
1764 REQUIRE(VALID_MEMPOOL(mpctx0));
1765
1766 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1767 isc_mem_t *mctx = (isc_mem_t *)mpctx->mctx;
1768
1769 /*
1770 * Don't let the caller go over quota
1771 */
1772 if (ISC_UNLIKELY(mpctx->allocated >= mpctx->maxalloc)) {
1773 goto out;
1774 }
1775
1776 item = isc__mem_get(mctx, mpctx->size FLARG_PASS);
1777 mpctx->gets++;
1778 mpctx->allocated++;
1779
1780 out:
1781 return (item);
1782 }
1783
1784 void
isc__mempool_put(isc_mempool_t * mpctx0,void * mem FLARG)1785 isc__mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) {
1786 REQUIRE(VALID_MEMPOOL(mpctx0));
1787
1788 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1789 isc_mem_t *mctx = (isc_mem_t *)mpctx->mctx;
1790
1791 REQUIRE(mem != NULL);
1792
1793 INSIST(mpctx->allocated > 0);
1794 mpctx->allocated--;
1795
1796 isc__mem_put(mctx, mem, mpctx->size FLARG_PASS);
1797 }
1798
1799 #else /* __SANITIZE_ADDRESS__ */
1800 void *
isc__mempool_get(isc_mempool_t * mpctx0 FLARG)1801 isc__mempool_get(isc_mempool_t *mpctx0 FLARG) {
1802 REQUIRE(VALID_MEMPOOL(mpctx0));
1803
1804 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1805 element *item;
1806 isc__mem_t *mctx;
1807 unsigned int i;
1808
1809 mctx = mpctx->mctx;
1810
1811 /*
1812 * Don't let the caller go over quota
1813 */
1814 if (ISC_UNLIKELY(mpctx->allocated >= mpctx->maxalloc)) {
1815 item = NULL;
1816 goto out;
1817 }
1818
1819 if (ISC_UNLIKELY(mpctx->items == NULL)) {
1820 /*
1821 * We need to dip into the well. Lock the memory context
1822 * here and fill up our free list.
1823 */
1824 MCTXLOCK(mctx);
1825 for (i = 0; i < mpctx->fillcount; i++) {
1826 item = mem_get(mctx, mpctx->size);
1827 mem_getstats(mctx, mpctx->size);
1828 item->next = mpctx->items;
1829 mpctx->items = item;
1830 mpctx->freecount++;
1831 }
1832 MCTXUNLOCK(mctx);
1833 }
1834
1835 /*
1836 * If we didn't get any items, return NULL.
1837 */
1838 item = mpctx->items;
1839 if (ISC_UNLIKELY(item == NULL)) {
1840 goto out;
1841 }
1842
1843 mpctx->items = item->next;
1844 INSIST(mpctx->freecount > 0);
1845 mpctx->freecount--;
1846 mpctx->gets++;
1847 mpctx->allocated++;
1848
1849 out:
1850 #if ISC_MEM_TRACKLINES
1851 if (ISC_UNLIKELY(((isc_mem_debugging & TRACE_OR_RECORD) != 0) &&
1852 item != NULL))
1853 {
1854 MCTXLOCK(mctx);
1855 ADD_TRACE(mctx, item, mpctx->size, file, line);
1856 MCTXUNLOCK(mctx);
1857 }
1858 #endif /* ISC_MEM_TRACKLINES */
1859
1860 return (item);
1861 }
1862
1863 /* coverity[+free : arg-1] */
1864 void
isc__mempool_put(isc_mempool_t * mpctx0,void * mem FLARG)1865 isc__mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) {
1866 REQUIRE(VALID_MEMPOOL(mpctx0));
1867 REQUIRE(mem != NULL);
1868
1869 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1870 isc__mem_t *mctx = mpctx->mctx;
1871 element *item;
1872
1873 INSIST(mpctx->allocated > 0);
1874 mpctx->allocated--;
1875
1876 #if ISC_MEM_TRACKLINES
1877 if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0)) {
1878 MCTXLOCK(mctx);
1879 DELETE_TRACE(mctx, mem, mpctx->size, file, line);
1880 MCTXUNLOCK(mctx);
1881 }
1882 #endif /* ISC_MEM_TRACKLINES */
1883
1884 /*
1885 * If our free list is full, return this to the mctx directly.
1886 */
1887 if (mpctx->freecount >= mpctx->freemax) {
1888 MCTXLOCK(mctx);
1889 mem_putstats(mctx, mem, mpctx->size);
1890 mem_put(mctx, mem, mpctx->size);
1891 MCTXUNLOCK(mctx);
1892 return;
1893 }
1894
1895 /*
1896 * Otherwise, attach it to our free list and bump the counter.
1897 */
1898 mpctx->freecount++;
1899 item = (element *)mem;
1900 item->next = mpctx->items;
1901 mpctx->items = item;
1902 }
1903
1904 #endif /* __SANITIZE_ADDRESS__ */
1905
1906 /*
1907 * Quotas
1908 */
1909
1910 void
isc_mempool_setfreemax(isc_mempool_t * mpctx0,unsigned int limit)1911 isc_mempool_setfreemax(isc_mempool_t *mpctx0, unsigned int limit) {
1912 REQUIRE(VALID_MEMPOOL(mpctx0));
1913
1914 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1915
1916 mpctx->freemax = limit;
1917 }
1918
1919 unsigned int
isc_mempool_getfreemax(isc_mempool_t * mpctx0)1920 isc_mempool_getfreemax(isc_mempool_t *mpctx0) {
1921 REQUIRE(VALID_MEMPOOL(mpctx0));
1922
1923 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1924
1925 return (mpctx->freemax);
1926 }
1927
1928 unsigned int
isc_mempool_getfreecount(isc_mempool_t * mpctx0)1929 isc_mempool_getfreecount(isc_mempool_t *mpctx0) {
1930 REQUIRE(VALID_MEMPOOL(mpctx0));
1931
1932 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1933
1934 return (mpctx->freecount);
1935 }
1936
1937 void
isc_mempool_setmaxalloc(isc_mempool_t * mpctx0,unsigned int limit)1938 isc_mempool_setmaxalloc(isc_mempool_t *mpctx0, unsigned int limit) {
1939 REQUIRE(VALID_MEMPOOL(mpctx0));
1940 REQUIRE(limit > 0);
1941
1942 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1943
1944 mpctx->maxalloc = limit;
1945 }
1946
1947 unsigned int
isc_mempool_getmaxalloc(isc_mempool_t * mpctx0)1948 isc_mempool_getmaxalloc(isc_mempool_t *mpctx0) {
1949 REQUIRE(VALID_MEMPOOL(mpctx0));
1950
1951 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1952
1953 return (mpctx->maxalloc);
1954 }
1955
1956 unsigned int
isc_mempool_getallocated(isc_mempool_t * mpctx0)1957 isc_mempool_getallocated(isc_mempool_t *mpctx0) {
1958 REQUIRE(VALID_MEMPOOL(mpctx0));
1959
1960 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1961
1962 return (mpctx->allocated);
1963 }
1964
1965 void
isc_mempool_setfillcount(isc_mempool_t * mpctx0,unsigned int limit)1966 isc_mempool_setfillcount(isc_mempool_t *mpctx0, unsigned int limit) {
1967 REQUIRE(VALID_MEMPOOL(mpctx0));
1968 REQUIRE(limit > 0);
1969
1970 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1971
1972 mpctx->fillcount = limit;
1973 }
1974
1975 unsigned int
isc_mempool_getfillcount(isc_mempool_t * mpctx0)1976 isc_mempool_getfillcount(isc_mempool_t *mpctx0) {
1977 REQUIRE(VALID_MEMPOOL(mpctx0));
1978
1979 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1980
1981 return (mpctx->fillcount);
1982 }
1983
1984 /*
1985 * Requires contextslock to be held by caller.
1986 */
1987 static void
print_contexts(FILE * file)1988 print_contexts(FILE *file) {
1989 isc__mem_t *ctx;
1990
1991 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
1992 ctx = ISC_LIST_NEXT(ctx, link))
1993 {
1994 fprintf(file, "context: %p (%s): %" PRIuFAST32 " references\n",
1995 ctx, ctx->name[0] == 0 ? "<unknown>" : ctx->name,
1996 isc_refcount_current(&ctx->references));
1997 print_active(ctx, file);
1998 }
1999 fflush(file);
2000 }
2001
2002 static atomic_uintptr_t checkdestroyed = 0;
2003
2004 void
isc_mem_checkdestroyed(FILE * file)2005 isc_mem_checkdestroyed(FILE *file) {
2006 atomic_store_release(&checkdestroyed, (uintptr_t)file);
2007 }
2008
2009 void
isc__mem_checkdestroyed(void)2010 isc__mem_checkdestroyed(void) {
2011 FILE *file = (FILE *)atomic_load_acquire(&checkdestroyed);
2012
2013 if (file == NULL) {
2014 return;
2015 }
2016
2017 LOCK(&contextslock);
2018 if (!ISC_LIST_EMPTY(contexts)) {
2019 #if ISC_MEM_TRACKLINES
2020 if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0)) {
2021 print_contexts(file);
2022 }
2023 #endif /* if ISC_MEM_TRACKLINES */
2024 UNREACHABLE();
2025 }
2026 UNLOCK(&contextslock);
2027 }
2028
2029 unsigned int
isc_mem_references(isc_mem_t * ctx0)2030 isc_mem_references(isc_mem_t *ctx0) {
2031 isc__mem_t *ctx = (isc__mem_t *)ctx0;
2032 return (isc_refcount_current(&ctx->references));
2033 }
2034
2035 typedef struct summarystat {
2036 uint64_t total;
2037 uint64_t inuse;
2038 uint64_t malloced;
2039 uint64_t blocksize;
2040 uint64_t contextsize;
2041 } summarystat_t;
2042
2043 #ifdef HAVE_LIBXML2
2044 #define TRY0(a) \
2045 do { \
2046 xmlrc = (a); \
2047 if (xmlrc < 0) \
2048 goto error; \
2049 } while (0)
2050 static int
xml_renderctx(isc__mem_t * ctx,summarystat_t * summary,xmlTextWriterPtr writer)2051 xml_renderctx(isc__mem_t *ctx, summarystat_t *summary,
2052 xmlTextWriterPtr writer) {
2053 REQUIRE(VALID_CONTEXT(ctx));
2054
2055 int xmlrc;
2056
2057 MCTXLOCK(ctx);
2058
2059 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context"));
2060
2061 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"));
2062 TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx));
2063 TRY0(xmlTextWriterEndElement(writer)); /* id */
2064
2065 if (ctx->name[0] != 0) {
2066 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
2067 TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name));
2068 TRY0(xmlTextWriterEndElement(writer)); /* name */
2069 }
2070
2071 summary->contextsize += sizeof(*ctx) +
2072 (ctx->max_size + 1) * sizeof(struct stats) +
2073 ctx->max_size * sizeof(element *) +
2074 ctx->basic_table_count * sizeof(char *);
2075 #if ISC_MEM_TRACKLINES
2076 if (ctx->debuglist != NULL) {
2077 summary->contextsize += DEBUG_TABLE_COUNT *
2078 sizeof(debuglist_t) +
2079 ctx->debuglistcnt * sizeof(debuglink_t);
2080 }
2081 #endif /* if ISC_MEM_TRACKLINES */
2082 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references"));
2083 TRY0(xmlTextWriterWriteFormatString(
2084 writer, "%" PRIuFAST32,
2085 isc_refcount_current(&ctx->references)));
2086 TRY0(xmlTextWriterEndElement(writer)); /* references */
2087
2088 summary->total += ctx->total;
2089 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "total"));
2090 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2091 (uint64_t)ctx->total));
2092 TRY0(xmlTextWriterEndElement(writer)); /* total */
2093
2094 summary->inuse += ctx->inuse;
2095 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse"));
2096 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2097 (uint64_t)ctx->inuse));
2098 TRY0(xmlTextWriterEndElement(writer)); /* inuse */
2099
2100 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse"));
2101 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2102 (uint64_t)ctx->maxinuse));
2103 TRY0(xmlTextWriterEndElement(writer)); /* maxinuse */
2104
2105 summary->malloced += ctx->malloced;
2106 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "malloced"));
2107 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2108 (uint64_t)ctx->malloced));
2109 TRY0(xmlTextWriterEndElement(writer)); /* malloced */
2110
2111 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxmalloced"));
2112 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2113 (uint64_t)ctx->maxmalloced));
2114 TRY0(xmlTextWriterEndElement(writer)); /* maxmalloced */
2115
2116 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize"));
2117 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2118 summary->blocksize += ctx->basic_table_count *
2119 NUM_BASIC_BLOCKS * ctx->mem_target;
2120 TRY0(xmlTextWriterWriteFormatString(
2121 writer, "%" PRIu64 "",
2122 (uint64_t)ctx->basic_table_count * NUM_BASIC_BLOCKS *
2123 ctx->mem_target));
2124 } else {
2125 TRY0(xmlTextWriterWriteFormatString(writer, "%s", "-"));
2126 }
2127 TRY0(xmlTextWriterEndElement(writer)); /* blocksize */
2128
2129 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools"));
2130 TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt));
2131 TRY0(xmlTextWriterEndElement(writer)); /* pools */
2132 summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
2133
2134 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater"));
2135 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2136 (uint64_t)ctx->hi_water));
2137 TRY0(xmlTextWriterEndElement(writer)); /* hiwater */
2138
2139 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater"));
2140 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2141 (uint64_t)ctx->lo_water));
2142 TRY0(xmlTextWriterEndElement(writer)); /* lowater */
2143
2144 TRY0(xmlTextWriterEndElement(writer)); /* context */
2145
2146 error:
2147 MCTXUNLOCK(ctx);
2148
2149 return (xmlrc);
2150 }
2151
2152 int
isc_mem_renderxml(void * writer0)2153 isc_mem_renderxml(void *writer0) {
2154 isc__mem_t *ctx;
2155 summarystat_t summary;
2156 uint64_t lost;
2157 int xmlrc;
2158 xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
2159
2160 memset(&summary, 0, sizeof(summary));
2161
2162 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts"));
2163
2164 LOCK(&contextslock);
2165 lost = totallost;
2166 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
2167 ctx = ISC_LIST_NEXT(ctx, link))
2168 {
2169 xmlrc = xml_renderctx(ctx, &summary, writer);
2170 if (xmlrc < 0) {
2171 UNLOCK(&contextslock);
2172 goto error;
2173 }
2174 }
2175 UNLOCK(&contextslock);
2176
2177 TRY0(xmlTextWriterEndElement(writer)); /* contexts */
2178
2179 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary"));
2180
2181 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse"));
2182 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2183 summary.total));
2184 TRY0(xmlTextWriterEndElement(writer)); /* TotalUse */
2185
2186 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse"));
2187 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2188 summary.inuse));
2189 TRY0(xmlTextWriterEndElement(writer)); /* InUse */
2190
2191 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Malloced"));
2192 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2193 summary.malloced));
2194 TRY0(xmlTextWriterEndElement(writer)); /* InUse */
2195
2196 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize"));
2197 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2198 summary.blocksize));
2199 TRY0(xmlTextWriterEndElement(writer)); /* BlockSize */
2200
2201 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize"));
2202 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
2203 summary.contextsize));
2204 TRY0(xmlTextWriterEndElement(writer)); /* ContextSize */
2205
2206 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost"));
2207 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", lost));
2208 TRY0(xmlTextWriterEndElement(writer)); /* Lost */
2209
2210 TRY0(xmlTextWriterEndElement(writer)); /* summary */
2211 error:
2212 return (xmlrc);
2213 }
2214
2215 #endif /* HAVE_LIBXML2 */
2216
2217 #ifdef HAVE_JSON_C
2218 #define CHECKMEM(m) RUNTIME_CHECK(m != NULL)
2219
2220 static isc_result_t
json_renderctx(isc__mem_t * ctx,summarystat_t * summary,json_object * array)2221 json_renderctx(isc__mem_t *ctx, summarystat_t *summary, json_object *array) {
2222 REQUIRE(VALID_CONTEXT(ctx));
2223 REQUIRE(summary != NULL);
2224 REQUIRE(array != NULL);
2225
2226 json_object *ctxobj, *obj;
2227 char buf[1024];
2228
2229 MCTXLOCK(ctx);
2230
2231 summary->contextsize += sizeof(*ctx) +
2232 (ctx->max_size + 1) * sizeof(struct stats) +
2233 ctx->max_size * sizeof(element *) +
2234 ctx->basic_table_count * sizeof(char *);
2235 summary->total += ctx->total;
2236 summary->inuse += ctx->inuse;
2237 summary->malloced += ctx->malloced;
2238 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2239 summary->blocksize += ctx->basic_table_count *
2240 NUM_BASIC_BLOCKS * ctx->mem_target;
2241 }
2242 #if ISC_MEM_TRACKLINES
2243 if (ctx->debuglist != NULL) {
2244 summary->contextsize += DEBUG_TABLE_COUNT *
2245 sizeof(debuglist_t) +
2246 ctx->debuglistcnt * sizeof(debuglink_t);
2247 }
2248 #endif /* if ISC_MEM_TRACKLINES */
2249
2250 ctxobj = json_object_new_object();
2251 CHECKMEM(ctxobj);
2252
2253 snprintf(buf, sizeof(buf), "%p", ctx);
2254 obj = json_object_new_string(buf);
2255 CHECKMEM(obj);
2256 json_object_object_add(ctxobj, "id", obj);
2257
2258 if (ctx->name[0] != 0) {
2259 obj = json_object_new_string(ctx->name);
2260 CHECKMEM(obj);
2261 json_object_object_add(ctxobj, "name", obj);
2262 }
2263
2264 obj = json_object_new_int64(isc_refcount_current(&ctx->references));
2265 CHECKMEM(obj);
2266 json_object_object_add(ctxobj, "references", obj);
2267
2268 obj = json_object_new_int64(ctx->total);
2269 CHECKMEM(obj);
2270 json_object_object_add(ctxobj, "total", obj);
2271
2272 obj = json_object_new_int64(ctx->inuse);
2273 CHECKMEM(obj);
2274 json_object_object_add(ctxobj, "inuse", obj);
2275
2276 obj = json_object_new_int64(ctx->maxinuse);
2277 CHECKMEM(obj);
2278 json_object_object_add(ctxobj, "maxinuse", obj);
2279
2280 obj = json_object_new_int64(ctx->malloced);
2281 CHECKMEM(obj);
2282 json_object_object_add(ctxobj, "malloced", obj);
2283
2284 obj = json_object_new_int64(ctx->maxmalloced);
2285 CHECKMEM(obj);
2286 json_object_object_add(ctxobj, "maxmalloced", obj);
2287
2288 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2289 uint64_t blocksize;
2290 blocksize = ctx->basic_table_count * NUM_BASIC_BLOCKS *
2291 ctx->mem_target;
2292 obj = json_object_new_int64(blocksize);
2293 CHECKMEM(obj);
2294 json_object_object_add(ctxobj, "blocksize", obj);
2295 }
2296
2297 obj = json_object_new_int64(ctx->poolcnt);
2298 CHECKMEM(obj);
2299 json_object_object_add(ctxobj, "pools", obj);
2300
2301 summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
2302
2303 obj = json_object_new_int64(ctx->hi_water);
2304 CHECKMEM(obj);
2305 json_object_object_add(ctxobj, "hiwater", obj);
2306
2307 obj = json_object_new_int64(ctx->lo_water);
2308 CHECKMEM(obj);
2309 json_object_object_add(ctxobj, "lowater", obj);
2310
2311 MCTXUNLOCK(ctx);
2312 json_object_array_add(array, ctxobj);
2313 return (ISC_R_SUCCESS);
2314 }
2315
2316 isc_result_t
isc_mem_renderjson(void * memobj0)2317 isc_mem_renderjson(void *memobj0) {
2318 isc_result_t result = ISC_R_SUCCESS;
2319 isc__mem_t *ctx;
2320 summarystat_t summary;
2321 uint64_t lost;
2322 json_object *ctxarray, *obj;
2323 json_object *memobj = (json_object *)memobj0;
2324
2325 memset(&summary, 0, sizeof(summary));
2326
2327 ctxarray = json_object_new_array();
2328 CHECKMEM(ctxarray);
2329
2330 LOCK(&contextslock);
2331 lost = totallost;
2332 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
2333 ctx = ISC_LIST_NEXT(ctx, link))
2334 {
2335 result = json_renderctx(ctx, &summary, ctxarray);
2336 if (result != ISC_R_SUCCESS) {
2337 UNLOCK(&contextslock);
2338 goto error;
2339 }
2340 }
2341 UNLOCK(&contextslock);
2342
2343 obj = json_object_new_int64(summary.total);
2344 CHECKMEM(obj);
2345 json_object_object_add(memobj, "TotalUse", obj);
2346
2347 obj = json_object_new_int64(summary.inuse);
2348 CHECKMEM(obj);
2349 json_object_object_add(memobj, "InUse", obj);
2350
2351 obj = json_object_new_int64(summary.malloced);
2352 CHECKMEM(obj);
2353 json_object_object_add(memobj, "Malloced", obj);
2354
2355 obj = json_object_new_int64(summary.blocksize);
2356 CHECKMEM(obj);
2357 json_object_object_add(memobj, "BlockSize", obj);
2358
2359 obj = json_object_new_int64(summary.contextsize);
2360 CHECKMEM(obj);
2361 json_object_object_add(memobj, "ContextSize", obj);
2362
2363 obj = json_object_new_int64(lost);
2364 CHECKMEM(obj);
2365 json_object_object_add(memobj, "Lost", obj);
2366
2367 json_object_object_add(memobj, "contexts", ctxarray);
2368 return (ISC_R_SUCCESS);
2369
2370 error:
2371 if (ctxarray != NULL) {
2372 json_object_put(ctxarray);
2373 }
2374 return (result);
2375 }
2376 #endif /* HAVE_JSON_C */
2377
2378 void
isc_mem_create(isc_mem_t ** mctxp)2379 isc_mem_create(isc_mem_t **mctxp) {
2380 mem_create(mctxp, isc_mem_defaultflags);
2381 }
2382
2383 void *
isc__mem_get(isc_mem_t * mctx,size_t size FLARG)2384 isc__mem_get(isc_mem_t *mctx, size_t size FLARG) {
2385 REQUIRE(ISCAPI_MCTX_VALID(mctx));
2386
2387 return (mctx->methods->memget(mctx, size FLARG_PASS));
2388 }
2389
2390 void
isc__mem_put(isc_mem_t * mctx,void * ptr,size_t size FLARG)2391 isc__mem_put(isc_mem_t *mctx, void *ptr, size_t size FLARG) {
2392 REQUIRE(ISCAPI_MCTX_VALID(mctx));
2393
2394 mctx->methods->memput(mctx, ptr, size FLARG_PASS);
2395 }
2396
2397 void
isc__mem_putanddetach(isc_mem_t ** mctxp,void * ptr,size_t size FLARG)2398 isc__mem_putanddetach(isc_mem_t **mctxp, void *ptr, size_t size FLARG) {
2399 REQUIRE(mctxp != NULL && ISCAPI_MCTX_VALID(*mctxp));
2400
2401 (*mctxp)->methods->memputanddetach(mctxp, ptr, size FLARG_PASS);
2402 }
2403
2404 void *
isc__mem_allocate(isc_mem_t * mctx,size_t size FLARG)2405 isc__mem_allocate(isc_mem_t *mctx, size_t size FLARG) {
2406 REQUIRE(ISCAPI_MCTX_VALID(mctx));
2407
2408 return (mctx->methods->memallocate(mctx, size FLARG_PASS));
2409 }
2410
2411 void *
isc__mem_reallocate(isc_mem_t * mctx,void * ptr,size_t size FLARG)2412 isc__mem_reallocate(isc_mem_t *mctx, void *ptr, size_t size FLARG) {
2413 REQUIRE(ISCAPI_MCTX_VALID(mctx));
2414
2415 return (mctx->methods->memreallocate(mctx, ptr, size FLARG_PASS));
2416 }
2417
2418 char *
isc__mem_strdup(isc_mem_t * mctx,const char * s FLARG)2419 isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
2420 REQUIRE(ISCAPI_MCTX_VALID(mctx));
2421
2422 return (mctx->methods->memstrdup(mctx, s FLARG_PASS));
2423 }
2424
2425 char *
isc__mem_strndup(isc_mem_t * mctx,const char * s,size_t size FLARG)2426 isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) {
2427 REQUIRE(ISCAPI_MCTX_VALID(mctx));
2428
2429 return (mctx->methods->memstrndup(mctx, s, size FLARG_PASS));
2430 }
2431
2432 void
isc__mem_free(isc_mem_t * mctx,void * ptr FLARG)2433 isc__mem_free(isc_mem_t *mctx, void *ptr FLARG) {
2434 REQUIRE(ISCAPI_MCTX_VALID(mctx));
2435
2436 mctx->methods->memfree(mctx, ptr FLARG_PASS);
2437 }
2438
2439 void
isc__mem_printactive(isc_mem_t * ctx0,FILE * file)2440 isc__mem_printactive(isc_mem_t *ctx0, FILE *file) {
2441 #if ISC_MEM_TRACKLINES
2442 REQUIRE(VALID_CONTEXT(ctx0));
2443 REQUIRE(file != NULL);
2444
2445 isc__mem_t *ctx = (isc__mem_t *)ctx0;
2446
2447 print_active(ctx, file);
2448 #else /* if ISC_MEM_TRACKLINES */
2449 UNUSED(ctx0);
2450 UNUSED(file);
2451 #endif /* if ISC_MEM_TRACKLINES */
2452 }
2453