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