libclamav/mpool.c
e21657df
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2008-2013 Sourcefire, Inc.
e21657df
  *
946bd266
  *  Authors: aCaB <acab@clamav.net>
e21657df
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
 /* a naive pool allocator */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #ifdef USE_MPOOL
160930d4
 
e21657df
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 #if HAVE_STRING_H
 #include <string.h>
 #endif
11195c0b
 #if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H)
 #include <sys/mman.h>
 #endif
e21657df
 #include <stddef.h>
 
60d8d2c3
 #include "clamav.h"
946bd266
 #include "others.h"
ad0fd728
 #include "str.h"
d6e1ef16
 #include "readdb.h"
e21657df
 
d068119a
 /*#define CL_DEBUG*/
1b802d3c
 #ifdef CL_DEBUG
 #include <assert.h>
d068119a
 #define MPOOLMAGIC 0xadde
1b802d3c
 #define ALLOCPOISON 0x5a
 #define FREEPOISON 0xde
 #endif
 
d068119a
 /*#define DEBUGMPOOL
 #define EXIT_ON_FLUSH*/
e21657df
 #ifdef DEBUGMPOOL
288057e9
 #define spam(...) cli_warnmsg(__VA_ARGS__)
e21657df
 #else
288057e9
 static inline void spam(const char *fmt, ...)
 {
     UNUSEDPARAM(fmt);
 }
e21657df
 #endif
 
 #include "mpool.h"
 
80871017
 #undef CL_DEBUG /* bb#2222 */
 
18f620f2
 #ifdef C_HPUX
288057e9
 #define MIN_FRAGSIZE 1048576 /* Goes with LDFLAGS=-Wl,+pd,1M */
18f620f2
 #else
34e79a80
 #define MIN_FRAGSIZE 262144
18f620f2
 #endif
e21657df
 
288057e9
 #if SIZEOF_VOID_P == 8
0281fb4d
 static const unsigned int fragsz[] = {
697b276e
     8,
     11,
     13,
     16,
     17,
     19,
     20,
     21,
     22,
     23,
     24,
     25,
     26,
     27,
     28,
     29,
     30,
     31,
     32,
     33,
     37,
     40,
     41,
     48,
     56,
     72,
     74,
     75,
     76,
     78,
     79,
     80,
     81,
     101,
     104,
     109,
     113,
     116,
     120,
     128,
     131,
     143,
     151,
     152,
     153,
     196,
     256,
     360,
     403,
     404,
     432,
     486,
     514,
     548,
     578,
     604,
     633,
     697,
     743,
     784,
     839,
288057e9
     1176,
     1536,
     1666,
     2056,
     2168,
     2392,
     2985,
     3221,
     3433,
     3753,
     3832,
     4104,
     4280,
     4696,
     4952,
     5256,
     5826,
     6264,
     7176,
     8440,
     9096,
     16392,
     32780,
     50961,
     63504,
     65558,
     101912,
     131088,
     262144,
     507976,
     524296,
     1048584,
     2097152,
     4194304,
     8388608,
     16777216,
     33554432,
     67108864,
     134217728,
     /* MAX_ALLOCATION is 184549376 but that's really not need here */
     /* ^^ This MAX_ALLOCATION warning for Mac OS should now be fixed */
0281fb4d
 };
 
 #else
 
5684130b
 static const unsigned int fragsz[] = {
d4c6e1d6
     4,
     5,
     8,
     9,
     11,
     12,
     13,
     14,
     15,
     16,
     17,
     19,
     20,
     21,
     22,
     23,
     24,
     25,
     26,
     27,
     28,
     29,
     30,
     31,
     32,
     33,
     35,
     36,
     37,
     39,
     40,
     41,
     44,
     48,
     49,
     52,
     53,
     56,
     58,
     59,
     60,
     61,
     62,
     63,
     64,
     65,
     68,
     69,
     72,
     73,
     77,
     80,
     81,
     83,
     85,
     88,
     89,
     93,
     96,
     99,
     101,
     103,
     104,
     105,
     108,
     112,
     113,
     115,
     116,
     117,
     119,
     120,
     121,
     124,
     128,
     129,
     131,
     133,
     136,
     137,
     141,
     143,
     145,
     148,
     151,
     152,
     153,
     160,
     168,
     173,
     176,
     184,
     194,
     200,
     208,
     216,
     224,
     229,
     232,
     241,
     244,
     248,
     256,
     257,
     264,
     274,
     280,
     293,
     296,
     304,
     307,
     312,
     326,
     344,
     354,
     372,
     396,
     403,
     418,
     456,
     485,
     514,
     546,
     581,
     608,
     646,
     693,
     740,
     776,
     805,
     828,
     902,
     964,
     1028,
     1032,
     1136,
     1238,
     1314,
     1420,
     1501,
     1668,
     1720,
     1832,
     1940,
     2048,
     2119,
     2264,
     2584,
     2724,
     2994,
     3336,
     3428,
     3828,
     4104,
     4471,
     4836,
     5044,
     5176,
     5912,
     6227,
     6792,
     7732,
     8192,
     11272,
     12500,
     16384,
     32768,
     63500,
     65536,
     131080,
     253988,
     262148,
     524292,
     1048576,
     2097152,
     4194304,
     8388608,
288057e9
     16777216,
     33554432,
     67108864,
     134217728,
0281fb4d
 };
 #endif
cd7c9a4f
 
288057e9
 #define FRAGSBITS (sizeof(fragsz) / sizeof(fragsz[0]))
0281fb4d
 
 struct MPMAP {
288057e9
     struct MPMAP *next;
     size_t size;
     size_t usize;
0281fb4d
 };
 
 struct MP {
288057e9
     size_t psize;
     struct FRAG *avail[FRAGSBITS];
     union {
         struct MPMAP mpm;
         uint64_t dummy_align;
     } u;
0281fb4d
 };
e21657df
 
d068119a
 /* alignment of fake handled in the code! */
 struct alloced {
     uint8_t padding;
     uint8_t sbits;
     uint8_t fake;
 };
 
e21657df
 struct FRAG {
261e29da
 #ifdef CL_DEBUG
288057e9
     uint16_t magic;
261e29da
 #endif
288057e9
     union {
         struct alloced a;
         struct unaligned_ptr next;
     } u;
e21657df
 };
d068119a
 #define FRAG_OVERHEAD (offsetof(struct FRAG, u.a.fake))
e21657df
 
288057e9
 static size_t align_to_pagesize(struct MP *mp, size_t size)
 {
     return (size / mp->psize + (size % mp->psize != 0)) * mp->psize;
e21657df
 }
 
288057e9
 static unsigned int to_bits(size_t size)
 {
     unsigned int i;
     for (i = 0; i < FRAGSBITS; i++)
         if (fragsz[i] >= size) return i;
     return FRAGSBITS;
e21657df
 }
d068119a
 
288057e9
 static size_t from_bits(unsigned int bits)
 {
     if (bits >= FRAGSBITS) return 0;
     return fragsz[bits];
e21657df
 }
 
110714bb
 static inline unsigned int alignof(size_t size)
d068119a
 {
     /* conservative estimate of alignment.
      * A struct that needs alignment of 'align' is padded by the compiler
544fa973
      * so that sizeof(struct)%align == 0
d068119a
      * (otherwise you wouldn't be able to use it in an array)
      * Also align = 2^n.
      * Largest alignment we need is 8 bytes (ptr/int64), since we don't use long
      * double or __aligned attribute.
      * This conservatively estimates that size 32 needs alignment of 8 (even if it might only
      * need an alignment of 4).
      */
288057e9
     switch (size % 8) {
         case 0:
             return 8;
         case 2:
         case 6:
             return 2;
         case 4:
             return 4;
         default:
             return 1;
d068119a
     }
 }
 
 static inline size_t alignto(size_t p, size_t size)
 {
     /* size is power of 2 */
288057e9
     return (p + size - 1) & (~(size - 1));
d068119a
 }
 
288057e9
 struct MP *mpool_create()
 {
     struct MP mp, *mpool_p;
     size_t sz;
     memset(&mp, 0, sizeof(mp));
     mp.psize       = cli_getpagesize();
     sz             = align_to_pagesize(&mp, MIN_FRAGSIZE);
     mp.u.mpm.usize = sizeof(struct MPMAP);
     mp.u.mpm.size  = sz - sizeof(mp);
     if (FRAGSBITS > 255) {
         cli_errmsg("At most 255 frags possible!\n");
         return NULL;
     }
     if (fragsz[0] < sizeof(void *)) {
         cli_errmsg("fragsz[0] too small!\n");
         return NULL;
     }
f526f0b2
 #ifndef _WIN32
288057e9
     if ((mpool_p = (struct MP *)mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | ANONYMOUS_MAP, -1, 0)) == MAP_FAILED)
f526f0b2
 #else
288057e9
     if (!(mpool_p = (struct MP *)VirtualAlloc(NULL, sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)))
f526f0b2
 #endif
288057e9
         return NULL;
1b802d3c
 #ifdef CL_DEBUG
288057e9
     memset(mpool_p, ALLOCPOISON, sz);
1b802d3c
 #endif
288057e9
     memcpy(mpool_p, &mp, sizeof(mp));
     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);
     return mpool_p;
e21657df
 }
 
288057e9
 void mpool_destroy(struct MP *mp)
 {
     struct MPMAP *mpm_next = mp->u.mpm.next, *mpm;
     size_t mpmsize;
1b802d3c
 
50876732
     spam("Destroying map @%p\n", mp);
288057e9
     while ((mpm = mpm_next)) {
         mpmsize  = mpm->size;
         mpm_next = mpm->next;
1b802d3c
 #ifdef CL_DEBUG
288057e9
         memset(mpm, FREEPOISON, mpmsize);
1b802d3c
 #endif
4743d54f
 #ifndef _WIN32
288057e9
         munmap((void *)mpm, mpmsize);
4743d54f
 #else
288057e9
         VirtualFree(mpm, 0, MEM_RELEASE);
4743d54f
 #endif
288057e9
     }
     mpmsize = mp->u.mpm.size;
1b802d3c
 #ifdef CL_DEBUG
288057e9
     memset(mp, FREEPOISON, mpmsize + sizeof(*mp));
1b802d3c
 #endif
4743d54f
 #ifndef _WIN32
288057e9
     munmap((void *)mp, mpmsize + sizeof(*mp));
4743d54f
 #else
288057e9
     VirtualFree(mp, 0, MEM_RELEASE);
4743d54f
 #endif
e21657df
 }
 
288057e9
 void mpool_flush(struct MP *mp)
 {
     size_t used            = 0, mused;
630d47cb
     struct MPMAP *mpm_next = mp->u.mpm.next, *mpm;
1b802d3c
 
34e79a80
 #ifdef EXIT_ON_FLUSH
     exit(0);
 #endif
 
288057e9
     while ((mpm = mpm_next)) {
         mpm_next = mpm->next;
         mused    = align_to_pagesize(mp, mpm->usize);
         if (mused < mpm->size) {
1b802d3c
 #ifdef CL_DEBUG
288057e9
             memset((char *)mpm + mused, FREEPOISON, mpm->size - mused);
1b802d3c
 #endif
4743d54f
 #ifndef _WIN32
288057e9
             munmap((char *)mpm + mused, mpm->size - mused);
4743d54f
 #else
288057e9
             VirtualFree((char *)mpm + mused, mpm->size - mused, MEM_DECOMMIT);
4743d54f
 #endif
288057e9
             mpm->size = mused;
         }
         used += mpm->size;
34e79a80
     }
 
d068119a
     mused = align_to_pagesize(mp, mp->u.mpm.usize + sizeof(*mp));
     if (mused < mp->u.mpm.size + sizeof(*mp)) {
1b802d3c
 #ifdef CL_DEBUG
288057e9
         memset((char *)mp + mused, FREEPOISON, mp->u.mpm.size + sizeof(*mp) - mused);
1b802d3c
 #endif
4743d54f
 #ifndef _WIN32
288057e9
         munmap((char *)mp + mused, mp->u.mpm.size + sizeof(*mp) - mused);
4743d54f
 #else
288057e9
         VirtualFree((char *)mp + mused, mp->u.mpm.size + sizeof(*mp) - mused, MEM_DECOMMIT);
4743d54f
 #endif
288057e9
         mp->u.mpm.size = mused - sizeof(*mp);
34e79a80
     }
630d47cb
     used += mp->u.mpm.size;
288057e9
     cli_dbgmsg("pool memory used: %.3f MB\n", used / (1024 * 1024.0));
110714bb
     spam("Map flushed @%p, in use: %lu\n", mp, (unsigned long)used);
deb30312
 }
 
47d40feb
 int mpool_getstats(const struct cl_engine *eng, size_t *used, size_t *total)
deb30312
 {
288057e9
     size_t sum_used = 0, sum_total = 0;
     const struct MPMAP *mpm;
     const mpool_t *mp;
 
     /* checking refcount is not necessary, but safer */
     if (!eng || !eng->refcount)
         return -1;
     mp = eng->mempool;
     if (!mp)
         return -1;
     for (mpm = &mp->u.mpm; mpm; mpm = mpm->next) {
         sum_used += mpm->usize;
         sum_total += mpm->size;
     }
     *used  = sum_used;
     *total = sum_total;
     return 0;
e21657df
 }
 
110714bb
 static inline size_t align_increase(size_t size, size_t a)
d068119a
 {
     /* we must pad with at most a-1 bytes to align start of struct */
     return size + a - 1;
 }
 
288057e9
 static void *allocate_aligned(struct MPMAP *mpm, size_t size, unsigned align, const char *dbg)
d068119a
 {
     /* We could always align the size to maxalign (8), however that wastes
      * space.
      * So just align the start of each allocation as needed, and then see in
      * which sbits bin we fit into.
      * Since we are no longer allocating in multiple of 8, we must always
      * align the start of each allocation!
      *| end of previous allocation | padding | FRAG_OVERHEAD | ptr_aligned |*/
288057e9
     unsigned p         = mpm->usize + FRAG_OVERHEAD;
d068119a
     unsigned p_aligned = alignto(p, align);
288057e9
     struct FRAG *f     = (struct FRAG *)((char *)mpm + p_aligned - FRAG_OVERHEAD);
     unsigned realneed  = p_aligned + size - mpm->usize;
110714bb
     unsigned int sbits = to_bits(realneed);
288057e9
     size_t needed      = from_bits(sbits);
d068119a
 #ifdef CL_DEBUG
     assert(p_aligned + size <= mpm->size);
 #endif
288057e9
     f->u.a.sbits   = sbits;
d068119a
     f->u.a.padding = p_aligned - p;
 
     mpm->usize += needed;
 #ifdef CL_DEBUG
     assert(mpm->usize <= mpm->size);
 #endif
110714bb
     spam("malloc @%p size %lu (%s) origsize %lu overhead %lu\n", f, (unsigned long)realneed, dbg, (unsigned long)size, (unsigned long)(needed - size));
d068119a
 #ifdef CL_DEBUG
     f->magic = MPOOLMAGIC;
     memset(&f->u.a.fake, ALLOCPOISON, size);
 #endif
     return &f->u.a.fake;
 }
 
288057e9
 void *mpool_malloc(struct MP *mp, size_t size)
 {
     size_t align = alignof(size);
     size_t i, needed = align_increase(size + FRAG_OVERHEAD, align);
     const unsigned int sbits = to_bits(needed);
     struct FRAG *f           = NULL;
     struct MPMAP *mpm        = &mp->u.mpm;
 
     /*  check_all(mp); */
     if (!size || sbits == FRAGSBITS) {
         cli_errmsg("mpool_malloc(): Attempt to allocate %lu bytes. Please report to https://bugzilla.clamav.net\n", (unsigned long)size);
         return NULL;
     }
e21657df
 
288057e9
     /* Case 1: We have a free'd frag */
     if ((f = mp->avail[sbits])) {
         struct FRAG *fold = f;
         mp->avail[sbits]  = f->u.next.ptr;
         /* we always have enough space for this, align_increase ensured that */
ac20e91b
 #ifdef _WIN64
288057e9
         f = (struct FRAG *)(alignto((unsigned long long)f + FRAG_OVERHEAD, align) - FRAG_OVERHEAD);
ac20e91b
 #else
288057e9
         f = (struct FRAG *)(alignto((unsigned long)f + FRAG_OVERHEAD, align) - FRAG_OVERHEAD);
ac20e91b
 #endif
288057e9
         f->u.a.sbits   = sbits;
         f->u.a.padding = (char *)f - (char *)fold;
ad0fd728
 #ifdef CL_DEBUG
288057e9
         f->magic = MPOOLMAGIC;
         memset(&f->u.a.fake, ALLOCPOISON, size);
ad0fd728
 #endif
288057e9
         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));
         return &f->u.a.fake;
     }
e21657df
 
288057e9
     if (!(needed = from_bits(sbits))) {
         cli_errmsg("mpool_malloc(): Attempt to allocate %lu bytes. Please report to https://bugzilla.clamav.net\n", (unsigned long)size);
         return NULL;
     }
e21657df
 
288057e9
     /* Case 2: We have nuff room available for this frag already */
     while (mpm) {
         if (mpm->size - mpm->usize >= needed)
             return allocate_aligned(mpm, size, align, "hole");
         mpm = mpm->next;
     }
e21657df
 
288057e9
     /* Case 3: We allocate more */
     if (needed + sizeof(*mpm) > MIN_FRAGSIZE)
         i = align_to_pagesize(mp, needed + sizeof(*mpm));
     else
         i = align_to_pagesize(mp, MIN_FRAGSIZE);
4743d54f
 
 #ifndef _WIN32
288057e9
     if ((mpm = (struct MPMAP *)mmap(NULL, i, PROT_READ | PROT_WRITE, MAP_PRIVATE | ANONYMOUS_MAP, -1, 0)) == MAP_FAILED) {
4743d54f
 #else
288057e9
     if (!(mpm = (struct MPMAP *)VirtualAlloc(NULL, i, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE))) {
4743d54f
 #endif
288057e9
         cli_errmsg("mpool_malloc(): Can't allocate memory (%lu bytes).\n", (unsigned long)i);
         spam("failed to alloc %lu bytes (%lu requested)\n", (unsigned long)i, (unsigned long)size);
         return NULL;
     }
1b802d3c
 #ifdef CL_DEBUG
288057e9
     memset(mpm, ALLOCPOISON, i);
1b802d3c
 #endif
288057e9
     mpm->size      = i;
     mpm->usize     = sizeof(*mpm);
     mpm->next      = mp->u.mpm.next;
     mp->u.mpm.next = mpm;
     return allocate_aligned(mpm, size, align, "new map");
d068119a
 }
 
 static void *allocbase_fromfrag(struct FRAG *f)
 {
7866b37c
 #ifdef CL_DEBUG
d068119a
     assert(f->u.a.padding < 8);
7866b37c
 #endif
288057e9
     return (char *)f - f->u.a.padding;
e21657df
 }
 
288057e9
 void mpool_free(struct MP *mp, void *ptr)
 {
     struct FRAG *f = (struct FRAG *)((char *)ptr - FRAG_OVERHEAD);
     unsigned int sbits;
     if (!ptr) return;
e21657df
 
7866b37c
 #ifdef CL_DEBUG
288057e9
     assert(f->magic == MPOOLMAGIC && "Attempt to mpool_free a pointer we did not allocate!");
7866b37c
 #endif
 
288057e9
     spam("free @%p\n", f);
     sbits = f->u.a.sbits;
     f     = allocbase_fromfrag(f);
d068119a
 #ifdef CL_DEBUG
288057e9
     memset(f, FREEPOISON, from_bits(sbits));
d068119a
 #endif
 
288057e9
     f->u.next.ptr    = mp->avail[sbits];
     mp->avail[sbits] = f;
e21657df
 }
 
288057e9
 void *mpool_calloc(struct MP *mp, size_t nmemb, size_t size)
 {
     size_t needed = nmemb * size;
     void *ptr;
e21657df
 
288057e9
     if (!needed) return NULL;
     if ((ptr = mpool_malloc(mp, needed)))
         memset(ptr, 0, needed);
     return ptr;
e21657df
 }
 
288057e9
 void *mpool_realloc(struct MP *mp, void *ptr, size_t size)
 {
     struct FRAG *f = (struct FRAG *)((char *)ptr - FRAG_OVERHEAD);
     size_t csize;
     void *new_ptr;
     if (!ptr) return mpool_malloc(mp, size);
 
     if (!size || !(csize = from_bits(f->u.a.sbits))) {
         cli_errmsg("mpool_realloc(): Attempt to allocate %lu bytes. Please report to https://bugzilla.clamav.net\n", (unsigned long)size);
         return NULL;
     }
     csize -= FRAG_OVERHEAD + f->u.a.padding;
     if (csize >= size && (!f->u.a.sbits || from_bits(f->u.a.sbits - 1) - FRAG_OVERHEAD - f->u.a.padding < size)) {
         spam("free @%p\n", f);
         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));
         return ptr;
     }
     if (!(new_ptr = mpool_malloc(mp, size)))
         return NULL;
     memcpy(new_ptr, ptr, csize <= size ? csize : size);
     mpool_free(mp, ptr);
     return new_ptr;
e21657df
 }
 
288057e9
 void *mpool_realloc2(struct MP *mp, void *ptr, size_t size)
 {
34e79a80
     void *new_ptr = mpool_realloc(mp, ptr, size);
288057e9
     if (new_ptr)
         return new_ptr;
47d40feb
     mpool_free(mp, ptr);
0281fb4d
     return NULL;
e21657df
 }
 
288057e9
 char *cli_mpool_hex2str(mpool_t *mp, const char *hex)
 {
7b1f1aaf
     char *str;
288057e9
     size_t len = strlen((const char *)hex);
eaf2aebd
 
288057e9
     if (len & 1) {
544fa973
         cli_errmsg("cli_mpool_hex2str(): Malformed hexstring: %s (length: %lu)\n", hex, (unsigned long)len);
288057e9
         return NULL;
eaf2aebd
     }
 
288057e9
     str = mpool_malloc(mp, (len / 2) + 1);
e522909e
     if (str == NULL) { /* oops, we have a memory pool allocation failure */
288057e9
         cli_errmsg("cli_mpool_hex2str(): Can't allocate memory (%lu bytes).\n", (unsigned long)(len / 2 + 1));
         return NULL;
e522909e
     }
7b1f1aaf
     if (cli_hex2str_to(hex, str, len) == -1) {
288057e9
         mpool_free(mp, str);
         return NULL;
eaf2aebd
     }
288057e9
     str[len / 2] = '\0';
eaf2aebd
     return str;
ad0fd728
 }
 
288057e9
 char *cli_mpool_strdup(mpool_t *mp, const char *s)
 {
     char *alloc;
     size_t strsz;
d6e1ef16
 
288057e9
     if (s == NULL) {
         cli_errmsg("cli_mpool_strdup(): s == NULL. Please report to https://bugzilla.clamav.net\n");
         return NULL;
     }
 
     strsz = strlen(s) + 1;
     alloc = mpool_malloc(mp, strsz);
     if (!alloc)
         cli_errmsg("cli_mpool_strdup(): Can't allocate memory (%lu bytes).\n", (unsigned long)strsz);
     else
         memcpy(alloc, s, strsz);
     return alloc;
d6e1ef16
 }
 
288057e9
 char *cli_mpool_strndup(mpool_t *mp, const char *s, size_t n)
 {
     char *alloc;
     size_t strsz;
67f8441d
 
288057e9
     if (s == NULL) {
         cli_errmsg("cli_mpool_strndup(): s == NULL. Please report to https://bugzilla.clamav.net\n");
         return NULL;
     }
 
     strsz = cli_strnlen(s, n) + 1;
     alloc = mpool_malloc(mp, strsz);
     if (!alloc)
         cli_errmsg("cli_mpool_strndup(): Can't allocate memory (%lu bytes).\n", (unsigned long)strsz);
     else
         memcpy(alloc, s, strsz - 1);
     alloc[strsz - 1] = '\0';
     return alloc;
67f8441d
 }
 
4f798ac6
 /* #define EXPAND_PUA */
288057e9
 char *cli_mpool_virname(mpool_t *mp, const char *virname, unsigned int official)
 {
     char *newname, *pt;
4f798ac6
 #ifdef EXPAND_PUA
288057e9
     char buf[1024];
4f798ac6
 #endif
 
288057e9
     if (!virname)
         return NULL;
d6e1ef16
 
288057e9
     if ((pt = strchr(virname, ' ')))
         if ((pt = strstr(pt, " (Clam)")))
             *pt = '\0';
d6e1ef16
 
288057e9
     if (!virname[0]) {
544fa973
         cli_errmsg("cli_mpool_virname: Empty virus name\n");
288057e9
         return NULL;
     }
d6e1ef16
 
4f798ac6
 #ifdef EXPAND_PUA
288057e9
     if (!strncmp(virname, "PUA.", 4)) {
         snprintf(buf, sizeof(buf), "Possibly-Unwanted-Application(www.clamav.net/support/pua).%s", virname + 4);
         buf[sizeof(buf) - 1] = '\0';
         virname              = buf;
4f798ac6
     }
 #endif
288057e9
     if (official)
         return cli_mpool_strdup(mp, virname);
d6e1ef16
 
288057e9
     newname = (char *)mpool_malloc(mp, strlen(virname) + 11 + 1);
     if (!newname) {
544fa973
         cli_errmsg("cli_mpool_virname: Can't allocate memory for newname\n");
288057e9
         return NULL;
     }
     sprintf(newname, "%s.UNOFFICIAL", virname);
     return newname;
d6e1ef16
 }
 
288057e9
 uint16_t *cli_mpool_hex2ui(mpool_t *mp, const char *hex)
 {
     uint16_t *str;
     size_t len;
d6e1ef16
 
288057e9
     len = strlen(hex);
38e881e3
 
288057e9
     if (len % 2 != 0) {
         cli_errmsg("cli_mpool_hex2ui(): Malformed hexstring: %s (length: %lu)\n", hex, (unsigned long)len);
         return NULL;
     }
38e881e3
 
288057e9
     str = mpool_calloc(mp, (len / 2) + 1, sizeof(uint16_t));
     if (!str)
         return NULL;
38e881e3
 
288057e9
     if (cli_realhex2ui(hex, str, len))
         return str;
38e881e3
 
288057e9
     mpool_free(mp, str);
     return NULL;
 }
38e881e3
 
946bd266
 #ifdef DEBUGMPOOL
288057e9
 void mpool_stats(struct MP *mp)
 {
     size_t i = 0, ta = 0, tu = 0;
     struct MPMAP *mpm = &mp->u.mpm;
 
     cli_warnmsg("MEMORY POOL STATISTICS\n map  \tsize\tused\t%\n");
     while (mpm) {
         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);
         ta += mpm->size;
         tu += mpm->usize;
         i++;
         mpm = mpm->next;
     }
     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);
946bd266
 }
 
288057e9
 void check_all(struct MP *mp)
 {
     struct MPMAP *mpm = &mp->u.mpm;
     while (mpm) {
         volatile unsigned char *c = (unsigned char *)mpm;
         size_t len                = mpm->size;
         spam("checking object %p - size %lu\n", mpm, (unsigned long)len);
         while (len--) {
             c[len];
         }
         mpm = mpm->next;
946bd266
     }
 }
 #endif /* DEBUGMPOOL */
 
db43492c
 #else
 /* dummy definitions to make Solaris linker happy.
  * these symbols are declared in libclamav.map */
47d40feb
 void mpool_free() {}
 void mpool_create() {}
 void mpool_destroy() {}
 void mpool_getstats() {}
8a6c2921
 void mpool_calloc() {}
ad0fd728
 
e21657df
 #endif /* USE_MPOOL */