/* * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * Authors: Alberto Wu * * Acknowledgements: Written from scratch based on specs from PKWARE: * http://www.pkware.com/documents/casestudies/APPNOTE.TXT * * 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. */ /* * Written from scratch based on specs from PKWARE: * see www.pkware.com/documents/casestudies/APPNOTE.TXT * * To the best of my knowledge, it's patent free: * http://www.unisys.com/about__unisys/lzw */ /* To Cami and Dario, the only lawyers I can stand */ #if HAVE_CONFIG_H #include "clamav-config.h" #endif #if HAVE_STRING_H #include #endif #include "clamav.h" #include "explode.h" #include "others.h" /* NOTE: sorting algo must be stable! */ static void bs(uint8_t *k, uint8_t *v, unsigned int elements) { uint8_t tmp; unsigned int i = 0, l = 0, stop = 0, r = elements; while (!stop) { stop = 1; for (; i < r; i++) { if (v[k[i]] > v[k[i + 1]]) { tmp = k[i]; k[i] = k[i + 1]; k[i + 1] = tmp; stop = 0; } } if (stop) break; r--; i--; for (; i > l; i--) { if (v[k[i]] < v[k[i - 1]]) { tmp = k[i]; k[i] = k[i - 1]; k[i - 1] = tmp; stop = 0; } } l++; i++; } } static int unpack_tree(struct xplstate *X, uint32_t *tree, unsigned int expected) { uint8_t temptree[256], order[256], *ttree = temptree; uint8_t *cur = X->window; uint8_t packsz; unsigned int i; uint16_t code = 0, codeinc = 0, lastlen = 0; packsz = *cur++; for (i = 0; i < expected; i++) order[i] = i; i = expected; do { uint8_t values, len; values = *cur++; len = (values & 15) + 1; values = (values >> 4) + 1; if (values > i) return 1; i -= values; while (values--) *ttree++ = len; } while (packsz--); if (i) return 1; bs(order, temptree, expected - 1); i = expected - 1; do { code = code + codeinc; if (temptree[order[i]] != lastlen) { lastlen = temptree[order[i]]; codeinc = 1 << (16 - lastlen); } tree[order[i]] = code | ((uint32_t)lastlen << 16); } while (i--); return 0; } /* bit lame of a lookup, but prolly not worth optimizing */ static int lookup_tree(uint32_t *tree, unsigned int size, uint16_t code, uint8_t len) { uint32_t lookup = ((uint32_t)(len + 1)) << 16 | code; unsigned int i; for (i = 0; i < size; i++) if (tree[i] == lookup) return i; return -1; } int explode_init(struct xplstate *X, uint16_t flags) { X->bits = X->cur = 0; if (flags & 2) { X->largewin = 1; X->mask = 0x1fff; } else { X->largewin = 0; X->mask = 0xfff; } if (flags & 4) { X->state = GRABLITS; X->litcodes = 1; X->minlen = 3; } else { X->state = GRABLENS; X->litcodes = 0; X->minlen = 2; } X->got = 0; return EXPLODE_OK; } #define GETBIT \ if (X->bits) { \ X->bits--; \ val = X->bitmap & 1; \ X->bitmap >>= 1; \ } else { \ if (!X->avail_in) return EXPLODE_EBUFF; \ if (X->avail_in >= 4) { \ X->bitmap = cli_readint32(X->next_in); \ X->bits = 31; \ X->next_in += 4; \ X->avail_in -= 4; \ } else { \ X->bitmap = *X->next_in; \ X->bits = 7; \ X->next_in++; \ X->avail_in--; \ } \ val = X->bitmap & 1; \ X->bitmap >>= 1; \ } #define GETBITS(NUM) \ if (X->bits >= (NUM)) { \ val = X->bitmap & ((1 << (NUM)) - 1); \ X->bitmap >>= (NUM); \ X->bits -= (NUM); \ } else { \ if (X->avail_in * 8 + X->bits < (NUM)) return EXPLODE_EBUFF; \ val = X->bitmap; \ if (X->avail_in >= 4) { \ X->bitmap = cli_readint32(X->next_in); \ X->next_in += 4; \ X->avail_in -= 4; \ val |= (X->bitmap & ((1 << ((NUM)-X->bits)) - 1)) << X->bits; \ X->bitmap >>= (NUM)-X->bits; \ X->bits = 32 - ((NUM)-X->bits); \ } else { \ X->bitmap = *X->next_in; \ X->next_in++; \ X->avail_in--; \ val |= (X->bitmap & ((1 << ((NUM)-X->bits)) - 1)) << X->bits; \ X->bitmap >>= (NUM)-X->bits; \ X->bits = 8 - ((NUM)-X->bits); \ } \ } #define GETCODES(CASE, WHICH, HOWMANY) \ case CASE: { \ if (!X->avail_in) return EXPLODE_EBUFF; \ if (!X->got) \ need = *X->next_in; \ else \ need = X->window[0]; \ if (need > HOWMANY - 1) return EXPLODE_ESTREAM; /* too many codes */ \ need = need + 2 - X->got; /* bytes remaining */ \ if (need > X->avail_in) { /* if not enuff */ \ /* just copy what's avail... */ \ memcpy(&X->window[X->got], X->next_in, X->avail_in); \ X->got += X->avail_in; \ X->next_in += X->avail_in; \ X->avail_in = 0; \ return EXPLODE_EBUFF; /* ...and beg for more */ \ } \ /* else fetch what's needed */ \ memcpy(&X->window[X->got], X->next_in, need); \ X->avail_in -= need; \ X->next_in += need; \ if (unpack_tree(X, X->WHICH, HOWMANY)) return EXPLODE_ESTREAM; \ /* and move on */ \ X->got = 0; \ X->state++; \ } #define SETCASE(CASE) \ X->state = (CASE); \ case (CASE): { /* FAKE */ \ } int explode(struct xplstate *X) { unsigned int val, need; int temp = -1; switch (X->state) { /* grab compressed coded literals, if present */ GETCODES(GRABLITS, lit_tree, 256); /* grab compressed coded lens */ GETCODES(GRABLENS, len_tree, 64); /* grab compressed coded dists */ GETCODES(GRABDISTS, dist_tree, 64); case EXPLODE: while (X->avail_in || X->bits) { GETBIT; /* can't fail */ if (val) { if (X->litcodes) { X->backsize = 0; X->state = EXPLODE_LITCODES; for (X->got = 0; X->got <= 15; X->got++) { case EXPLODE_LITCODES: GETBIT; X->backsize |= val << (15 - X->got); if ((temp = lookup_tree(X->lit_tree, 256, X->backsize, X->got)) != -1) break; } if (temp == -1) return EXPLODE_ESTREAM; X->got = temp; } else { SETCASE(EXPLODE_LITS); GETBITS(8); X->got = val; } SETCASE(EXPLODE_WBYTE); if (!X->avail_out) return EXPLODE_EBUFF; X->avail_out--; *X->next_out = X->window[X->cur & X->mask] = X->got; X->cur++; X->next_out++; } else { SETCASE(EXPLODE_BASEDIST); GETBITS(6u + X->largewin); X->backbytes = val; X->backsize = 0; X->state = EXPLODE_DECODEDISTS; for (X->got = 0; X->got <= 15; X->got++) { case EXPLODE_DECODEDISTS: GETBIT; X->backsize |= val << (15 - X->got); if ((temp = lookup_tree(X->dist_tree, 64, X->backsize, X->got)) != -1) break; } if (temp == -1) return EXPLODE_ESTREAM; X->backbytes |= temp << (6 + X->largewin); X->backbytes++; X->backsize = 0; X->state = EXPLODE_DECODELENS; for (X->got = 0; X->got <= 15; X->got++) { case EXPLODE_DECODELENS: GETBIT; X->backsize |= val << (15 - X->got); if ((temp = lookup_tree(X->len_tree, 64, X->backsize, X->got)) != -1) break; } if (temp == -1) return EXPLODE_ESTREAM; if (temp == 63) { SETCASE(EXPLODE_DECODEEXTRA); GETBITS(8); temp = 63 + val; } X->backsize = temp + X->minlen; X->state = EXPLODE_BACKCOPY; while (X->backsize--) { case EXPLODE_BACKCOPY: if (!X->avail_out) return EXPLODE_EBUFF; X->avail_out--; if (X->cur >= X->backbytes) *X->next_out = X->window[X->cur & X->mask] = X->window[(X->cur - X->backbytes) & X->mask]; else *X->next_out = X->window[X->cur & X->mask] = 0; X->cur++; X->next_out++; } } X->state = EXPLODE; } } return EXPLODE_EBUFF; } void explode_shutdown(void) {}