1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2008-2013 Sourcefire, Inc.
4  *
5  *  Authors: aCaB <acab@clamav.net>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA 02110-1301, USA.
20  */
21 
22 /* a naive pool allocator */
23 
24 #if HAVE_CONFIG_H
25 #include "clamav-config.h"
26 #endif
27 
28 #ifdef USE_MPOOL
29 
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #if HAVE_STRING_H
37 #include <string.h>
38 #endif
39 #if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H)
40 #include <sys/mman.h>
41 #endif
42 #include <stddef.h>
43 
44 #include "clamav.h"
45 #include "others.h"
46 #include "str.h"
47 #include "readdb.h"
48 
49 /*#define CL_DEBUG*/
50 #ifdef CL_DEBUG
51 #include <assert.h>
52 #define MPOOLMAGIC 0xadde
53 #define ALLOCPOISON 0x5a
54 #define FREEPOISON 0xde
55 #endif
56 
57 /*#define DEBUGMPOOL
58 #define EXIT_ON_FLUSH*/
59 #ifdef DEBUGMPOOL
60 #define spam(...) cli_warnmsg(__VA_ARGS__)
61 #else
spam(const char * fmt,...)62 static inline void spam(const char *fmt, ...)
63 {
64     UNUSEDPARAM(fmt);
65 }
66 #endif
67 
68 #include "mpool.h"
69 
70 #undef CL_DEBUG /* bb#2222 */
71 
72 #ifdef C_HPUX
73 #define MIN_FRAGSIZE 1048576 /* Goes with LDFLAGS=-Wl,+pd,1M */
74 #else
75 #define MIN_FRAGSIZE 262144
76 #endif
77 
78 #if SIZEOF_VOID_P == 8
79 static const unsigned int fragsz[] = {
80     8,
81     11,
82     13,
83     16,
84     17,
85     19,
86     20,
87     21,
88     22,
89     23,
90     24,
91     25,
92     26,
93     27,
94     28,
95     29,
96     30,
97     31,
98     32,
99     33,
100     37,
101     40,
102     41,
103     48,
104     56,
105     72,
106     74,
107     75,
108     76,
109     78,
110     79,
111     80,
112     81,
113     101,
114     104,
115     109,
116     113,
117     116,
118     120,
119     128,
120     131,
121     143,
122     151,
123     152,
124     153,
125     196,
126     256,
127     360,
128     403,
129     404,
130     432,
131     486,
132     514,
133     548,
134     578,
135     604,
136     633,
137     697,
138     743,
139     784,
140     839,
141     1176,
142     1536,
143     1666,
144     2056,
145     2168,
146     2392,
147     2985,
148     3221,
149     3433,
150     3753,
151     3832,
152     4104,
153     4280,
154     4696,
155     4952,
156     5256,
157     5826,
158     6264,
159     7176,
160     8440,
161     9096,
162     16392,
163     32780,
164     50961,
165     63504,
166     65558,
167     101912,
168     131088,
169     262144,
170     507976,
171     524296,
172     1048584,
173     2097152,
174     4194304,
175     8388608,
176     16777216,
177     33554432,
178     67108864,
179     134217728,
180     /* MAX_ALLOCATION is 184549376 but that's really not need here */
181     /* ^^ This MAX_ALLOCATION warning for Mac OS should now be fixed */
182 };
183 
184 #else
185 
186 static const unsigned int fragsz[] = {
187     4,
188     5,
189     8,
190     9,
191     11,
192     12,
193     13,
194     14,
195     15,
196     16,
197     17,
198     19,
199     20,
200     21,
201     22,
202     23,
203     24,
204     25,
205     26,
206     27,
207     28,
208     29,
209     30,
210     31,
211     32,
212     33,
213     35,
214     36,
215     37,
216     39,
217     40,
218     41,
219     44,
220     48,
221     49,
222     52,
223     53,
224     56,
225     58,
226     59,
227     60,
228     61,
229     62,
230     63,
231     64,
232     65,
233     68,
234     69,
235     72,
236     73,
237     77,
238     80,
239     81,
240     83,
241     85,
242     88,
243     89,
244     93,
245     96,
246     99,
247     101,
248     103,
249     104,
250     105,
251     108,
252     112,
253     113,
254     115,
255     116,
256     117,
257     119,
258     120,
259     121,
260     124,
261     128,
262     129,
263     131,
264     133,
265     136,
266     137,
267     141,
268     143,
269     145,
270     148,
271     151,
272     152,
273     153,
274     160,
275     168,
276     173,
277     176,
278     184,
279     194,
280     200,
281     208,
282     216,
283     224,
284     229,
285     232,
286     241,
287     244,
288     248,
289     256,
290     257,
291     264,
292     274,
293     280,
294     293,
295     296,
296     304,
297     307,
298     312,
299     326,
300     344,
301     354,
302     372,
303     396,
304     403,
305     418,
306     456,
307     485,
308     514,
309     546,
310     581,
311     608,
312     646,
313     693,
314     740,
315     776,
316     805,
317     828,
318     902,
319     964,
320     1028,
321     1032,
322     1136,
323     1238,
324     1314,
325     1420,
326     1501,
327     1668,
328     1720,
329     1832,
330     1940,
331     2048,
332     2119,
333     2264,
334     2584,
335     2724,
336     2994,
337     3336,
338     3428,
339     3828,
340     4104,
341     4471,
342     4836,
343     5044,
344     5176,
345     5912,
346     6227,
347     6792,
348     7732,
349     8192,
350     11272,
351     12500,
352     16384,
353     32768,
354     63500,
355     65536,
356     131080,
357     253988,
358     262148,
359     524292,
360     1048576,
361     2097152,
362     4194304,
363     8388608,
364     16777216,
365     33554432,
366     67108864,
367     134217728,
368 };
369 #endif
370 
371 #define FRAGSBITS (sizeof(fragsz) / sizeof(fragsz[0]))
372 
373 struct MPMAP {
374     struct MPMAP *next;
375     size_t size;
376     size_t usize;
377 };
378 
379 struct MP {
380     size_t psize;
381     struct FRAG *avail[FRAGSBITS];
382     union {
383         struct MPMAP mpm;
384         uint64_t dummy_align;
385     } u;
386 };
387 
388 /* alignment of fake handled in the code! */
389 struct alloced {
390     uint8_t padding;
391     uint8_t sbits;
392     uint8_t fake;
393 };
394 
395 struct FRAG {
396 #ifdef CL_DEBUG
397     uint16_t magic;
398 #endif
399     union {
400         struct alloced a;
401         struct unaligned_ptr next;
402     } u;
403 };
404 #define FRAG_OVERHEAD (offsetof(struct FRAG, u.a.fake))
405 
align_to_pagesize(struct MP * mp,size_t size)406 static size_t align_to_pagesize(struct MP *mp, size_t size)
407 {
408     return (size / mp->psize + (size % mp->psize != 0)) * mp->psize;
409 }
410 
to_bits(size_t size)411 static unsigned int to_bits(size_t size)
412 {
413     unsigned int i;
414     for (i = 0; i < FRAGSBITS; i++)
415         if (fragsz[i] >= size) return i;
416     return FRAGSBITS;
417 }
418 
from_bits(unsigned int bits)419 static size_t from_bits(unsigned int bits)
420 {
421     if (bits >= FRAGSBITS) return 0;
422     return fragsz[bits];
423 }
424 
alignof(size_t size)425 static inline unsigned int alignof(size_t size)
426 {
427     /* conservative estimate of alignment.
428      * A struct that needs alignment of 'align' is padded by the compiler
429      * so that sizeof(struct)%align == 0
430      * (otherwise you wouldn't be able to use it in an array)
431      * Also align = 2^n.
432      * Largest alignment we need is 8 bytes (ptr/int64), since we don't use long
433      * double or __aligned attribute.
434      * This conservatively estimates that size 32 needs alignment of 8 (even if it might only
435      * need an alignment of 4).
436      */
437     switch (size % 8) {
438         case 0:
439             return 8;
440         case 2:
441         case 6:
442             return 2;
443         case 4:
444             return 4;
445         default:
446             return 1;
447     }
448 }
449 
alignto(size_t p,size_t size)450 static inline size_t alignto(size_t p, size_t size)
451 {
452     /* size is power of 2 */
453     return (p + size - 1) & (~(size - 1));
454 }
455 
mpool_create()456 struct MP *mpool_create()
457 {
458     struct MP mp, *mpool_p;
459     size_t sz;
460     memset(&mp, 0, sizeof(mp));
461     mp.psize       = cli_getpagesize();
462     sz             = align_to_pagesize(&mp, MIN_FRAGSIZE);
463     mp.u.mpm.usize = sizeof(struct MPMAP);
464     mp.u.mpm.size  = sz - sizeof(mp);
465     if (FRAGSBITS > 255) {
466         cli_errmsg("At most 255 frags possible!\n");
467         return NULL;
468     }
469     if (fragsz[0] < sizeof(void *)) {
470         cli_errmsg("fragsz[0] too small!\n");
471         return NULL;
472     }
473 #ifndef _WIN32
474     if ((mpool_p = (struct MP *)mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | ANONYMOUS_MAP, -1, 0)) == MAP_FAILED)
475 #else
476     if (!(mpool_p = (struct MP *)VirtualAlloc(NULL, sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)))
477 #endif
478         return NULL;
479 #ifdef CL_DEBUG
480     memset(mpool_p, ALLOCPOISON, sz);
481 #endif
482     memcpy(mpool_p, &mp, sizeof(mp));
483     spam("Map created @%p->%p - size %lu out of %lu - voidptr=%lu\n", mpool_p, (char *)mpool_p + mp.u.mpm.size, (unsigned long)mp.u.mpm.usize, (unsigned long)mp.u.mpm.size, (unsigned long)SIZEOF_VOID_P);
484     return mpool_p;
485 }
486 
mpool_destroy(struct MP * mp)487 void mpool_destroy(struct MP *mp)
488 {
489     struct MPMAP *mpm_next = mp->u.mpm.next, *mpm;
490     size_t mpmsize;
491 
492     spam("Destroying map @%p\n", mp);
493     while ((mpm = mpm_next)) {
494         mpmsize  = mpm->size;
495         mpm_next = mpm->next;
496 #ifdef CL_DEBUG
497         memset(mpm, FREEPOISON, mpmsize);
498 #endif
499 #ifndef _WIN32
500         munmap((void *)mpm, mpmsize);
501 #else
502         VirtualFree(mpm, 0, MEM_RELEASE);
503 #endif
504     }
505     mpmsize = mp->u.mpm.size;
506 #ifdef CL_DEBUG
507     memset(mp, FREEPOISON, mpmsize + sizeof(*mp));
508 #endif
509 #ifndef _WIN32
510     munmap((void *)mp, mpmsize + sizeof(*mp));
511 #else
512     VirtualFree(mp, 0, MEM_RELEASE);
513 #endif
514 }
515 
mpool_flush(struct MP * mp)516 void mpool_flush(struct MP *mp)
517 {
518     size_t used            = 0, mused;
519     struct MPMAP *mpm_next = mp->u.mpm.next, *mpm;
520 
521 #ifdef EXIT_ON_FLUSH
522     exit(0);
523 #endif
524 
525     while ((mpm = mpm_next)) {
526         mpm_next = mpm->next;
527         mused    = align_to_pagesize(mp, mpm->usize);
528         if (mused < mpm->size) {
529 #ifdef CL_DEBUG
530             memset((char *)mpm + mused, FREEPOISON, mpm->size - mused);
531 #endif
532 #ifndef _WIN32
533             munmap((char *)mpm + mused, mpm->size - mused);
534 #else
535             VirtualFree((char *)mpm + mused, mpm->size - mused, MEM_DECOMMIT);
536 #endif
537             mpm->size = mused;
538         }
539         used += mpm->size;
540     }
541 
542     mused = align_to_pagesize(mp, mp->u.mpm.usize + sizeof(*mp));
543     if (mused < mp->u.mpm.size + sizeof(*mp)) {
544 #ifdef CL_DEBUG
545         memset((char *)mp + mused, FREEPOISON, mp->u.mpm.size + sizeof(*mp) - mused);
546 #endif
547 #ifndef _WIN32
548         munmap((char *)mp + mused, mp->u.mpm.size + sizeof(*mp) - mused);
549 #else
550         VirtualFree((char *)mp + mused, mp->u.mpm.size + sizeof(*mp) - mused, MEM_DECOMMIT);
551 #endif
552         mp->u.mpm.size = mused - sizeof(*mp);
553     }
554     used += mp->u.mpm.size;
555     cli_dbgmsg("pool memory used: %.3f MB\n", used / (1024 * 1024.0));
556     spam("Map flushed @%p, in use: %lu\n", mp, (unsigned long)used);
557 }
558 
mpool_getstats(const struct cl_engine * eng,size_t * used,size_t * total)559 int mpool_getstats(const struct cl_engine *eng, size_t *used, size_t *total)
560 {
561     size_t sum_used = 0, sum_total = 0;
562     const struct MPMAP *mpm;
563     const mpool_t *mp;
564 
565     /* checking refcount is not necessary, but safer */
566     if (!eng || !eng->refcount)
567         return -1;
568     mp = eng->mempool;
569     if (!mp)
570         return -1;
571     for (mpm = &mp->u.mpm; mpm; mpm = mpm->next) {
572         sum_used += mpm->usize;
573         sum_total += mpm->size;
574     }
575     *used  = sum_used;
576     *total = sum_total;
577     return 0;
578 }
579 
align_increase(size_t size,size_t a)580 static inline size_t align_increase(size_t size, size_t a)
581 {
582     /* we must pad with at most a-1 bytes to align start of struct */
583     return size + a - 1;
584 }
585 
allocate_aligned(struct MPMAP * mpm,size_t size,unsigned align,const char * dbg)586 static void *allocate_aligned(struct MPMAP *mpm, size_t size, unsigned align, const char *dbg)
587 {
588     /* We could always align the size to maxalign (8), however that wastes
589      * space.
590      * So just align the start of each allocation as needed, and then see in
591      * which sbits bin we fit into.
592      * Since we are no longer allocating in multiple of 8, we must always
593      * align the start of each allocation!
594      *| end of previous allocation | padding | FRAG_OVERHEAD | ptr_aligned |*/
595     unsigned p         = mpm->usize + FRAG_OVERHEAD;
596     unsigned p_aligned = alignto(p, align);
597     struct FRAG *f     = (struct FRAG *)((char *)mpm + p_aligned - FRAG_OVERHEAD);
598     unsigned realneed  = p_aligned + size - mpm->usize;
599     unsigned int sbits = to_bits(realneed);
600     size_t needed      = from_bits(sbits);
601 #ifdef CL_DEBUG
602     assert(p_aligned + size <= mpm->size);
603 #endif
604     f->u.a.sbits   = sbits;
605     f->u.a.padding = p_aligned - p;
606 
607     mpm->usize += needed;
608 #ifdef CL_DEBUG
609     assert(mpm->usize <= mpm->size);
610 #endif
611     spam("malloc @%p size %lu (%s) origsize %lu overhead %lu\n", f, (unsigned long)realneed, dbg, (unsigned long)size, (unsigned long)(needed - size));
612 #ifdef CL_DEBUG
613     f->magic = MPOOLMAGIC;
614     memset(&f->u.a.fake, ALLOCPOISON, size);
615 #endif
616     return &f->u.a.fake;
617 }
618 
mpool_malloc(struct MP * mp,size_t size)619 void *mpool_malloc(struct MP *mp, size_t size)
620 {
621     size_t align = alignof(size);
622     size_t i, needed = align_increase(size + FRAG_OVERHEAD, align);
623     const unsigned int sbits = to_bits(needed);
624     struct FRAG *f           = NULL;
625     struct MPMAP *mpm        = &mp->u.mpm;
626 
627     /*  check_all(mp); */
628     if (!size || sbits == FRAGSBITS) {
629         cli_errmsg("mpool_malloc(): Attempt to allocate %lu bytes. Please report to https://github.com/Cisco-Talos/clamav/issues\n", (unsigned long)size);
630         return NULL;
631     }
632 
633     /* Case 1: We have a free'd frag */
634     if ((f = mp->avail[sbits])) {
635         struct FRAG *fold = f;
636         mp->avail[sbits]  = f->u.next.ptr;
637         /* we always have enough space for this, align_increase ensured that */
638 #ifdef _WIN64
639         f = (struct FRAG *)(alignto((unsigned long long)f + FRAG_OVERHEAD, align) - FRAG_OVERHEAD);
640 #else
641         f = (struct FRAG *)(alignto((unsigned long)f + FRAG_OVERHEAD, align) - FRAG_OVERHEAD);
642 #endif
643         f->u.a.sbits   = sbits;
644         f->u.a.padding = (char *)f - (char *)fold;
645 #ifdef CL_DEBUG
646         f->magic = MPOOLMAGIC;
647         memset(&f->u.a.fake, ALLOCPOISON, size);
648 #endif
649         spam("malloc @%p size %lu (freed) origsize %lu overhead %lu\n", f, (unsigned long)(f->u.a.padding + FRAG_OVERHEAD + size), (unsigned long)size, (unsigned long)(needed - size));
650         return &f->u.a.fake;
651     }
652 
653     if (!(needed = from_bits(sbits))) {
654         cli_errmsg("mpool_malloc(): Attempt to allocate %lu bytes. Please report to https://github.com/Cisco-Talos/clamav/issues\n", (unsigned long)size);
655         return NULL;
656     }
657 
658     /* Case 2: We have nuff room available for this frag already */
659     while (mpm) {
660         if (mpm->size - mpm->usize >= needed)
661             return allocate_aligned(mpm, size, align, "hole");
662         mpm = mpm->next;
663     }
664 
665     /* Case 3: We allocate more */
666     if (needed + sizeof(*mpm) > MIN_FRAGSIZE)
667         i = align_to_pagesize(mp, needed + sizeof(*mpm));
668     else
669         i = align_to_pagesize(mp, MIN_FRAGSIZE);
670 
671 #ifndef _WIN32
672     if ((mpm = (struct MPMAP *)mmap(NULL, i, PROT_READ | PROT_WRITE, MAP_PRIVATE | ANONYMOUS_MAP, -1, 0)) == MAP_FAILED) {
673 #else
674     if (!(mpm = (struct MPMAP *)VirtualAlloc(NULL, i, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE))) {
675 #endif
676         cli_errmsg("mpool_malloc(): Can't allocate memory (%lu bytes).\n", (unsigned long)i);
677         spam("failed to alloc %lu bytes (%lu requested)\n", (unsigned long)i, (unsigned long)size);
678         return NULL;
679     }
680 #ifdef CL_DEBUG
681     memset(mpm, ALLOCPOISON, i);
682 #endif
683     mpm->size      = i;
684     mpm->usize     = sizeof(*mpm);
685     mpm->next      = mp->u.mpm.next;
686     mp->u.mpm.next = mpm;
687     return allocate_aligned(mpm, size, align, "new map");
688 }
689 
690 static void *allocbase_fromfrag(struct FRAG *f)
691 {
692 #ifdef CL_DEBUG
693     assert(f->u.a.padding < 8);
694 #endif
695     return (char *)f - f->u.a.padding;
696 }
697 
698 void mpool_free(struct MP *mp, void *ptr)
699 {
700     struct FRAG *f = (struct FRAG *)((char *)ptr - FRAG_OVERHEAD);
701     unsigned int sbits;
702     if (!ptr) return;
703 
704 #ifdef CL_DEBUG
705     assert(f->magic == MPOOLMAGIC && "Attempt to mpool_free a pointer we did not allocate!");
706 #endif
707 
708     spam("free @%p\n", f);
709     sbits = f->u.a.sbits;
710     f     = allocbase_fromfrag(f);
711 #ifdef CL_DEBUG
712     memset(f, FREEPOISON, from_bits(sbits));
713 #endif
714 
715     f->u.next.ptr    = mp->avail[sbits];
716     mp->avail[sbits] = f;
717 }
718 
719 void *mpool_calloc(struct MP *mp, size_t nmemb, size_t size)
720 {
721     size_t needed = nmemb * size;
722     void *ptr;
723 
724     if (!needed) return NULL;
725     if ((ptr = mpool_malloc(mp, needed)))
726         memset(ptr, 0, needed);
727     return ptr;
728 }
729 
730 void *mpool_realloc(struct MP *mp, void *ptr, size_t size)
731 {
732     struct FRAG *f = (struct FRAG *)((char *)ptr - FRAG_OVERHEAD);
733     size_t csize;
734     void *new_ptr;
735     if (!ptr) return mpool_malloc(mp, size);
736 
737     if (!size || !(csize = from_bits(f->u.a.sbits))) {
738         cli_errmsg("mpool_realloc(): Attempt to allocate %lu bytes. Please report to https://github.com/Cisco-Talos/clamav/issues\n", (unsigned long)size);
739         return NULL;
740     }
741     csize -= FRAG_OVERHEAD + f->u.a.padding;
742     if (csize >= size && (!f->u.a.sbits || from_bits(f->u.a.sbits - 1) - FRAG_OVERHEAD - f->u.a.padding < size)) {
743         spam("free @%p\n", f);
744         spam("malloc @%p size %lu (self) origsize %lu overhead %lu\n", f, (unsigned long)(size + FRAG_OVERHEAD + f->u.a.padding), (unsigned long)size, (unsigned long)(csize - size + FRAG_OVERHEAD + f->u.a.padding));
745         return ptr;
746     }
747     if (!(new_ptr = mpool_malloc(mp, size)))
748         return NULL;
749     memcpy(new_ptr, ptr, csize <= size ? csize : size);
750     mpool_free(mp, ptr);
751     return new_ptr;
752 }
753 
754 void *mpool_realloc2(struct MP *mp, void *ptr, size_t size)
755 {
756     void *new_ptr = mpool_realloc(mp, ptr, size);
757     if (new_ptr)
758         return new_ptr;
759     mpool_free(mp, ptr);
760     return NULL;
761 }
762 
763 char *cli_mpool_hex2str(mpool_t *mp, const char *hex)
764 {
765     char *str;
766     size_t len = strlen((const char *)hex);
767 
768     if (len & 1) {
769         cli_errmsg("cli_mpool_hex2str(): Malformed hexstring: %s (length: %lu)\n", hex, (unsigned long)len);
770         return NULL;
771     }
772 
773     str = mpool_malloc(mp, (len / 2) + 1);
774     if (str == NULL) { /* oops, we have a memory pool allocation failure */
775         cli_errmsg("cli_mpool_hex2str(): Can't allocate memory (%lu bytes).\n", (unsigned long)(len / 2 + 1));
776         return NULL;
777     }
778     if (cli_hex2str_to(hex, str, len) == -1) {
779         mpool_free(mp, str);
780         return NULL;
781     }
782     str[len / 2] = '\0';
783     return str;
784 }
785 
786 char *cli_mpool_strdup(mpool_t *mp, const char *s)
787 {
788     char *alloc;
789     size_t strsz;
790 
791     if (s == NULL) {
792         cli_errmsg("cli_mpool_strdup(): s == NULL. Please report to https://github.com/Cisco-Talos/clamav/issues\n");
793         return NULL;
794     }
795 
796     strsz = strlen(s) + 1;
797     alloc = mpool_malloc(mp, strsz);
798     if (!alloc)
799         cli_errmsg("cli_mpool_strdup(): Can't allocate memory (%lu bytes).\n", (unsigned long)strsz);
800     else
801         memcpy(alloc, s, strsz);
802     return alloc;
803 }
804 
805 char *cli_mpool_strndup(mpool_t *mp, const char *s, size_t n)
806 {
807     char *alloc;
808     size_t strsz;
809 
810     if (s == NULL) {
811         cli_errmsg("cli_mpool_strndup(): s == NULL. Please report to https://github.com/Cisco-Talos/clamav/issues\n");
812         return NULL;
813     }
814 
815     strsz = CLI_STRNLEN(s, n) + 1;
816     alloc = mpool_malloc(mp, strsz);
817     if (!alloc)
818         cli_errmsg("cli_mpool_strndup(): Can't allocate memory (%lu bytes).\n", (unsigned long)strsz);
819     else
820         memcpy(alloc, s, strsz - 1);
821     alloc[strsz - 1] = '\0';
822     return alloc;
823 }
824 
825 /* #define EXPAND_PUA */
826 char *cli_mpool_virname(mpool_t *mp, const char *virname, unsigned int official)
827 {
828     char *newname, *pt;
829 #ifdef EXPAND_PUA
830     char buf[1024];
831 #endif
832 
833     if (!virname)
834         return NULL;
835 
836     if ((pt = strchr(virname, ' ')))
837         if ((pt = strstr(pt, " (Clam)")))
838             *pt = '\0';
839 
840     if (!virname[0]) {
841         cli_errmsg("cli_mpool_virname: Empty virus name\n");
842         return NULL;
843     }
844 
845 #ifdef EXPAND_PUA
846     if (!strncmp(virname, "PUA.", 4)) {
847         snprintf(buf, sizeof(buf), "Possibly-Unwanted-Application(www.clamav.net/support/pua).%s", virname + 4);
848         buf[sizeof(buf) - 1] = '\0';
849         virname              = buf;
850     }
851 #endif
852     if (official)
853         return cli_mpool_strdup(mp, virname);
854 
855     newname = (char *)mpool_malloc(mp, strlen(virname) + 11 + 1);
856     if (!newname) {
857         cli_errmsg("cli_mpool_virname: Can't allocate memory for newname\n");
858         return NULL;
859     }
860     sprintf(newname, "%s.UNOFFICIAL", virname);
861     return newname;
862 }
863 
864 uint16_t *cli_mpool_hex2ui(mpool_t *mp, const char *hex)
865 {
866     uint16_t *str;
867     size_t len;
868 
869     len = strlen(hex);
870 
871     if (len % 2 != 0) {
872         cli_errmsg("cli_mpool_hex2ui(): Malformed hexstring: %s (length: %lu)\n", hex, (unsigned long)len);
873         return NULL;
874     }
875 
876     str = mpool_calloc(mp, (len / 2) + 1, sizeof(uint16_t));
877     if (!str)
878         return NULL;
879 
880     if (cli_realhex2ui(hex, str, len))
881         return str;
882 
883     mpool_free(mp, str);
884     return NULL;
885 }
886 
887 #ifdef DEBUGMPOOL
888 void mpool_stats(struct MP *mp)
889 {
890     size_t i = 0, ta = 0, tu = 0;
891     struct MPMAP *mpm = &mp->u.mpm;
892 
893     cli_warnmsg("MEMORY POOL STATISTICS\n map  \tsize\tused\t%\n");
894     while (mpm) {
895         cli_warnmsg("- %lu\t%lu\t%lu\t%f%%\n", (unsigned long)i, (unsigned long)(mpm->size), (unsigned long)(mpm->usize), (float)mpm->usize / (float)mpm->size * 100);
896         ta += mpm->size;
897         tu += mpm->usize;
898         i++;
899         mpm = mpm->next;
900     }
901     cli_warnmsg("MEMORY POOL SUMMARY\nMaps: %lu\nTotal: %lu\nUsed: %lu (%f%%)\n", (unsigned long)i, (unsigned long)ta, (unsigned long)tu, (float)tu / (float)ta * 100);
902 }
903 
904 void check_all(struct MP *mp)
905 {
906     struct MPMAP *mpm = &mp->u.mpm;
907     while (mpm) {
908         volatile unsigned char *c = (unsigned char *)mpm;
909         size_t len                = mpm->size;
910         spam("checking object %p - size %lu\n", mpm, (unsigned long)len);
911         while (len--) {
912             c[len];
913         }
914         mpm = mpm->next;
915     }
916 }
917 #endif /* DEBUGMPOOL */
918 
919 #else
920 /* dummy definitions to make Solaris linker happy.
921  * these symbols are declared in libclamav.map */
922 void mpool_free() {}
923 void mpool_create() {}
924 void mpool_destroy() {}
925 void mpool_getstats() {}
926 void mpool_calloc() {}
927 
928 #endif /* USE_MPOOL */
929