|
|
|
@ -1,7 +1,7 @@ |
|
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2010 Sourcefire, Inc. |
|
|
|
|
* |
|
|
|
|
* Authors: aCaB <acab@clamav.net> |
|
|
|
|
* 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 |
|
|
|
@ -18,14 +18,15 @@ |
|
|
|
|
* MA 02110-1301, USA. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#include <string.h> |
|
|
|
|
#include <stdlib.h> |
|
|
|
|
#include <pthread.h> |
|
|
|
|
|
|
|
|
|
#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" |
|
|
|
@ -34,7 +35,6 @@ |
|
|
|
|
|
|
|
|
|
#define CACHE_PERTURB 8 |
|
|
|
|
/* 1/10th */ |
|
|
|
|
|
|
|
|
|
static mpool_t *mempool = NULL; |
|
|
|
|
static struct CACHE { |
|
|
|
|
struct CACHE_ENTRY { |
|
|
|
@ -220,3 +220,295 @@ int cache_check(unsigned char *hash, cli_ctx *ctx) { |
|
|
|
|
cli_md5_final(hash, &md5); |
|
|
|
|
return cache_lookup_hash(hash, ctx); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* struct cache_key { */ |
|
|
|
|
/* char digest[16]; */ |
|
|
|
|
/* 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 capacity; */ |
|
|
|
|
/* size_t maxelements; /\* considering load factor *\/ */ |
|
|
|
|
/* size_t elements; */ |
|
|
|
|
/* size_t version; */ |
|
|
|
|
/* struct cache_key *lru_head, *lru_tail; */ |
|
|
|
|
/* pthread_mutex_t mutex; */ |
|
|
|
|
/* }; */ |
|
|
|
|
|
|
|
|
|
/* #define CACHE_INVALID_VERSION ~0u */ |
|
|
|
|
/* #define CACHE_KEY_DELETED ~0u */ |
|
|
|
|
/* #define CACHE_KEY_EMPTY 0 */ |
|
|
|
|
|
|
|
|
|
/* /\* size must be power of 2! *\/ */ |
|
|
|
|
/* static int cacheset_init(struct cache_set* map, size_t maxsize, uint8_t loadfactor) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* map->data = cli_calloc(maxsize, sizeof(*map->data)); */ |
|
|
|
|
/* if (!map->data) */ |
|
|
|
|
/* return CL_EMEM; */ |
|
|
|
|
/* map->capacity = maxsize; */ |
|
|
|
|
/* map->maxelements = loadfactor*maxsize / 100; */ |
|
|
|
|
/* map->elements = 0; */ |
|
|
|
|
/* map->version = CACHE_INVALID_VERSION; */ |
|
|
|
|
/* map->lru_head = map->lru_tail = NULL; */ |
|
|
|
|
/* if (pthread_mutex_init(&map->mutex, NULL)) { */ |
|
|
|
|
/* cli_errmsg("mutex init fail\n"); */ |
|
|
|
|
/* return CL_EMEM; */ |
|
|
|
|
/* } */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* static void cacheset_destroy(struct cache_set *map) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* pthread_mutex_destroy(&map->mutex); */ |
|
|
|
|
/* free(map->data); */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* static void cacheset_acquire(struct cache_set *map) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* pthread_mutex_lock(&map->mutex); */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* static void cache_setversion(struct cache_set* map, uint32_t version) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* unsigned i; */ |
|
|
|
|
/* if (map->version == version) */ |
|
|
|
|
/* return; */ |
|
|
|
|
/* map->version = version; */ |
|
|
|
|
/* map->elements = 0; /\* all elements have expired now *\/ */ |
|
|
|
|
/* for (i=0;i<map->capacity;i++) */ |
|
|
|
|
/* map->data[i].size = 0; */ |
|
|
|
|
/* map->lru_head = map->lru_tail = NULL; */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* 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; */ |
|
|
|
|
/* } */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* static inline uint32_t hash32shift(uint32_t key) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* key = ~key + (key << 15); */ |
|
|
|
|
/* key = key ^ (key >> 12); */ |
|
|
|
|
/* key = key + (key << 2); */ |
|
|
|
|
/* key = key ^ (key >> 4); */ |
|
|
|
|
/* key = (key + (key << 3)) + (key << 11); */ |
|
|
|
|
/* key = key ^ (key >> 16); */ |
|
|
|
|
/* return key; */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* static inline size_t hash(const unsigned char* k,const size_t len,const size_t SIZE) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* size_t Hash = 1; */ |
|
|
|
|
/* size_t i; */ |
|
|
|
|
/* for(i=0;i<len;i++) { */ |
|
|
|
|
/* /\* a simple add is good, because we use the mixing function below *\/ */ |
|
|
|
|
/* Hash += k[i]; */ |
|
|
|
|
/* /\* mixing function *\/ */ |
|
|
|
|
/* Hash = hash32shift(Hash); */ |
|
|
|
|
/* } */ |
|
|
|
|
/* /\* SIZE is power of 2 *\/ */ |
|
|
|
|
/* return Hash & (SIZE - 1); */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* int cacheset_lookup_internal(struct cache_set *map, const struct cache_key *key, */ |
|
|
|
|
/* uint32_t *insert_pos, int deletedok) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* uint32_t idx = hash((const unsigned char*)key, sizeof(*key), map->capacity); */ |
|
|
|
|
/* uint32_t tries = 0; */ |
|
|
|
|
/* struct cache_key *k = &map->data[idx]; */ |
|
|
|
|
/* while (k->size != CACHE_KEY_EMPTY) { */ |
|
|
|
|
/* if (k->size == key->size && */ |
|
|
|
|
/* !memcmp(k->digest, key, 16)) { */ |
|
|
|
|
/* /\* 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++)&(map->capacity-1); */ |
|
|
|
|
/* k = &map->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 void cacheset_add(struct cache_set *map, const struct cache_key *key) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* int ret; */ |
|
|
|
|
/* uint32_t pos; */ |
|
|
|
|
/* struct cache_key *newkey; */ |
|
|
|
|
/* if (map->elements >= map->maxelements) */ |
|
|
|
|
/* cacheset_lru_remove(map, 1); */ |
|
|
|
|
/* assert(map->elements < map->maxelements); */ |
|
|
|
|
|
|
|
|
|
/* ret = cacheset_lookup_internal(map, key, &pos, 1); */ |
|
|
|
|
/* newkey = &map->data[pos]; */ |
|
|
|
|
/* if (ret) { */ |
|
|
|
|
/* /\* was already added, remove from LRU list *\/ */ |
|
|
|
|
/* lru_remove(map, newkey); */ |
|
|
|
|
/* } */ |
|
|
|
|
/* /\* add new key to tail of LRU list *\/ */ |
|
|
|
|
/* lru_addtail(map, newkey); */ |
|
|
|
|
|
|
|
|
|
/* map->elements++; */ |
|
|
|
|
|
|
|
|
|
/* assert(pos < map->maxelements); */ |
|
|
|
|
|
|
|
|
|
/* memcpy(&map->data[pos], key, sizeof(*key)); */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* static int cacheset_lookup(struct cache_set *map, const struct cache_key *key) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* struct cache_key *newkey; */ |
|
|
|
|
/* int ret; */ |
|
|
|
|
/* uint32_t pos; */ |
|
|
|
|
/* ret = cacheset_lookup_internal(map, key, &pos, 0); */ |
|
|
|
|
/* if (!ret) */ |
|
|
|
|
/* return CACHE_INVALID_VERSION; */ |
|
|
|
|
/* newkey = &map->data[pos]; */ |
|
|
|
|
/* /\* update LRU position: move to tail *\/ */ |
|
|
|
|
/* lru_remove(map, newkey); */ |
|
|
|
|
/* lru_addtail(map, newkey); */ |
|
|
|
|
|
|
|
|
|
/* return map->version; */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* static void cacheset_release(struct cache_set *map) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* pthread_mutex_unlock(&map->mutex); */ |
|
|
|
|
/* } */ |
|
|
|
|
|
|
|
|
|
/* #if 0 */ |
|
|
|
|
/* int main(int argc, char **argv) */ |
|
|
|
|
/* { */ |
|
|
|
|
/* struct cache_key key; */ |
|
|
|
|
/* struct cache_set map; */ |
|
|
|
|
/* cacheset_init(&map, 256, 80); */ |
|
|
|
|
/* cacheset_acquire(&map); */ |
|
|
|
|
/* cache_setversion(&map, 10); */ |
|
|
|
|
|
|
|
|
|
/* key.size = 1024; */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123456", 16); */ |
|
|
|
|
/* cacheset_add(&map, &key); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123457", 16); */ |
|
|
|
|
/* cacheset_add(&map, &key); */ |
|
|
|
|
/* memcpy(key.digest, "0123456789012345", 16); */ |
|
|
|
|
/* cacheset_add(&map, &key); */ |
|
|
|
|
|
|
|
|
|
/* key.size = 1024; */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123456", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123456", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123457", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* memcpy(key.digest, "0123456789012345", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* memcpy(key.digest, "0123456789012346", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) == 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
|
|
|
|
|
/* cache_setversion(&map, 1); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123456", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123456", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123457", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* memcpy(key.digest, "0123456789012345", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
|
|
|
|
|
/* cacheset_release(&map); */ |
|
|
|
|
|
|
|
|
|
/* cacheset_destroy(&map); */ |
|
|
|
|
|
|
|
|
|
/* cacheset_init(&map, 8, 50); */ |
|
|
|
|
/* cacheset_acquire(&map); */ |
|
|
|
|
/* cache_setversion(&map, 10); */ |
|
|
|
|
|
|
|
|
|
/* key.size = 416; */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123456", 16); */ |
|
|
|
|
/* cacheset_add(&map, &key); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123457", 16); */ |
|
|
|
|
/* cacheset_add(&map, &key); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123459", 16); */ |
|
|
|
|
/* cacheset_add(&map, &key); */ |
|
|
|
|
/* key.size = 400; */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123450", 16); */ |
|
|
|
|
/* cacheset_add(&map, &key); */ |
|
|
|
|
|
|
|
|
|
/* key.size = 416; */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123456", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) != 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
|
|
|
|
|
/* key.size = 500; */ |
|
|
|
|
/* cacheset_add(&map, &key); */ |
|
|
|
|
/* memcpy(key.digest, "1234567890123457", 16); */ |
|
|
|
|
/* if (cacheset_lookup(&map, &key) == 10) */ |
|
|
|
|
/* abort(); */ |
|
|
|
|
|
|
|
|
|
/* cacheset_release(&map); */ |
|
|
|
|
/* cacheset_destroy(&map); */ |
|
|
|
|
|
|
|
|
|
/* return 0; */ |
|
|
|
|
/* } */ |
|
|
|
|
/* #endif */ |
|
|
|
|