Merge branch 'cache'

0.96
aCaB 16 years ago
commit 4365fec1e5
  1. 33
      countme.pl
  2. BIN
      hashes.lzma
  3. 4
      libclamav/Makefile.am
  4. 18
      libclamav/Makefile.in
  5. 744
      libclamav/cache.c
  6. 31
      libclamav/cache.h
  7. 1
      libclamav/libclamav.map
  8. 15
      libclamav/matcher.c
  9. 3
      libclamav/matcher.h
  10. 3
      libclamav/others.h
  11. 6
      libclamav/readdb.c
  12. 24
      libclamav/scanners.c

@ -0,0 +1,33 @@
#!/usr/bin/perl
use strict;
use warnings;
my %h = ();
my $added = 0;
my $found = 0;
my $notfound = 0;
while(1) {
my $hash = '';
last if(read(STDIN, $hash, 17) != 17);
my $op = substr($hash, 0, 1);
$hash = substr($hash, 1);
if($op eq "A") {
$h{$hash} = 1;
$added++;
} elsif ($op eq "C") {
if(exists($h{$hash})) {
$found++;
} else {
$notfound++;
}
} else {
die "bad command $op\n";
}
}
my $lookups = $found + $notfound;
print "added: $added\nlooked up: $lookups (found $found, not found $notfound)\n";
printf "items in the hash: ".(scalar keys %h)."\n";

Binary file not shown.

@ -345,7 +345,9 @@ libclamav_la_SOURCES = \
bytecode_api_decl.c \
bytecode_api.h \
bytecode_api_impl.h \
bytecode_hooks.h
bytecode_hooks.h \
cache.c \
cache.h
if !LINK_TOMMATH
libclamav_la_SOURCES += bignum.c \

@ -153,7 +153,8 @@ am__libclamav_la_SOURCES_DIST = clamav.h matcher-ac.c matcher-ac.h \
bytecode_vm.c bytecode_priv.h clambc.h cpio.c cpio.h macho.c \
macho.h ishield.c ishield.h type_desc.h bcfeatures.h \
bytecode_api.c bytecode_api_decl.c bytecode_api.h \
bytecode_api_impl.h bytecode_hooks.h bignum.c bignum_class.h
bytecode_api_impl.h bytecode_hooks.h cache.c cache.h bignum.c \
bignum_class.h
@LINK_TOMMATH_FALSE@am__objects_1 = libclamav_la-bignum.lo
am_libclamav_la_OBJECTS = libclamav_la-matcher-ac.lo \
libclamav_la-matcher-bm.lo libclamav_la-matcher.lo \
@ -202,7 +203,8 @@ am_libclamav_la_OBJECTS = libclamav_la-matcher-ac.lo \
libclamav_la-bytecode.lo libclamav_la-bytecode_vm.lo \
libclamav_la-cpio.lo libclamav_la-macho.lo \
libclamav_la-ishield.lo libclamav_la-bytecode_api.lo \
libclamav_la-bytecode_api_decl.lo $(am__objects_1)
libclamav_la-bytecode_api_decl.lo libclamav_la-cache.lo \
$(am__objects_1)
libclamav_la_OBJECTS = $(am_libclamav_la_OBJECTS)
AM_V_lt = $(am__v_lt_$(V))
am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
@ -649,7 +651,8 @@ libclamav_la_SOURCES = clamav.h matcher-ac.c matcher-ac.h matcher-bm.c \
bytecode_vm.c bytecode_priv.h clambc.h cpio.c cpio.h macho.c \
macho.h ishield.c ishield.h type_desc.h bcfeatures.h \
bytecode_api.c bytecode_api_decl.c bytecode_api.h \
bytecode_api_impl.h bytecode_hooks.h $(am__append_7)
bytecode_api_impl.h bytecode_hooks.h cache.c cache.h \
$(am__append_7)
noinst_LTLIBRARIES = libclamav_internal_utils.la libclamav_internal_utils_nothreads.la libclamav_nocxx.la
COMMON_CLEANFILES = version.h version.h.tmp *.gcda *.gcno
@MAINTAINER_MODE_TRUE@BUILT_SOURCES = jsparse/generated/operators.h jsparse/generated/keywords.h jsparse-keywords.gperf
@ -795,6 +798,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-bytecode_vm.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-bzlib.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-cab.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-cache.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-chmunpack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-cpio.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-cvd.Plo@am__quote@
@ -1659,6 +1663,14 @@ libclamav_la-bytecode_api_decl.lo: bytecode_api_decl.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-bytecode_api_decl.lo `test -f 'bytecode_api_decl.c' || echo '$(srcdir)/'`bytecode_api_decl.c
libclamav_la-cache.lo: cache.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-cache.lo -MD -MP -MF $(DEPDIR)/libclamav_la-cache.Tpo -c -o libclamav_la-cache.lo `test -f 'cache.c' || echo '$(srcdir)/'`cache.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-cache.Tpo $(DEPDIR)/libclamav_la-cache.Plo
@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cache.c' object='libclamav_la-cache.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-cache.lo `test -f 'cache.c' || echo '$(srcdir)/'`cache.c
libclamav_la-bignum.lo: bignum.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-bignum.lo -MD -MP -MF $(DEPDIR)/libclamav_la-bignum.Tpo -c -o libclamav_la-bignum.lo `test -f 'bignum.c' || echo '$(srcdir)/'`bignum.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-bignum.Tpo $(DEPDIR)/libclamav_la-bignum.Plo

@ -0,0 +1,744 @@
/*
* Copyright (C) 2010 Sourcefire, Inc.
*
* Authors: aCaB <acab@clamav.net>, Török Edvin <edwin@clamav.net>
*
* 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.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <assert.h>
#include "md5.h"
#include "mpool.h"
#include "clamav.h"
#include "cache.h"
#include "fmap.h"
/* The number of root trees and the chooser function
Each tree is protected by a mutex against concurrent access */
/* #define TREES 1 */
/* static inline unsigned int getkey(uint8_t *hash) { return 0; } */
#define TREES 256
static inline unsigned int getkey(uint8_t *hash) { return *hash; }
/* #define TREES 4096 */
/* static inline unsigned int getkey(uint8_t *hash) { return hash[0] | ((unsigned int)(hash[1] & 0xf)<<8) ; } */
/* #define TREES 65536 */
/* static inline unsigned int getkey(uint8_t *hash) { return hash[0] | (((unsigned int)hash[1])<<8) ; } */
/* The number of nodes in each tree */
#define NODES 256
/* The replacement policy algorithm to use */
/* #define USE_LRUHASHCACHE */
#define USE_SPLAY
/* LRUHASHCACHE --------------------------------------------------------------------- */
#ifdef USE_LRUHASHCACHE
struct cache_key {
int64_t digest[2];
uint32_t size; /* 0 is used to mark an empty hash slot! */
struct cache_key *lru_next, *lru_prev;
};
struct cache_set {
struct cache_key *data;
size_t maxelements; /* considering load factor */
size_t maxdeleted;
size_t elements;
size_t deleted;
struct cache_key *lru_head, *lru_tail;
};
#define CACHE_KEY_DELETED ~0u
#define CACHE_KEY_EMPTY 0
static void cacheset_lru_remove(struct cache_set *map, size_t howmany)
{
while (howmany--) {
struct cache_key *old;
assert(map->lru_head);
assert(!old->lru_prev);
/* Remove a key from the head of the list */
old = map->lru_head;
map->lru_head = old->lru_next;
old->size = CACHE_KEY_DELETED;
/* This slot is now deleted, it is not empty,
* because previously we could have inserted a key that has seen this
* slot as occupied, to find that key we need to ensure that all keys
* that were occupied when the key was inserted, are seen as occupied
* when searching too.
* Of course when inserting a new value, we treat deleted slots as
* empty.
* We only replace old values with new values, but there is no guarantee
* that the newly inserted value would hash to same place as the value
* we remove due to LRU! */
if (old == map->lru_tail)
map->lru_tail = 0;
map->elements--;
map->deleted++;
}
}
static inline int cacheset_lookup_internal(struct cache_set *map,
const char *md5, size_t size,
uint32_t *insert_pos, int deletedok)
{
const struct cache_key*data = map->data;
uint32_t capmask = NODES - 1;
const struct cache_key *k;
uint32_t idx, tries = 0;
uint64_t md5_0, md5_1;
uint64_t md5a[2];
memcpy(&md5a, md5, 16);
md5_0 = md5a[0];
md5_1 = md5a[1];
idx = md5_1 & capmask;
k = &data[idx];
while (k->size != CACHE_KEY_EMPTY && tries <= capmask) {
if (k->digest[0] == md5_0 &&
k->digest[1] == md5_1 &&
k->size == size) {
/* found key */
*insert_pos = idx;
return 1;
}
if (deletedok && k->size == CACHE_KEY_DELETED) {
/* treat deleted slot as empty */
*insert_pos = idx;
return 0;
}
idx = (idx + tries++) & capmask;
k = &data[idx];
}
/* found empty pos */
*insert_pos = idx;
return 0;
}
static inline void lru_remove(struct cache_set *map, struct cache_key *newkey)
{
if (newkey->lru_next)
newkey->lru_next->lru_prev = newkey->lru_prev;
if (newkey->lru_prev)
newkey->lru_prev->lru_next = newkey->lru_next;
if (newkey == map->lru_head)
map->lru_head = newkey->lru_next;
}
static inline void lru_addtail(struct cache_set *map, struct cache_key *newkey)
{
if (!map->lru_head)
map->lru_head = newkey;
if (map->lru_tail)
map->lru_tail->lru_next = newkey;
newkey->lru_next = NULL;
newkey->lru_prev = map->lru_tail;
map->lru_tail = newkey;
}
static pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER;
static void cacheset_add(struct cache_set *map, unsigned char *md5, size_t size, mpool_t *mempool);
static int cacheset_init(struct cache_set *map, mpool_t *mempool);
static void cacheset_rehash(struct cache_set *map, mpool_t *mempool)
{
unsigned i;
int ret;
struct cache_set tmp_set;
struct cache_key *key;
pthread_mutex_lock(&pool_mutex);
ret = cacheset_init(&tmp_set, mempool);
pthread_mutex_unlock(&pool_mutex);
if (ret)
return;
key = map->lru_head;
for (i=0;key && i < tmp_set.maxelements/2;i++) {
cacheset_add(&tmp_set, (unsigned char*)&key->digest, key->size, mempool);
key = key->lru_next;
}
pthread_mutex_lock(&pool_mutex);
mpool_free(mempool, map->data);
pthread_mutex_unlock(&pool_mutex);
memcpy(map, &tmp_set, sizeof(tmp_set));
}
static void cacheset_add(struct cache_set *map, unsigned char *md5, size_t size, mpool_t *mempool)
{
int ret;
uint32_t pos;
struct cache_key *newkey;
if (map->elements >= map->maxelements) {
cacheset_lru_remove(map, 1);
if (map->deleted >= map->maxdeleted) {
cacheset_rehash(map, mempool);
}
}
assert(map->elements < map->maxelements);
ret = cacheset_lookup_internal(map, md5, size, &pos, 1);
newkey = &map->data[pos];
if (newkey->size == CACHE_KEY_DELETED)
map->deleted--;
if (ret) {
/* was already added, remove from LRU list */
lru_remove(map, newkey);
}
/* add new key to tail of LRU list */
memcpy(&map->data[pos].digest, md5, sizeof(map->data[pos].digest));
map->data[pos].size = size;
lru_addtail(map, newkey);
map->elements++;
assert(pos < map->maxelements);
}
static int cacheset_lookup(struct cache_set *map, unsigned char *md5, size_t size)
{
struct cache_key *newkey;
int ret;
uint32_t pos;
ret = cacheset_lookup_internal(map, md5, size, &pos, 0);
if (!ret)
return 0;
newkey = &map->data[pos];
/* update LRU position: move to tail */
lru_remove(map, newkey);
lru_addtail(map, newkey);
return 1;
}
static int cacheset_init(struct cache_set *map, mpool_t *mempool) {
map->data = mpool_calloc(mempool, NODES, sizeof(*map->data));
if (!map->data)
return CL_EMEM;
map->maxelements = 80 * NODES / 100;
map->maxdeleted = NODES - map->maxelements - 1;
map->elements = 0;
map->lru_head = map->lru_tail = NULL;
return 0;
}
static inline void cacheset_destroy(struct cache_set *cs, mpool_t *mempool) {
mpool_free(mempool, cs->data);
cs->data = NULL;
}
#endif /* USE_LRUHASHCACHE */
/* SPLAY --------------------------------------------------------------------- */
#ifdef USE_SPLAY
struct node { /* a node */
int64_t digest[2];
struct node *left;
struct node *right;
struct node *up;
struct node *next;
struct node *prev;
uint32_t size;
};
struct cache_set { /* a tree */
struct node *data;
struct node *root;
struct node *first;
struct node *last;
};
/* Allocates all the nodes and sets up the replacement chain */
static int cacheset_init(struct cache_set *cs, mpool_t *mempool) {
unsigned int i;
cs->data = mpool_calloc(mempool, NODES, sizeof(*cs->data));
cs->root = NULL;
if(!cs->data)
return 1;
for(i=1; i<NODES; i++) {
cs->data[i-1].next = &cs->data[i];
cs->data[i].prev = &cs->data[i-1];
}
cs->first = cs->data;
cs->last = &cs->data[NODES-1];
return 0;
}
/* Frees all the nodes */
static inline void cacheset_destroy(struct cache_set *cs, mpool_t *mempool) {
mpool_free(mempool, cs->data);
cs->data = NULL;
}
/* The left/right cooser for the splay tree */
static inline int cmp(int64_t *a, ssize_t sa, int64_t *b, ssize_t sb) {
if(a[1] < b[1]) return -1;
if(a[1] > b[1]) return 1;
if(a[0] < b[0]) return -1;
if(a[0] > b[0]) return 1;
if(sa < sb) return -1;
if(sa > sb) return 1;
return 0;
}
/* #define PRINT_TREE */
#ifdef PRINT_TREE
#define ptree printf
#else
#define ptree(...)
#endif
/* Debug function to print the tree and check its consistency */
/* #define CHECK_TREE */
#ifdef CHECK_TREE
static int printtree(struct cache_set *cs, struct node *n, int d) {
int i;
int ab = 0;
if (n == NULL) return 0;
if(n == cs->root) { ptree("--------------------------\n"); }
ab |= printtree(cs, n->right, d+1);
if(n->right) {
if(cmp(n->digest, n->size, n->right->digest, n->right->size) >= 0) {
for (i=0; i<d; i++) ptree(" ");
ptree("^^^^ %lld >= %lld\n", n->digest[1], n->right->digest[1]);
ab = 1;
}
}
for (i=0; i<d; i++) ptree(" ");
ptree("%08x(%02u)\n", n->digest[1]>>48, n - cs->data);
if(n->left) {
if(cmp(n->digest, n->size, n->left->digest, n->left->size) <= 0) {
for (i=0; i<d; i++) ptree(" ");
ptree("vvvv %lld <= %lld\n", n->digest[1], n->left->digest[1]);
ab = 1;
}
}
if(d){
if(!n->up) {
ptree("no parent!\n");
ab = 1;
} else {
if(n->up->left != n && n->up->right != n) {
ptree("broken parent\n");
ab = 1;
}
}
} else {
if(n->up) {
ptree("root with a parent!\n");
ab = 1;
}
}
ab |= printtree(cs, n->left, d+1);
return ab;
}
#else
#define printtree(a,b,c) (0)
#endif
/* Looks up a node and splays it up to the root of the tree */
static int splay(int64_t *md5, size_t len, struct cache_set *cs) {
struct node next = {{0, 0}, NULL, NULL, NULL, NULL, NULL, 0}, *right = &next, *left = &next, *temp, *root = cs->root;
int comp, found = 0;
if(!root)
return 0;
while(1) {
comp = cmp(md5, len, root->digest, root->size);
if(comp < 0) {
if(!root->left) break;
if(cmp(md5, len, root->left->digest, root->left->size) < 0) {
temp = root->left;
root->left = temp->right;
if(temp->right) temp->right->up = root;
temp->right = root;
root->up = temp;
root = temp;
if(!root->left) break;
}
right->left = root;
root->up = right;
right = root;
root = root->left;
} else if(comp > 0) {
if(!root->right) break;
if(cmp(md5, len, root->right->digest, root->right->size) > 0) {
temp = root->right;
root->right = temp->left;
if(temp->left) temp->left->up = root;
temp->left = root;
root->up = temp;
root = temp;
if(!root->right) break;
}
left->right = root;
root->up = left;
left = root;
root = root->right;
} else {
found = 1;
break;
}
}
left->right = root->left;
if(root->left) root->left->up = left;
right->left = root->right;
if(root->right) root->right->up = right;
root->left = next.right;
if(next.right) next.right->up = root;
root->right = next.left;
if(next.left) next.left->up = root;
root->up = NULL;
cs->root = root;
return found;
}
/* Looks up an hash in the tree and maintains the replacement chain */
static int cacheset_lookup(struct cache_set *cs, unsigned char *md5, size_t size) {
int64_t hash[2];
memcpy(hash, md5, 16);
if(splay(hash, size, cs)) {
struct node *o = cs->root->prev, *p = cs->root, *q = cs->root->next;
#ifdef PRINT_CHAINS
printf("promoting %02d\n", p - cs->data);
{
struct node *x = cs->first;
printf("before: ");
while(x) {
printf("%02d,", x - cs->data);
x=x->next;
}
printf(" --- ");
x=cs->last;
while(x) {
printf("%02d,", x - cs->data);
x=x->prev;
}
printf("\n");
}
#endif
#define TO_END_OF_CHAIN
#ifdef TO_END_OF_CHAIN
if(q) {
if(o)
o->next = q;
else
cs->first = q;
q->prev = o;
cs->last->next = p;
p->prev = cs->last;
p->next = NULL;
cs->last = p;
}
#else
if(cs->last != p) {
if(cs->last == q) cs->last = p;
if(o) o->next = q;
else cs->first = q;
p->next = q->next;
if(q->next) q->next->prev = p;
q->next = p;
q->prev = o;
p->prev = q;
}
#endif
#ifdef PRINT_CHAINS
{
struct node *x = cs->first;
printf("after : ");
while(x) {
printf("%02d,", x - cs->data);
x=x->next;
}
printf(" --- ");
x=cs->last;
while(x) {
printf("%02d,", x - cs->data);
x=x->prev;
}
printf("\n");
}
#endif
return 1;
}
return 0;
}
/* If the hash is present nothing happens.
Otherwise a new node is created for the hash picking one from the begin of the chain.
Used nodes are moved to the end of the chain */
static void cacheset_add(struct cache_set *cs, unsigned char *md5, size_t size) {
struct node *newnode;
int64_t hash[2];
memcpy(hash, md5, 16);
if(splay(hash, size, cs))
return; /* Already there */
ptree("1:\n");
if(printtree(cs, cs->root, 0)) {
abort();
}
newnode = cs->first;
/*#define TAKE_FIRST*/
#ifdef TAKE_FIRST
if((newnode->left || newnode->right || newnode->up)) {
if(!splay(newnode->digest, newnode->size, cs)) {
cli_errmsg("WTF\n");
abort();
}
if(!newnode->left) {
cs->root = newnode->right;
newnode->right->up = NULL;
} else if(!newnode->right) {
cs->root = newnode->left;
newnode->left->up = NULL;
} else {
cs->root = newnode->left;
newnode->left->up = NULL;
if(splay(newnode->digest, newnode->size, cs)) {
cli_errmsg("WTF #2\n");
abort();
}
cs->root->up = NULL;
cs->root->right = newnode->right;
if(newnode->right) newnode->right->up = cs->root;
}
newnode->up = NULL;
newnode->right = NULL;
newnode->left = NULL;
if(splay(hash, size, cs)) {
cli_errmsg("WTF #3\n");
abort();
}
}
newnode->prev = cs->last;
cs->last->next = newnode;
cs->last = newnode;
newnode->next->prev = NULL;
cs->first = newnode->next;
newnode->next = NULL;
#else
while(newnode) {
if(!newnode->right && !newnode->left)
break;
newnode = newnode->next;
}
if(!newnode) {
cli_errmsg("NO NEWNODE!\n");
abort();
}
if(newnode->up) {
if(newnode->up->left == newnode)
newnode->up->left = NULL;
else
newnode->up->right = NULL;
}
if(newnode->prev)
newnode->prev->next = newnode->next;
if(newnode->next)
newnode->next->prev = newnode->prev;
if(cs->first == newnode)
cs->first = newnode->next;
newnode->prev = cs->last;
newnode->next = NULL;
cs->last->next = newnode;
cs->last = newnode;
#endif
ptree("2:\n");
if(printtree(cs, cs->root, 0)) {
abort();
}
if(!cs->root) {
newnode->left = NULL;
newnode->right = NULL;
} else {
if(cmp(hash, size, cs->root->digest, cs->root->size) < 0) {
newnode->left = cs->root->left;
newnode->right = cs->root;
cs->root->left = NULL;
} else {
newnode->right = cs->root->right;
newnode->left = cs->root;
cs->root->right = NULL;
}
if(newnode->left) newnode->left->up = newnode;
if(newnode->right) newnode->right->up = newnode;
}
newnode->digest[0] = hash[0];
newnode->digest[1] = hash[1];
newnode->up = NULL;
newnode->size = size;
cs->root = newnode;
ptree("3: %lld\n", hash[1]);
if(printtree(cs, cs->root, 0)) {
abort();
}
}
#endif /* USE_SPLAY */
/* COMMON STUFF --------------------------------------------------------------------- */
struct CACHE {
struct cache_set cacheset;
pthread_mutex_t mutex;
};
/* Allocates the trees for the engine cache */
int cli_cache_init(struct cl_engine *engine) {
static struct CACHE *cache;
unsigned int i, j;
if(!engine) {
cli_errmsg("cli_cache_init: mpool malloc fail\n");
return 1;
}
if(!(cache = mpool_malloc(engine->mempool, sizeof(struct CACHE) * TREES))) {
cli_errmsg("cli_cache_init: mpool malloc fail\n");
return 1;
}
for(i=0; i<TREES; i++) {
if(pthread_mutex_init(&cache[i].mutex, NULL)) {
cli_errmsg("cli_cache_init: mutex init fail\n");
for(j=0; j<i; j++) cacheset_destroy(&cache[j].cacheset, engine->mempool);
for(j=0; j<i; j++) pthread_mutex_destroy(&cache[j].mutex);
mpool_free(engine->mempool, cache);
return 1;
}
if(cacheset_init(&cache[i].cacheset, engine->mempool)) {
for(j=0; j<i; j++) cacheset_destroy(&cache[j].cacheset, engine->mempool);
for(j=0; j<=i; j++) pthread_mutex_destroy(&cache[j].mutex);
mpool_free(engine->mempool, cache);
return 1;
}
}
engine->cache = cache;
return 0;
}
/* Frees the engine cache */
void cli_cache_destroy(struct cl_engine *engine) {
static struct CACHE *cache;
unsigned int i;
if(!engine || !(cache = engine->cache))
return;
for(i=0; i<TREES; i++) {
cacheset_destroy(&cache[i].cacheset, engine->mempool);
pthread_mutex_destroy(&cache[i].mutex);
}
mpool_free(engine->mempool, cache);
}
/* Looks up an hash in the proper tree */
static int cache_lookup_hash(unsigned char *md5, size_t len, struct CACHE *cache) {
unsigned int key = getkey(md5);
int ret = CL_VIRUS;
struct CACHE *c;
c = &cache[key];
if(pthread_mutex_lock(&c->mutex)) {
cli_errmsg("cache_lookup_hash: cache_lookup_hash: mutex lock fail\n");
return ret;
}
ret = (cacheset_lookup(&c->cacheset, md5, len)) ? CL_CLEAN : CL_VIRUS;
pthread_mutex_unlock(&c->mutex);
/* if(ret == CL_CLEAN) cli_warnmsg("cached\n"); */
return ret;
}
/* Adds an hash to the cache */
void cache_add(unsigned char *md5, size_t size, cli_ctx *ctx) {
unsigned int key = getkey(md5);
struct CACHE *c;
if(!ctx || !ctx->engine || !ctx->engine->cache)
return;
c = &ctx->engine->cache[key];
if(pthread_mutex_lock(&c->mutex)) {
cli_errmsg("cli_add: mutex lock fail\n");
return;
}
#ifdef USE_LRUHASHCACHE
cacheset_add(&c->cacheset, md5, size, ctx->engine->mempool);
#else
#ifdef USE_SPLAY
cacheset_add(&c->cacheset, md5, size);
#else
#error #define USE_SPLAY or USE_LRUHASHCACHE
#endif
#endif
pthread_mutex_unlock(&c->mutex);
return;
}
/* Hashes a file onto the provided buffer and looks it up the cache.
Returns CL_VIRUS if found, CL_CLEAN if not FIXME or an error */
int cache_check(unsigned char *hash, cli_ctx *ctx) {
fmap_t *map = *ctx->fmap;
size_t todo = map->len, at = 0;
cli_md5_ctx md5;
if(!ctx || !ctx->engine || !ctx->engine->cache)
return CL_VIRUS;
cli_md5_init(&md5);
while(todo) {
void *buf;
size_t readme = todo < FILEBUFF ? todo : FILEBUFF;
if(!(buf = fmap_need_off_once(map, at, readme)))
return CL_VIRUS;
todo -= readme;
at += readme;
cli_md5_update(&md5, buf, readme);
}
cli_md5_final(hash, &md5);
return cache_lookup_hash(hash, map->len, ctx->engine->cache);
}

@ -0,0 +1,31 @@
/*
* Copyright (C) 2010 Sourcefire, Inc.
*
* Authors: aCaB <acab@clamav.net>
*
* 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.
*/
#ifndef __CACHE_H
#define __CACHE_H
#include "clamav.h"
#include "others.h"
void cache_add(unsigned char *md5, size_t size, cli_ctx *ctx);
int cache_check(unsigned char *hash, cli_ctx *ctx);
int cli_cache_init(struct cl_engine *engine);
void cli_cache_destroy(struct cl_engine *engine);
#endif

@ -27,6 +27,7 @@ CLAMAV_PUBLIC {
cl_statfree;
cl_statinidir;
cl_strerror;
cl_cache_init;
local:
*;
};

@ -345,7 +345,7 @@ int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struc
fmap_t *map = *ctx->fmap;
if((*ctx->fmap = fmap(desc, 0, 0))) {
ret = cli_fmap_scandesc(ctx, ftype, ftonly, ftoffset, acmode);
ret = cli_fmap_scandesc(ctx, ftype, ftonly, ftoffset, acmode, NULL);
funmap(*ctx->fmap);
}
*ctx->fmap = map;
@ -353,7 +353,7 @@ int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struc
}
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode)
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, unsigned char *refhash)
{
unsigned char *buff;
int ret = CL_CLEAN, type = CL_CLEAN, bytes;
@ -421,7 +421,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
}
}
if(!ftonly && ctx->engine->md5_hdb)
if(!refhash && !ftonly && ctx->engine->md5_hdb)
cli_md5_init(&md5ctx);
while(offset < map->len) {
@ -460,7 +460,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
type = ret;
}
if(ctx->engine->md5_hdb)
if(!refhash && ctx->engine->md5_hdb)
cli_md5_update(&md5ctx, buff + maxpatlen * (offset!=0), bytes - maxpatlen * (offset!=0));
}
@ -537,8 +537,11 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli
if(!ftonly && ctx->engine->md5_hdb) {
const struct cli_bm_patt *patt;
cli_md5_final(digest, &md5ctx);
if(cli_bm_scanbuff(digest, 16, ctx->virname, &patt, ctx->engine->md5_hdb, 0, NULL, NULL) == CL_VIRUS && patt->filesize == map->len && (cli_bm_scanbuff(digest, 16, NULL, &patt, ctx->engine->md5_fp, 0, NULL, NULL) != CL_VIRUS || patt->filesize != map->len))
if(!refhash) {
cli_md5_final(digest, &md5ctx);
refhash = digest;
}
if(cli_bm_scanbuff(refhash, 16, ctx->virname, &patt, ctx->engine->md5_hdb, 0, NULL, NULL) == CL_VIRUS && patt->filesize == map->len && (cli_bm_scanbuff(refhash, 16, NULL, &patt, ctx->engine->md5_fp, 0, NULL, NULL) != CL_VIRUS || patt->filesize != map->len))
return CL_VIRUS;
}

@ -159,8 +159,7 @@ struct cli_target_info {
int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset, cli_ctx *ctx, cli_file_t ftype, struct cli_ac_data **acdata);
int cli_scandesc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode);
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode);
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, unsigned char *digest);
int cli_caloff(const char *offstr, struct cli_target_info *info, fmap_t *map, unsigned int target, uint32_t *offdata, uint32_t *offset_min, uint32_t *offset_max);
int cli_checkfp(int fd, cli_ctx *ctx);

@ -217,6 +217,9 @@ struct cl_engine {
/* Icon reference storage */
struct icon_matcher *iconcheck;
/* Negative cache storage */
struct CACHE *cache;
/* Used for memory pools */
mpool_t *mempool;

@ -2369,6 +2369,9 @@ int cl_load(const char *path, struct cl_engine *engine, unsigned int *signo, uns
cli_dbgmsg("Bytecode engine disabled\n");
}
if(cli_cache_init(engine))
return CL_EMEM;
engine->dboptions |= dboptions;
switch(sb.st_mode & S_IFMT) {
@ -2706,6 +2709,9 @@ int cl_engine_free(struct cl_engine *engine)
if(engine->tmpdir)
mpool_free(engine->mempool, engine->tmpdir);
if(engine->cache)
cli_cache_destroy(engine);
cli_ftfree(engine);
if(engine->ignored) {
cli_bm_free(engine->ignored);

@ -85,6 +85,7 @@
#include "ishield.h"
#include "7z.h"
#include "fmap.h"
#include "cache.h"
#ifdef HAVE_BZLIB_H
#include <bzlib.h>
@ -1663,7 +1664,7 @@ static int cli_scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_file_
if(typercg)
acmode |= AC_SCAN_FT;
ret = cli_fmap_scandesc(ctx, type == CL_TYPE_TEXT_ASCII ? 0 : type, 0, &ftoffset, acmode);
ret = cli_fmap_scandesc(ctx, type == CL_TYPE_TEXT_ASCII ? 0 : type, 0, &ftoffset, acmode, NULL);
if(ret >= CL_TYPENO) {
ctx->recursion++;
@ -1812,7 +1813,8 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
struct stat sb;
uint8_t typercg = 1;
cli_file_t current_container_type = ctx->container_type; /* TODO: container tracking code TBD - bb#1293 */
size_t current_container_size = ctx->container_size;
size_t current_container_size = ctx->container_size, hashed_size;
unsigned char hash[16];
if(ctx->engine->maxreclevel && ctx->recursion > ctx->engine->maxreclevel) {
cli_dbgmsg("cli_magic_scandesc: Archive recursion limit exceeded (%u, max: %u)\n", ctx->recursion, ctx->engine->maxreclevel);
@ -1845,16 +1847,28 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
ctx->fmap++;
if(!(*ctx->fmap = fmap(desc, 0, sb.st_size))) {
cli_errmsg("CRITICAL: fmap() failed\n");
ctx->fmap--;
return CL_EMEM;
}
if(cache_check(hash, ctx) == CL_CLEAN) {
funmap(*ctx->fmap);
ctx->fmap--;
return CL_CLEAN;
}
hashed_size = (*ctx->fmap)->len;
if(!ctx->options || (ctx->recursion == ctx->engine->maxreclevel)) { /* raw mode (stdin, etc.) or last level of recursion */
if(ctx->recursion == ctx->engine->maxreclevel)
cli_dbgmsg("cli_magic_scandesc: Hit recursion limit, only scanning raw file\n");
else
cli_dbgmsg("Raw mode: No support for special files\n");
if((ret = cli_fmap_scandesc(ctx, 0, 0, NULL, AC_SCAN_VIR)) == CL_VIRUS)
if((ret = cli_fmap_scandesc(ctx, 0, 0, NULL, AC_SCAN_VIR, hash)) == CL_VIRUS)
cli_dbgmsg("%s found in descriptor %d\n", *ctx->virname, desc);
else
cache_add(hash, hashed_size, ctx);
funmap(*ctx->fmap);
ctx->fmap--;
return ret;
@ -2171,7 +2185,7 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
ctx->fmap--;
if(ret == CL_VIRUS)
return cli_checkfp(desc, ctx) ? CL_CLEAN : CL_VIRUS;
ret = cli_checkfp(desc, ctx) ? CL_CLEAN : CL_VIRUS;
switch(ret) {
case CL_EFORMAT:
@ -2179,6 +2193,8 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
case CL_EMAXSIZE:
case CL_EMAXFILES:
cli_dbgmsg("Descriptor[%d]: %s\n", desc, cl_strerror(ret));
case CL_CLEAN:
cache_add(hash, hashed_size, ctx);
return CL_CLEAN;
default:
return ret;

Loading…
Cancel
Save