2
* Copyright (C) 2008 Sourcefire, Inc.
4
* Authors: aCaB <acab@clamav.net>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License version 2 as
8
* published by the Free Software Foundation.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21
/* a naive pool allocator */
24
#include "clamav-config.h"
29
#include <sys/types.h>
38
#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H)
50
#define MPOOLMAGIC 0xadde
51
#define ALLOCPOISON 0x5a
52
#define FREEPOISON 0xde
56
#define EXIT_ON_FLUSH*/
58
#define spam(...) cli_warnmsg( __VA_ARGS__)
60
static inline void spam(const char *fmt, ...) { fmt = fmt; } /* gcc STFU */
65
#undef CL_DEBUG /* bb#2222 */
67
#define MIN_FRAGSIZE 262144
70
static const unsigned int fragsz[] = {
167
/* MAX_ALLOCATION is 184549376 but that's really not need here */
172
static const unsigned int fragsz[] = {
352
#define FRAGSBITS (sizeof(fragsz)/sizeof(fragsz[0]))
362
struct FRAG *avail[FRAGSBITS];
365
uint64_t dummy_align;
369
/* alignment of fake handled in the code! */
382
struct unaligned_ptr next;
385
#define FRAG_OVERHEAD (offsetof(struct FRAG, u.a.fake))
387
static unsigned int align_to_pagesize(struct MP *mp, unsigned int size) {
388
return (size / mp->psize + (size % mp->psize != 0)) * mp->psize;
391
static unsigned int to_bits(unsigned int size) {
393
for(i=0; i<FRAGSBITS; i++)
394
if(fragsz[i] >= size) return i;
398
static unsigned int from_bits(unsigned int bits) {
399
if (bits >= FRAGSBITS) return 0;
403
static inline unsigned int alignof(unsigned int size)
405
/* conservative estimate of alignment.
406
* A struct that needs alignment of 'align' is padded by the compiler
407
* so that sizeof(struct)%align == 0
408
* (otherwise you wouldn't be able to use it in an array)
410
* Largest alignment we need is 8 bytes (ptr/int64), since we don't use long
411
* double or __aligned attribute.
412
* This conservatively estimates that size 32 needs alignment of 8 (even if it might only
413
* need an alignment of 4).
428
static inline size_t alignto(size_t p, size_t size)
430
/* size is power of 2 */
431
return (p+size-1)&(~(size-1));
434
struct MP *mpool_create() {
435
struct MP mp, *mpool_p;
437
memset(&mp, 0, sizeof(mp));
438
mp.psize = cli_getpagesize();
439
sz = align_to_pagesize(&mp, MIN_FRAGSIZE);
440
mp.u.mpm.usize = sizeof(struct MPMAP);
441
mp.u.mpm.size = sz - sizeof(mp);
443
if ((mpool_p = (struct MP *)mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE|ANONYMOUS_MAP, -1, 0)) == MAP_FAILED)
445
if(!(mpool_p = (struct MP *)VirtualAlloc(NULL, sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)))
448
if (FRAGSBITS > 255) {
449
cli_errmsg("At most 255 frags possible!\n");
452
if (fragsz[0] < sizeof(void*)) {
453
cli_errmsg("fragsz[0] too small!\n");
457
memset(mpool_p, ALLOCPOISON, sz);
459
memcpy(mpool_p, &mp, sizeof(mp));
460
spam("Map created @%p->%p - size %u out of %u - voidptr=%u\n", mpool_p, (char *)mpool_p + mp.u.mpm.size, mp.u.mpm.usize, mp.u.mpm.size, SIZEOF_VOID_P);
464
void mpool_destroy(struct MP *mp) {
465
struct MPMAP *mpm_next = mp->u.mpm.next, *mpm;
466
unsigned int mpmsize;
468
while((mpm = mpm_next)) {
470
mpm_next = mpm->next;
472
memset(mpm, FREEPOISON, mpmsize);
475
munmap((void *)mpm, mpmsize);
477
VirtualFree(mpm, 0, MEM_RELEASE);
480
mpmsize = mp->u.mpm.size;
482
memset(mp, FREEPOISON, mpmsize + sizeof(*mp));
485
munmap((void *)mp, mpmsize + sizeof(*mp));
487
VirtualFree(mp, 0, MEM_RELEASE);
489
spam("Map destroyed @%p\n", mp);
492
void mpool_flush(struct MP *mp) {
493
size_t used = 0, mused;
494
struct MPMAP *mpm_next = mp->u.mpm.next, *mpm;
500
while((mpm = mpm_next)) {
501
mpm_next = mpm->next;
502
mused = align_to_pagesize(mp, mpm->usize);
503
if(mused < mpm->size) {
505
memset((char *)mpm + mused, FREEPOISON, mpm->size - mused);
508
munmap((char *)mpm + mused, mpm->size - mused);
510
VirtualFree((char *)mpm + mused, mpm->size - mused, MEM_DECOMMIT);
517
mused = align_to_pagesize(mp, mp->u.mpm.usize + sizeof(*mp));
518
if (mused < mp->u.mpm.size + sizeof(*mp)) {
520
memset((char *)mp + mused, FREEPOISON, mp->u.mpm.size + sizeof(*mp) - mused);
523
munmap((char *)mp + mused, mp->u.mpm.size + sizeof(*mp) - mused);
525
VirtualFree((char *)mp + mused, mp->u.mpm.size + sizeof(*mp) - mused, MEM_DECOMMIT);
527
mp->u.mpm.size = mused - sizeof(*mp);
529
used += mp->u.mpm.size;
530
cli_dbgmsg("pool memory used: %.3f MB\n", used/(1024*1024.0));
531
spam("Map flushed @%p, in use: %lu\n", mp, used);
534
int mpool_getstats(const struct cl_engine *eng, size_t *used, size_t *total)
536
size_t sum_used = 0, sum_total = 0;
537
const struct MPMAP *mpm;
540
/* checking refcount is not necessary, but safer */
541
if (!eng || !eng->refcount)
546
for(mpm = &mp->u.mpm; mpm; mpm = mpm->next) {
547
sum_used += mpm->usize;
548
sum_total += mpm->size;
555
static inline unsigned align_increase(unsigned size, unsigned a)
557
/* we must pad with at most a-1 bytes to align start of struct */
561
static void* allocate_aligned(struct MPMAP *mpm, unsigned long size, unsigned align, const char *dbg)
563
/* We could always align the size to maxalign (8), however that wastes
565
* So just align the start of each allocation as needed, and then see in
566
* which sbits bin we fit into.
567
* Since we are no longer allocating in multiple of 8, we must always
568
* align the start of each allocation!
569
*| end of previous allocation | padding | FRAG_OVERHEAD | ptr_aligned |*/
570
unsigned p = mpm->usize + FRAG_OVERHEAD;
571
unsigned p_aligned = alignto(p, align);
572
struct FRAG *f = (struct FRAG*)((char*)mpm + p_aligned - FRAG_OVERHEAD);
573
unsigned realneed = p_aligned + size - mpm->usize;
574
unsigned sbits = to_bits(realneed);
575
unsigned needed = from_bits(sbits);
577
assert(p_aligned + size <= mpm->size);
579
f->u.a.sbits = sbits;
580
f->u.a.padding = p_aligned - p;
582
mpm->usize += needed;
584
assert(mpm->usize <= mpm->size);
586
spam("malloc @%p size %u (%s) origsize %u overhead %u\n", f, realneed, dbg, size, needed - size);
588
f->magic = MPOOLMAGIC;
589
memset(&f->u.a.fake, ALLOCPOISON, size);
594
void *mpool_malloc(struct MP *mp, size_t size) {
595
unsigned align = alignof(size);
596
unsigned int i, needed = align_increase(size+FRAG_OVERHEAD, align);
597
const unsigned int sbits = to_bits(needed);
598
struct FRAG *f = NULL;
599
struct MPMAP *mpm = &mp->u.mpm;
602
if (!size || sbits == FRAGSBITS) {
603
cli_errmsg("mpool_malloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
607
/* Case 1: We have a free'd frag */
608
if((f = mp->avail[sbits])) {
609
struct FRAG *fold = f;
610
mp->avail[sbits] = f->u.next.ptr;
611
/* we always have enough space for this, align_increase ensured that */
612
f = (struct FRAG*)(alignto((unsigned long)f + FRAG_OVERHEAD, align)-FRAG_OVERHEAD);
613
f->u.a.sbits = sbits;
614
f->u.a.padding = (char*)f - (char*)fold;
616
f->magic = MPOOLMAGIC;
617
memset(&f->u.a.fake, ALLOCPOISON, size);
619
spam("malloc @%p size %u (freed) origsize %u overhead %u\n", f, f->u.a.padding + FRAG_OVERHEAD + size, size, needed - size);
623
if (!(needed = from_bits(sbits))) {
624
cli_errmsg("mpool_malloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
628
/* Case 2: We have nuff room available for this frag already */
630
if(mpm->size - mpm->usize >= needed)
631
return allocate_aligned(mpm, size, align, "hole");
635
/* Case 3: We allocate more */
636
if (needed + sizeof(*mpm) > MIN_FRAGSIZE)
637
i = align_to_pagesize(mp, needed + sizeof(*mpm));
639
i = align_to_pagesize(mp, MIN_FRAGSIZE);
642
if ((mpm = (struct MPMAP *)mmap(NULL, i, PROT_READ | PROT_WRITE, MAP_PRIVATE|ANONYMOUS_MAP, -1, 0)) == MAP_FAILED) {
644
if (!(mpm = (struct MPMAP *)VirtualAlloc(NULL, i, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE))) {
646
cli_errmsg("mpool_malloc(): Can't allocate memory (%lu bytes).\n", (unsigned long int)i);
647
spam("failed to alloc %u bytes (%u requested)\n", i, size);
651
memset(mpm, ALLOCPOISON, i);
654
mpm->usize = sizeof(*mpm);
655
mpm->next = mp->u.mpm.next;
656
mp->u.mpm.next = mpm;
657
return allocate_aligned(mpm, size, align, "new map");
660
static void *allocbase_fromfrag(struct FRAG *f)
663
assert(f->u.a.padding < 8);
665
return (char*)f - f->u.a.padding;
668
void mpool_free(struct MP *mp, void *ptr) {
669
struct FRAG *f = (struct FRAG *)((char *)ptr - FRAG_OVERHEAD);
674
assert(f->magic == MPOOLMAGIC && "Attempt to mpool_free a pointer we did not allocate!");
677
spam("free @%p\n", f);
678
sbits = f->u.a.sbits;
679
f = allocbase_fromfrag(f);
681
memset(f, FREEPOISON, from_bits(sbits));
684
f->u.next.ptr = mp->avail[sbits];
685
mp->avail[sbits] = f;
688
void *mpool_calloc(struct MP *mp, size_t nmemb, size_t size) {
689
unsigned int needed = nmemb*size;
692
if(!needed) return NULL;
693
if((ptr = mpool_malloc(mp, needed)))
694
memset(ptr, 0, needed);
698
void *mpool_realloc(struct MP *mp, void *ptr, size_t size) {
699
struct FRAG *f = (struct FRAG *)((char *)ptr - FRAG_OVERHEAD);
702
if (!ptr) return mpool_malloc(mp, size);
704
if(!size || !(csize = from_bits(f->u.a.sbits))) {
705
cli_errmsg("mpool_realloc(): Attempt to allocate %lu bytes. Please report to http://bugs.clamav.net\n", (unsigned long int) size);
708
csize -= FRAG_OVERHEAD + f->u.a.padding;
709
if (csize >= size && (!f->u.a.sbits || from_bits(f->u.a.sbits-1)-FRAG_OVERHEAD-f->u.a.padding < size)) {
710
spam("free @%p\n", f);
711
spam("malloc @%p size %u (self) origsize %u overhead %u\n", f, size + FRAG_OVERHEAD + f->u.a.padding, size, csize-size+FRAG_OVERHEAD+f->u.a.padding);
714
if (!(new_ptr = mpool_malloc(mp, size)))
716
memcpy(new_ptr, ptr, csize <= size ? csize : size);
721
void *mpool_realloc2(struct MP *mp, void *ptr, size_t size) {
722
void *new_ptr = mpool_realloc(mp, ptr, size);
729
unsigned char *cli_mpool_hex2str(mpool_t *mp, const char *hex) {
731
size_t len = strlen((const char*)hex);
734
cli_errmsg("cli_hex2str(): Malformed hexstring: %s (length: %u)\n", hex, (unsigned)len);
738
str = mpool_malloc(mp, (len/2) + 1);
739
if (cli_hex2str_to(hex, (char*)str, len) == -1) {
747
char *cli_mpool_strdup(mpool_t *mp, const char *s) {
752
cli_errmsg("cli_mpool_strdup(): s == NULL. Please report to http://bugs.clamav.net\n");
756
strsz = strlen(s) + 1;
757
alloc = mpool_malloc(mp, strsz);
759
cli_errmsg("cli_mpool_strdup(): Can't allocate memory (%u bytes).\n", (unsigned int) strsz);
761
memcpy(alloc, s, strsz);
765
/* #define EXPAND_PUA */
766
char *cli_mpool_virname(mpool_t *mp, const char *virname, unsigned int official) {
775
if((pt = strchr(virname, ' ')))
776
if((pt = strstr(pt, " (Clam)")))
780
cli_errmsg("cli_virname: Empty virus name\n");
785
if(!strncmp(virname, "PUA.", 4)) {
786
snprintf(buf, sizeof(buf), "Possibly-Unwanted-Application(www.clamav.net/support/pua).%s", virname + 4);
787
buf[sizeof(buf)-1] = '\0';
792
return cli_mpool_strdup(mp, virname);
794
newname = (char *)mpool_malloc(mp, strlen(virname) + 11 + 1);
796
cli_errmsg("cli_virname: Can't allocate memory for newname\n");
799
sprintf(newname, "%s.UNOFFICIAL", virname);
804
uint16_t *cli_mpool_hex2ui(mpool_t *mp, const char *hex) {
811
cli_errmsg("cli_mpool_hex2ui(): Malformed hexstring: %s (length: %u)\n", hex, len);
815
str = mpool_calloc(mp, (len / 2) + 1, sizeof(uint16_t));
819
if(cli_realhex2ui(hex, str, len))
828
void mpool_stats(struct MP *mp) {
829
unsigned int i=0, ta=0, tu=0;
830
struct MPMAP *mpm = &mp->u.mpm;
832
cli_warnmsg("MEMORY POOL STATISTICS\n map \tsize\tused\t%\n");
834
cli_warnmsg("- %u\t%u\t%u\t%f%%\n", i, mpm->size, mpm->usize, (float)mpm->usize/(float)mpm->size*100);
840
cli_warnmsg("MEMORY POOL SUMMARY\nMaps: %u\nTotal: %u\nUsed: %u (%f%%)\n", i, ta, tu, (float)tu/(float)ta*100);
843
void check_all(struct MP *mp) {
844
struct MPMAP *mpm = &mp->u.mpm;
846
volatile unsigned char *c = (unsigned char *)mpm;
847
unsigned int len = mpm->size;
848
spam("checking object %p - size %u\n", mpm, len);
855
#endif /* DEBUGMPOOL */
859
/* dummy definitions to make Solaris linker happy.
860
* these symbols are declared in libclamav.map */
862
void mpool_create() {}
863
void mpool_destroy() {}
864
void mpool_getstats() {}
866
#endif /* USE_MPOOL */