libclamav/matcher-ac.c
8000d078
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
1a2906f4
  *
2023340a
  *  Authors: Tomasz Kojm
8000d078
  *
  *  This program is free software; you can redistribute it and/or modify
bb34cb31
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
8000d078
  *
  *  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
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
8000d078
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
677fc4ba
 #include <ctype.h>
6bb841e8
 #include <sys/stat.h>
563582a1
 
 #include <assert.h>
9290ddf2
 #ifdef HAVE_UNISTD_H
8000d078
 #include <unistd.h>
b58fdfc2
 #endif
8000d078
 
 #include "clamav.h"
 #include "others.h"
b68d11d2
 #include "matcher.h"
8000d078
 #include "matcher-ac.h"
 #include "filetypes.h"
fbcef1b0
 #include "str.h"
b5513f8d
 #include "readdb.h"
589d8d8e
 #include "default.h"
02eabc6d
 #include "filtering.h"
8000d078
 
b94e66c4
 #include "mpool.h"
 
27948a03
 // clang-format off
 
9290ddf2
 #define AC_SPECIAL_ALT_CHAR             1
 #define AC_SPECIAL_ALT_STR_FIXED        2
 #define AC_SPECIAL_ALT_STR              3
 #define AC_SPECIAL_LINE_MARKER          4
 #define AC_SPECIAL_BOUNDARY             5
 #define AC_SPECIAL_WORD_MARKER          6
 
 #define AC_BOUNDARY_LEFT                0x0001
 #define AC_BOUNDARY_LEFT_NEGATIVE       0x0002
 #define AC_BOUNDARY_RIGHT               0x0004
 #define AC_BOUNDARY_RIGHT_NEGATIVE      0x0008
 #define AC_LINE_MARKER_LEFT             0x0010
 #define AC_LINE_MARKER_LEFT_NEGATIVE    0x0020
 #define AC_LINE_MARKER_RIGHT            0x0040
 #define AC_LINE_MARKER_RIGHT_NEGATIVE   0x0080
 #define AC_WORD_MARKER_LEFT             0x0100
 #define AC_WORD_MARKER_LEFT_NEGATIVE    0x0200
 #define AC_WORD_MARKER_RIGHT            0x0400
 #define AC_WORD_MARKER_RIGHT_NEGATIVE   0x0800
8c3c77b4
 
 static char boundary[256] = {
544fa973
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     3, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 1, 3,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 0,
     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8c3c77b4
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 
27948a03
 // clang-format on
 
3e265c46
 static inline int insert_list(struct cli_matcher *root, struct cli_ac_patt *pattern, struct cli_ac_node *pt)
8000d078
 {
3e265c46
     struct cli_ac_list *ph, *new, *ph_prev, *ph_add_after;
     struct cli_ac_list **newtable;
51ca644c
     struct cli_ac_patt *php;
e772c090
     struct cli_ac_special *a1, *a2;
59506ff5
     struct cli_alt_node *b1, *b2;
4d860bd5
     int match;
     uint16_t i, j;
e772c090
 
544fa973
     new = (struct cli_ac_list *)MPOOL_CALLOC(root->mempool, 1, sizeof(struct cli_ac_list));
51ca644c
     if (!new) {
3e265c46
         cli_errmsg("cli_ac_addpatt: Can't allocate memory for list node\n");
e772c090
         return CL_EMEM;
fbcef1b0
     }
51ca644c
     new->me = pattern;
e38ab7c1
 
3e265c46
     root->ac_lists++;
544fa973
     newtable = MPOOL_REALLOC(root->mempool, root->ac_listtable, root->ac_lists * sizeof(struct cli_ac_list *));
288057e9
     if (!newtable) {
3e265c46
         root->ac_lists--;
         cli_errmsg("cli_ac_addpatt: Can't realloc ac_listtable\n");
544fa973
         MPOOL_FREE(root->mempool, new);
3e265c46
         return CL_EMEM;
     }
 
288057e9
     root->ac_listtable                     = newtable;
3e265c46
     root->ac_listtable[root->ac_lists - 1] = new;
 
288057e9
     ph           = pt->list;
b87fe385
     ph_add_after = ph_prev = NULL;
288057e9
     while (ph) {
51ca644c
         php = ph->me;
288057e9
         if (!ph_add_after && php->partno <= pattern->partno && (!ph->next || ph->next->me->partno > pattern->partno))
e772c090
             ph_add_after = ph;
288057e9
         if ((php->length[0] == pattern->length[0]) && (php->prefix_length[0] == pattern->prefix_length[0]) && (php->ch[0] == pattern->ch[0]) && (php->ch[1] == pattern->ch[1]) && (php->boundary == pattern->boundary)) {
             if (!memcmp(php->pattern, pattern->pattern, php->length[0] * sizeof(uint16_t)) && !memcmp(php->prefix, pattern->prefix, php->prefix_length[0] * sizeof(uint16_t))) {
                 if (!php->special && !pattern->special) {
e772c090
                     match = 1;
288057e9
                 } else if (php->special == pattern->special) {
e772c090
                     match = 1;
288057e9
                     for (i = 0; i < php->special; i++) {
51ca644c
                         a1 = php->special_table[i];
e772c090
                         a2 = pattern->special_table[i];
 
288057e9
                         if (a1->num != a2->num) {
e772c090
                             match = 0;
                             break;
                         }
 
288057e9
                         if (a1->negative != a2->negative) {
e772c090
                             match = 0;
                             break;
                         }
 
288057e9
                         if (a1->type != a2->type) {
e772c090
                             match = 0;
                             break;
288057e9
                         } else if (a1->type == AC_SPECIAL_ALT_CHAR) {
                             if (memcmp((a1->alt).byte, (a2->alt).byte, a1->num)) {
51ca644c
                                 match = 0;
                                 break;
e772c090
                             }
288057e9
                         } else if (a1->type == AC_SPECIAL_ALT_STR_FIXED) {
                             if (a1->len != a2->len) {
9290ddf2
                                 match = 0;
                                 break;
                             }
 
288057e9
                             for (j = 0; j < a1->num; j++) {
                                 if (memcmp((a1->alt).f_str[j], (a2->alt).f_str[j], a1->len[0]))
9290ddf2
                                     break;
                             }
 
288057e9
                             if (j < a1->num) {
9290ddf2
                                 match = 0;
                                 break;
                             }
288057e9
                         } else if (a1->type == AC_SPECIAL_ALT_STR) {
9290ddf2
                             b1 = (a1->alt).v_str;
                             b2 = (a2->alt).v_str;
288057e9
                             while (b1 && b2) {
                                 if ((b1->len != b2->len) || memcmp(b1->str, b2->str, b1->len))
9290ddf2
                                     break;
                                 b1 = b1->next;
                                 b2 = b2->next;
                             }
 
288057e9
                             if (b1 || b2) {
9290ddf2
                                 match = 0;
                                 break;
                             }
                         }
e772c090
                     }
                 } else {
                     match = 0;
                 }
 
288057e9
                 if (match) {
                     if (pattern->partno < php->partno) {
51ca644c
                         new->next_same = ph;
288057e9
                         if (ph_prev)
e772c090
                             ph_prev->next = ph->next;
                         else
                             pt->list = ph->next;
 
                         ph->next = NULL;
                         break;
                     } else {
288057e9
                         while (ph->next_same && ph->next_same->me->partno < pattern->partno)
e772c090
                             ph = ph->next_same;
 
51ca644c
                         new->next_same = ph->next_same;
288057e9
                         ph->next_same  = new;
e772c090
                         return CL_SUCCESS;
                     }
                 }
             }
         }
 
         ph_prev = ph;
288057e9
         ph      = ph->next;
e38ab7c1
     }
 
288057e9
     if (ph_add_after) {
         new->next          = ph_add_after->next;
51ca644c
         ph_add_after->next = new;
b87fe385
     } else {
51ca644c
         new->next = pt->list;
288057e9
         pt->list  = new;
b87fe385
     }
8000d078
 
8d3aca30
     return CL_SUCCESS;
8000d078
 }
 
51ca644c
 static inline struct cli_ac_node *add_new_node(struct cli_matcher *root, uint16_t i, uint16_t len)
 {
     struct cli_ac_node *new;
     struct cli_ac_node **newtable;
 
544fa973
     new = (struct cli_ac_node *)MPOOL_CALLOC(root->mempool, 1, sizeof(struct cli_ac_node));
288057e9
     if (!new) {
51ca644c
         cli_errmsg("cli_ac_addpatt: Can't allocate memory for AC node\n");
         return NULL;
     }
 
288057e9
     if (i != len - 1) {
544fa973
         new->trans = (struct cli_ac_node **)MPOOL_CALLOC(root->mempool, 256, sizeof(struct cli_ac_node *));
288057e9
         if (!new->trans) {
51ca644c
             cli_errmsg("cli_ac_addpatt: Can't allocate memory for new->trans\n");
544fa973
             MPOOL_FREE(root->mempool, new);
51ca644c
             return NULL;
         }
     }
 
     root->ac_nodes++;
544fa973
     newtable = MPOOL_REALLOC(root->mempool, root->ac_nodetable, root->ac_nodes * sizeof(struct cli_ac_node *));
288057e9
     if (!newtable) {
51ca644c
         root->ac_nodes--;
         cli_errmsg("cli_ac_addpatt: Can't realloc ac_nodetable\n");
288057e9
         if (new->trans)
544fa973
             MPOOL_FREE(root->mempool, new->trans);
         MPOOL_FREE(root->mempool, new);
51ca644c
         return NULL;
     }
 
288057e9
     root->ac_nodetable                     = newtable;
51ca644c
     root->ac_nodetable[root->ac_nodes - 1] = new;
 
     return new;
 }
 
 static int cli_ac_addpatt_recursive(struct cli_matcher *root, struct cli_ac_patt *pattern, struct cli_ac_node *pt, uint16_t i, uint16_t len)
 {
     struct cli_ac_node *next;
     int ret;
 
     /* last node, insert pattern here (base case)*/
288057e9
     if (i >= len) {
3e265c46
         return insert_list(root, pattern, pt);
51ca644c
     }
 
     /* if current node has no trans table, generate one */
288057e9
     if (!pt->trans) {
544fa973
         pt->trans = (struct cli_ac_node **)MPOOL_CALLOC(root->mempool, 256, sizeof(struct cli_ac_node *));
288057e9
         if (!pt->trans) {
51ca644c
             cli_errmsg("cli_ac_addpatt: Can't allocate memory for pt->trans\n");
             return CL_EMEM;
         }
     }
 
     /* if pattern is nocase, we need to enumerate all the combinations if applicable
      * it's why this function was re-written to be recursive
      */
288057e9
     if ((pattern->sigopts & ACPATT_OPTION_NOCASE) && isalpha(pattern->pattern[i] & 0xff)) {
2e06875d
         next = pt->trans[CLI_NOCASEI((unsigned char)(pattern->pattern[i] & 0xff))];
288057e9
         if (!next)
51ca644c
             next = add_new_node(root, i, len);
288057e9
         if (!next)
51ca644c
             return CL_EMEM;
         else
2e06875d
             pt->trans[CLI_NOCASEI((unsigned char)(pattern->pattern[i] & 0xff))] = next;
51ca644c
 
288057e9
         if ((ret = cli_ac_addpatt_recursive(root, pattern, next, i + 1, len)) != CL_SUCCESS)
51ca644c
             return ret;
     }
 
     /* normal transition, also enumerates the 'normal' nocase */
288057e9
     next = pt->trans[(unsigned char)(pattern->pattern[i] & 0xff)];
     if (!next)
51ca644c
         next = add_new_node(root, i, len);
288057e9
     if (!next)
51ca644c
         return CL_EMEM;
     else
288057e9
         pt->trans[(unsigned char)(pattern->pattern[i] & 0xff)] = next;
51ca644c
 
288057e9
     return cli_ac_addpatt_recursive(root, pattern, next, i + 1, len);
51ca644c
 }
 
102cd430
 cl_error_t cli_ac_addpatt(struct cli_matcher *root, struct cli_ac_patt *pattern)
51ca644c
 {
     struct cli_ac_patt **newtable;
f2102544
     uint16_t len = MIN(root->ac_maxdepth, pattern->length[0]);
4d860bd5
     uint16_t i;
51ca644c
 
288057e9
     for (i = 0; i < len; i++) {
         if (pattern->pattern[i] & CLI_MATCH_WILDCARD) {
51ca644c
             len = i;
             break;
         }
     }
 
288057e9
     if (len < root->ac_mindepth) {
51ca644c
         /* cli_errmsg("cli_ac_addpatt: Signature for %s is too short\n", pattern->virname); */
         return CL_EMALFDB;
     }
 
     /* pattern added to master list */
     root->ac_patterns++;
544fa973
     newtable = MPOOL_REALLOC(root->mempool, root->ac_pattable, root->ac_patterns * sizeof(struct cli_ac_patt *));
288057e9
     if (!newtable) {
51ca644c
         root->ac_patterns--;
         cli_errmsg("cli_ac_addpatt: Can't realloc ac_pattable\n");
         return CL_EMEM;
     }
 
288057e9
     root->ac_pattable                        = newtable;
51ca644c
     root->ac_pattable[root->ac_patterns - 1] = pattern;
 
     pattern->depth = len;
 
     return cli_ac_addpatt_recursive(root, pattern, root->ac_root, 0, len);
 }
 
fbcef1b0
 struct bfs_list {
     struct cli_ac_node *node;
     struct bfs_list *next;
 };
 
1a648b37
 static int bfs_enqueue(struct bfs_list **bfs, struct bfs_list **last, struct cli_ac_node *n)
8000d078
 {
e772c090
     struct bfs_list *new;
8000d078
 
288057e9
     new = (struct bfs_list *)cli_malloc(sizeof(struct bfs_list));
     if (!new) {
e772c090
         cli_errmsg("bfs_enqueue: Can't allocate memory for bfs_list\n");
         return CL_EMEM;
8000d078
     }
e772c090
 
1a648b37
     new->next = NULL;
8000d078
     new->node = n;
1a648b37
 
288057e9
     if (*last) {
e772c090
         (*last)->next = new;
288057e9
         *last         = new;
1a648b37
     } else {
e772c090
         *bfs = *last = new;
1a648b37
     }
fbcef1b0
 
8d3aca30
     return CL_SUCCESS;
8000d078
 }
 
a2f97877
 static struct cli_ac_node *bfs_dequeue(struct bfs_list **bfs, struct bfs_list **last)
8000d078
 {
e772c090
     struct bfs_list *lpt;
     struct cli_ac_node *pt;
8000d078
 
288057e9
     if (!(lpt = *bfs)) {
e772c090
         return NULL;
8000d078
     } else {
e772c090
         *bfs = (*bfs)->next;
288057e9
         pt   = lpt->node;
e772c090
 
288057e9
         if (lpt == *last)
e772c090
             *last = NULL;
 
         free(lpt);
         return pt;
8000d078
     }
 }
 
fbcef1b0
 static int ac_maketrans(struct cli_matcher *root)
8000d078
 {
e772c090
     struct bfs_list *bfs = NULL, *bfs_last = NULL;
     struct cli_ac_node *ac_root = root->ac_root, *child, *node, *fail;
     int i, ret;
8000d078
 
288057e9
     for (i = 0; i < 256; i++) {
e772c090
         node = ac_root->trans[i];
288057e9
         if (!node) {
e772c090
             ac_root->trans[i] = ac_root;
         } else {
             node->fail = ac_root;
288057e9
             if ((ret = bfs_enqueue(&bfs, &bfs_last, node)))
e772c090
                 return ret;
         }
8000d078
     }
 
288057e9
     while ((node = bfs_dequeue(&bfs, &bfs_last))) {
         if (IS_LEAF(node)) {
e772c090
             struct cli_ac_node *failtarget = node->fail;
 
             while (NULL != failtarget && (IS_LEAF(failtarget) || !IS_FINAL(failtarget)))
                 failtarget = failtarget->fail;
 
51c9a991
             if (NULL != failtarget)
                 node->fail = failtarget;
e772c090
 
             continue;
         }
 
288057e9
         for (i = 0; i < 256; i++) {
e772c090
             child = node->trans[i];
288057e9
             if (child) {
e772c090
                 fail = node->fail;
 
288057e9
                 while (IS_LEAF(fail) || !fail->trans[i])
e772c090
                     fail = fail->fail;
 
                 child->fail = fail->trans[i];
 
288057e9
                 if ((ret = bfs_enqueue(&bfs, &bfs_last, child)) != 0)
e772c090
                     return ret;
             }
         }
8000d078
     }
fbcef1b0
 
a305a261
     bfs = bfs_last = NULL;
288057e9
     for (i = 0; i < 256; i++) {
e772c090
         node = ac_root->trans[i];
288057e9
         if (node != ac_root) {
             if ((ret = bfs_enqueue(&bfs, &bfs_last, node)))
9290ddf2
                 return ret;
e772c090
         }
a305a261
     }
e772c090
 
288057e9
     while ((node = bfs_dequeue(&bfs, &bfs_last))) {
         if (IS_LEAF(node))
e772c090
             continue;
288057e9
         for (i = 0; i < 256; i++) {
e772c090
             child = node->trans[i];
             if (!child || (!IS_FINAL(child) && IS_LEAF(child))) {
                 struct cli_ac_node *failtarget = node->fail;
 
288057e9
                 while (IS_LEAF(failtarget) || !failtarget->trans[i])
e772c090
                     failtarget = failtarget->fail;
 
288057e9
                 failtarget     = failtarget->trans[i];
e772c090
                 node->trans[i] = failtarget;
             } else if (IS_FINAL(child) && IS_LEAF(child)) {
3e265c46
                 struct cli_ac_list *list;
e772c090
 
                 list = child->list;
                 if (list) {
                     while (list->next)
                         list = list->next;
 
                     list->next = child->fail->list;
                 } else {
                     child->list = child->fail->list;
                 }
 
                 child->trans = child->fail->trans;
             } else {
288057e9
                 if ((ret = bfs_enqueue(&bfs, &bfs_last, child)) != 0)
e772c090
                     return ret;
             }
         }
a305a261
     }
 
8d3aca30
     return CL_SUCCESS;
8000d078
 }
 
102cd430
 cl_error_t cli_ac_buildtrie(struct cli_matcher *root)
8000d078
 {
288057e9
     if (!root)
e772c090
         return CL_EMALFDB;
8000d078
 
288057e9
     if (!(root->ac_root)) {
e772c090
         cli_dbgmsg("cli_ac_buildtrie: AC pattern matcher is not initialised\n");
         return CL_SUCCESS;
8000d078
     }
 
380ae304
     if (root->filter)
e772c090
         cli_dbgmsg("Using filter for trie %d\n", root->type);
 
fbcef1b0
     return ac_maketrans(root);
8000d078
 }
 
102cd430
 cl_error_t cli_ac_init(struct cli_matcher *root, uint8_t mindepth, uint8_t maxdepth, uint8_t dconf_prefiltering)
8000d078
 {
563582a1
 #ifdef USE_MPOOL
     assert(root->mempool && "mempool must be initialized");
 #endif
8000d078
 
544fa973
     root->ac_root = (struct cli_ac_node *)MPOOL_CALLOC(root->mempool, 1, sizeof(struct cli_ac_node));
288057e9
     if (!root->ac_root) {
e772c090
         cli_errmsg("cli_ac_init: Can't allocate memory for ac_root\n");
         return CL_EMEM;
fbcef1b0
     }
8000d078
 
544fa973
     root->ac_root->trans = (struct cli_ac_node **)MPOOL_CALLOC(root->mempool, 256, sizeof(struct cli_ac_node *));
288057e9
     if (!root->ac_root->trans) {
e772c090
         cli_errmsg("cli_ac_init: Can't allocate memory for ac_root->trans\n");
544fa973
         MPOOL_FREE(root->mempool, root->ac_root);
e772c090
         return CL_EMEM;
8000d078
     }
fbcef1b0
 
     root->ac_mindepth = mindepth;
     root->ac_maxdepth = maxdepth;
 
5b74e89a
     if (cli_mtargets[root->type].enable_prefiltering && dconf_prefiltering) {
544fa973
         root->filter = MPOOL_MALLOC(root->mempool, sizeof(*root->filter));
e772c090
         if (!root->filter) {
             cli_errmsg("cli_ac_init: Can't allocate memory for ac_root->filter\n");
544fa973
             MPOOL_FREE(root->mempool, root->ac_root->trans);
             MPOOL_FREE(root->mempool, root->ac_root);
e772c090
             return CL_EMEM;
         }
         filter_init(root->filter);
02eabc6d
     }
 
fbcef1b0
     return CL_SUCCESS;
8000d078
 }
 
b94e66c4
 #ifdef USE_MPOOL
a6d4c62e
 #define mpool_ac_free_special(a, b) ac_free_special(a, b)
 static void ac_free_special(mpool_t *mempool, struct cli_ac_patt *p)
b94e66c4
 #else
a6d4c62e
 #define mpool_ac_free_special(a, b) ac_free_special(b)
 static void ac_free_special(struct cli_ac_patt *p)
b94e66c4
 #endif
1a648b37
 {
59506ff5
     unsigned int i, j;
     struct cli_ac_special *a1;
     struct cli_alt_node *b1, *b2;
1a648b37
 
288057e9
     if (!p->special)
e772c090
         return;
1a648b37
 
288057e9
     for (i = 0; i < p->special; i++) {
e772c090
         a1 = p->special_table[i];
9290ddf2
         if (a1->type == AC_SPECIAL_ALT_CHAR) {
544fa973
             MPOOL_FREE(mempool, (a1->alt).byte);
9290ddf2
         } else if (a1->type == AC_SPECIAL_ALT_STR_FIXED) {
             for (j = 0; j < a1->num; j++)
544fa973
                 MPOOL_FREE(mempool, (a1->alt).f_str[j]);
             MPOOL_FREE(mempool, (a1->alt).f_str);
9290ddf2
         } else if (a1->type == AC_SPECIAL_ALT_STR) {
             b1 = (a1->alt).v_str;
             while (b1) {
                 b2 = b1->next;
544fa973
                 MPOOL_FREE(mempool, b1->str);
                 MPOOL_FREE(mempool, b1);
9290ddf2
                 b1 = b2;
             }
         }
544fa973
         MPOOL_FREE(mempool, a1);
1a648b37
     }
544fa973
     MPOOL_FREE(mempool, p->special_table);
1a648b37
 }
 
5612732c
 void cli_ac_free(struct cli_matcher *root)
8000d078
 {
e772c090
     uint32_t i;
     struct cli_ac_patt *patt;
8000d078
 
288057e9
     for (i = 0; i < root->ac_patterns; i++) {
e772c090
         patt = root->ac_pattable[i];
544fa973
         MPOOL_FREE(root->mempool, patt->prefix ? patt->prefix : patt->pattern);
         MPOOL_FREE(root->mempool, patt->virname);
288057e9
         if (patt->special)
e772c090
             mpool_ac_free_special(root->mempool, patt);
544fa973
         MPOOL_FREE(root->mempool, patt);
fbcef1b0
     }
e772c090
 
288057e9
     if (root->ac_pattable)
544fa973
         MPOOL_FREE(root->mempool, root->ac_pattable);
fbcef1b0
 
288057e9
     if (root->ac_reloff)
544fa973
         MPOOL_FREE(root->mempool, root->ac_reloff);
33872a43
 
74628936
     /* Freeing trans nodes must be done before freeing table nodes! */
288057e9
     for (i = 0; i < root->ac_nodes; i++) {
         if (!IS_LEAF(root->ac_nodetable[i]) &&
             root->ac_nodetable[i]->fail &&
             root->ac_nodetable[i]->trans != root->ac_nodetable[i]->fail->trans) {
544fa973
             MPOOL_FREE(root->mempool, root->ac_nodetable[i]->trans);
e772c090
         }
74628936
     }
 
288057e9
     for (i = 0; i < root->ac_lists; i++)
544fa973
         MPOOL_FREE(root->mempool, root->ac_listtable[i]);
3e265c46
 
288057e9
     if (root->ac_listtable)
544fa973
         MPOOL_FREE(root->mempool, root->ac_listtable);
31ab943a
 
288057e9
     for (i = 0; i < root->ac_nodes; i++)
544fa973
         MPOOL_FREE(root->mempool, root->ac_nodetable[i]);
8000d078
 
288057e9
     if (root->ac_nodetable)
544fa973
         MPOOL_FREE(root->mempool, root->ac_nodetable);
e772c090
 
288057e9
     if (root->ac_root) {
544fa973
         MPOOL_FREE(root->mempool, root->ac_root->trans);
         MPOOL_FREE(root->mempool, root->ac_root);
fbcef1b0
     }
e772c090
 
02eabc6d
     if (root->filter)
544fa973
         MPOOL_FREE(root->mempool, root->filter);
8000d078
 }
 
677fc4ba
 /*
  * In parse_only mode this function returns -1 on error or the max subsig id
  */
ee8bd2fb
 int cli_ac_chklsig(const char *expr, const char *end, uint32_t *lsigcnt, unsigned int *cnt, uint64_t *ids, unsigned int parse_only)
677fc4ba
 {
e772c090
     unsigned int i, len = end - expr, pth = 0, opoff = 0, op1off = 0, val;
     unsigned int blkend = 0, id, modval1, modval2 = 0, lcnt = 0, rcnt = 0, tcnt, modoff = 0;
     uint64_t lids = 0, rids = 0, tids;
     int ret, lval, rval;
     char op = 0, op1 = 0, mod = 0, blkmod = 0;
     const char *lstart = expr, *lend = NULL, *rstart = NULL, *rend = end, *pt;
677fc4ba
 
288057e9
     for (i = 0; i < len; i++) {
         switch (expr[i]) {
             case '(':
                 pth++;
                 break;
e772c090
 
288057e9
             case ')':
                 if (!pth) {
                     cli_errmsg("cli_ac_chklsig: Syntax error: Missing opening parenthesis\n");
                     return -1;
                 }
                 pth--;
e772c090
 
288057e9
             case '>':
             case '<':
             case '=':
                 mod    = expr[i];
                 modoff = i;
                 break;
e772c090
 
288057e9
             default:
                 if (strchr("&|", expr[i])) {
                     if (!pth) {
                         op    = expr[i];
                         opoff = i;
                     } else if (pth == 1) {
                         op1    = expr[i];
                         op1off = i;
                     }
e772c090
                 }
         }
 
288057e9
         if (op)
e772c090
             break;
 
288057e9
         if (op1 && !pth) {
e772c090
             blkend = i;
288057e9
             if (expr[i + 1] == '>' || expr[i + 1] == '<' || expr[i + 1] == '=') {
e772c090
                 blkmod = expr[i + 1];
 
                 ret = sscanf(&expr[i + 2], "%u,%u", &modval1, &modval2);
288057e9
                 if (ret != 2)
e772c090
                     ret = sscanf(&expr[i + 2], "%u", &modval1);
 
288057e9
                 if (!ret || ret == EOF) {
e772c090
                     cli_errmsg("chklexpr: Syntax error: Missing number after '%c'\n", expr[i + 1]);
                     return -1;
                 }
 
288057e9
                 for (i += 2; i + 1 < len && (isdigit(expr[i + 1]) || expr[i + 1] == ','); i++)
e772c090
                     ;
             }
 
288057e9
             if (&expr[i + 1] == rend)
e772c090
                 break;
             else
                 blkmod = 0;
         }
677fc4ba
     }
 
288057e9
     if (pth) {
e772c090
         cli_errmsg("cli_ac_chklsig: Syntax error: Missing closing parenthesis\n");
         return -1;
677fc4ba
     }
 
288057e9
     if (!op && !op1) {
         if (expr[0] == '(')
e772c090
             return cli_ac_chklsig(++expr, --end, lsigcnt, cnt, ids, parse_only);
 
         ret = sscanf(expr, "%u", &id);
288057e9
         if (!ret || ret == EOF) {
e772c090
             cli_errmsg("cli_ac_chklsig: Can't parse %s\n", expr);
             return -1;
         }
 
288057e9
         if (parse_only)
e772c090
             val = id;
         else
             val = lsigcnt[id];
 
288057e9
         if (mod) {
             pt  = expr + modoff + 1;
e772c090
             ret = sscanf(pt, "%u", &modval1);
288057e9
             if (!ret || ret == EOF) {
e772c090
                 cli_errmsg("chklexpr: Syntax error: Missing number after '%c'\n", mod);
                 return -1;
             }
 
288057e9
             if (!parse_only) {
                 switch (mod) {
                     case '=':
                         if (val != modval1)
                             return 0;
                         break;
                     case '<':
                         if (val >= modval1)
                             return 0;
                         break;
                     case '>':
                         if (val <= modval1)
                             return 0;
                         break;
                     default:
e772c090
                         return 0;
                 }
 
                 *cnt += val;
288057e9
                 *ids |= (uint64_t)1 << id;
e772c090
                 return 1;
             }
         }
 
288057e9
         if (parse_only) {
e772c090
             return val;
         } else {
288057e9
             if (val) {
e772c090
                 *cnt += val;
288057e9
                 *ids |= (uint64_t)1 << id;
e772c090
                 return 1;
             } else {
                 return 0;
             }
         }
677fc4ba
     }
 
288057e9
     if (!op) {
         op    = op1;
e772c090
         opoff = op1off;
         lstart++;
         rend = &expr[blkend];
677fc4ba
     }
 
288057e9
     if (!opoff) {
e772c090
         cli_errmsg("cli_ac_chklsig: Syntax error: Missing left argument\n");
         return -1;
677fc4ba
     }
e772c090
 
677fc4ba
     lend = &expr[opoff];
288057e9
     if (opoff + 1 == len) {
e772c090
         cli_errmsg("cli_ac_chklsig: Syntax error: Missing right argument\n");
         return -1;
677fc4ba
     }
e772c090
 
677fc4ba
     rstart = &expr[opoff + 1];
 
ee8bd2fb
     lval = cli_ac_chklsig(lstart, lend, lsigcnt, &lcnt, &lids, parse_only);
288057e9
     if (lval == -1) {
e772c090
         cli_errmsg("cli_ac_chklsig: Calculation of lval failed\n");
         return -1;
677fc4ba
     }
 
ee8bd2fb
     rval = cli_ac_chklsig(rstart, rend, lsigcnt, &rcnt, &rids, parse_only);
288057e9
     if (rval == -1) {
e772c090
         cli_errmsg("cli_ac_chklsig: Calculation of rval failed\n");
         return -1;
677fc4ba
     }
 
288057e9
     if (parse_only) {
         switch (op) {
             case '&':
             case '|':
                 return MAX(lval, rval);
             default:
                 cli_errmsg("cli_ac_chklsig: Incorrect operator type\n");
                 return -1;
e772c090
         }
677fc4ba
     } else {
288057e9
         switch (op) {
             case '&':
                 ret = lval && rval;
                 break;
             case '|':
                 ret = lval || rval;
                 break;
             default:
                 cli_errmsg("cli_ac_chklsig: Incorrect operator type\n");
                 return -1;
e772c090
         }
 
288057e9
         if (!blkmod) {
             if (ret) {
e772c090
                 *cnt += lcnt + rcnt;
                 *ids |= lids | rids;
             }
 
             return ret;
         } else {
288057e9
             if (ret) {
e772c090
                 tcnt = lcnt + rcnt;
                 tids = lids | rids;
             } else {
                 tcnt = 0;
                 tids = 0;
             }
 
288057e9
             switch (blkmod) {
                 case '=':
                     if (tcnt != modval1)
                         return 0;
                     break;
                 case '<':
                     if (tcnt >= modval1)
                         return 0;
                     break;
                 case '>':
                     if (tcnt <= modval1)
                         return 0;
                     break;
                 default:
e772c090
                     return 0;
             }
 
288057e9
             if (modval2) {
e772c090
                 val = 0;
288057e9
                 while (tids) {
                     val += tids & (uint64_t)1;
e772c090
                     tids >>= 1;
                 }
 
288057e9
                 if (val < modval2)
e772c090
                     return 0;
             }
 
             *cnt += tcnt;
             return 1;
         }
677fc4ba
     }
 }
 
a2345bb5
 inline static int ac_findmatch_special(const unsigned char *buffer, uint32_t offset, uint32_t bp, uint32_t fileoffset, uint32_t length,
                                        const struct cli_ac_patt *pattern, uint32_t pp, uint16_t specialcnt, uint32_t *start, uint32_t *end, int rev);
 static int ac_backward_match_branch(const unsigned char *buffer, uint32_t bp, uint32_t offset, uint32_t length, uint32_t fileoffset,
                                     const struct cli_ac_patt *pattern, uint32_t pp, uint16_t specialcnt, uint32_t *start, uint32_t *end);
 static int ac_forward_match_branch(const unsigned char *buffer, uint32_t bp, uint32_t offset, uint32_t length, uint32_t fileoffset,
                                    const struct cli_ac_patt *pattern, uint32_t pp, uint16_t specialcnt, uint32_t *start, uint32_t *end);
659a8077
 
dda6b057
 /* call only by ac_findmatch_special! Does not handle recursive specials */
288057e9
 #define AC_MATCH_CHAR2(p, b)                                         \
     switch (wc = p & CLI_MATCH_METADATA) {                           \
         case CLI_MATCH_CHAR:                                         \
             if ((unsigned char)p != b)                               \
                 match = 0;                                           \
             break;                                                   \
                                                                      \
         case CLI_MATCH_NOCASE:                                       \
2e06875d
             if ((unsigned char)(p & 0xff) != CLI_NOCASE(b))          \
288057e9
                 match = 0;                                           \
             break;                                                   \
                                                                      \
         case CLI_MATCH_IGNORE:                                       \
             break;                                                   \
                                                                      \
         case CLI_MATCH_NIBBLE_HIGH:                                  \
             if ((unsigned char)(p & 0x00f0) != (b & 0xf0))           \
                 match = 0;                                           \
             break;                                                   \
                                                                      \
         case CLI_MATCH_NIBBLE_LOW:                                   \
             if ((unsigned char)(p & 0x000f) != (b & 0x0f))           \
                 match = 0;                                           \
             break;                                                   \
                                                                      \
         default:                                                     \
             cli_errmsg("ac_findmatch: Unknown metatype 0x%x\n", wc); \
             match = 0;                                               \
dda6b057
     }
 
a2345bb5
 /* call only by ac_XX_match_branch! */
288057e9
 #define AC_MATCH_CHAR(p, b, rev)                                                              \
     switch (wc = p & CLI_MATCH_METADATA) {                                                    \
         case CLI_MATCH_CHAR:                                                                  \
             if ((unsigned char)p != b)                                                        \
                 match = 0;                                                                    \
             break;                                                                            \
                                                                                               \
         case CLI_MATCH_NOCASE:                                                                \
2e06875d
             if ((unsigned char)(p & 0xff) != CLI_NOCASE(b))                                   \
288057e9
                 match = 0;                                                                    \
             break;                                                                            \
                                                                                               \
         case CLI_MATCH_IGNORE:                                                                \
             break;                                                                            \
                                                                                               \
         case CLI_MATCH_SPECIAL:                                                               \
             /* >1 = movement, 0 = fail, <1 = resolved in branch */                            \
             if ((match = ac_findmatch_special(buffer, offset, bp, fileoffset, length,         \
                                               pattern, i, specialcnt, start, end, rev)) <= 0) \
                 return match;                                                                 \
                                                                                               \
             if (!rev) {                                                                       \
                 bp += (match - 1); /* -1 is for bp++ in parent loop */                        \
                 specialcnt++;                                                                 \
             } else {                                                                          \
                 bp = bp + 1 - match; /* +1 is for bp-- in parent loop */                      \
                 specialcnt--;                                                                 \
             }                                                                                 \
                                                                                               \
             break;                                                                            \
                                                                                               \
         case CLI_MATCH_NIBBLE_HIGH:                                                           \
             if ((unsigned char)(p & 0x00f0) != (b & 0xf0))                                    \
                 match = 0;                                                                    \
             break;                                                                            \
                                                                                               \
         case CLI_MATCH_NIBBLE_LOW:                                                            \
             if ((unsigned char)(p & 0x000f) != (b & 0x0f))                                    \
                 match = 0;                                                                    \
             break;                                                                            \
                                                                                               \
         default:                                                                              \
             cli_errmsg("ac_findmatch: Unknown metatype 0x%x\n", wc);                          \
             match = 0;                                                                        \
4ffcf308
     }
 
659a8077
 /* special handler */
a2345bb5
 inline static int ac_findmatch_special(const unsigned char *buffer, uint32_t offset, uint32_t bp, uint32_t fileoffset, uint32_t length,
                                        const struct cli_ac_patt *pattern, uint32_t pp, uint16_t specialcnt, uint32_t *start, uint32_t *end, int rev)
659a8077
 {
59506ff5
     int match, cmp;
28a56447
     uint16_t j, b = buffer[bp];
dda6b057
     uint16_t wc;
a2345bb5
     uint32_t subbp;
659a8077
     struct cli_ac_special *special = pattern->special_table[specialcnt];
288057e9
     struct cli_alt_node *alt       = NULL;
659a8077
 
     match = special->negative;
 
288057e9
     switch (special->type) {
         case AC_SPECIAL_ALT_CHAR: /* single-byte */
             for (j = 0; j < special->num; j++) {
                 cmp = b - (special->alt).byte[j];
                 if (cmp == 0) {
                     match = !special->negative;
                     break;
                 } else if (cmp < 0)
                     break;
             }
             break;
2b512658
 
288057e9
         case AC_SPECIAL_ALT_STR_FIXED: /* fixed length multi-byte */
a2345bb5
             if (!rev) {
288057e9
                 if (bp + special->len[0] > length)
                     break;
a2345bb5
                 subbp = bp;
             } else {
288057e9
                 if (bp < (special->len[0] - 1))
                     break;
                 subbp = bp - (special->len[0] - 1);
9290ddf2
             }
 
288057e9
             match *= special->len[0];
             for (j = 0; j < special->num; j++) {
                 cmp = memcmp(&buffer[subbp], (special->alt).f_str[j], special->len[0]);
                 if (cmp == 0) {
                     match = (!special->negative) * special->len[0];
dda6b057
                     break;
288057e9
                 } else if (cmp < 0)
590d4023
                     break;
dda6b057
             }
288057e9
             break;
9290ddf2
 
288057e9
         case AC_SPECIAL_ALT_STR: /* generic */
             alt = (special->alt).v_str;
             while (alt) {
                 if (!rev) {
                     if (bp + alt->len > length) {
                         alt = alt->next;
                         continue;
                     }
                     subbp = bp;
                 } else {
                     if (bp < (alt->len - 1)) {
                         alt = alt->next;
                         continue;
                     }
                     subbp = bp - (alt->len - 1);
                 }
 
                 /* note that generic alternates CANNOT be negated */
                 match = 1;
                 for (j = 0; j < alt->len; j++) {
                     AC_MATCH_CHAR2(alt->str[j], buffer[subbp + j]);
                     if (!match)
                         break;
                 }
                 if (match) {
                     /* if match is unique (has no derivatives), we can pass it directly back */
                     if (alt->unique) {
                         match = alt->len;
                         break;
                     }
                     /* branch for backtracking */
                     if (!rev)
                         match = ac_forward_match_branch(buffer, subbp + alt->len, offset, fileoffset, length, pattern, pp + 1, specialcnt + 1, start, end);
                     else
                         match = ac_backward_match_branch(buffer, subbp - 1, offset, fileoffset, length, pattern, pp - 1, specialcnt - 1, start, end);
                     if (match)
                         return -1; /* alerts caller that match has been resolved in child callee */
                 }
 
                 alt = alt->next;
             }
             break;
659a8077
 
288057e9
         case AC_SPECIAL_LINE_MARKER:
             if (b == '\n')
                 match = !special->negative;
             else if (b == '\r' && (bp + 1 < length && buffer[bp + 1] == '\n'))
                 match = (!special->negative) * 2;
             break;
659a8077
 
288057e9
         case AC_SPECIAL_BOUNDARY:
             if (boundary[b])
                 match = !special->negative;
             break;
659a8077
 
288057e9
         case AC_SPECIAL_WORD_MARKER:
             if (!isalnum(b))
                 match = !special->negative;
             break;
659a8077
 
288057e9
         default:
             cli_errmsg("ac_findmatch: Unknown special\n");
             match = 0;
659a8077
     }
 
     return match;
 }
 
 /* state should reset on call, recursion depth = number of alternate specials */
a2345bb5
 /* each loop iteration starts on the NEXT sequence to be validated */
 static int ac_backward_match_branch(const unsigned char *buffer, uint32_t bp, uint32_t offset, uint32_t fileoffset, uint32_t length,
                                     const struct cli_ac_patt *pattern, uint32_t pp, uint16_t specialcnt, uint32_t *start, uint32_t *end)
659a8077
 {
927b2bab
     int match = 0;
659a8077
     uint16_t wc, i;
a2345bb5
     uint32_t filestart;
2242da43
 
a2345bb5
     /* backwards (prefix) validation, determines start */
288057e9
     if (pattern->prefix && pattern->prefix_length[0]) {
a2345bb5
         match = 1;
e772c090
 
a2345bb5
         for (i = pp; 1; i--) {
288057e9
             AC_MATCH_CHAR(pattern->prefix[i], buffer[bp], 1);
             if (!match)
a2345bb5
                 return 0;
 
             /* needs to perform check before decrement due to unsignedness */
             if (i == 0 || bp == 0)
                 break;
 
             bp--;
         }
 
288057e9
         *start    = bp;
a2345bb5
         filestart = fileoffset - offset + bp;
     } else {
         /* bp is set to buffer offset */
         *start = bp = offset;
288057e9
         filestart   = fileoffset;
8000d078
     }
 
a2345bb5
     /* left-side special checks, bp = start */
288057e9
     if (pattern->boundary & AC_BOUNDARY_LEFT) {
e772c090
         match = !!(pattern->boundary & AC_BOUNDARY_LEFT_NEGATIVE);
288057e9
         if (!filestart || (bp && (boundary[buffer[bp - 1]] == 1 || boundary[buffer[bp - 1]] == 3)))
e772c090
             match = !match;
 
288057e9
         if (!match)
e772c090
             return 0;
8c3c77b4
     }
 
288057e9
     if (pattern->boundary & AC_LINE_MARKER_LEFT) {
a2345bb5
         match = !!(pattern->boundary & AC_LINE_MARKER_LEFT_NEGATIVE);
288057e9
         if (!filestart || (bp && (buffer[bp - 1] == '\n')))
e772c090
             match = !match;
 
288057e9
         if (!match)
e772c090
             return 0;
8c3c77b4
     }
 
288057e9
     if (pattern->boundary & AC_WORD_MARKER_LEFT) {
a2345bb5
         match = !!(pattern->boundary & AC_WORD_MARKER_LEFT_NEGATIVE);
288057e9
         if (!filestart)
a2345bb5
             match = !match;
288057e9
         else if (pattern->sigopts & ACPATT_OPTION_WIDE) {
             if (filestart - 1 == 0)
a2345bb5
                 match = !match;
288057e9
             if (bp - 1 && bp && !(isalnum(buffer[bp - 2]) && buffer[bp - 1] == '\0'))
a2345bb5
                 match = !match;
288057e9
         } else if (bp && !isalnum(buffer[bp - 1]))
e772c090
             match = !match;
 
288057e9
         if (!match)
e772c090
             return 0;
723a44d5
     }
 
a2345bb5
     /* bp is shifted for left anchor check, thus invalidated as pattern start */
288057e9
     if (!(pattern->ch[0] & CLI_MATCH_IGNORE)) {
         if (pattern->ch_mindist[0] + (uint32_t)1 > bp)
a2345bb5
             return 0;
 
         bp -= pattern->ch_mindist[0] + 1;
288057e9
         for (i = pattern->ch_mindist[0]; i <= pattern->ch_maxdist[0]; i++) {
a2345bb5
             match = 1;
288057e9
             AC_MATCH_CHAR(pattern->ch[0], buffer[bp], 1);
             if (match)
a2345bb5
                 break;
e772c090
 
288057e9
             if (!bp)
a2345bb5
                 return 0;
             else
                 bp--;
         }
288057e9
         if (!match)
e772c090
             return 0;
723a44d5
     }
 
a2345bb5
     return 1;
 }
 
 /* state should reset on call, recursion depth = number of alternate specials */
 /* each loop iteration starts on the NEXT sequence to validate */
 static int ac_forward_match_branch(const unsigned char *buffer, uint32_t bp, uint32_t offset, uint32_t fileoffset, uint32_t length,
                                    const struct cli_ac_patt *pattern, uint32_t pp, uint16_t specialcnt, uint32_t *start, uint32_t *end)
 {
     int match;
     uint16_t wc, i;
 
     match = 1;
 
     /* forward (pattern) validation; determines end */
288057e9
     for (i = pp; i < pattern->length[0] && bp < length; i++) {
         AC_MATCH_CHAR(pattern->pattern[i], buffer[bp], 0);
a2345bb5
         if (!match)
             return 0;
 
         bp++;
     }
     *end = bp;
 
     /* right-side special checks, bp = end */
288057e9
     if (pattern->boundary & AC_BOUNDARY_RIGHT) {
a2345bb5
         match = !!(pattern->boundary & AC_BOUNDARY_RIGHT_NEGATIVE);
288057e9
         if ((length <= SCANBUFF) && (bp == length || boundary[buffer[bp]] >= 2))
f22d89ee
             match = !match;
a2345bb5
 
288057e9
         if (!match)
a2345bb5
             return 0;
     }
 
288057e9
     if (pattern->boundary & AC_LINE_MARKER_RIGHT) {
a2345bb5
         match = !!(pattern->boundary & AC_LINE_MARKER_RIGHT_NEGATIVE);
288057e9
         if ((length <= SCANBUFF) && (bp == length || buffer[bp] == '\n' || (buffer[bp] == '\r' && bp + 1 < length && buffer[bp + 1] == '\n')))
6b725bb7
             match = !match;
 
288057e9
         if (!match)
6b725bb7
             return 0;
     }
 
288057e9
     if (pattern->boundary & AC_WORD_MARKER_RIGHT) {
6b725bb7
         match = !!(pattern->boundary & AC_WORD_MARKER_RIGHT_NEGATIVE);
288057e9
         if (length <= SCANBUFF) {
             if (bp == length)
f22d89ee
                 match = !match;
288057e9
             else if ((pattern->sigopts & ACPATT_OPTION_WIDE) && (bp + 1 < length)) {
                 if (!(isalnum(buffer[bp]) && buffer[bp + 1] == '\0'))
f22d89ee
                     match = !match;
288057e9
             } else if (!isalnum(buffer[bp]))
f22d89ee
                 match = !match;
         }
6b725bb7
 
288057e9
         if (!match)
6b725bb7
             return 0;
     }
 
a2345bb5
     /* bp is shifted for right anchor check, thus invalidated as pattern right-side */
288057e9
     if (!(pattern->ch[1] & CLI_MATCH_IGNORE)) {
e772c090
         bp += pattern->ch_mindist[1];
 
288057e9
         for (i = pattern->ch_mindist[1]; i <= pattern->ch_maxdist[1]; i++) {
             if (bp >= length)
e772c090
                 return 0;
 
             match = 1;
288057e9
             AC_MATCH_CHAR(pattern->ch[1], buffer[bp], 0);
             if (match)
e772c090
                 break;
 
             bp++;
         }
 
288057e9
         if (!match)
e772c090
             return 0;
1a2906f4
     }
 
288057e9
     return ac_backward_match_branch(buffer, offset - 1, offset, fileoffset, length, pattern, pattern->prefix_length[0] - 1, pattern->special_pattern - 1, start, end);
8000d078
 }
 
a2345bb5
 inline static int ac_findmatch(const unsigned char *buffer, uint32_t offset, uint32_t fileoffset, uint32_t length, const struct cli_ac_patt *pattern, uint32_t *start, uint32_t *end)
659a8077
 {
     int match;
     uint16_t specialcnt = pattern->special_pattern;
 
a2345bb5
     /* minimal check as the maximum variable length may exceed the buffer */
288057e9
     if ((offset + pattern->length[1] > length) || (pattern->prefix_length[1] > offset))
659a8077
         return 0;
 
288057e9
     match = ac_forward_match_branch(buffer, offset + pattern->depth, offset, fileoffset, length, pattern, pattern->depth, specialcnt, start, end);
     if (match)
9290ddf2
         return 1;
659a8077
     return 0;
 }
 
102cd430
 cl_error_t cli_ac_initdata(struct cli_ac_data *data, uint32_t partsigs, uint32_t lsigs, uint32_t reloffsigs, uint8_t tracklen)
4e9ab8ed
 {
e772c090
     unsigned int i, j;
677fc4ba
 
cd94be7a
     UNUSEDPARAM(tracklen);
4e9ab8ed
 
288057e9
     if (!data) {
e772c090
         cli_errmsg("cli_ac_init: data == NULL\n");
         return CL_ENULLARG;
4e9ab8ed
     }
c1206103
     memset((void *)data, 0, sizeof(struct cli_ac_data));
4e9ab8ed
 
aca9ea82
     data->reloffsigs = reloffsigs;
288057e9
     if (reloffsigs) {
         data->offset = (uint32_t *)cli_malloc(reloffsigs * 2 * sizeof(uint32_t));
         if (!data->offset) {
e772c090
             cli_errmsg("cli_ac_init: Can't allocate memory for data->offset\n");
             return CL_EMEM;
         }
288057e9
         for (i = 0; i < reloffsigs * 2; i += 2)
e772c090
             data->offset[i] = CLI_OFF_NONE;
aca9ea82
     }
4e9ab8ed
 
aca9ea82
     data->partsigs = partsigs;
288057e9
     if (partsigs) {
         data->offmatrix = (uint32_t ***)cli_calloc(partsigs, sizeof(uint32_t **));
         if (!data->offmatrix) {
e772c090
             cli_errmsg("cli_ac_init: Can't allocate memory for data->offmatrix\n");
 
288057e9
             if (reloffsigs)
e772c090
                 free(data->offset);
 
             return CL_EMEM;
         }
4e9ab8ed
     }
288057e9
 
677fc4ba
     data->lsigs = lsigs;
288057e9
     if (lsigs) {
         data->lsigcnt = (uint32_t **)cli_malloc(lsigs * sizeof(uint32_t *));
         if (!data->lsigcnt) {
             if (partsigs)
e772c090
                 free(data->offmatrix);
 
288057e9
             if (reloffsigs)
e772c090
                 free(data->offset);
 
             cli_errmsg("cli_ac_init: Can't allocate memory for data->lsigcnt\n");
             return CL_EMEM;
         }
288057e9
         data->lsigcnt[0] = (uint32_t *)cli_calloc(lsigs * 64, sizeof(uint32_t));
         if (!data->lsigcnt[0]) {
e772c090
             free(data->lsigcnt);
288057e9
             if (partsigs)
e772c090
                 free(data->offmatrix);
 
288057e9
             if (reloffsigs)
e772c090
                 free(data->offset);
 
             cli_errmsg("cli_ac_init: Can't allocate memory for data->lsigcnt[0]\n");
             return CL_EMEM;
         }
288057e9
         for (i = 1; i < lsigs; i++)
e772c090
             data->lsigcnt[i] = data->lsigcnt[0] + 64 * i;
288057e9
         data->yr_matches = (uint8_t *)cli_calloc(lsigs, sizeof(uint8_t));
7665e02d
         if (data->yr_matches == NULL) {
             free(data->lsigcnt[0]);
             free(data->lsigcnt);
288057e9
             if (partsigs)
7665e02d
                 free(data->offmatrix);
288057e9
 
             if (reloffsigs)
7665e02d
                 free(data->offset);
             return CL_EMEM;
         }
e772c090
 
         /* subsig offsets */
288057e9
         data->lsig_matches = (struct cli_lsig_matches **)cli_calloc(lsigs, sizeof(struct cli_lsig_matches *));
         if (!data->lsig_matches) {
7665e02d
             free(data->yr_matches);
b7999b89
             free(data->lsigcnt[0]);
             free(data->lsigcnt);
288057e9
             if (partsigs)
b7999b89
                 free(data->offmatrix);
 
288057e9
             if (reloffsigs)
b7999b89
                 free(data->offset);
 
             cli_errmsg("cli_ac_init: Can't allocate memory for data->lsig_matches\n");
             return CL_EMEM;
         }
288057e9
         data->lsigsuboff_last  = (uint32_t **)cli_malloc(lsigs * sizeof(uint32_t *));
         data->lsigsuboff_first = (uint32_t **)cli_malloc(lsigs * sizeof(uint32_t *));
         if (!data->lsigsuboff_last || !data->lsigsuboff_first) {
b7999b89
             free(data->lsig_matches);
e772c090
             free(data->lsigsuboff_last);
             free(data->lsigsuboff_first);
7665e02d
             free(data->yr_matches);
e772c090
             free(data->lsigcnt[0]);
             free(data->lsigcnt);
288057e9
             if (partsigs)
e772c090
                 free(data->offmatrix);
 
288057e9
             if (reloffsigs)
e772c090
                 free(data->offset);
 
             cli_errmsg("cli_ac_init: Can't allocate memory for data->lsigsuboff_(last|first)\n");
             return CL_EMEM;
         }
288057e9
         data->lsigsuboff_last[0]  = (uint32_t *)cli_calloc(lsigs * 64, sizeof(uint32_t));
         data->lsigsuboff_first[0] = (uint32_t *)cli_calloc(lsigs * 64, sizeof(uint32_t));
         if (!data->lsigsuboff_last[0] || !data->lsigsuboff_first[0]) {
b7999b89
             free(data->lsig_matches);
e772c090
             free(data->lsigsuboff_last[0]);
             free(data->lsigsuboff_first[0]);
             free(data->lsigsuboff_last);
             free(data->lsigsuboff_first);
7665e02d
             free(data->yr_matches);
e772c090
             free(data->lsigcnt[0]);
             free(data->lsigcnt);
288057e9
             if (partsigs)
e772c090
                 free(data->offmatrix);
 
288057e9
             if (reloffsigs)
e772c090
                 free(data->offset);
 
             cli_errmsg("cli_ac_init: Can't allocate memory for data->lsigsuboff_(last|first)[0]\n");
             return CL_EMEM;
         }
288057e9
         for (j = 0; j < 64; j++) {
             data->lsigsuboff_last[0][j]  = CLI_OFF_NONE;
e772c090
             data->lsigsuboff_first[0][j] = CLI_OFF_NONE;
         }
288057e9
         for (i = 1; i < lsigs; i++) {
             data->lsigsuboff_last[i]  = data->lsigsuboff_last[0] + 64 * i;
e772c090
             data->lsigsuboff_first[i] = data->lsigsuboff_first[0] + 64 * i;
288057e9
             for (j = 0; j < 64; j++) {
                 data->lsigsuboff_last[i][j]  = CLI_OFF_NONE;
e772c090
                 data->lsigsuboff_first[i][j] = CLI_OFF_NONE;
             }
         }
aca9ea82
     }
288057e9
     for (i = 0; i < 32; i++)
e772c090
         data->macro_lastmatch[i] = CLI_OFF_NONE;
aca9ea82
 
b87fe385
     data->min_partno = 1;
 
fbcef1b0
     return CL_SUCCESS;
 }
4e9ab8ed
 
102cd430
 cl_error_t cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, const struct cli_target_info *info)
33872a43
 {
e772c090
     int ret;
     unsigned int i;
     struct cli_ac_patt *patt;
33872a43
 
288057e9
     if (info)
e772c090
         data->vinfo = &info->exeinfo.vinfo;
d2ba6f98
 
288057e9
     for (i = 0; i < root->ac_reloff_num; i++) {
e772c090
         patt = root->ac_reloff[i];
288057e9
         if (!info) {
e772c090
             data->offset[patt->offset_min] = CLI_OFF_NONE;
288057e9
         } else if ((ret = cli_caloff(NULL, info, root->type, patt->offdata, &data->offset[patt->offset_min], &data->offset[patt->offset_max]))) {
e772c090
             cli_errmsg("cli_ac_caloff: Can't calculate relative offset in signature for %s\n", patt->virname);
             return ret;
288057e9
         } else if ((data->offset[patt->offset_min] != CLI_OFF_NONE) && (data->offset[patt->offset_min] + patt->length[1] > info->fsize)) {
e772c090
             data->offset[patt->offset_min] = CLI_OFF_NONE;
         }
33872a43
     }
 
     return CL_SUCCESS;
 }
 
fbcef1b0
 void cli_ac_freedata(struct cli_ac_data *data)
 {
e772c090
     uint32_t i;
4e9ab8ed
 
b7999b89
     if (!data)
         return;
 
288057e9
     if (data->partsigs) {
         for (i = 0; i < data->partsigs; i++) {
             if (data->offmatrix[i]) {
e772c090
                 free(data->offmatrix[i][0]);
                 free(data->offmatrix[i]);
             }
         }
         free(data->offmatrix);
         data->offmatrix = NULL;
288057e9
         data->partsigs  = 0;
677fc4ba
     }
 
288057e9
     if (data->lsigs) {
b7999b89
         if (data->lsig_matches) {
             for (i = 0; i < data->lsigs; i++) {
288057e9
                 struct cli_lsig_matches *ls_matches;
b7999b89
                 if ((ls_matches = data->lsig_matches[i])) {
                     uint32_t j;
                     for (j = 0; j < ls_matches->subsigs; j++) {
                         if (ls_matches->matches[j]) {
                             free(ls_matches->matches[j]);
                             ls_matches->matches[j] = 0;
                         }
                     }
                     free(data->lsig_matches[i]);
                     data->lsig_matches[i] = 0;
                 }
             }
             free(data->lsig_matches);
             data->lsig_matches = 0;
         }
7665e02d
         free(data->yr_matches);
e772c090
         free(data->lsigcnt[0]);
         free(data->lsigcnt);
         free(data->lsigsuboff_last[0]);
         free(data->lsigsuboff_last);
         free(data->lsigsuboff_first[0]);
         free(data->lsigsuboff_first);
         data->lsigs = 0;
227f8f7c
     }
aca9ea82
 
288057e9
     if (data->reloffsigs) {
e772c090
         free(data->offset);
         data->reloffsigs = 0;
aca9ea82
     }
fbcef1b0
 }
227f8f7c
 
aec1e3be
 /* returns only CL_SUCCESS or CL_EMEM */
5025967e
 inline static int ac_addtype(struct cli_matched_type **list, cli_file_t type, off_t offset, const cli_ctx *ctx)
fbcef1b0
 {
e772c090
     struct cli_matched_type *tnode, *tnode_last;
4e9ab8ed
 
288057e9
     if (type == CL_TYPE_ZIPSFX) {
         if (*list && ctx && ctx->engine->maxfiles && (*list)->cnt > ctx->engine->maxfiles)
e772c090
             return CL_SUCCESS;
288057e9
     } else if (*list && (*list)->cnt >= MAX_EMBEDDED_OBJ) {
e772c090
         return CL_SUCCESS;
     }
4e9ab8ed
 
288057e9
     if (!(tnode = cli_calloc(1, sizeof(struct cli_matched_type)))) {
e772c090
         cli_errmsg("cli_ac_addtype: Can't allocate memory for new type node\n");
         return CL_EMEM;
4e9ab8ed
     }
 
288057e9
     tnode->type   = type;
fbcef1b0
     tnode->offset = offset;
4e9ab8ed
 
fbcef1b0
     tnode_last = *list;
288057e9
     while (tnode_last && tnode_last->next)
e772c090
         tnode_last = tnode_last->next;
4e9ab8ed
 
288057e9
     if (tnode_last)
e772c090
         tnode_last->next = tnode;
fbcef1b0
     else
e772c090
         *list = tnode;
4e9ab8ed
 
fbcef1b0
     (*list)->cnt++;
4e9ab8ed
     return CL_SUCCESS;
 }
 
102cd430
 cl_error_t lsig_sub_matched(const struct cli_matcher *root, struct cli_ac_data *mdata, uint32_t lsigid1, uint32_t lsigid2, uint32_t realoff, int partial)
ab893605
 {
b7999b89
     const struct cli_ac_lsig *ac_lsig = root->ac_lsigtable[lsigid1];
288057e9
     const struct cli_lsig_tdb *tdb    = &ac_lsig->tdb;
70b3c2cc
 
288057e9
     if (realoff != CLI_OFF_NONE) {
         if (mdata->lsigsuboff_first[lsigid1][lsigid2] == CLI_OFF_NONE)
e772c090
             mdata->lsigsuboff_first[lsigid1][lsigid2] = realoff;
 
288057e9
         if (mdata->lsigsuboff_last[lsigid1][lsigid2] != CLI_OFF_NONE && ((!partial && realoff <= mdata->lsigsuboff_last[lsigid1][lsigid2]) || (partial && realoff < mdata->lsigsuboff_last[lsigid1][lsigid2])))
b7999b89
             return CL_SUCCESS;
e772c090
 
         mdata->lsigcnt[lsigid1][lsigid2]++;
288057e9
         if (mdata->lsigcnt[lsigid1][lsigid2] <= 1 || !tdb->macro_ptids || !tdb->macro_ptids[lsigid2])
e772c090
             mdata->lsigsuboff_last[lsigid1][lsigid2] = realoff;
70b3c2cc
     }
94f8946c
 
b7999b89
     if (ac_lsig->type & CLI_YARA_OFFSET && realoff != CLI_OFF_NONE) {
288057e9
         struct cli_subsig_matches *ss_matches;
         struct cli_lsig_matches *ls_matches;
b7999b89
         cli_dbgmsg("lsig_sub_matched lsig %u:%u at %u\n", lsigid1, lsigid2, realoff);
 
         ls_matches = mdata->lsig_matches[lsigid1];
         if (ls_matches == NULL) { /* allocate cli_lsig_matches */
             ls_matches = mdata->lsig_matches[lsigid1] = (struct cli_lsig_matches *)cli_calloc(1, sizeof(struct cli_lsig_matches) +
288057e9
                                                                                                      (ac_lsig->tdb.subsigs - 1) * sizeof(struct cli_subsig_matches *));
b7999b89
             if (ls_matches == NULL) {
                 cli_errmsg("lsig_sub_matched: cli_calloc failed for cli_lsig_matches\n");
                 return CL_EMEM;
             }
             ls_matches->subsigs = ac_lsig->tdb.subsigs;
         }
         ss_matches = ls_matches->matches[lsigid2];
         if (ss_matches == NULL) { /*  allocate cli_subsig_matches */
             ss_matches = ls_matches->matches[lsigid2] = cli_malloc(sizeof(struct cli_subsig_matches));
             if (ss_matches == NULL) {
                 cli_errmsg("lsig_sub_matched: cli_malloc failed for cli_subsig_matches struct\n");
                 return CL_EMEM;
             }
             ss_matches->next = 0;
288057e9
             ss_matches->last = sizeof(ss_matches->offsets) / sizeof(uint32_t) - 1;
b7999b89
         }
288057e9
         if (ss_matches->next > ss_matches->last) { /* cli_matches out of space? realloc */
b7999b89
             ss_matches = ls_matches->matches[lsigid2] = cli_realloc(ss_matches, sizeof(struct cli_subsig_matches) + sizeof(uint32_t) * ss_matches->last * 2);
             if (ss_matches == NULL) {
                 cli_errmsg("lsig_sub_matched: cli_realloc failed for cli_subsig_matches struct\n");
                 return CL_EMEM;
             }
288057e9
             ss_matches->last = sizeof(ss_matches->offsets) / sizeof(uint32_t) + ss_matches->last * 2 - 1;
b7999b89
         }
288057e9
 
b7999b89
         ss_matches->offsets[ss_matches->next] = realoff; /* finally, store the offset */
         ss_matches->next++;
     }
 
70b3c2cc
     if (mdata->lsigcnt[lsigid1][lsigid2] > 1) {
544fa973
         /* Check that the previous match had a macro match following it at the
e772c090
          * correct distance. This check is only done after the 1st match.*/
         const struct cli_ac_patt *macropt;
         uint32_t id, last_macro_match, smin, smax, last_macroprev_match;
 
         if (!tdb->macro_ptids)
b7999b89
             return CL_SUCCESS;
e772c090
 
         id = tdb->macro_ptids[lsigid2];
         if (!id)
b7999b89
             return CL_SUCCESS;
e772c090
 
         macropt = root->ac_pattable[id];
288057e9
         smin    = macropt->ch_mindist[0];
         smax    = macropt->ch_maxdist[0];
e772c090
         /* start of last macro match */
         last_macro_match = mdata->macro_lastmatch[macropt->sigid];
         /* start of previous lsig subsig match */
         last_macroprev_match = mdata->lsigsuboff_last[lsigid1][lsigid2];
         if (last_macro_match != CLI_OFF_NONE)
             cli_dbgmsg("Checking macro match: %u + (%u - %u) == %u\n",
9290ddf2
                        last_macroprev_match, smin, smax, last_macro_match);
e772c090
 
         if (last_macro_match == CLI_OFF_NONE ||
             last_macroprev_match + smin > last_macro_match ||
             last_macroprev_match + smax < last_macro_match) {
             cli_dbgmsg("Canceled false lsig macro match\n");
             /* Previous match was false - cancel it */
             mdata->lsigcnt[lsigid1][lsigid2]--;
             mdata->lsigsuboff_last[lsigid1][lsigid2] = realoff;
         } else {
             /* mark the macro sig itself matched */
288057e9
             mdata->lsigcnt[lsigid1][lsigid2 + 1]++;
             mdata->lsigsuboff_last[lsigid1][lsigid2 + 1] = last_macro_match;
e772c090
         }
ab893605
     }
b7999b89
     return CL_SUCCESS;
ab893605
 }
 
102cd430
 cl_error_t cli_ac_chkmacro(struct cli_matcher *root, struct cli_ac_data *data, unsigned lsigid1)
ab893605
 {
     const struct cli_lsig_tdb *tdb = &root->ac_lsigtable[lsigid1]->tdb;
     unsigned i;
b7999b89
     int rc;
 
ab893605
     /* Loop through all subsigs, and if they are tied to macros check that the
      * macro matched at a correct distance */
288057e9
     for (i = 0; i < tdb->subsigs; i++) {
b7999b89
         rc = lsig_sub_matched(root, data, lsigid1, i, CLI_OFF_NONE, 0);
         if (rc != CL_SUCCESS)
             return rc;
ab893605
     }
b7999b89
     return CL_SUCCESS;
ab893605
 }
 
102cd430
 cl_error_t cli_ac_scanbuff(
288057e9
     const unsigned char *buffer,
     uint32_t length,
     const char **virname,
     void **customdata,
     struct cli_ac_result **res,
     const struct cli_matcher *root,
     struct cli_ac_data *mdata,
     uint32_t offset,
     cli_file_t ftype,
     struct cli_matched_type **ftoffset,
     unsigned int mode,
d0cba11e
     cli_ctx *ctx)
8000d078
 {
e772c090
     struct cli_ac_node *current;
3e265c46
     struct cli_ac_list *pattN, *ptN;
e772c090
     struct cli_ac_patt *patt, *pt;
a2345bb5
     uint32_t i, bp, exptoff[2], realoff, matchstart, matchend;
e772c090
     uint16_t j;
     uint8_t found, viruses_found = 0;
d0cba11e
     uint32_t **offmatrix, swp;
e772c090
     int type = CL_CLEAN;
     struct cli_ac_result *newres;
b7999b89
     int rc;
8000d078
 
288057e9
     if (!root->ac_root)
e772c090
         return CL_CLEAN;
8000d078
 
288057e9
     if (!mdata && (root->ac_partsigs || root->ac_lsigs || root->ac_reloff_num)) {
e772c090
         cli_errmsg("cli_ac_scanbuff: mdata == NULL\n");
         return CL_ENULLARG;
8000d078
     }
 
     current = root->ac_root;
 
288057e9
     for (i = 0; i < length; i++) {
23d7c6e6
         current = current->trans[buffer[i]];
e772c090
 
288057e9
         if (UNLIKELY(IS_FINAL(current))) {
3e265c46
             struct cli_ac_list *faillist = current->fail->list;
288057e9
             pattN                        = current->list;
             while (pattN) {
51ca644c
                 patt = pattN->me;
288057e9
                 if (patt->partno > mdata->min_partno) {
                     pattN    = faillist;
e772c090
                     faillist = NULL;
                     continue;
                 }
                 bp = i + 1 - patt->depth;
288057e9
                 if (patt->offdata[0] != CLI_OFF_VERSION && patt->offdata[0] != CLI_OFF_MACRO && !pattN->next_same && (patt->offset_min != CLI_OFF_ANY) && (!patt->sigid || patt->partno == 1)) {
                     if (patt->offset_min == CLI_OFF_NONE) {
51ca644c
                         pattN = pattN->next;
e772c090
                         continue;
                     }
a2345bb5
                     exptoff[0] = offset + bp - patt->prefix_length[2]; /* lower offset end */
                     exptoff[1] = offset + bp - patt->prefix_length[1]; /* higher offset end */
288057e9
                     if (patt->offdata[0] == CLI_OFF_ABSOLUTE) {
                         if (patt->offset_max < exptoff[0] || patt->offset_min > exptoff[1]) {
51ca644c
                             pattN = pattN->next;
e772c090
                             continue;
                         }
                     } else {
288057e9
                         if (mdata->offset[patt->offset_min] == CLI_OFF_NONE || mdata->offset[patt->offset_max] < exptoff[0] || mdata->offset[patt->offset_min] > exptoff[1]) {
51ca644c
                             pattN = pattN->next;
e772c090
                             continue;
                         }
                     }
                 }
 
51ca644c
                 ptN = pattN;
288057e9
                 if (ac_findmatch(buffer, bp, offset + bp, length, patt, &matchstart, &matchend)) {
                     while (ptN) {
51ca644c
                         pt = ptN->me;
288057e9
                         if (pt->partno > mdata->min_partno)
e772c090
                             break;
 
288057e9
                         if ((pt->type && !(mode & AC_SCAN_FT)) || (!pt->type && !(mode & AC_SCAN_VIR))) {
51ca644c
                             ptN = ptN->next_same;
e772c090
                             continue;
                         }
 
a2345bb5
                         realoff = offset + matchstart;
288057e9
                         if (pt->offdata[0] == CLI_OFF_VERSION) {
                             if (!cli_hashset_contains_maybe_noalloc(mdata->vinfo, realoff)) {
51ca644c
                                 ptN = ptN->next_same;
e772c090
                                 continue;
                             }
                             cli_dbgmsg("cli_ac_scanbuff: VI match for offset %x\n", realoff);
288057e9
                         } else if (pt->offdata[0] == CLI_OFF_MACRO) {
e772c090
                             mdata->macro_lastmatch[patt->offdata[1]] = realoff;
288057e9
                             ptN                                      = ptN->next_same;
e772c090
                             continue;
288057e9
                         } else if (pt->offset_min != CLI_OFF_ANY && (!pt->sigid || pt->partno == 1)) {
                             if (pt->offset_min == CLI_OFF_NONE) {
51ca644c
                                 ptN = ptN->next_same;
e772c090
                                 continue;
                             }
288057e9
                             if (pt->offdata[0] == CLI_OFF_ABSOLUTE) {
                                 if (pt->offset_max < realoff || pt->offset_min > realoff) {
51ca644c
                                     ptN = ptN->next_same;
e772c090
                                     continue;
                                 }
                             } else {
288057e9
                                 if (mdata->offset[pt->offset_min] == CLI_OFF_NONE || mdata->offset[pt->offset_max] < realoff || mdata->offset[pt->offset_min] > realoff) {
51ca644c
                                     ptN = ptN->next_same;
e772c090
                                     continue;
                                 }
                             }
                         }
 
288057e9
                         if (pt->sigid) { /* it's a partial signature */
e772c090
 
                             /* if 2nd or later part, confirm some prior part has matched */
288057e9
                             if (pt->partno != 1 && (!mdata->offmatrix[pt->sigid - 1] || !mdata->offmatrix[pt->sigid - 1][pt->partno - 2][0])) {
51ca644c
                                 ptN = ptN->next_same;
e772c090
                                 continue;
                             }
 
288057e9
                             if (pt->partno + 1 > mdata->min_partno)
e772c090
                                 mdata->min_partno = pt->partno + 1;
 
                             /* sparsely populated matrix, so allocate and initialize if NULL */
288057e9
                             if (!mdata->offmatrix[pt->sigid - 1]) {
e772c090
                                 mdata->offmatrix[pt->sigid - 1] = cli_malloc(pt->parts * sizeof(int32_t *));
288057e9
                                 if (!mdata->offmatrix[pt->sigid - 1]) {
e772c090
                                     cli_errmsg("cli_ac_scanbuff: Can't allocate memory for mdata->offmatrix[%u]\n", pt->sigid - 1);
                                     return CL_EMEM;
                                 }
 
d0cba11e
                                 mdata->offmatrix[pt->sigid - 1][0] = cli_malloc(pt->parts * (CLI_DEFAULT_AC_TRACKLEN + 2) * sizeof(uint32_t));
288057e9
                                 if (!mdata->offmatrix[pt->sigid - 1][0]) {
e772c090
                                     cli_errmsg("cli_ac_scanbuff: Can't allocate memory for mdata->offmatrix[%u][0]\n", pt->sigid - 1);
                                     free(mdata->offmatrix[pt->sigid - 1]);
                                     mdata->offmatrix[pt->sigid - 1] = NULL;
                                     return CL_EMEM;
                                 }
d0cba11e
                                 memset(mdata->offmatrix[pt->sigid - 1][0], (uint32_t)-1, pt->parts * (CLI_DEFAULT_AC_TRACKLEN + 2) * sizeof(uint32_t));
e772c090
                                 mdata->offmatrix[pt->sigid - 1][0][0] = 0;
288057e9
                                 for (j = 1; j < pt->parts; j++) {
                                     mdata->offmatrix[pt->sigid - 1][j]    = mdata->offmatrix[pt->sigid - 1][0] + j * (CLI_DEFAULT_AC_TRACKLEN + 2);
e772c090
                                     mdata->offmatrix[pt->sigid - 1][j][0] = 0;
                                 }
                             }
                             offmatrix = mdata->offmatrix[pt->sigid - 1];
 
                             found = 0;
288057e9
                             if (pt->partno != 1) {
                                 for (j = 1; (j <= CLI_DEFAULT_AC_TRACKLEN + 1) && (offmatrix[pt->partno - 2][j] != (uint32_t)-1); j++) {
e772c090
                                     found = j;
288057e9
                                     if (realoff < offmatrix[pt->partno - 2][j])
f37565c3
                                         found = 0;
 
288057e9
                                     if (found && pt->maxdist)
                                         if (realoff - offmatrix[pt->partno - 2][j] > pt->maxdist)
e772c090
                                             found = 0;
 
288057e9
                                     if (found && pt->mindist)
                                         if (realoff - offmatrix[pt->partno - 2][j] < pt->mindist)
e772c090
                                             found = 0;
 
288057e9
                                     if (found)
e772c090
                                         break;
                                 }
                             }
 
288057e9
                             if (pt->partno == 2 && found > 1) {
                                 swp                 = offmatrix[0][1];
                                 offmatrix[0][1]     = offmatrix[0][found];
e772c090
                                 offmatrix[0][found] = swp;
 
288057e9
                                 if (pt->type != CL_TYPE_MSEXE) {
                                     swp                             = offmatrix[pt->parts - 1][1];
                                     offmatrix[pt->parts - 1][1]     = offmatrix[pt->parts - 1][found];
e772c090
                                     offmatrix[pt->parts - 1][found] = swp;
                                 }
                             }
 
288057e9
                             if (pt->partno == 1 || (found && (pt->partno != pt->parts))) {
                                 if (offmatrix[pt->partno - 1][0] == CLI_DEFAULT_AC_TRACKLEN + 1)
e772c090
                                     offmatrix[pt->partno - 1][0] = 1; /* wrap, ends up at 2 */
                                 offmatrix[pt->partno - 1][0]++;
                                 offmatrix[pt->partno - 1][offmatrix[pt->partno - 1][0]] = offset + matchend;
 
288057e9
                                 if (pt->partno == 1) /* save realoff for the first part */
e772c090
                                     offmatrix[pt->parts - 1][offmatrix[pt->partno - 1][0]] = realoff;
288057e9
                             } else if (found && pt->partno == pt->parts) {
                                 if (pt->type) {
e772c090
 
288057e9
                                     if (pt->type == CL_TYPE_IGNORED && (!pt->rtype || ftype == pt->rtype))
e772c090
                                         return CL_TYPE_IGNORED;
 
288057e9
                                     if ((pt->type > type || pt->type >= CL_TYPE_SFX || pt->type == CL_TYPE_MSEXE) && (!pt->rtype || ftype == pt->rtype)) {
e772c090
                                         cli_dbgmsg("Matched signature for file type %s\n", pt->virname);
                                         type = pt->type;
288057e9
                                         if (ftoffset && (!*ftoffset || (*ftoffset)->cnt < MAX_EMBEDDED_OBJ || type == CL_TYPE_ZIPSFX) && (type >= CL_TYPE_SFX || ((ftype == CL_TYPE_MSEXE || ftype == CL_TYPE_ZIP || ftype == CL_TYPE_MSOLE2) && type == CL_TYPE_MSEXE))) {
e772c090
                                             /* FIXME: the first offset in the array is most likely the correct one but
                                              * it may happen it is not
                                              */
288057e9
                                             for (j = 1; j <= CLI_DEFAULT_AC_TRACKLEN + 1 && offmatrix[0][j] != (uint32_t)-1; j++)
                                                 if (ac_addtype(ftoffset, type, offmatrix[pt->parts - 1][j], ctx))
e772c090
                                                     return CL_EMEM;
                                         }
 
d0cba11e
                                         memset(offmatrix[0], (uint32_t)-1, pt->parts * (CLI_DEFAULT_AC_TRACKLEN + 2) * sizeof(uint32_t));
288057e9
                                         for (j = 0; j < pt->parts; j++)
e772c090
                                             offmatrix[j][0] = 0;
                                     }
 
                                 } else { /* !pt->type */
288057e9
                                     if (pt->lsigid[0]) {
b7999b89
                                         rc = lsig_sub_matched(root, mdata, pt->lsigid[1], pt->lsigid[2], offmatrix[pt->parts - 1][1], 1);
                                         if (rc != CL_SUCCESS)
                                             return rc;
51ca644c
                                         ptN = ptN->next_same;
e772c090
                                         continue;
                                     }
 
288057e9
                                     if (res) {
                                         newres = (struct cli_ac_result *)malloc(sizeof(struct cli_ac_result));
                                         if (!newres) {
e772c090
                                             cli_errmsg("cli_ac_scanbuff: Can't allocate memory for newres %lu\n", (unsigned long)sizeof(struct cli_ac_result));
                                             return CL_EMEM;
                                         }
288057e9
                                         newres->virname    = pt->virname;
e772c090
                                         newres->customdata = pt->customdata;
288057e9
                                         newres->next       = *res;
                                         newres->offset     = (off_t)offmatrix[pt->parts - 1][1];
                                         *res               = newres;
e772c090
 
51ca644c
                                         ptN = ptN->next_same;
e772c090
                                         continue;
                                     } else {
288057e9
                                         if (ctx && SCAN_ALLMATCHES) {
e772c090
                                             cli_append_virus(ctx, (const char *)pt->virname);
                                             viruses_found = 1;
                                         }
                                         if (virname)
                                             *virname = pt->virname;
288057e9
                                         if (customdata)
e772c090
                                             *customdata = pt->customdata;
048a88e6
                                         if (!ctx || !SCAN_ALLMATCHES)
e772c090
                                             return CL_VIRUS;
51ca644c
                                         ptN = ptN->next_same;
e772c090
                                         continue;
                                     }
                                 }
                             }
 
                         } else { /* old type signature */
288057e9
                             if (pt->type) {
                                 if (pt->type == CL_TYPE_IGNORED && (!pt->rtype || ftype == pt->rtype))
e772c090
                                     return CL_TYPE_IGNORED;
 
288057e9
                                 if ((pt->type > type || pt->type >= CL_TYPE_SFX || pt->type == CL_TYPE_MSEXE) && (!pt->rtype || ftype == pt->rtype)) {
e772c090
 
                                     cli_dbgmsg("Matched signature for file type %s at %u\n", pt->virname, realoff);
                                     type = pt->type;
288057e9
                                     if (ftoffset && (!*ftoffset || (*ftoffset)->cnt < MAX_EMBEDDED_OBJ || type == CL_TYPE_ZIPSFX) && (type == CL_TYPE_MBR || type >= CL_TYPE_SFX || ((ftype == CL_TYPE_MSEXE || ftype == CL_TYPE_ZIP || ftype == CL_TYPE_MSOLE2) && type == CL_TYPE_MSEXE))) {
e772c090
 
288057e9
                                         if (ac_addtype(ftoffset, type, realoff, ctx))
e772c090
                                             return CL_EMEM;
                                     }
                                 }
                             } else {
288057e9
                                 if (pt->lsigid[0]) {
b7999b89
                                     rc = lsig_sub_matched(root, mdata, pt->lsigid[1], pt->lsigid[2], realoff, 0);
                                     if (rc != CL_SUCCESS)
                                         return rc;
51ca644c
                                     ptN = ptN->next_same;
e772c090
                                     continue;
                                 }
 
288057e9
                                 if (res) {
                                     newres = (struct cli_ac_result *)malloc(sizeof(struct cli_ac_result));
                                     if (!newres) {
e772c090
                                         cli_errmsg("cli_ac_scanbuff: Can't allocate memory for newres %lu\n", (unsigned long)sizeof(struct cli_ac_result));
                                         return CL_EMEM;
                                     }
288057e9
                                     newres->virname    = pt->virname;
e772c090
                                     newres->customdata = pt->customdata;
288057e9
                                     newres->offset     = (off_t)realoff;
                                     newres->next       = *res;
                                     *res               = newres;
e772c090
 
51ca644c
                                     ptN = ptN->next_same;
e772c090
                                     continue;
                                 } else {
288057e9
                                     if (ctx && SCAN_ALLMATCHES) {
e772c090
                                         cli_append_virus(ctx, (const char *)pt->virname);
                                         viruses_found = 1;
                                     }
 
                                     if (virname)
                                         *virname = pt->virname;
 
288057e9
                                     if (customdata)
e772c090
                                         *customdata = pt->customdata;
 
048a88e6
                                     if (!ctx || !SCAN_ALLMATCHES)
e772c090
                                         return CL_VIRUS;
 
51ca644c
                                     ptN = ptN->next_same;
e772c090
                                     continue;
                                 }
                             }
                         }
51ca644c
                         ptN = ptN->next_same;
e772c090
                     }
                 }
51ca644c
                 pattN = pattN->next;
e772c090
             }
         }
8000d078
     }
 
aec1e3be
     if (viruses_found)
e772c090
         return CL_VIRUS;
 
6038397e
     return (mode & AC_SCAN_FT) ? type : CL_CLEAN;
8000d078
 }
fbcef1b0
 
cdd018d5
 static int qcompare_byte(const void *a, const void *b)
57e5af13
 {
bb1e844c
     return *(const unsigned char *)a - *(const unsigned char *)b;
57e5af13
 }
 
cdd018d5
 static int qcompare_fstr(const void *arg, const void *a, const void *b)
 {
     uint16_t len = *(uint16_t *)arg;
     return memcmp(*(const unsigned char **)a, *(const unsigned char **)b, len);
 }
 
2b512658
 /* returns if level of nesting, end set to MATCHING paren, start AFTER staring paren */
4d860bd5
 inline static size_t find_paren_end(char *hexstr, char **end)
2b512658
 {
4d860bd5
     size_t i;
     size_t nest = 0, level = 0;
2b512658
 
     *end = NULL;
     for (i = 0; i < strlen(hexstr); i++) {
9290ddf2
         if (hexstr[i] == '(') {
             nest++;
             level++;
         } else if (hexstr[i] == ')') {
             if (!level) {
                 *end = &hexstr[i];
                 break;
             }
             level--;
         }
2b512658
     }
 
     return nest;
 }
 
59506ff5
 /* analyzes expr, returns number of subexpr, if fixed length subexpr and longest subexpr len *
  * goes to either end of string or to closing parenthesis; allowed to be unbalanced          *
  * counts applied to start of expr (not end, i.e. numexpr starts at 1 for the first expr     */
 inline static int ac_analyze_expr(char *hexstr, int *fixed_len, int *sub_len)
2b512658
 {
d0cba11e
     unsigned long i;
     int level = 0, len = 0, numexpr = 1;
59506ff5
     int flen, slen;
2b512658
 
59506ff5
     flen = 1;
     slen = 0;
2b512658
     for (i = 0; i < strlen(hexstr); i++) {
9290ddf2
         if (hexstr[i] == '(') {
             flen = 0;
             level++;
         } else if (hexstr[i] == ')') {
             if (!level) {
                 if (!slen) {
                     slen = len;
                 } else if (len != slen) {
                     flen = 0;
                     if (len > slen)
                         slen = len;
                 }
                 break;
             }
             level--;
         }
         if (!level && hexstr[i] == '|') {
             if (!slen) {
                 slen = len;
             } else if (len != slen) {
                 flen = 0;
                 if (len > slen)
                     slen = len;
             }
             len = 0;
             numexpr++;
         } else {
dda6b057
             if (hexstr[i] == '?')
                 flen = 0;
9290ddf2
             len++;
         }
2b512658
     }
9ae2ec70
     if (!slen) {
9290ddf2
         slen = len;
9ae2ec70
     } else if (len != slen) {
         flen = 0;
         if (len > slen)
             slen = len;
     }
59506ff5
 
     if (sub_len)
9290ddf2
         *sub_len = slen;
59506ff5
     if (fixed_len)
9290ddf2
         *fixed_len = flen;
2b512658
 
     return numexpr;
 }
 
590d4023
 inline static int ac_uicmp(uint16_t *a, size_t alen, uint16_t *b, size_t blen, int *wild)
 {
d0cba11e
     uint16_t awild, bwild, side_wild;
590d4023
     size_t i, minlen = MIN(alen, blen);
 
     side_wild = 0;
 
     for (i = 0; i < minlen; i++) {
         awild = a[i] & CLI_MATCH_WILDCARD;
         bwild = b[i] & CLI_MATCH_WILDCARD;
 
         if (awild == bwild) {
             switch (awild) {
288057e9
                 case CLI_MATCH_CHAR:
                     if ((a[i] & 0xff) != (b[i] & 0xff)) {
                         return (b[i] & 0xff) - (a[i] & 0xff);
                     }
                     break;
590d4023
                 case CLI_MATCH_IGNORE:
                     break;
                 case CLI_MATCH_NIBBLE_HIGH:
                     if ((a[i] & 0xf0) != (b[i] & 0xf0)) {
288057e9
                         return (b[i] & 0xf0) - (a[i] & 0xf0);
590d4023
                     }
                     break;
                 case CLI_MATCH_NIBBLE_LOW:
                     if ((a[i] & 0x0f) != (b[i] & 0x0f)) {
288057e9
                         return (b[i] & 0x0f) - (a[i] & 0x0f);
590d4023
                     }
                     break;
                 default:
                     cli_errmsg("ac_uicmp: unhandled wildcard type\n");
288057e9
                     return 1;
             }
         } else {                           /* not identical wildcard types */
             if (awild == CLI_MATCH_CHAR) { /* b is only wild */
                 switch (bwild) {
                     case CLI_MATCH_IGNORE:
                         side_wild |= 2;
                         break;
                     case CLI_MATCH_NIBBLE_HIGH:
                         if ((a[i] & 0xf0) != (b[i] & 0xf0)) {
                             return (b[i] & 0xf0) - (a[i] & 0xff);
                         }
                         side_wild |= 2;
                         break;
                     case CLI_MATCH_NIBBLE_LOW:
                         if ((a[i] & 0x0f) != (b[i] & 0x0f)) {
                             return (b[i] & 0x0f) - (a[i] & 0xff);
                         }
                         side_wild |= 2;
                         break;
                     default:
                         cli_errmsg("ac_uicmp: unhandled wildcard type\n");
                         return -1;
590d4023
                 }
             } else if (bwild == CLI_MATCH_CHAR) { /* a is only wild */
                 switch (awild) {
288057e9
                     case CLI_MATCH_IGNORE:
                         side_wild |= 1;
                         break;
                     case CLI_MATCH_NIBBLE_HIGH:
                         if ((a[i] & 0xf0) != (b[i] & 0xf0)) {
                             return (b[i] & 0xff) - (a[i] & 0xf0);
                         }
                         side_wild |= 1;
                         break;
                     case CLI_MATCH_NIBBLE_LOW:
                         if ((a[i] & 0x0f) != (b[i] & 0x0f)) {
                             return (b[i] & 0xff) - (a[i] & 0x0f);
                         }
                         side_wild |= 1;
                         break;
                     default:
                         cli_errmsg("ac_uicmp: unhandled wild typing\n");
                         return 1;
590d4023
                 }
             } else { /* not identical, both wildcards */
                 if (awild == CLI_MATCH_IGNORE || bwild == CLI_MATCH_IGNORE) {
                     if (awild == CLI_MATCH_IGNORE) {
                         side_wild |= 1;
288057e9
                     } else if (bwild == CLI_MATCH_IGNORE) {
590d4023
                         side_wild |= 2;
                     }
                 } else {
                     /* only high and low nibbles should be left here */
                     side_wild |= 3;
                 }
             }
         }
 
         /* both sides contain a wildcard that contains the other, therefore unique by wildcards */
         if (side_wild == 3)
             return 1;
     }
 
     if (wild)
         *wild = side_wild;
     return 0;
 }
 
59506ff5
 /* add new generic alternate node to special */
d00f762f
 inline static int ac_addspecial_add_alt_node(const char *subexpr, uint8_t sigopts, struct cli_ac_special *special, struct cli_matcher *root)
2b512658
 {
59506ff5
     struct cli_alt_node *newnode, **prev, *ins;
dda6b057
     uint16_t *s;
590d4023
     int i, cmp, wild;
59506ff5
 
544fa973
 #ifndef USE_MPOOL
     UNUSEDPARAM(root);
 #endif
 
     newnode = (struct cli_alt_node *)MPOOL_CALLOC(root->mempool, 1, sizeof(struct cli_alt_node));
59506ff5
     if (!newnode) {
9290ddf2
         cli_errmsg("ac_addspecial_add_alt_node: Can't allocate new alternate node\n");
         return CL_EMEM;
59506ff5
     }
 
544fa973
     s = CLI_MPOOL_HEX2UI(root->mempool, subexpr);
dda6b057
     if (!s) {
544fa973
         MPOOL_FREE(root->mempool, newnode);
9290ddf2
         return CL_EMALFDB;
59506ff5
     }
 
288057e9
     newnode->str    = s;
     newnode->len    = (uint16_t)strlen(subexpr) / 2;
590d4023
     newnode->unique = 1;
59506ff5
 
d00f762f
     /* setting nocase match */
     if (sigopts & ACPATT_OPTION_NOCASE) {
         for (i = 0; i < newnode->len; ++i)
             if ((newnode->str[i] & CLI_MATCH_METADATA) == CLI_MATCH_CHAR) {
2e06875d
                 newnode->str[i] = CLI_NOCASE(newnode->str[i] & 0xff);
d00f762f
                 newnode->str[i] += CLI_MATCH_NOCASE;
             }
     }
 
590d4023
     /* search for uniqueness, TODO: directed acyclic word graph */
59506ff5
     prev = &((special->alt).v_str);
288057e9
     ins  = (special->alt).v_str;
59506ff5
     while (ins) {
590d4023
         cmp = ac_uicmp(ins->str, ins->len, newnode->str, newnode->len, &wild);
4ffcf308
         if (cmp == 0) {
             if (newnode->len != ins->len) { /* derivative */
590d4023
                 newnode->unique = 0;
288057e9
                 ins->unique     = 0;
4ffcf308
             } else if (wild == 0) { /* duplicate */
544fa973
                 MPOOL_FREE(root->mempool, newnode);
9290ddf2
                 return CL_SUCCESS;
             }
590d4023
         } /* TODO - possible sorting of altstr uniques and derivative groups? */
 
9290ddf2
         prev = &(ins->next);
288057e9
         ins  = ins->next;
2b512658
     }
 
288057e9
     *prev         = newnode;
59506ff5
     newnode->next = ins;
b7d0b832
     if ((special->num == 0) || (newnode->len < special->len[0]))
         special->len[0] = newnode->len;
     if ((special->num == 0) || (newnode->len > special->len[1]))
         special->len[1] = newnode->len;
59506ff5
     special->num++;
     return CL_SUCCESS;
 }
2b512658
 
59506ff5
 /* recursive special handler for expanding and adding generic alternates */
d00f762f
 static int ac_special_altexpand(char *hexpr, char *subexpr, uint16_t maxlen, int lvl, int maxlvl, uint8_t sigopts, struct cli_ac_special *special, struct cli_matcher *root)
59506ff5
 {
     int ret, scnt = 0, numexpr;
     char *ept, *sexpr, *end, term;
     char *fp;
 
     ept = sexpr = hexpr;
288057e9
     fp          = subexpr + strlen(subexpr);
59506ff5
 
     numexpr = ac_analyze_expr(hexpr, NULL, NULL);
 
     /* while there are expressions to resolve */
     while (scnt < numexpr) {
9290ddf2
         scnt++;
         while ((*ept != '(') && (*ept != '|') && (*ept != ')') && (*ept != '\0'))
             ept++;
 
         /* check for invalid negation */
         term = *ept;
288057e9
         if ((*ept == '(') && (ept >= hexpr + 1)) {
9290ddf2
             if (ept[-1] == '!') {
                 cli_errmsg("ac_special_altexpand: Generic alternates cannot contain negations\n");
                 return CL_EMALFDB;
             }
         }
 
         /* appended token */
         *ept = 0;
         if (cli_strlcat(subexpr, sexpr, maxlen) >= maxlen) {
             cli_errmsg("ac_special_altexpand: Unexpected expression larger than expected\n");
             return CL_EMEM;
         }
 
4ffcf308
         *ept++ = term;
288057e9
         sexpr  = ept;
9290ddf2
 
         if (term == '|') {
             if (lvl == 0) {
d00f762f
                 if ((ret = ac_addspecial_add_alt_node(subexpr, sigopts, special, root)) != CL_SUCCESS)
9290ddf2
                     return ret;
             } else {
                 find_paren_end(ept, &end);
                 if (!end) {
                     cli_errmsg("ac_special_altexpand: Missing closing parenthesis\n");
                     return CL_EMALFDB;
                 }
                 end++;
 
288057e9
                 if ((ret = ac_special_altexpand(end, subexpr, maxlen, lvl - 1, lvl, sigopts, special, root)) != CL_SUCCESS)
9290ddf2
                     return ret;
             }
 
             *fp = 0;
         } else if (term == ')') {
             if (lvl == 0) {
                 cli_errmsg("ac_special_altexpand: Unexpected closing parenthesis\n");
                 return CL_EPARSE;
             }
 
288057e9
             if ((ret = ac_special_altexpand(ept, subexpr, maxlen, lvl - 1, lvl, sigopts, special, root)) != CL_SUCCESS)
9290ddf2
                 return ret;
             break;
         } else if (term == '(') {
             int inner, found;
             find_paren_end(ept, &end);
             if (!end) {
                 cli_errmsg("ac_special_altexpand: Missing closing parenthesis\n");
                 return CL_EMALFDB;
             }
             end++;
 
288057e9
             if ((ret = ac_special_altexpand(ept, subexpr, maxlen, lvl + 1, lvl + 1, sigopts, special, root)) != CL_SUCCESS)
9290ddf2
                 return ret;
 
4ffcf308
             /* move ept to end of current alternate expression (recursive call already populates them) */
288057e9
             ept   = end;
9290ddf2
             inner = 0;
             found = 0;
             while (!found && *ept != '\0') {
288057e9
                 switch (*ept) {
                     case '|':
                         if (!inner)
                             found = 1;
                         break;
                     case '(':
                         inner++;
                         break;
                     case ')':
                         inner--;
                         break;
9290ddf2
                 }
                 ept++;
             }
             if (*ept == '|')
                 ept++;
 
             sexpr = ept;
288057e9
             *fp   = 0;
9290ddf2
         } else if (term == '\0') {
d00f762f
             if ((ret = ac_addspecial_add_alt_node(subexpr, sigopts, special, root)) != CL_SUCCESS)
9290ddf2
                 return ret;
             break;
         }
 
         if (lvl != maxlvl)
             return CL_SUCCESS;
2b512658
     }
59506ff5
     if (scnt != numexpr) {
9290ddf2
         cli_errmsg("ac_addspecial: Mismatch in parsed and expected signature\n");
         return CL_EMALFDB;
59506ff5
     }
2b512658
 
     return CL_SUCCESS;
 }
 
59506ff5
 /* alternate string specials (so many specials!) */
d00f762f
 inline static int ac_special_altstr(const char *hexpr, uint8_t sigopts, struct cli_ac_special *special, struct cli_matcher *root)
2b512658
 {
59506ff5
     char *hexprcpy, *h, *c;
     int i, ret, num, fixed, slen, len;
 
     if (!(hexprcpy = cli_strdup(hexpr))) {
9290ddf2
         cli_errmsg("ac_special_altstr: Can't duplicate alternate expression\n");
         return CL_EDUP;
2b512658
     }
 
59506ff5
     len = strlen(hexpr);
     num = ac_analyze_expr(hexprcpy, &fixed, &slen);
2b512658
 
d00f762f
     if (!sigopts && fixed) {
288057e9
         special->num    = 0;
b7d0b832
         special->len[0] = special->len[1] = slen / 2;
9290ddf2
         /* single-bytes are len 2 in hex */
         if (slen == 2) {
288057e9
             special->type       = AC_SPECIAL_ALT_CHAR;
544fa973
             (special->alt).byte = (unsigned char *)MPOOL_MALLOC(root->mempool, num);
9290ddf2
             if (!((special->alt).byte)) {
                 cli_errmsg("cli_ac_special_altstr: Can't allocate newspecial->str\n");
                 free(hexprcpy);
                 return CL_EMEM;
             }
         } else {
288057e9
             special->type        = AC_SPECIAL_ALT_STR_FIXED;
544fa973
             (special->alt).f_str = (unsigned char **)MPOOL_MALLOC(root->mempool, num * sizeof(unsigned char *));
9290ddf2
             if (!((special->alt).f_str)) {
                 cli_errmsg("cli_ac_special_altstr: Can't allocate newspecial->str\n");
                 free(hexprcpy);
                 return CL_EMEM;
             }
         }
 
         for (i = 0; i < num; i++) {
             if (num == 1) {
544fa973
                 c = CLI_MPOOL_HEX2STR(root->mempool, hexprcpy);
9290ddf2
             } else {
288057e9
                 if (!(h = cli_strtok(hexprcpy, i, "|"))) {
9290ddf2
                     free(hexprcpy);
                     return CL_EMEM;
                 }
544fa973
                 c = CLI_MPOOL_HEX2STR(root->mempool, h);
9290ddf2
                 free(h);
             }
             if (!c) {
                 free(hexprcpy);
                 return CL_EMALFDB;
             }
 
             if (special->type == AC_SPECIAL_ALT_CHAR) {
7b1f1aaf
                 (special->alt).byte[i] = (unsigned char)*c;
544fa973
                 MPOOL_FREE(root->mempool, c);
9290ddf2
             } else {
288057e9
                 (special->alt).f_str[i] = (unsigned char *)c;
9290ddf2
             }
             special->num++;
         }
         /* sorting byte alternates */
         if (special->num > 1 && special->type == AC_SPECIAL_ALT_CHAR)
cdd018d5
             cli_qsort((special->alt).byte, special->num, sizeof(unsigned char), qcompare_byte);
         /* sorting str alternates */
         if (special->num > 1 && special->type == AC_SPECIAL_ALT_STR_FIXED)
             cli_qsort_r((special->alt).f_str, special->num, sizeof(unsigned char *), qcompare_fstr, &(special->len));
59506ff5
     } else { /* generic alternates */
9290ddf2
         char *subexpr;
         if (special->negative) {
             cli_errmsg("ac_special_altstr: Can't apply negation operation to generic alternate strings\n");
             free(hexprcpy);
             return CL_EMALFDB;
         }
2b512658
 
9290ddf2
         special->type = AC_SPECIAL_ALT_STR;
2b512658
 
9290ddf2
         /* allocate reusable subexpr */
288057e9
         if (!(subexpr = cli_calloc(slen + 1, sizeof(char)))) {
9290ddf2
             cli_errmsg("ac_special_altstr: Can't allocate subexpr container\n");
817636eb
             free(hexprcpy);
9290ddf2
             return CL_EMEM;
         }
2b512658
 
288057e9
         ret = ac_special_altexpand(hexprcpy, subexpr, slen + 1, 0, 0, sigopts, special, root);
59506ff5
 
9290ddf2
         free(subexpr);
         free(hexprcpy);
         return ret;
2b512658
     }
 
59506ff5
     free(hexprcpy);
2b512658
     return CL_SUCCESS;
 }
 
fbcef1b0
 /* FIXME: clean up the code */
102cd430
 cl_error_t cli_ac_addsig(struct cli_matcher *root, const char *virname, const char *hexsig, uint8_t sigopts, uint32_t sigid, uint16_t parts, uint16_t partno, uint16_t rtype, uint16_t type, uint32_t mindist, uint32_t maxdist, const char *offset, const uint32_t *lsigid, unsigned int options)
fbcef1b0
 {
bc1c7469
     struct cli_ac_patt *new;
     char *pt, *pt2, *hex = NULL, *hexcpy = NULL;
     uint16_t i, j, ppos = 0, pend, *dec, nzpos = 0;
dc70379a
     uint8_t wprefix = 0, zprefix = 1, plen = 0, nzplen = 0;
d0cba11e
     struct cli_ac_special *newspecial, **newtable;
bc1c7469
     int ret, error = CL_SUCCESS;
fbcef1b0
 
288057e9
     if (!root) {
bc1c7469
         cli_errmsg("cli_ac_addsig: root == NULL\n");
         return CL_ENULLARG;
677fc4ba
     }
 
288057e9
     if (strlen(hexsig) / 2 < root->ac_mindepth) {
bc1c7469
         cli_errmsg("cli_ac_addsig: Signature for %s is too short\n", virname);
         return CL_EMALFDB;
871177cd
     }
 
544fa973
     if ((new = (struct cli_ac_patt *)MPOOL_CALLOC(root->mempool, 1, sizeof(struct cli_ac_patt))) == NULL)
bc1c7469
         return CL_EMEM;
fbcef1b0
 
288057e9
     new->rtype      = rtype;
     new->type       = type;
     new->sigid      = sigid;
     new->parts      = parts;
     new->partno     = partno;
     new->mindist    = mindist;
     new->maxdist    = maxdist;
10290ba3
     new->customdata = NULL;
1a2906f4
     new->ch[0] |= CLI_MATCH_IGNORE;
     new->ch[1] |= CLI_MATCH_IGNORE;
288057e9
     if (lsigid) {
bc1c7469
         new->lsigid[0] = 1;
         memcpy(&new->lsigid[1], lsigid, 2 * sizeof(uint32_t));
677fc4ba
     }
1a2906f4
 
288057e9
     if (strchr(hexsig, '[')) {
         if (!(hexcpy = cli_strdup(hexsig))) {
544fa973
             MPOOL_FREE(root->mempool, new);
bc1c7469
             return CL_EMEM;
         }
1a2906f4
 
bc1c7469
         hex = hexcpy;
288057e9
         for (i = 0; i < 2; i++) {
82cc35c0
             unsigned int n, n1, n2;
1a2906f4
 
288057e9
             if (!(pt = strchr(hex, '[')))
bc1c7469
                 break;
1a2906f4
 
bc1c7469
             *pt++ = 0;
1a2906f4
 
288057e9
             if (!(pt2 = strchr(pt, ']'))) {
bc1c7469
                 cli_dbgmsg("cli_ac_addsig: missing closing square bracket\n");
                 error = CL_EMALFDB;
                 break;
             }
1a2906f4
 
bc1c7469
             *pt2++ = 0;
1a2906f4
 
82cc35c0
             n = sscanf(pt, "%u-%u", &n1, &n2);
288057e9
             if (n == 1) {
82cc35c0
                 n2 = n1;
288057e9
             } else if (n != 2) {
bc1c7469
                 cli_dbgmsg("cli_ac_addsig: incorrect range inside square brackets\n");
                 error = CL_EMALFDB;
                 break;
             }
 
288057e9
             if ((n1 > n2) || (n2 > AC_CH_MAXDIST)) {
bc1c7469
                 cli_dbgmsg("cli_ac_addsig: incorrect range inside square brackets\n");
                 error = CL_EMALFDB;
                 break;
             }
 
288057e9
             if (strlen(hex) == 2) {
                 if (i) {
bc1c7469
                     error = CL_EMALFDB;
                     break;
                 }
 
                 dec = cli_hex2ui(hex);
288057e9
                 if (!dec) {
bc1c7469
                     error = CL_EMALFDB;
                     break;
                 }
 
288057e9
                 if ((sigopts & ACPATT_OPTION_NOCASE) && ((*dec & CLI_MATCH_METADATA) == CLI_MATCH_CHAR))
2e06875d
                     new->ch[i] = CLI_NOCASE(*dec) | CLI_MATCH_NOCASE;
a1924d14
                 else
                     new->ch[i] = *dec;
bc1c7469
                 free(dec);
                 new->ch_mindist[i] = n1;
                 new->ch_maxdist[i] = n2;
288057e9
                 hex                = pt2;
             } else if (strlen(pt2) == 2) {
                 i   = 1;
bc1c7469
                 dec = cli_hex2ui(pt2);
288057e9
                 if (!dec) {
bc1c7469
                     error = CL_EMALFDB;
                     break;
                 }
 
288057e9
                 if ((sigopts & ACPATT_OPTION_NOCASE) && ((*dec & CLI_MATCH_METADATA) == CLI_MATCH_CHAR))
2e06875d
                     new->ch[i] = CLI_NOCASE(*dec) | CLI_MATCH_NOCASE;
a1924d14
                 else
                     new->ch[i] = *dec;
bc1c7469
                 free(dec);
                 new->ch_mindist[i] = n1;
                 new->ch_maxdist[i] = n2;
             } else {
                 error = CL_EMALFDB;
                 break;
             }
         }
 
288057e9
         if (error) {
bc1c7469
             free(hexcpy);
544fa973
             MPOOL_FREE(root->mempool, new);
bc1c7469
             return error;
         }
 
         hex = cli_strdup(hex);
         free(hexcpy);
288057e9
         if (!hex) {
544fa973
             MPOOL_FREE(root->mempool, new);
bc1c7469
             return CL_EMEM;
         }
1a2906f4
     }
fbcef1b0
 
288057e9
     if (strchr(hexsig, '(')) {
9290ddf2
         char *hexnew, *start;
4d860bd5
         size_t nest;
9290ddf2
         size_t hexnewsz;
 
288057e9
         if (hex) {
9290ddf2
             hexcpy = hex;
288057e9
         } else if (!(hexcpy = cli_strdup(hexsig))) {
544fa973
             MPOOL_FREE(root->mempool, new);
9290ddf2
             return CL_EMEM;
         }
 
         hexnewsz = strlen(hexsig) + 1;
288057e9
         if (!(hexnew = (char *)cli_calloc(1, hexnewsz))) {
9290ddf2
             free(new);
             free(hexcpy);
             return CL_EMEM;
         }
 
         start = pt = hexcpy;
288057e9
         while ((pt = strchr(start, '('))) {
9290ddf2
             *pt++ = 0;
 
288057e9
             if (!start) {
9290ddf2
                 error = CL_EMALFDB;
                 break;
             }
544fa973
             newspecial = (struct cli_ac_special *)MPOOL_CALLOC(root->mempool, 1, sizeof(struct cli_ac_special));
288057e9
             if (!newspecial) {
9290ddf2
                 cli_errmsg("cli_ac_addsig: Can't allocate newspecial\n");
                 error = CL_EMEM;
                 break;
             }
288057e9
             if (pt >= hexcpy + 2) {
                 if (pt[-2] == '!') {
9290ddf2
                     newspecial->negative = 1;
288057e9
                     pt[-2]               = 0;
9290ddf2
                 }
             }
             cli_strlcat(hexnew, start, hexnewsz);
 
             nest = find_paren_end(pt, &start);
288057e9
             if (!start) {
9290ddf2
                 cli_errmsg("cli_ac_addsig: Missing closing parenthesis\n");
544fa973
                 MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                 error = CL_EMALFDB;
                 break;
             }
             *start++ = 0;
288057e9
             if (!strlen(pt)) {
9290ddf2
                 cli_errmsg("cli_ac_addsig: Empty block\n");
544fa973
                 MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                 error = CL_EMALFDB;
                 break;
             }
 
4ffcf308
             if (nest > ACPATT_ALTN_MAXNEST) {
                 cli_errmsg("ac_addspecial: Expression exceeds maximum alternate nesting limit\n");
544fa973
                 MPOOL_FREE(root->mempool, newspecial);
0f236303
                 error = CL_EMALFDB;
                 break;
9290ddf2
             }
 
288057e9
             if (!strcmp(pt, "B")) {
                 if (!*start) {
9290ddf2
                     new->boundary |= AC_BOUNDARY_RIGHT;
288057e9
                     if (newspecial->negative)
9290ddf2
                         new->boundary |= AC_BOUNDARY_RIGHT_NEGATIVE;
544fa973
                     MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                     continue;
288057e9
                 } else if (pt - 1 == hexcpy) {
9290ddf2
                     new->boundary |= AC_BOUNDARY_LEFT;
288057e9
                     if (newspecial->negative)
9290ddf2
                         new->boundary |= AC_BOUNDARY_LEFT_NEGATIVE;
544fa973
                     MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                     continue;
                 }
288057e9
             } else if (!strcmp(pt, "L")) {
                 if (!*start) {
9290ddf2
                     new->boundary |= AC_LINE_MARKER_RIGHT;
288057e9
                     if (newspecial->negative)
9290ddf2
                         new->boundary |= AC_LINE_MARKER_RIGHT_NEGATIVE;
544fa973
                     MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                     continue;
288057e9
                 } else if (pt - 1 == hexcpy) {
9290ddf2
                     new->boundary |= AC_LINE_MARKER_LEFT;
288057e9
                     if (newspecial->negative)
9290ddf2
                         new->boundary |= AC_LINE_MARKER_LEFT_NEGATIVE;
544fa973
                     MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                     continue;
                 }
288057e9
             } else if (!strcmp(pt, "W")) {
                 if (!*start) {
9290ddf2
                     new->boundary |= AC_WORD_MARKER_RIGHT;
288057e9
                     if (newspecial->negative)
9290ddf2
                         new->boundary |= AC_WORD_MARKER_RIGHT_NEGATIVE;
544fa973
                     MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                     continue;
288057e9
                 } else if (pt - 1 == hexcpy) {
9290ddf2
                     new->boundary |= AC_WORD_MARKER_LEFT;
288057e9
                     if (newspecial->negative)
9290ddf2
                         new->boundary |= AC_WORD_MARKER_LEFT_NEGATIVE;
544fa973
                     MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                     continue;
                 }
             }
             cli_strlcat(hexnew, "()", hexnewsz);
             new->special++;
544fa973
             newtable = (struct cli_ac_special **)MPOOL_REALLOC(root->mempool, new->special_table, new->special * sizeof(struct cli_ac_special *));
288057e9
             if (!newtable) {
9290ddf2
                 new->special--;
544fa973
                 MPOOL_FREE(root->mempool, newspecial);
9290ddf2
                 cli_errmsg("cli_ac_addsig: Can't realloc new->special_table\n");
                 error = CL_EMEM;
                 break;
             }
             newtable[new->special - 1] = newspecial;
288057e9
             new->special_table         = newtable;
9290ddf2
 
288057e9
             if (!strcmp(pt, "B")) {
9290ddf2
                 newspecial->type = AC_SPECIAL_BOUNDARY;
288057e9
             } else if (!strcmp(pt, "L")) {
9290ddf2
                 newspecial->type = AC_SPECIAL_LINE_MARKER;
288057e9
             } else if (!strcmp(pt, "W")) {
9290ddf2
                 newspecial->type = AC_SPECIAL_WORD_MARKER;
             } else {
d00f762f
                 if ((ret = ac_special_altstr(pt, sigopts, newspecial, root)) != CL_SUCCESS) {
9290ddf2
                     error = ret;
                     break;
                 }
             }
         }
 
288057e9
         if (start)
9290ddf2
             cli_strlcat(hexnew, start, hexnewsz);
 
         hex = hexnew;
         free(hexcpy);
 
288057e9
         if (error) {
9290ddf2
             free(hex);
288057e9
             if (new->special) {
9290ddf2
                 mpool_ac_free_special(root->mempool, new);
             }
544fa973
             MPOOL_FREE(root->mempool, new);
9290ddf2
             return error;
         }
fbcef1b0
     }
 
544fa973
     new->pattern = CLI_MPOOL_HEX2UI(root->mempool, hex ? hex : hexsig);
288057e9
     if (new->pattern == NULL) {
         if (new->special)
bc1c7469
             mpool_ac_free_special(root->mempool, new);
 
544fa973
         MPOOL_FREE(root->mempool, new);
bc1c7469
         free(hex);
         return CL_EMALFDB;
fbcef1b0
     }
b94e66c4
 
8efbf4a0
     new->length[0] = (uint16_t)strlen(hex ? hex : hexsig) / 2;
288057e9
     for (i = 0, j = 0; i < new->length[0]; i++) {
         if ((new->pattern[i] & CLI_MATCH_METADATA) == CLI_MATCH_SPECIAL) {
6953341f
             new->length[1] += new->special_table[j]->len[0];
             new->length[2] += new->special_table[j]->len[1];
             j++;
         } else {
             new->length[1]++;
             new->length[2]++;
         }
     }
 
1a2906f4
     free(hex);
fbcef1b0
 
dc70379a
     new->sigopts = sigopts;
68369b0f
     /* setting nocase match */
dc70379a
     if (sigopts & ACPATT_OPTION_NOCASE) {
6953341f
         for (i = 0; i < new->length[0]; i++)
9290ddf2
             if ((new->pattern[i] & CLI_MATCH_METADATA) == CLI_MATCH_CHAR) {
2e06875d
                 new->pattern[i] = CLI_NOCASE(new->pattern[i] & 0xff);
9290ddf2
                 new->pattern[i] += CLI_MATCH_NOCASE;
             }
a02acd50
     }
 
4ffcf308
     /* TODO - sigopts affect on filters? */
     if (root->filter) {
bc1c7469
         /* so that we can show meaningful messages */
288057e9
         new->virname = (char *)virname;
bc1c7469
         if (filter_add_acpatt(root->filter, new) == -1) {
             cli_warnmsg("cli_ac_addpatt: cannot use filter for trie\n");
544fa973
             MPOOL_FREE(root->mempool, root->filter);
bc1c7469
             root->filter = NULL;
         }
 
         /* TODO: should this affect maxpatlen? */
02eabc6d
     }
 
288057e9
     for (i = 0; i < root->ac_maxdepth && i < new->length[0]; i++) {
         if (new->pattern[i] & CLI_MATCH_WILDCARD) {
bc1c7469
             wprefix = 1;
             break;
         }
 
288057e9
         if (zprefix && new->pattern[i])
bc1c7469
             zprefix = 0;
fbcef1b0
     }
 
288057e9
     if (wprefix || zprefix) {
f2102544
         pend = new->length[0] - root->ac_mindepth + 1;
288057e9
         for (i = 0; i < pend; i++) {
             for (j = i; j < i + root->ac_maxdepth && j < new->length[0]; j++) {
                 if (new->pattern[j] & CLI_MATCH_WILDCARD) {
bc1c7469
                     break;
                 } else {
288057e9
                     if (j - i + 1 >= plen) {
bc1c7469
                         plen = j - i + 1;
                         ppos = i;
                     }
                 }
 
288057e9
                 if (new->pattern[ppos] || new->pattern[ppos + 1]) {
                     if (plen >= root->ac_maxdepth) {
bc1c7469
                         break;
288057e9
                     } else if (plen >= root->ac_mindepth && plen > nzplen) {
bc1c7469
                         nzplen = plen;
288057e9
                         nzpos  = ppos;
bc1c7469
                     }
                 }
             }
fbcef1b0
 
288057e9
             if (plen >= root->ac_maxdepth && (new->pattern[ppos] || new->pattern[ppos + 1]))
bc1c7469
                 break;
         }
 
288057e9
         if (!new->pattern[ppos] && !new->pattern[ppos + 1] && nzplen) {
bc1c7469
             plen = nzplen;
             ppos = nzpos;
         }
 
288057e9
         if (plen < root->ac_mindepth) {
bc1c7469
             cli_errmsg("cli_ac_addsig: Can't find a static subpattern of length %u\n", root->ac_mindepth);
             mpool_ac_free_special(root->mempool, new);
544fa973
             MPOOL_FREE(root->mempool, new->pattern);
             MPOOL_FREE(root->mempool, new);
bc1c7469
             return CL_EMALFDB;
         }
fbcef1b0
 
288057e9
         new->prefix           = new->pattern;
f2102544
         new->prefix_length[0] = ppos;
288057e9
         for (i = 0, j = 0; i < new->prefix_length[0]; i++) {
             if ((new->prefix[i] & CLI_MATCH_WILDCARD) == CLI_MATCH_SPECIAL)
bc1c7469
                 new->special_pattern++;
6953341f
 
288057e9
             if ((new->prefix[i] & CLI_MATCH_METADATA) == CLI_MATCH_SPECIAL) {
6953341f
                 new->prefix_length[1] += new->special_table[j]->len[0];
                 new->prefix_length[2] += new->special_table[j]->len[1];
                 j++;
             } else {
                 new->prefix_length[1]++;
                 new->prefix_length[2]++;
             }
         }
 
         new->pattern = &new->prefix[ppos];
         new->length[0] -= new->prefix_length[0];
         new->length[1] -= new->prefix_length[1];
         new->length[2] -= new->prefix_length[2];
fbcef1b0
     }
 
288057e9
     if (new->length[2] + new->prefix_length[2] > root->maxpatlen)
600c04f0
         root->maxpatlen = new->length[2] + new->prefix_length[2];
fbcef1b0
 
544fa973
     new->virname = CLI_MPOOL_VIRNAME(root->mempool, virname, options & CL_DB_OFFICIAL);
288057e9
     if (!new->virname) {
544fa973
         MPOOL_FREE(root->mempool, new->prefix ? new->prefix : new->pattern);
bc1c7469
         mpool_ac_free_special(root->mempool, new);
544fa973
         MPOOL_FREE(root->mempool, new);
bc1c7469
         return CL_EMEM;
fbcef1b0
     }
 
288057e9
     if (new->lsigid[0])
bc1c7469
         root->ac_lsigtable[new->lsigid[1]]->virname = new->virname;
677fc4ba
 
294558a5
     ret = cli_caloff(offset, NULL, root->type, new->offdata, &new->offset_min, &new->offset_max);
288057e9
     if (ret != CL_SUCCESS) {
544fa973
         MPOOL_FREE(root->mempool, new->prefix ? new->prefix : new->pattern);
bc1c7469
         mpool_ac_free_special(root->mempool, new);
544fa973
         MPOOL_FREE(root->mempool, new->virname);
         MPOOL_FREE(root->mempool, new);
bc1c7469
         return ret;
1a648b37
     }
 
288057e9
     if ((ret = cli_ac_addpatt(root, new))) {
544fa973
         MPOOL_FREE(root->mempool, new->prefix ? new->prefix : new->pattern);
         MPOOL_FREE(root->mempool, new->virname);
bc1c7469
         mpool_ac_free_special(root->mempool, new);
544fa973
         MPOOL_FREE(root->mempool, new);
bc1c7469
         return ret;
fbcef1b0
     }
 
288057e9
     if (new->offdata[0] != CLI_OFF_ANY && new->offdata[0] != CLI_OFF_ABSOLUTE && new->offdata[0] != CLI_OFF_MACRO) {
544fa973
         root->ac_reloff = (struct cli_ac_patt **)MPOOL_REALLOC2(root->mempool, root->ac_reloff, (root->ac_reloff_num + 1) * sizeof(struct cli_ac_patt *));
288057e9
         if (!root->ac_reloff) {
bc1c7469
             cli_errmsg("cli_ac_addsig: Can't allocate memory for root->ac_reloff\n");
             return CL_EMEM;
         }
 
         root->ac_reloff[root->ac_reloff_num] = new;
288057e9
         new->offset_min                      = root->ac_reloff_num * 2;
         new->offset_max                      = new->offset_min + 1;
bc1c7469
         root->ac_reloff_num++;
33872a43
     }
 
fbcef1b0
     return CL_SUCCESS;
 }