libclamav/bytecode.c
997a1efe
 /*
7a9022c9
  *  Load, and verify ClamAV bytecode.
997a1efe
  *
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6a9086d2
  *  Copyright (C) 2009-2013 Sourcefire, Inc.
997a1efe
  *
  *  Authors: Török Edvin
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
b2e7c931
 #include <string.h>
c074ecec
 #include <assert.h>
b00a7cc8
 #include <fcntl.h>
b2e7c931
 
6eeadbfe
 #include "dconf.h"
997a1efe
 #include "clamav.h"
 #include "others.h"
ab636570
 #include "pe.h"
997a1efe
 #include "bytecode.h"
7a9022c9
 #include "bytecode_priv.h"
88d54dcb
 #include "bytecode_detect.h"
997a1efe
 #include "readdb.h"
d0934caf
 #include "scanners.h"
3735fda1
 #include "bytecode_api.h"
9aa1d725
 #include "bytecode_api_impl.h"
0d4c9946
 #include "builtin_bytecodes.h"
c6a3b294
 #if HAVE_JSON
 #include "json.h"
 #endif
997a1efe
 
f9ad2811
 #ifndef MAX_TRACKED_BC
 #define MAX_TRACKED_BC 64
afe940da
 #endif
54402320
 #define BC_EVENTS_PER_SIG 2
f9ad2811
 #define MAX_BC_SIGEVENT_ID MAX_TRACKED_BC *BC_EVENTS_PER_SIG
54402320
 
288057e9
 cli_events_t *g_sigevents = NULL;
54402320
 unsigned int g_sigid;
 
74f5816c
 /* dummy values */
 static const uint32_t nomatch[64] = {
     0xdeadbeef, 0xdeaddead, 0xbeefdead, 0xdeaddead, 0xdeadbeef, 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,
288057e9
     0, 0, 0, 0, 0, 0, 0, 0};
1c4683ac
 static const uint32_t nooffsets[64] = {
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
288057e9
     CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE};
74f5816c
 
 static const uint16_t nokind;
 static const uint32_t nofilesize;
 static const struct cli_pe_hook_data nopedata;
 
 static void context_safe(struct cli_bc_ctx *ctx)
 {
     /* make sure these are never NULL */
     if (!ctx->hooks.kind)
288057e9
         ctx->hooks.kind = &nokind;
74f5816c
     if (!ctx->hooks.match_counts)
288057e9
         ctx->hooks.match_counts = nomatch;
1c4683ac
     if (!ctx->hooks.match_offsets)
288057e9
         ctx->hooks.match_offsets = nooffsets;
74f5816c
     if (!ctx->hooks.filesize)
288057e9
         ctx->hooks.filesize = &nofilesize;
74f5816c
     if (!ctx->hooks.pedata)
288057e9
         ctx->hooks.pedata = &nopedata;
74f5816c
 }
 
7a7365ef
 static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx);
7a9022c9
 struct cli_bc_ctx *cli_bytecode_context_alloc(void)
 {
d9fbce26
     struct cli_bc_ctx *ctx = cli_calloc(1, sizeof(*ctx));
e522909e
     if (!ctx) {
         cli_errmsg("Out of memory allocating cli_bytecode_context_reset\n");
         return NULL;
     }
12876d3c
     ctx->bytecode_timeout = 60000;
7a7365ef
     cli_bytecode_context_reset(ctx);
7a9022c9
     return ctx;
 }
997a1efe
 
7a9022c9
 void cli_bytecode_context_destroy(struct cli_bc_ctx *ctx)
 {
288057e9
     cli_bytecode_context_clear(ctx);
     free(ctx);
7a9022c9
 }
997a1efe
 
3ae0a76d
 int cli_bytecode_context_getresult_file(struct cli_bc_ctx *ctx, char **tempfilename)
 {
     int fd;
     *tempfilename = ctx->tempfile;
288057e9
     fd            = ctx->outfd;
3ae0a76d
     ctx->tempfile = NULL;
288057e9
     ctx->outfd    = 0;
3ae0a76d
     return fd;
 }
 
ab636570
 /* resets bytecode state, so you can run another bytecode with same ctx */
d0934caf
 static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx)
7a9022c9
 {
778df8c2
     unsigned i;
 
6922903a
     free(ctx->opsizes);
778df8c2
     ctx->opsizes = NULL;
 
7a9022c9
     free(ctx->values);
778df8c2
     ctx->values = NULL;
 
7a9022c9
     free(ctx->operands);
53bd5bb1
     ctx->operands = NULL;
778df8c2
 
d9fbce26
     if (ctx->outfd) {
288057e9
         cli_ctx *cctx = ctx->ctx;
         if (ctx->outfd)
             close(ctx->outfd);
         if (ctx->tempfile && (!cctx || !cctx->engine->keeptmp)) {
             cli_unlink(ctx->tempfile);
         }
         free(ctx->tempfile);
         ctx->tempfile = NULL;
         ctx->outfd    = 0;
3ae0a76d
     }
b00a7cc8
     if (ctx->jsnormdir) {
288057e9
         char fullname[1025];
         cli_ctx *cctx = ctx->ctx;
         int fd, ret = CL_CLEAN;
 
         if (!ctx->found) {
             snprintf(fullname, 1024, "%s" PATHSEP "javascript", ctx->jsnormdir);
             fd = open(fullname, O_RDONLY | O_BINARY);
             if (fd >= 0) {
                 ret = cli_scandesc(fd, cctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL);
                 if (ret == CL_CLEAN) {
                     if (lseek(fd, 0, SEEK_SET) == -1)
                         cli_dbgmsg("cli_bytecode: call to lseek() has failed\n");
                     else
                         ret = cli_scandesc(fd, cctx, CL_TYPE_TEXT_ASCII, 0, NULL, AC_SCAN_VIR, NULL);
                 }
                 close(fd);
             }
         }
         if (!cctx || !cctx->engine->keeptmp) {
             cli_rmdirs(ctx->jsnormdir);
         }
         free(ctx->jsnormdir);
         if (ret != CL_CLEAN)
             ctx->found = 1;
b00a7cc8
     }
778df8c2
     ctx->numParams = 0;
288057e9
     ctx->funcid    = 0;
35d6165f
     /* don't touch fmap, file_size, and hooks, sections, ctx, timeout, pdf* */
288057e9
     ctx->off           = 0;
     ctx->written       = 0;
b00a7cc8
     ctx->jsnormwritten = 0;
2d45ef06
 #if USE_MPOOL
     if (ctx->mpool) {
288057e9
         mpool_destroy(ctx->mpool);
         ctx->mpool = NULL;
2d45ef06
     }
 #else
c0afc517
     /*TODO: implement for no-mmap case too*/
2d45ef06
 #endif
288057e9
     for (i = 0; i < ctx->ninflates; i++)
         cli_bcapi_inflate_done(ctx, i);
778df8c2
     free(ctx->inflates);
288057e9
     ctx->inflates  = NULL;
778df8c2
     ctx->ninflates = 0;
 
288057e9
     for (i = 0; i < ctx->nbuffers; i++)
         cli_bcapi_buffer_pipe_done(ctx, i);
778df8c2
     free(ctx->buffers);
288057e9
     ctx->buffers  = NULL;
778df8c2
     ctx->nbuffers = 0;
 
288057e9
     for (i = 0; i < ctx->nhashsets; i++)
         cli_bcapi_hashset_done(ctx, i);
778df8c2
     free(ctx->hashsets);
288057e9
     ctx->hashsets  = NULL;
778df8c2
     ctx->nhashsets = 0;
b00a7cc8
 
288057e9
     for (i = 0; i < ctx->njsnorms; i++)
         cli_bcapi_jsnorm_done(ctx, i);
b00a7cc8
     free(ctx->jsnorms);
288057e9
     ctx->jsnorms   = NULL;
     ctx->njsnorms  = 0;
b00a7cc8
     ctx->jsnormdir = NULL;
7a7365ef
 
288057e9
     for (i = 0; i < ctx->nmaps; i++)
         cli_bcapi_map_done(ctx, i);
7a7365ef
     free(ctx->maps);
288057e9
     ctx->maps  = NULL;
7a7365ef
     ctx->nmaps = 0;
 
f3575db2
 #if HAVE_JSON
288057e9
     free((json_object **)(ctx->jsonobjs));
     ctx->jsonobjs  = NULL;
f3575db2
     ctx->njsonobjs = 0;
 #endif
 
7a7365ef
     ctx->containertype = CL_TYPE_ANY;
ab636570
     return CL_SUCCESS;
 }
 
 int cli_bytecode_context_clear(struct cli_bc_ctx *ctx)
 {
     cli_bytecode_context_reset(ctx);
f6798708
     memset(ctx, 0, sizeof(*ctx));
957bf651
     return CL_SUCCESS;
7a9022c9
 }
997a1efe
 
6922903a
 static unsigned typesize(const struct cli_bc *bc, uint16_t type)
 {
669623d5
     struct cli_bc_type *ty;
     unsigned j;
 
a45e2577
     type &= 0x7fff;
6922903a
     if (!type)
288057e9
         return 0;
6922903a
     if (type <= 8)
288057e9
         return 1;
6922903a
     if (type <= 16)
288057e9
         return 2;
6922903a
     if (type <= 32)
288057e9
         return 4;
6922903a
     if (type <= 64)
288057e9
         return 8;
     ty = &bc->types[type - 65];
669623d5
     if (ty->size)
288057e9
         return ty->size;
669623d5
     switch (ty->kind) {
288057e9
         case 2:
         case 3:
             for (j = 0; j < ty->numElements; j++)
                 ty->size += typesize(bc, ty->containedTypes[j]);
             break;
         case 4:
             ty->size = ty->numElements * typesize(bc, ty->containedTypes[0]);
             break;
         default:
             break;
669623d5
     }
     if (!ty->size && ty->kind != DFunctionType) {
288057e9
         cli_warnmsg("type %d size is 0\n", type - 65);
669623d5
     }
     return ty->size;
6922903a
 }
 
 static unsigned typealign(const struct cli_bc *bc, uint16_t type)
 {
a45e2577
     type &= 0x7fff;
7a14dc4c
     if (type <= 64) {
288057e9
         unsigned size = typesize(bc, type);
         return size ? size : 1;
7a14dc4c
     }
288057e9
     return bc->types[type - 65].align;
6922903a
 }
 
30188fcc
 int cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *bc, unsigned funcid)
7a9022c9
 {
288057e9
     unsigned i, s = 0;
30188fcc
     const struct cli_bc_func *func;
7a9022c9
     if (funcid >= bc->num_func) {
288057e9
         cli_errmsg("bytecode: function ID doesn't exist: %u\n", funcid);
         return CL_EARG;
7a9022c9
     }
e0c4fd85
     func = ctx->func = &bc->funcs[funcid];
288057e9
     ctx->bc          = bc;
     ctx->numParams   = func->numArgs;
     ctx->funcid      = funcid;
e0c4fd85
     if (func->numArgs) {
288057e9
         ctx->operands = cli_malloc(sizeof(*ctx->operands) * func->numArgs);
         if (!ctx->operands) {
             cli_errmsg("bytecode: error allocating memory for parameters\n");
             return CL_EMEM;
         }
         ctx->opsizes = cli_malloc(sizeof(*ctx->opsizes) * func->numArgs);
         if (!ctx->opsizes) {
             cli_errmsg("bytecode: error allocating memory for opsizes\n");
             return CL_EMEM;
         }
         for (i = 0; i < func->numArgs; i++) {
             unsigned al          = typealign(bc, func->types[i]);
             s                    = (s + al - 1) & ~(al - 1);
             ctx->operands[i]     = s;
             s += ctx->opsizes[i] = typesize(bc, func->types[i]);
         }
     }
     s += 8; /* return value */
     ctx->bytes  = s;
6922903a
     ctx->values = cli_malloc(s);
     if (!ctx->values) {
288057e9
         cli_errmsg("bytecode: error allocating memory for parameters\n");
         return CL_EMEM;
7a9022c9
     }
     return CL_SUCCESS;
 }
997a1efe
 
7a9022c9
 static inline int type_isint(uint16_t type)
997a1efe
 {
7a9022c9
     return type > 0 && type <= 64;
997a1efe
 }
 
7a9022c9
 int cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64_t c)
997a1efe
 {
7a9022c9
     if (i >= ctx->numParams) {
288057e9
         cli_errmsg("bytecode: param index out of bounds: %u\n", i);
         return CL_EARG;
7a9022c9
     }
     if (!type_isint(ctx->func->types[i])) {
288057e9
         cli_errmsg("bytecode: parameter type mismatch\n");
         return CL_EARG;
7a9022c9
     }
6922903a
     switch (ctx->opsizes[i]) {
288057e9
         case 1:
             ctx->values[ctx->operands[i]] = c;
             break;
         case 2:
             *(uint16_t *)&ctx->values[ctx->operands[i]] = c;
             break;
         case 4:
             *(uint32_t *)&ctx->values[ctx->operands[i]] = c;
             break;
         case 8:
             *(uint64_t *)&ctx->values[ctx->operands[i]] = c;
             break;
6922903a
     }
7a9022c9
     return CL_SUCCESS;
997a1efe
 }
 
7a9022c9
 int cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *data, unsigned datalen)
 {
cd94be7a
     UNUSEDPARAM(ctx);
     UNUSEDPARAM(i);
     UNUSEDPARAM(data);
     UNUSEDPARAM(datalen);
7a9022c9
     cli_errmsg("Pointer parameters are not implemented yet!\n");
     return CL_EARG;
 }
997a1efe
 
 static inline uint64_t readNumber(const unsigned char *p, unsigned *off, unsigned len, char *ok)
 {
288057e9
     uint64_t n = 0;
     unsigned i, newoff, lim, p0 = p[*off], shift = 0;
997a1efe
 
     lim = p0 - 0x60;
     if (lim > 0x10) {
288057e9
         cli_errmsg("Invalid number type: %c\n", p0);
         *ok = 0;
         return 0;
997a1efe
     }
288057e9
     newoff = *off + lim + 1;
997a1efe
     if (newoff > len) {
288057e9
         cli_errmsg("End of line encountered while reading number\n");
         *ok = 0;
         return 0;
997a1efe
     }
 
     if (p0 == 0x60) {
288057e9
         *off = newoff;
         return 0;
     }
 
     for (i = *off + 1; i < newoff; i++) {
         uint64_t v = p[i];
         if (UNLIKELY((v & 0xf0) != 0x60)) {
             cli_errmsg("Invalid number part: %c\n", (char)v);
             *ok = 0;
             return 0;
         }
         v &= 0xf;
         v <<= shift;
         n |= v;
         shift += 4;
997a1efe
     }
     *off = newoff;
     return n;
 }
 
3555ee75
 static inline funcid_t readFuncID(struct cli_bc *bc, unsigned char *p,
288057e9
                                   unsigned *off, unsigned len, char *ok)
997a1efe
 {
288057e9
     funcid_t id = readNumber(p, off, len, ok) - 1;
e0c4fd85
     if (*ok && id >= bc->num_func) {
288057e9
         cli_errmsg("Called function out of range: %u >= %u\n", id, bc->num_func);
         *ok = 0;
         return ~0;
3555ee75
     }
     return id;
 }
 
cf0cd429
 static inline funcid_t readAPIFuncID(struct cli_bc *bc, unsigned char *p,
288057e9
                                      unsigned *off, unsigned len, char *ok)
cf0cd429
 {
288057e9
     funcid_t id = readNumber(p, off, len, ok) - 1;
cf0cd429
     if (*ok && !cli_bitset_test(bc->uses_apis, id)) {
288057e9
         cli_errmsg("Called undeclared API function: %u\n", id);
         *ok = 0;
         return ~0;
cf0cd429
     }
     return id;
 }
 
997a1efe
 static inline unsigned readFixedNumber(const unsigned char *p, unsigned *off,
288057e9
                                        unsigned len, char *ok, unsigned width)
997a1efe
 {
288057e9
     unsigned i, n = 0, shift = 0;
997a1efe
     unsigned newoff = *off + width;
     if (newoff > len) {
288057e9
         cli_errmsg("Newline encountered while reading number\n");
         *ok = 0;
         return 0;
     }
     for (i = *off; i < newoff; i++) {
         unsigned v = p[i];
         if (UNLIKELY((v & 0xf0) != 0x60)) {
             cli_errmsg("Invalid number part: %c\n", v);
             *ok = 0;
             return 0;
         }
         v &= 0xf;
         v <<= shift;
         n |= v;
         shift += 4;
997a1efe
     }
     *off = newoff;
     return n;
 }
 
09bd9839
 static inline operand_t readOperand(struct cli_bc_func *func, unsigned char *p,
288057e9
                                     unsigned *off, unsigned len, char *ok)
09bd9839
 {
     uint64_t v;
288057e9
     if ((p[*off] & 0xf0) == 0x40 || p[*off] == 0x50) {
         uint64_t *dest;
         uint16_t ty;
         p[*off] |= 0x20;
         /* TODO: unique constants */
         func->constants = cli_realloc2(func->constants, (func->numConstants + 1) * sizeof(*func->constants));
         if (!func->constants) {
             *ok = 0;
             return MAX_OP;
         }
         v    = readNumber(p, off, len, ok);
         dest = &func->constants[func->numConstants];
         /* Write the constant to the correct place according to its type.
09bd9839
 	 * This is needed on big-endian machines, because constants are always
7cd9337a
 	 * read as u64, but accessed as one of these types: u8, u16, u32, u64 */
288057e9
         *dest = 0;
         ty    = 8 * readFixedNumber(p, off, len, ok, 1);
         if (!ty) {
             /* This is a global variable */
             return 0x80000000 | v;
         }
         if (ty <= 8)
             *(uint8_t *)dest = v;
         else if (ty <= 16)
             *(uint16_t *)dest = v;
         else if (ty <= 32)
             *(uint32_t *)dest = v;
         else
             *dest = v;
         return func->numValues + func->numConstants++;
09bd9839
     }
     v = readNumber(p, off, len, ok);
     if (!*ok)
288057e9
         return MAX_OP;
09bd9839
     if (v >= func->numValues) {
288057e9
         cli_errmsg("Operand index exceeds bounds: %u >= %u!\n", (unsigned)v, (unsigned)func->numValues);
         *ok = 0;
         return MAX_OP;
09bd9839
     }
     return v;
 }
 
e4a0f2c9
 static inline char *readData(const unsigned char *p, unsigned *off, unsigned len, char *ok, unsigned *datalen)
997a1efe
 {
     unsigned char *dat, *q;
     unsigned l, newoff, i;
     if (p[*off] != '|') {
288057e9
         cli_errmsg("Data start marker missing: %c\n", p[*off]);
         *ok = 0;
         return NULL;
997a1efe
     }
     (*off)++;
     l = readNumber(p, off, len, ok);
53bd5bb1
     if (!l || !ok) {
288057e9
         *datalen = l;
         return NULL;
8c0933ce
     }
288057e9
     newoff = *off + 2 * l;
997a1efe
     if (newoff > len) {
288057e9
         cli_errmsg("Line ended while reading data\n");
         *ok = 0;
         return 0;
997a1efe
     }
     dat = cli_malloc(l);
     if (!dat) {
288057e9
         cli_errmsg("Cannot allocate memory for data\n");
         *ok = 0;
         return NULL;
997a1efe
     }
     q = dat;
288057e9
     for (i = *off; i < newoff; i += 2) {
         const unsigned char v0 = p[i];
         const unsigned char v1 = p[i + 1];
         if (UNLIKELY((v0 & 0xf0) != 0x60 || (v1 & 0xf0) != 0x60)) {
             cli_errmsg("Invalid data part: %c%c\n", v0, v1);
             *ok = 0;
             free(dat);
             return 0;
         }
         *q++ = (v0 & 0xf) | ((v1 & 0xf) << 4);
997a1efe
     }
288057e9
     *off     = newoff;
c3c97d4a
     *datalen = l;
288057e9
     return (char *)dat;
997a1efe
 }
 
c3c97d4a
 static inline char *readString(const unsigned char *p, unsigned *off, unsigned len, char *ok)
 {
c9a070c9
     unsigned stringlen = 0;
288057e9
     char *str          = readData(p, off, len, ok, &stringlen);
     if (*ok && stringlen && str[stringlen - 1] != '\0') {
         str[stringlen - 1] = '\0';
         cli_errmsg("bytecode: string missing \\0 terminator: %s\n", str);
         free(str);
         *ok = 0;
         return NULL;
c3c97d4a
     }
     return str;
 }
8cc286f5
 
482e97db
 static int parseHeader(struct cli_bc *bc, unsigned char *buffer, unsigned *linelength)
997a1efe
 {
     uint64_t magic1;
     unsigned magic2;
     char ok = 1;
     unsigned offset, len, flevel;
482e97db
     char *pos;
3735fda1
 
288057e9
     if (strncmp((const char *)buffer, BC_HEADER, sizeof(BC_HEADER) - 1)) {
         cli_errmsg("Missing file magic in bytecode");
         return CL_EMALFDB;
997a1efe
     }
288057e9
     offset                   = sizeof(BC_HEADER) - 1;
     len                      = strlen((const char *)buffer);
3735fda1
     bc->metadata.formatlevel = readNumber(buffer, &offset, len, &ok);
997a1efe
     if (!ok) {
288057e9
         cli_errmsg("Unable to parse (format) functionality level in bytecode header\n");
         return CL_EMALFDB;
997a1efe
     }
3735fda1
     /* we support 2 bytecode formats */
     if (bc->metadata.formatlevel != BC_FORMAT_096 &&
288057e9
         bc->metadata.formatlevel != BC_FORMAT_LEVEL) {
         cli_dbgmsg("Skipping bytecode with (format) functionality level: %u (current %u)\n",
                    bc->metadata.formatlevel, BC_FORMAT_LEVEL);
         return CL_BREAK;
997a1efe
     }
c0afc517
     /* Optimistic parsing, check for error only at the end.*/
288057e9
     bc->metadata.timestamp     = readNumber(buffer, &offset, len, &ok);
     bc->metadata.sigmaker      = readString(buffer, &offset, len, &ok);
a35cfe51
     bc->metadata.targetExclude = readNumber(buffer, &offset, len, &ok);
288057e9
     bc->kind                   = readNumber(buffer, &offset, len, &ok);
     bc->metadata.minfunc       = readNumber(buffer, &offset, len, &ok);
     bc->metadata.maxfunc       = readNumber(buffer, &offset, len, &ok);
     flevel                     = cl_retflevel();
3735fda1
     /* in 0.96 these 2 fields are unused / zero, in post 0.96 these mean
      * min/max flevel.
      * So 0 for min/max means no min/max
      * Note that post 0.96 bytecode/bytecode lsig needs format 7, because
      * 0.96 doesn't check lsig functionality level.
      */
     if ((bc->metadata.minfunc && bc->metadata.minfunc > flevel) ||
         (bc->metadata.maxfunc && bc->metadata.maxfunc < flevel)) {
288057e9
         cli_dbgmsg("Skipping bytecode with (engine) functionality level %u-%u (current %u)\n",
                    bc->metadata.minfunc, bc->metadata.maxfunc, flevel);
         return CL_BREAK;
3735fda1
     }
     bc->metadata.maxresource = readNumber(buffer, &offset, len, &ok);
288057e9
     bc->metadata.compiler    = readString(buffer, &offset, len, &ok);
     bc->num_types            = readNumber(buffer, &offset, len, &ok);
     bc->num_func             = readNumber(buffer, &offset, len, &ok);
     bc->state                = bc_loaded;
     bc->uses_apis            = NULL;
     bc->dbgnodes             = NULL;
     bc->dbgnode_cnt          = 0;
997a1efe
     if (!ok) {
288057e9
         cli_errmsg("Invalid bytecode header at %u\n", offset);
         return CL_EMALFDB;
997a1efe
     }
     magic1 = readNumber(buffer, &offset, len, &ok);
     magic2 = readFixedNumber(buffer, &offset, len, &ok, 2);
     if (!ok || magic1 != 0x53e5493e9f3d1c30ull || magic2 != 42) {
288057e9
         unsigned long m0 = magic1 >> 32;
         unsigned long m1 = magic1;
         cli_errmsg("Magic numbers don't match: %lx%lx, %u\n", m0, m1, magic2);
         return CL_EMALFDB;
997a1efe
     }
482e97db
     if (buffer[offset] != ':') {
288057e9
         cli_errmsg("Expected : but found: %c\n", buffer[offset]);
         return CL_EMALFDB;
482e97db
     }
     offset++;
288057e9
     *linelength = strtol((const char *)buffer + offset, &pos, 10);
c1aff5ec
     if (*pos != '\0') {
288057e9
         cli_errmsg("Invalid number: %s\n", buffer + offset);
         return CL_EMALFDB;
997a1efe
     }
 
     bc->funcs = cli_calloc(bc->num_func, sizeof(*bc->funcs));
     if (!bc->funcs) {
288057e9
         cli_errmsg("Out of memory allocating %u functions\n", bc->num_func);
         return CL_EMEM;
997a1efe
     }
8cc286f5
     bc->types = cli_calloc(bc->num_types, sizeof(*bc->types));
     if (!bc->types) {
288057e9
         cli_errmsg("Out of memory allocating %u types\n", bc->num_types);
         return CL_EMEM;
8cc286f5
     }
     return CL_SUCCESS;
 }
 
e4a0f2c9
 static int parseLSig(struct cli_bc *bc, char *buffer)
dcee45cc
 {
ec41017b
     const char *prefix;
     char *vnames, *vend = strchr(buffer, ';');
     if (vend) {
288057e9
         bc->lsig = cli_strdup(buffer);
         *vend++  = '\0';
         prefix   = buffer;
         vnames   = strchr(vend, '{');
ec41017b
     } else {
288057e9
         /* Not a logical signature, but we still have a virusname */
         bc->hook_name = cli_strdup(buffer);
         bc->lsig      = NULL;
ec41017b
     }
 
dcee45cc
     return CL_SUCCESS;
 }
 
8cc286f5
 static uint16_t readTypeID(struct cli_bc *bc, unsigned char *buffer,
288057e9
                            unsigned *offset, unsigned len, char *ok)
8cc286f5
 {
     uint64_t t = readNumber(buffer, offset, len, ok);
     if (!ok)
288057e9
         return ~0;
8cc286f5
     if (t >= bc->num_types + bc->start_tid) {
288057e9
         cli_errmsg("Invalid type id: %llu\n", (unsigned long long)t);
         *ok = 0;
         return ~0;
8cc286f5
     }
     return t;
 }
 
 static void parseType(struct cli_bc *bc, struct cli_bc_type *ty,
288057e9
                       unsigned char *buffer, unsigned *off, unsigned len,
                       char *ok)
8cc286f5
 {
     unsigned j;
 
34da9ae4
     ty->numElements = readNumber(buffer, off, len, ok);
d11cced2
     if (!*ok) {
288057e9
         cli_errmsg("Error parsing type\n");
         *ok = 0;
         return;
8cc286f5
     }
288057e9
     ty->containedTypes = cli_malloc(sizeof(*ty->containedTypes) * ty->numElements);
8cc286f5
     if (!ty->containedTypes) {
288057e9
         cli_errmsg("Out of memory allocating %u types\n", ty->numElements);
         *ok = 0;
         return;
8cc286f5
     }
288057e9
     for (j = 0; j < ty->numElements; j++) {
         ty->containedTypes[j] = readTypeID(bc, buffer, off, len, ok);
8cc286f5
     }
 }
 
288057e9
 static uint16_t containedTy[] = {8, 16, 32, 64};
8cc286f5
 
36fab4bd
 #define NUM_STATIC_TYPES 4
8cc286f5
 static void add_static_types(struct cli_bc *bc)
 {
     unsigned i;
288057e9
     for (i = 0; i < NUM_STATIC_TYPES; i++) {
         bc->types[i].kind           = DPointerType;
         bc->types[i].numElements    = 1;
         bc->types[i].containedTypes = &containedTy[i];
         bc->types[i].size = bc->types[i].align = 8;
8cc286f5
     }
 }
 
 static int parseTypes(struct cli_bc *bc, unsigned char *buffer)
 {
288057e9
     unsigned i, offset = 1, len = strlen((const char *)buffer);
     char ok = 1;
800a79e0
 
8cc286f5
     if (buffer[0] != 'T') {
288057e9
         cli_errmsg("Invalid function types header: %c\n", buffer[0]);
         return CL_EMALFDB;
8cc286f5
     }
     bc->start_tid = readFixedNumber(buffer, &offset, len, &ok, 2);
     if (bc->start_tid != BC_START_TID) {
288057e9
         cli_warnmsg("Type start id mismatch: %u != %u\n", bc->start_tid,
                     BC_START_TID);
         return CL_BREAK;
8cc286f5
     }
     add_static_types(bc);
288057e9
     for (i = (BC_START_TID - 65); i < bc->num_types - 1; i++) {
         struct cli_bc_type *ty = &bc->types[i];
         uint8_t t              = readFixedNumber(buffer, &offset, len, &ok, 1);
         if (!ok) {
             cli_errmsg("Error reading type kind\n");
             return CL_EMALFDB;
         }
         switch (t) {
             case 1:
                 ty->kind = DFunctionType;
                 ty->size = ty->align = sizeof(void *);
                 parseType(bc, ty, buffer, &offset, len, &ok);
                 if (!ok) {
                     cli_errmsg("Error parsing type %u\n", i);
                     return CL_EMALFDB;
                 }
                 if (!ty->numElements) {
                     cli_errmsg("Function with no return type? %u\n", i);
                     return CL_EMALFDB;
                 }
                 break;
             case 2:
             case 3:
                 ty->kind = (t == 2) ? DPackedStructType : DStructType;
                 ty->size = ty->align = 0; /* TODO:calculate size/align of structs */
                 ty->align            = 8;
                 parseType(bc, ty, buffer, &offset, len, &ok);
                 if (!ok) {
                     cli_errmsg("Error parsing type %u\n", i);
                     return CL_EMALFDB;
                 }
                 break;
             case 4:
                 ty->kind = DArrayType;
                 /* number of elements of array, not subtypes! */
                 ty->numElements = readNumber(buffer, &offset, len, &ok);
                 if (!ok) {
                     cli_errmsg("Error parsing type %u\n", i);
                     return CL_EMALFDB;
                 }
                 /* fall-through */
             case 5:
                 if (t == 5) {
                     ty->kind        = DPointerType;
                     ty->numElements = 1;
                 }
                 ty->containedTypes = cli_malloc(sizeof(*ty->containedTypes));
                 if (!ty->containedTypes) {
                     cli_errmsg("Out of memory allocating containedType\n");
                     return CL_EMALFDB;
                 }
                 ty->containedTypes[0] = readTypeID(bc, buffer, &offset, len, &ok);
                 if (!ok) {
                     cli_errmsg("Error parsing type %u\n", i);
                     return CL_EMALFDB;
                 }
                 if (t == 5) {
                     /* for interpreter, pointers 64-bit there */
                     ty->size = ty->align = 8;
                 } else {
                     ty->size  = ty->numElements * typesize(bc, ty->containedTypes[0]);
                     ty->align = typealign(bc, ty->containedTypes[0]);
                 }
                 break;
             default:
                 cli_errmsg("Invalid type kind: %u\n", t);
                 return CL_EMALFDB;
         }
     }
     for (i = (BC_START_TID - 65); i < bc->num_types - 1; i++) {
         struct cli_bc_type *ty = &bc->types[i];
         if (ty->kind == DArrayType) {
             ty->size  = ty->numElements * typesize(bc, ty->containedTypes[0]);
             ty->align = typealign(bc, ty->containedTypes[0]);
         }
6b5af78c
     }
8cc286f5
     return CL_SUCCESS;
 }
 
cf0cd429
 /* checks whether the type described by tid is the same as the one described by
7cd9337a
  * apitid. */
cf0cd429
 static int types_equal(const struct cli_bc *bc, uint16_t *apity2ty, uint16_t tid, uint16_t apitid)
 {
     unsigned i;
288057e9
     const struct cli_bc_type *ty    = &bc->types[tid - 65];
cf0cd429
     const struct cli_bc_type *apity = &cli_apicall_types[apitid];
     /* If we've already verified type equality, return.
      * Since we need to check equality of recursive types, we assume types are
      * equal while checking equality of contained types, unless proven
      * otherwise. */
288057e9
     if (apity2ty[apitid] == tid + 1)
         return 1;
     apity2ty[apitid] = tid + 1;
 
     if (ty->kind != apity->kind) {
         cli_dbgmsg("bytecode: type kind mismatch: %u != %u\n", ty->kind, apity->kind);
         return 0;
     }
     if (ty->numElements != apity->numElements) {
         cli_dbgmsg("bytecode: type numElements mismatch: %u != %u\n", ty->numElements, apity->numElements);
         return 0;
     }
     for (i = 0; i < ty->numElements; i++) {
         if (apity->containedTypes[i] < BC_START_TID) {
             if (ty->containedTypes[i] != apity->containedTypes[i]) {
                 cli_dbgmsg("bytecode: contained type mismatch: %u != %u\n",
                            ty->containedTypes[i], apity->containedTypes[i]);
                 return 0;
             }
         } else if (!types_equal(bc, apity2ty, ty->containedTypes[i], apity->containedTypes[i] - BC_START_TID))
             return 0;
         if (ty->kind == DArrayType)
             break; /* validated the contained type already */
     }
cf0cd429
     return 1;
 }
 
8cc286f5
 static int parseApis(struct cli_bc *bc, unsigned char *buffer)
 {
288057e9
     unsigned i, offset = 1, len = strlen((const char *)buffer), maxapi, calls;
     char ok = 1;
     uint16_t *apity2ty; /*map of api type to current bytecode type ID */
cf0cd429
 
     if (buffer[0] != 'E') {
288057e9
         cli_errmsg("bytecode: Invalid api header: %c\n", buffer[0]);
         return CL_EMALFDB;
cf0cd429
     }
 
     maxapi = readNumber(buffer, &offset, len, &ok);
     if (!ok)
288057e9
         return CL_EMALFDB;
cf0cd429
     if (maxapi > cli_apicall_maxapi) {
288057e9
         cli_dbgmsg("bytecode using API %u, but highest API known to libclamav is %u, skipping\n", maxapi, cli_apicall_maxapi);
         return CL_BREAK;
cf0cd429
     }
     calls = readNumber(buffer, &offset, len, &ok);
     if (!ok)
288057e9
         return CL_EMALFDB;
cf0cd429
     if (calls > maxapi) {
288057e9
         cli_errmsg("bytecode: attempting to describe more APIs than max: %u > %u\n", calls, maxapi);
         return CL_EMALFDB;
cf0cd429
     }
     bc->uses_apis = cli_bitset_init();
     if (!bc->uses_apis) {
288057e9
         cli_errmsg("Out of memory allocating apis bitset\n");
         return CL_EMEM;
cf0cd429
     }
     apity2ty = cli_calloc(cli_apicall_maxtypes, sizeof(*cli_apicall_types));
     if (!apity2ty) {
288057e9
         cli_errmsg("Out of memory allocating apity2ty\n");
         return CL_EMEM;
     }
     for (i = 0; i < calls; i++) {
         unsigned id  = readNumber(buffer, &offset, len, &ok);
         uint16_t tid = readTypeID(bc, buffer, &offset, len, &ok);
         char *name   = readString(buffer, &offset, len, &ok);
 
         /* validate APIcall prototype */
         if (id > maxapi) {
             cli_errmsg("bytecode: API id %u out of range, max %u\n", id, maxapi);
             ok = 0;
         }
         /* API ids start from 1 */
         id--;
         if (ok && name && strcmp(cli_apicalls[id].name, name)) {
             cli_errmsg("bytecode: API %u name mismatch: %s expected %s\n", id, name, cli_apicalls[id].name);
             ok = 0;
         }
         if (ok && !types_equal(bc, apity2ty, tid, cli_apicalls[id].type)) {
             cli_errmsg("bytecode: API %u prototype doesn't match\n", id);
             ok = 0;
         }
         /* don't need the name anymore */
         free(name);
         if (!ok) {
             free(apity2ty); /* free temporary map */
             return CL_EMALFDB;
         }
 
         /* APIcall is valid */
         cli_bitset_set(bc->uses_apis, id);
cf0cd429
     }
     free(apity2ty); /* free temporary map */
     cli_dbgmsg("bytecode: Parsed %u APIcalls, maxapi %u\n", calls, maxapi);
997a1efe
     return CL_SUCCESS;
 }
 
ec077929
 static uint16_t type_components(struct cli_bc *bc, uint16_t id, char *ok)
 {
288057e9
     unsigned i, sum = 0;
ec077929
     const struct cli_bc_type *ty;
     if (id <= 64)
288057e9
         return 1;
     ty = &bc->types[id - 65];
ec077929
     /* TODO: protect against recursive types */
     switch (ty->kind) {
288057e9
         case DFunctionType:
             cli_errmsg("bytecode: function type not accepted for constant: %u\n", id);
             /* don't accept functions as constant initializers */
             *ok = 0;
             return 0;
         case DPointerType:
             return 2;
         case DStructType:
         case DPackedStructType:
             for (i = 0; i < ty->numElements; i++) {
                 sum += type_components(bc, ty->containedTypes[i], ok);
             }
             return sum;
         case DArrayType:
             return type_components(bc, ty->containedTypes[0], ok) * ty->numElements;
         default:
             *ok = 0;
             return 0;
ec077929
     }
 }
 
 static void readConstant(struct cli_bc *bc, unsigned i, unsigned comp,
288057e9
                          unsigned char *buffer, unsigned *offset,
                          unsigned len, char *ok)
ec077929
 {
288057e9
     unsigned j = 0;
dcaa4038
     if (*ok && buffer[*offset] == 0x40 &&
288057e9
         buffer[*offset + 1] == 0x60) {
         /* zero initializer */
         memset(bc->globals[i], 0, sizeof(*bc->globals[0]) * comp);
         (*offset) += 2;
         return;
dcaa4038
     }
ec077929
     while (*ok && buffer[*offset] != 0x60) {
288057e9
         if (j >= comp) {
             cli_errmsg("bytecode: constant has too many subcomponents, expected %u\n", comp);
             *ok = 0;
             return;
         }
         buffer[*offset] |= 0x20;
         bc->globals[i][j++] = readNumber(buffer, offset, len, ok);
ec077929
     }
     if (*ok && j != comp) {
288057e9
         cli_errmsg("bytecode: constant has too few subcomponents: %u < %u\n", j, comp);
         *ok = 0;
ec077929
     }
7189addb
     (*offset)++;
ec077929
 }
 
 /* parse constant globals with constant initializers */
 static int parseGlobals(struct cli_bc *bc, unsigned char *buffer)
 {
288057e9
     unsigned i, offset = 1, len = strlen((const char *)buffer), numglobals;
9fac78c7
     unsigned maxglobal;
288057e9
     char ok = 1;
ec077929
 
     if (buffer[0] != 'G') {
288057e9
         cli_errmsg("bytecode: Invalid globals header: %c\n", buffer[0]);
         return CL_EMALFDB;
ec077929
     }
9fac78c7
     maxglobal = readNumber(buffer, &offset, len, &ok);
     if (maxglobal > cli_apicall_maxglobal) {
288057e9
         cli_dbgmsg("bytecode using global %u, but highest global known to libclamav is %u, skipping\n", maxglobal, cli_apicall_maxglobal);
         return CL_BREAK;
9fac78c7
     }
288057e9
     numglobals  = readNumber(buffer, &offset, len, &ok);
ec077929
     bc->globals = cli_calloc(numglobals, sizeof(*bc->globals));
     if (!bc->globals) {
288057e9
         cli_errmsg("bytecode: OOM allocating memory for %u globals\n", numglobals);
         return CL_EMEM;
ec077929
     }
     bc->globaltys = cli_calloc(numglobals, sizeof(*bc->globaltys));
     if (!bc->globaltys) {
288057e9
         cli_errmsg("bytecode: OOM allocating memory for %u global types\n", numglobals);
         return CL_EMEM;
ec077929
     }
     bc->num_globals = numglobals;
     if (!ok)
288057e9
         return CL_EMALFDB;
     for (i = 0; i < numglobals; i++) {
         unsigned comp;
         bc->globaltys[i] = readTypeID(bc, buffer, &offset, len, &ok);
         comp             = type_components(bc, bc->globaltys[i], &ok);
         if (!ok)
             return CL_EMALFDB;
         bc->globals[i] = cli_malloc(sizeof(*bc->globals[0]) * comp);
         if (!bc->globals[i])
             return CL_EMEM;
         readConstant(bc, i, comp, buffer, &offset, len, &ok);
ec077929
     }
     if (!ok)
288057e9
         return CL_EMALFDB;
7189addb
     if (offset != len) {
288057e9
         cli_errmsg("Trailing garbage in globals: %d extra bytes\n",
                    len - offset);
         return CL_EMALFDB;
7189addb
     }
ec077929
     return CL_SUCCESS;
 }
 
53bd5bb1
 static int parseMD(struct cli_bc *bc, unsigned char *buffer)
 {
288057e9
     unsigned offset = 1, len = strlen((const char *)buffer);
53bd5bb1
     unsigned numMD, i, b;
     char ok = 1;
     if (buffer[0] != 'D')
288057e9
         return CL_EMALFDB;
53bd5bb1
     numMD = readNumber(buffer, &offset, len, &ok);
     if (!ok) {
288057e9
         cli_errmsg("Unable to parse number of MD nodes\n");
         return CL_EMALFDB;
53bd5bb1
     }
     b = bc->dbgnode_cnt;
     bc->dbgnode_cnt += numMD;
     bc->dbgnodes = cli_realloc(bc->dbgnodes, bc->dbgnode_cnt * sizeof(*bc->dbgnodes));
     if (!bc->dbgnodes)
288057e9
         return CL_EMEM;
     for (i = 0; i < numMD; i++) {
         unsigned j;
         struct cli_bc_dbgnode_element *elts;
         unsigned el = readNumber(buffer, &offset, len, &ok);
         if (!ok) {
             cli_errmsg("Unable to parse number of elements\n");
             return CL_EMALFDB;
         }
         bc->dbgnodes[b + i].numelements = el;
         bc->dbgnodes[b + i].elements = elts = cli_calloc(el, sizeof(*elts));
         if (!elts)
             return CL_EMEM;
         for (j = 0; j < el; j++) {
             if (buffer[offset] == '|') {
                 elts[j].string = readData(buffer, &offset, len, &ok, &elts[j].len);
                 if (!ok)
                     return CL_EMALFDB;
             } else {
                 elts[j].len = readNumber(buffer, &offset, len, &ok);
                 if (!ok)
                     return CL_EMALFDB;
                 if (elts[j].len) {
                     elts[j].constant = readNumber(buffer, &offset, len, &ok);
                 } else
                     elts[j].nodeid = readNumber(buffer, &offset, len, &ok);
                 if (!ok)
                     return CL_EMALFDB;
             }
         }
53bd5bb1
     }
     cli_dbgmsg("bytecode: Parsed %u nodes total\n", bc->dbgnode_cnt);
     return CL_SUCCESS;
 }
 
c3c97d4a
 static int parseFunctionHeader(struct cli_bc *bc, unsigned fn, unsigned char *buffer)
997a1efe
 {
288057e9
     char ok = 1;
     unsigned offset, len, all_locals = 0, i;
997a1efe
     struct cli_bc_func *func;
 
     if (fn >= bc->num_func) {
288057e9
         cli_errmsg("Found more functions than declared: %u >= %u\n", fn,
                    bc->num_func);
         return CL_EMALFDB;
997a1efe
     }
     func = &bc->funcs[fn];
288057e9
     len  = strlen((const char *)buffer);
997a1efe
 
     if (buffer[0] != 'A') {
288057e9
         cli_errmsg("Invalid function arguments header: %c\n", buffer[0]);
         return CL_EMALFDB;
997a1efe
     }
288057e9
     offset           = 1;
     func->numArgs    = readFixedNumber(buffer, &offset, len, &ok, 1);
3b33bd68
     func->returnType = readTypeID(bc, buffer, &offset, len, &ok);
997a1efe
     if (buffer[offset] != 'L') {
288057e9
         cli_errmsg("Invalid function locals header: %c\n", buffer[offset]);
         return CL_EMALFDB;
997a1efe
     }
     offset++;
     func->numLocals = readNumber(buffer, &offset, len, &ok);
     if (!ok) {
288057e9
         cli_errmsg("Invalid number of arguments/locals\n");
         return CL_EMALFDB;
997a1efe
     }
     all_locals = func->numArgs + func->numLocals;
5475ec2a
     if (!all_locals) {
288057e9
         func->types = NULL;
5475ec2a
     } else {
288057e9
         func->types = cli_calloc(all_locals, sizeof(*func->types));
         if (!func->types) {
             cli_errmsg("Out of memory allocating function arguments\n");
             return CL_EMEM;
         }
997a1efe
     }
288057e9
     for (i = 0; i < all_locals; i++) {
         func->types[i] = readNumber(buffer, &offset, len, &ok);
         if (readFixedNumber(buffer, &offset, len, &ok, 1))
             func->types[i] |= 0x8000;
997a1efe
     }
     if (!ok) {
288057e9
         cli_errmsg("Invalid local types\n");
         return CL_EMALFDB;
997a1efe
     }
     if (buffer[offset] != 'F') {
288057e9
         cli_errmsg("Invalid function body header: %c\n", buffer[offset]);
         return CL_EMALFDB;
997a1efe
     }
     offset++;
3555ee75
     func->numInsts = readNumber(buffer, &offset, len, &ok);
288057e9
     if (!ok) {
         cli_errmsg("Invalid instructions count\n");
         return CL_EMALFDB;
     }
     func->numValues    = func->numArgs + func->numLocals;
     func->insn_idx     = 0;
     func->numConstants = 0;
     func->allinsts     = cli_calloc(func->numInsts, sizeof(*func->allinsts));
3555ee75
     if (!func->allinsts) {
288057e9
         cli_errmsg("Out of memory allocating instructions\n");
         return CL_EMEM;
3555ee75
     }
997a1efe
     func->numBB = readNumber(buffer, &offset, len, &ok);
     if (!ok) {
288057e9
         cli_errmsg("Invalid basic block count\n");
         return CL_EMALFDB;
997a1efe
     }
     func->BB = cli_calloc(func->numBB, sizeof(*func->BB));
     if (!func->BB) {
288057e9
         cli_errmsg("Out of memory allocating basic blocks\n");
         return CL_EMEM;
997a1efe
     }
     return CL_SUCCESS;
 }
 
288057e9
 static bbid_t readBBID(struct cli_bc_func *func, const unsigned char *buffer, unsigned *off, unsigned len, char *ok)
 {
c3c97d4a
     unsigned id = readNumber(buffer, off, len, ok);
     if (!id || id >= func->numBB) {
288057e9
         cli_errmsg("Basic block ID out of range: %u\n", id);
         *ok = 0;
c3c97d4a
     }
     if (!*ok)
288057e9
         return ~0;
3555ee75
     return id;
c3c97d4a
 }
 
800a79e0
 /*
6922903a
 static uint16_t get_type(struct cli_bc_func *func, operand_t op)
 {
     if (op >= func->numValues)
 	return 64;
     return func->types[op];
800a79e0
 }*/
48fc8b98
 static int16_t get_optype(const struct cli_bc_func *bcfunc, operand_t op)
 {
     if (op >= bcfunc->numArgs + bcfunc->numLocals)
288057e9
         return 0;
     return bcfunc->types[op] & 0x7fff;
48fc8b98
 }
6922903a
 
c3c97d4a
 static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char *buffer)
997a1efe
 {
288057e9
     char ok = 1;
6922903a
     unsigned offset, len, i, last = 0;
997a1efe
     struct cli_bc_bb *BB;
c3c97d4a
     struct cli_bc_func *bcfunc = &bc->funcs[func];
997a1efe
     struct cli_bc_inst inst;
 
c3c97d4a
     if (bb >= bcfunc->numBB) {
288057e9
         cli_errmsg("Found too many basic blocks\n");
         return CL_EMALFDB;
997a1efe
     }
 
288057e9
     BB  = &bcfunc->BB[bb];
     len = strlen((const char *)buffer);
997a1efe
     if (buffer[0] != 'B') {
288057e9
         cli_errmsg("Invalid basic block header: %c\n", buffer[0]);
         return CL_EMALFDB;
997a1efe
     }
288057e9
     offset       = 1;
997a1efe
     BB->numInsts = 0;
288057e9
     BB->insts    = &bcfunc->allinsts[bcfunc->insn_idx];
997a1efe
     while (!last) {
288057e9
         unsigned numOp;
         if (buffer[offset] == 'T') {
             last = 1;
             offset++;
             /* terminators are void */
             inst.type = 0;
             inst.dest = 0;
         } else {
             inst.type = readNumber(buffer, &offset, len, &ok);
             inst.dest = readNumber(buffer, &offset, len, &ok);
         }
         inst.opcode = readFixedNumber(buffer, &offset, len, &ok, 2);
         if (!ok) {
             cli_errmsg("Invalid type or operand\n");
             return CL_EMALFDB;
         }
         if (inst.opcode >= OP_BC_INVALID) {
             cli_errmsg("Invalid opcode: %u\n", inst.opcode);
             return CL_EMALFDB;
         }
 
         switch (inst.opcode) {
             case OP_BC_JMP:
                 inst.u.jump = readBBID(bcfunc, buffer, &offset, len, &ok);
                 break;
             case OP_BC_RET:
                 inst.type      = readNumber(buffer, &offset, len, &ok);
                 inst.u.unaryop = readOperand(bcfunc, buffer, &offset, len, &ok);
                 break;
             case OP_BC_BRANCH:
                 inst.u.branch.condition = readOperand(bcfunc, buffer, &offset, len, &ok);
                 inst.u.branch.br_true   = readBBID(bcfunc, buffer, &offset, len, &ok);
                 inst.u.branch.br_false  = readBBID(bcfunc, buffer, &offset, len, &ok);
                 break;
             case OP_BC_CALL_API: /* fall-through */
             case OP_BC_CALL_DIRECT:
                 numOp = readFixedNumber(buffer, &offset, len, &ok, 1);
                 if (ok) {
                     inst.u.ops.numOps  = numOp;
                     inst.u.ops.opsizes = NULL;
                     if (!numOp) {
                         inst.u.ops.ops = NULL;
                     } else {
                         inst.u.ops.ops = cli_calloc(numOp, sizeof(*inst.u.ops.ops));
                         if (!inst.u.ops.ops) {
                             cli_errmsg("Out of memory allocating operands\n");
                             return CL_EMEM;
                         }
                     }
                     if (inst.opcode == OP_BC_CALL_DIRECT)
                         inst.u.ops.funcid = readFuncID(bc, buffer, &offset, len, &ok);
                     else
                         inst.u.ops.funcid = readAPIFuncID(bc, buffer, &offset, len, &ok);
                     for (i = 0; i < numOp; i++) {
                         inst.u.ops.ops[i] = readOperand(bcfunc, buffer, &offset, len, &ok);
                     }
                 }
                 break;
             case OP_BC_ZEXT:
             case OP_BC_SEXT:
             case OP_BC_TRUNC:
                 inst.u.cast.source = readOperand(bcfunc, buffer, &offset, len, &ok);
                 inst.u.cast.mask   = bcfunc->types[inst.u.cast.source];
                 if (inst.u.cast.mask == 1)
                     inst.u.cast.size = 0;
                 else if (inst.u.cast.mask <= 8)
                     inst.u.cast.size = 1;
                 else if (inst.u.cast.mask <= 16)
                     inst.u.cast.size = 2;
                 else if (inst.u.cast.mask <= 32)
                     inst.u.cast.size = 3;
                 else if (inst.u.cast.mask <= 64)
                     inst.u.cast.size = 4;
                 /* calculate mask */
                 if (inst.opcode != OP_BC_SEXT)
                     inst.u.cast.mask = inst.u.cast.mask != 64 ? (1ull << inst.u.cast.mask) - 1 : ~0ull;
                 break;
             case OP_BC_GEP1:
             case OP_BC_GEPZ:
                 inst.u.three[0] = readNumber(buffer, &offset, len, &ok);
                 inst.u.three[1] = readOperand(bcfunc, buffer, &offset, len, &ok);
                 inst.u.three[2] = readOperand(bcfunc, buffer, &offset, len, &ok);
                 break;
             case OP_BC_GEPN:
                 numOp = readFixedNumber(buffer, &offset, len, &ok, 1);
                 if (ok) {
                     inst.u.ops.numOps  = numOp + 2;
                     inst.u.ops.opsizes = NULL;
                     inst.u.ops.ops     = cli_calloc(numOp + 2, sizeof(*inst.u.ops.ops));
                     if (!inst.u.ops.ops) {
                         cli_errmsg("Out of memory allocating operands\n");
                         return CL_EMEM;
                     }
                     inst.u.ops.ops[0] = readNumber(buffer, &offset, len, &ok);
                     for (i = 1; i < numOp + 2; i++)
                         inst.u.ops.ops[i] = readOperand(bcfunc, buffer, &offset, len, &ok);
                 }
                 break;
             case OP_BC_ICMP_EQ:
             case OP_BC_ICMP_NE:
             case OP_BC_ICMP_UGT:
             case OP_BC_ICMP_UGE:
             case OP_BC_ICMP_ULT:
             case OP_BC_ICMP_ULE:
             case OP_BC_ICMP_SGT:
             case OP_BC_ICMP_SGE:
             case OP_BC_ICMP_SLE:
             case OP_BC_ICMP_SLT:
                 /* instruction type must be correct before readOperand! */
                 inst.type = readNumber(buffer, &offset, len, &ok);
                 /* fall-through */
             default:
                 numOp = operand_counts[inst.opcode];
                 switch (numOp) {
                     case 0:
                         break;
                     case 1:
                         inst.u.unaryop = readOperand(bcfunc, buffer, &offset, len, &ok);
                         break;
                     case 2:
                         inst.u.binop[0] = readOperand(bcfunc, buffer, &offset, len, &ok);
                         inst.u.binop[1] = readOperand(bcfunc, buffer, &offset, len, &ok);
                         break;
                     case 3:
                         inst.u.three[0] = readOperand(bcfunc, buffer, &offset, len, &ok);
                         inst.u.three[1] = readOperand(bcfunc, buffer, &offset, len, &ok);
                         inst.u.three[2] = readOperand(bcfunc, buffer, &offset, len, &ok);
                         break;
                     default:
                         cli_errmsg("Opcode %u with too many operands: %u?\n", inst.opcode, numOp);
                         ok = 0;
                         break;
                 }
         }
         if (inst.opcode == OP_BC_STORE) {
             int16_t t = get_optype(bcfunc, inst.u.binop[0]);
             if (t)
                 inst.type = t;
         }
         if (inst.opcode == OP_BC_COPY)
             inst.type = get_optype(bcfunc, inst.u.binop[1]);
         if (!ok) {
             cli_errmsg("Invalid instructions or operands\n");
             return CL_EMALFDB;
         }
         if (bcfunc->insn_idx + BB->numInsts >= bcfunc->numInsts) {
             cli_errmsg("More instructions than declared in total: %u > %u!\n",
                        bcfunc->insn_idx + BB->numInsts, bcfunc->numInsts);
             return CL_EMALFDB;
         }
         inst.interp_op = inst.opcode * 5;
         if (inst.type > 1) {
             if (inst.type <= 8)
                 inst.interp_op += 1;
             else if (inst.type <= 16)
                 inst.interp_op += 2;
             else if (inst.type <= 32)
                 inst.interp_op += 3;
             else if (inst.type <= 65)
                 inst.interp_op += 4;
             else {
                 cli_dbgmsg("unknown inst type: %d\n", inst.type);
             }
         }
         BB->insts[BB->numInsts++] = inst;
     }
     if (bb + 1 == bc->funcs[func].numBB) {
         if (buffer[offset] != 'E') {
             cli_errmsg("Missing basicblock terminator, got: %c\n", buffer[offset]);
             return CL_EMALFDB;
         }
         offset++;
997a1efe
     }
53bd5bb1
     if (buffer[offset] == 'D') {
288057e9
         uint32_t num;
         offset += 3;
         if (offset >= len)
             return CL_EMALFDB;
         num = (uint32_t)readNumber(buffer, &offset, len, &ok);
         if (!ok)
             return CL_EMALFDB;
         if (num != bcfunc->numInsts) {
             cli_errmsg("invalid number of dbg nodes, expected: %u, got: %u\n", bcfunc->numInsts, num);
             return CL_EMALFDB;
         }
         bcfunc->dbgnodes = cli_malloc(num * sizeof(*bcfunc->dbgnodes));
         if (!bcfunc->dbgnodes) {
             cli_errmsg("Unable to allocate memory for dbg nodes: %u\n", num * (uint32_t)sizeof(*bcfunc->dbgnodes));
             return CL_EMEM;
         }
         for (i = 0; (uint32_t)i < num; i++) {
             bcfunc->dbgnodes[i] = readNumber(buffer, &offset, len, &ok);
             if (!ok)
                 return CL_EMALFDB;
         }
53bd5bb1
     }
997a1efe
     if (offset != len) {
288057e9
         cli_errmsg("Trailing garbage in basicblock: %d extra bytes\n",
                    len - offset);
         return CL_EMALFDB;
997a1efe
     }
6922903a
     bcfunc->numBytes = 0;
f3b2dc9e
     bcfunc->insn_idx += BB->numInsts;
997a1efe
     return CL_SUCCESS;
 }
 
 enum parse_state {
288057e9
     PARSE_BC_TYPES = 0,
8cc286f5
     PARSE_BC_APIS,
ec077929
     PARSE_BC_GLOBALS,
dcee45cc
     PARSE_BC_LSIG,
53bd5bb1
     PARSE_MD_OPT_HEADER,
997a1efe
     PARSE_FUNC_HEADER,
fcbfb1c6
     PARSE_BB,
     PARSE_SKIP
997a1efe
 };
 
54402320
 struct sigperf_elem {
288057e9
     const char *bc_name;
54402320
     uint64_t usecs;
     unsigned long run_count;
     unsigned long match_count;
 };
 
288057e9
 static int sigelem_comp(const void *a, const void *b)
54402320
 {
     const struct sigperf_elem *ela = a;
     const struct sigperf_elem *elb = b;
288057e9
     return elb->usecs / elb->run_count - ela->usecs / ela->run_count;
54402320
 }
 
 void cli_sigperf_print()
 {
f9ad2811
     struct sigperf_elem stats[MAX_TRACKED_BC], *elem = stats;
54402320
     int i, elems = 0, max_name_len = 0, name_len;
 
5c2c7233
     if (!g_sigid || !g_sigevents) {
         cli_warnmsg("cli_sigperf_print: statistics requested but no bytecodes were loaded!\n");
         return;
     }
 
54402320
     memset(stats, 0, sizeof(stats));
f9ad2811
     for (i = 0; i < MAX_TRACKED_BC; i++) {
288057e9
         union ev_val val;
         uint32_t count;
         const char *name = cli_event_get_name(g_sigevents, i * BC_EVENTS_PER_SIG);
         cli_event_get(g_sigevents, i * BC_EVENTS_PER_SIG, &val, &count);
         if (!count) {
             if (name)
                 cli_dbgmsg("No event triggered for %s\n", name);
             continue;
         }
         if (name)
02840644
             name_len = (int)strlen(name);
288057e9
         else
             name_len = 0;
         if (name_len > max_name_len)
             max_name_len = name_len;
         elem->bc_name   = name ? name : "\"noname\"";
         elem->usecs     = val.v_int;
         elem->run_count = count;
         cli_event_get(g_sigevents, i * BC_EVENTS_PER_SIG + 1, &val, &count);
         elem->match_count = count;
         elem++;
         elems++;
54402320
     }
02840644
     if (max_name_len < (int)strlen("Bytecode name"))
         max_name_len = (int)strlen("Bytecode name");
54402320
 
38956aac
     cli_qsort(stats, elems, sizeof(struct sigperf_elem), sigelem_comp);
54402320
 
     elem = stats;
     /* name runs matches microsecs avg */
288057e9
     cli_infomsg(NULL, "%-*s %*s %*s %*s %*s\n", max_name_len, "Bytecode name",
                 8, "#runs", 8, "#matches", 12, "usecs total", 9, "usecs avg");
     cli_infomsg(NULL, "%-*s %*s %*s %*s %*s\n", max_name_len, "=============",
                 8, "=====", 8, "========", 12, "===========", 9, "=========");
54402320
     while (elem->run_count) {
288057e9
         cli_infomsg(NULL, "%-*s %*lu %*lu %*" PRIu64 " %*.2f\n", max_name_len, elem->bc_name,
                     8, elem->run_count, 8, elem->match_count,
                     12, elem->usecs, 9, (double)elem->usecs / elem->run_count);
         elem++;
54402320
     }
 }
 
 static void sigperf_events_init(struct cli_bc *bc)
 {
     int ret;
288057e9
     char *bc_name;
54402320
 
     if (!g_sigevents)
288057e9
         g_sigevents = cli_events_new(MAX_BC_SIGEVENT_ID);
54402320
 
     if (!g_sigevents) {
288057e9
         cli_errmsg("No memory for events table\n");
         return;
54402320
     }
 
     if (g_sigid > MAX_BC_SIGEVENT_ID - BC_EVENTS_PER_SIG - 1) {
f9ad2811
         cli_errmsg("sigperf_events_init: events table full. Increase MAX_TRACKED_BC\n");
288057e9
         return;
54402320
     }
 
16c4fcdd
     if (!(bc_name = bc->lsig)) {
288057e9
         if (!(bc_name = bc->hook_name)) {
             cli_dbgmsg("cli_event_define error for time event id %d\n", bc->sigtime_id);
             return;
         }
54402320
     }
 
16c4fcdd
     cli_dbgmsg("sigperf_events_init(): adding sig ids starting %u for %s\n", g_sigid, bc_name);
 
54402320
     /* register time event */
     bc->sigtime_id = g_sigid;
288057e9
     ret            = cli_event_define(g_sigevents, g_sigid++, bc_name, ev_time, multiple_sum);
54402320
     if (ret) {
288057e9
         cli_errmsg("sigperf_events_init: cli_event_define() error for time event id %d\n", bc->sigtime_id);
         bc->sigtime_id = MAX_BC_SIGEVENT_ID + 1;
         return;
54402320
     }
 
     /* register match count */
     bc->sigmatch_id = g_sigid;
288057e9
     ret             = cli_event_define(g_sigevents, g_sigid++, bc_name, ev_int, multiple_sum);
54402320
     if (ret) {
288057e9
         cli_errmsg("sigperf_events_init: cli_event_define() error for matches event id %d\n", bc->sigmatch_id);
         bc->sigmatch_id = MAX_BC_SIGEVENT_ID + 1;
         return;
54402320
     }
 }
 
 void cli_sigperf_events_destroy()
 {
     cli_events_free(g_sigevents);
 }
 
 int cli_bytecode_load(struct cli_bc *bc, FILE *f, struct cli_dbio *dbio, int trust, int sigperf)
997a1efe
 {
288057e9
     unsigned row = 0, current_func = 0, bb = 0;
482e97db
     char *buffer;
288057e9
     unsigned linelength = 0;
482e97db
     char firstbuf[FILEBUFF];
     enum parse_state state;
288057e9
     int rc, end = 0;
997a1efe
 
2d45ef06
     memset(bc, 0, sizeof(*bc));
606fd6d0
     cli_dbgmsg("Loading %s bytecode\n", trust ? "trusted" : "untrusted");
be43f951
     bc->trusted = trust;
997a1efe
     if (!f && !dbio) {
288057e9
         cli_errmsg("Unable to load bytecode (null file)\n");
         return CL_ENULLARG;
997a1efe
     }
482e97db
     if (!cli_dbgets(firstbuf, FILEBUFF, f, dbio)) {
288057e9
         cli_errmsg("Unable to load bytecode (empty file)\n");
         return CL_EMALFDB;
482e97db
     }
c1aff5ec
     cli_chomp(firstbuf);
288057e9
     rc    = parseHeader(bc, (unsigned char *)firstbuf, &linelength);
fcbfb1c6
     state = PARSE_BC_LSIG;
482e97db
     if (rc == CL_BREAK) {
288057e9
         const char *len = strchr(firstbuf, ':');
         bc->state       = bc_skip;
         if (!linelength) {
             linelength = len ? atoi(len + 1) : 4096;
         }
         if (linelength < 4096)
             linelength = 4096;
         cli_dbgmsg("line: %d\n", linelength);
         state = PARSE_SKIP;
         rc    = CL_SUCCESS;
482e97db
     }
     if (rc != CL_SUCCESS) {
288057e9
         cli_errmsg("Error at bytecode line %u\n", row);
         return rc;
482e97db
     }
     buffer = cli_malloc(linelength);
     if (!buffer) {
288057e9
         cli_errmsg("Out of memory allocating line of length %u\n", linelength);
         return CL_EMEM;
482e97db
     }
9bdaf35d
     while (cli_dbgets(buffer, linelength, f, dbio) && !end) {
288057e9
         cli_chomp(buffer);
         row++;
         switch (state) {
             case PARSE_BC_LSIG:
                 rc = parseLSig(bc, buffer);
16c4fcdd
 #if 0
 DEAD CODE
 		if (rc == CL_BREAK) /* skip */ { //FIXME: parseLSig always returns CL_SUCCESS
dcee45cc
 		    bc->state = bc_skip;
fcbfb1c6
 		    state = PARSE_SKIP;
 		    continue;
dcee45cc
 		}
16c4fcdd
 		if (rc != CL_SUCCESS) { //FIXME: parseLSig always returns CL_SUCCESS
dcee45cc
 		    cli_errmsg("Error at bytecode line %u\n", row);
482e97db
 		    free(buffer);
dcee45cc
 		    return rc;
 		}
16c4fcdd
 #endif
288057e9
                 state = PARSE_BC_TYPES;
                 break;
             case PARSE_BC_TYPES:
                 rc = parseTypes(bc, (unsigned char *)buffer);
                 if (rc != CL_SUCCESS) {
                     cli_errmsg("Error at bytecode line %u\n", row);
                     free(buffer);
                     return rc;
                 }
                 state = PARSE_BC_APIS;
                 break;
             case PARSE_BC_APIS:
                 rc = parseApis(bc, (unsigned char *)buffer);
                 if (rc == CL_BREAK) /* skip */ {
                     bc->state = bc_skip;
                     state     = PARSE_SKIP;
                     continue;
                 }
                 if (rc != CL_SUCCESS) {
                     cli_errmsg("Error at bytecode line %u\n", row);
                     free(buffer);
                     return rc;
                 }
                 state = PARSE_BC_GLOBALS;
                 break;
             case PARSE_BC_GLOBALS:
                 rc = parseGlobals(bc, (unsigned char *)buffer);
                 if (rc == CL_BREAK) /* skip */ {
                     bc->state = bc_skip;
                     state     = PARSE_SKIP;
                     continue;
                 }
                 if (rc != CL_SUCCESS) {
                     cli_errmsg("Error at bytecode line %u\n", row);
                     free(buffer);
                     return rc;
                 }
                 state = PARSE_MD_OPT_HEADER;
                 break;
             case PARSE_MD_OPT_HEADER:
                 if (buffer[0] == 'D') {
                     rc = parseMD(bc, (unsigned char *)buffer);
                     if (rc != CL_SUCCESS) {
                         cli_errmsg("Error at bytecode line %u\n", row);
                         free(buffer);
                         return rc;
                     }
                     break;
                 }
                 /* fall-through */
             case PARSE_FUNC_HEADER:
9bdaf35d
                 if (*buffer == 'S') {
288057e9
                     end = 1;
                     break;
                 }
                 rc = parseFunctionHeader(bc, current_func, (unsigned char *)buffer);
                 if (rc != CL_SUCCESS) {
                     cli_errmsg("Error at bytecode line %u\n", row);
                     free(buffer);
                     return rc;
                 }
                 bb    = 0;
                 state = PARSE_BB;
                 break;
             case PARSE_BB:
                 rc = parseBB(bc, current_func, bb++, (unsigned char *)buffer);
                 if (rc != CL_SUCCESS) {
                     cli_errmsg("Error at bytecode line %u\n", row);
                     free(buffer);
                     return rc;
                 }
                 if (bb >= bc->funcs[current_func].numBB) {
                     if (bc->funcs[current_func].insn_idx != bc->funcs[current_func].numInsts) {
                         cli_errmsg("Parsed different number of instructions than declared: %u != %u\n",
                                    bc->funcs[current_func].insn_idx, bc->funcs[current_func].numInsts);
                         free(buffer);
                         return CL_EMALFDB;
                     }
                     cli_dbgmsg("Parsed %u BBs, %u instructions\n",
                                bb, bc->funcs[current_func].numInsts);
                     state = PARSE_FUNC_HEADER;
                     current_func++;
                 }
                 break;
             case PARSE_SKIP:
                 /* stop at S (source code), readdb.c knows how to skip this one
fcbfb1c6
 		 * */
288057e9
                 if (buffer[0] == 'S')
                     end = 1;
                 /* noop parse, but we need to use dbgets with dynamic buffer,
fcbfb1c6
 		 * otherwise we get 'Line too long for provided buffer' */
288057e9
                 break;
         }
997a1efe
     }
482e97db
     free(buffer);
997a1efe
     cli_dbgmsg("Parsed %d functions\n", current_func);
54402320
     if (sigperf)
288057e9
         sigperf_events_init(bc);
0796f1ca
     if (current_func != bc->num_func && bc->state != bc_skip) {
288057e9
         cli_errmsg("Loaded less functions than declared: %u vs. %u\n",
                    current_func, bc->num_func);
         return CL_EMALFDB;
957bf651
     }
997a1efe
     return CL_SUCCESS;
 }
 
8a8dbd59
 static struct {
     enum bc_events id;
     const char *name;
     enum ev_type type;
     enum multiple_handling multiple;
 } bc_events[] = {
     {BCEV_VIRUSNAME, "virusname", ev_string, multiple_last},
     {BCEV_EXEC_RETURNVALUE, "returnvalue", ev_int, multiple_last},
     {BCEV_WRITE, "bcapi_write", ev_data_fast, multiple_sum},
     {BCEV_OFFSET, "read offset", ev_int, multiple_sum},
     {BCEV_READ, "read data", ev_data_fast, multiple_sum},
     //{BCEV_READ, "read data", ev_data, multiple_concat},
     {BCEV_DBG_STR, "debug message", ev_data_fast, multiple_sum},
     {BCEV_DBG_INT, "debug int", ev_int, multiple_sum},
     {BCEV_MEM_1, "memmem 1", ev_data_fast, multiple_sum},
     {BCEV_MEM_2, "memmem 2", ev_data_fast, multiple_sum},
     {BCEV_FIND, "find", ev_data_fast, multiple_sum},
     {BCEV_EXTRACTED, "extracted files", ev_int, multiple_sum},
     {BCEV_READ_ERR, "read errors", ev_int, multiple_sum},
     {BCEV_DISASM_FAIL, "disasm fails", ev_int, multiple_sum},
288057e9
     {BCEV_EXEC_TIME, "bytecode execute", ev_time, multiple_sum}};
8a8dbd59
 
 static int register_events(cli_events_t *ev)
 {
d18d7221
     size_t i;
288057e9
     for (i = 0; i < sizeof(bc_events) / sizeof(bc_events[0]); i++) {
         if (cli_event_define(ev, bc_events[i].id, bc_events[i].name, bc_events[i].type,
                              bc_events[i].multiple) == -1)
             return -1;
8a8dbd59
     }
     return 0;
 }
 
85a25497
 int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, struct cli_bc_ctx *ctx)
997a1efe
 {
d7531f2a
     int ret = CL_SUCCESS;
7a9022c9
     struct cli_bc_inst inst;
     struct cli_bc_func func;
8a8dbd59
     cli_events_t *jit_ev = NULL, *interp_ev = NULL;
 
     int test_mode = 0;
288057e9
     cli_ctx *cctx = (cli_ctx *)ctx->ctx;
8a8dbd59
 
e0c4fd85
     if (!ctx || !ctx->bc || !ctx->func)
288057e9
         return CL_ENULLARG;
e0c4fd85
     if (ctx->numParams && (!ctx->values || !ctx->operands))
288057e9
         return CL_ENULLARG;
6a049897
 
     if (cctx && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
288057e9
         test_mode = 1;
6a049897
 
6922903a
     if (bc->state == bc_loaded) {
288057e9
         cli_errmsg("bytecode has to be prepared either for interpreter or JIT!\n");
         return CL_EARG;
7a9022c9
     }
d5ffa2ac
     if (bc->state == bc_disabled) {
288057e9
         cli_dbgmsg("bytecode triggered but running bytecodes is disabled\n");
         return CL_SUCCESS;
d5ffa2ac
     }
63feb6cd
     if (cctx)
6a049897
         cli_event_time_start(cctx->perf, PERFT_BYTECODE);
d5ffa2ac
     ctx->env = &bcs->env;
74f5816c
     context_safe(ctx);
8a8dbd59
     if (test_mode) {
288057e9
         jit_ev    = cli_events_new(BCEV_LASTEVENT);
         interp_ev = cli_events_new(BCEV_LASTEVENT);
         if (!jit_ev || !interp_ev) {
             cli_events_free(jit_ev);
             cli_events_free(interp_ev);
             return CL_EMEM;
         }
         if (register_events(jit_ev) == -1 ||
             register_events(interp_ev) == -1) {
             cli_events_free(jit_ev);
             cli_events_free(interp_ev);
             return CL_EBYTECODE_TESTFAIL;
         }
8a8dbd59
     }
54402320
     cli_event_time_start(g_sigevents, bc->sigtime_id);
8a8dbd59
     if (bc->state == bc_interp || test_mode) {
288057e9
         ctx->bc_events = interp_ev;
         memset(&func, 0, sizeof(func));
         func.numInsts     = 1;
         func.numValues    = 1;
         func.numConstants = 0;
         func.numBytes     = ctx->bytes;
         memset(ctx->values + ctx->bytes - 8, 0, 8);
 
         inst.opcode        = OP_BC_CALL_DIRECT;
         inst.interp_op     = OP_BC_CALL_DIRECT * 5;
         inst.dest          = func.numArgs;
         inst.type          = 0;
         inst.u.ops.numOps  = ctx->numParams;
         inst.u.ops.funcid  = ctx->funcid;
         inst.u.ops.ops     = ctx->operands;
         inst.u.ops.opsizes = ctx->opsizes;
         cli_dbgmsg("Bytecode %u: executing in interpreter mode\n", bc->id);
 
         ctx->on_jit = 0;
 
         cli_event_time_start(interp_ev, BCEV_EXEC_TIME);
         ret = cli_vm_execute(ctx->bc, ctx, &func, &inst);
         cli_event_time_stop(interp_ev, BCEV_EXEC_TIME);
 
         cli_event_int(interp_ev, BCEV_EXEC_RETURNVALUE, ret);
         cli_event_string(interp_ev, BCEV_VIRUSNAME, ctx->virname);
 
         /* need to be called here to catch any extracted but not yet scanned files
8a8dbd59
 	*/
288057e9
         if (ctx->outfd && (ret != CL_VIRUS || cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES))
             cli_bcapi_extract_new(ctx, -1);
8a8dbd59
     }
     if (bc->state == bc_jit || test_mode) {
288057e9
         if (test_mode) {
             ctx->off = 0;
         }
         ctx->bc_events = jit_ev;
         cli_dbgmsg("Bytecode %u: executing in JIT mode\n", bc->id);
8a8dbd59
 
288057e9
         ctx->on_jit = 1;
         cli_event_time_start(jit_ev, BCEV_EXEC_TIME);
         ret = cli_vm_execute_jit(bcs, ctx, &bc->funcs[ctx->funcid]);
         cli_event_time_stop(jit_ev, BCEV_EXEC_TIME);
8a8dbd59
 
288057e9
         cli_event_int(jit_ev, BCEV_EXEC_RETURNVALUE, ret);
         cli_event_string(jit_ev, BCEV_VIRUSNAME, ctx->virname);
8a8dbd59
 
288057e9
         /* need to be called here to catch any extracted but not yet scanned files
8a8dbd59
 	*/
288057e9
         if (ctx->outfd && (ret != CL_VIRUS || cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES))
             cli_bcapi_extract_new(ctx, -1);
8a8dbd59
     }
54402320
     cli_event_time_stop(g_sigevents, bc->sigtime_id);
     if (ctx->virname)
288057e9
         cli_event_count(g_sigevents, bc->sigmatch_id);
8a8dbd59
 
     if (test_mode) {
288057e9
         unsigned interp_errors = cli_event_errors(interp_ev);
         unsigned jit_errors    = cli_event_errors(jit_ev);
         unsigned interp_warns = 0, jit_warns = 0;
         int ok = 1;
         enum bc_events evid;
 
         if (interp_errors || jit_errors) {
             cli_infomsg(cctx, "bytecode %d encountered %u JIT and %u interpreter errors\n",
                         bc->id, interp_errors, jit_errors);
             ok = 0;
         }
         if (!ctx->no_diff && cli_event_diff_all(interp_ev, jit_ev, NULL)) {
             cli_infomsg(cctx, "bytecode %d execution different with JIT and interpreter, see --debug for details\n",
                         bc->id);
             ok = 0;
         }
         for (evid = BCEV_API_WARN_BEGIN + 1; evid < BCEV_API_WARN_END; evid++) {
             union ev_val v;
             uint32_t count = 0;
             cli_event_get(interp_ev, evid, &v, &count);
             interp_warns += count;
             count = 0;
             cli_event_get(jit_ev, evid, &v, &count);
             jit_warns += count;
         }
         if (interp_warns || jit_warns) {
             cli_infomsg(cctx, "bytecode %d encountered %u JIT and %u interpreter warnings\n",
                         bc->id, interp_warns, jit_warns);
             ok = 0;
         }
         /*cli_event_debug(jit_ev, BCEV_EXEC_TIME);
54402320
         cli_event_debug(interp_ev, BCEV_EXEC_TIME);
 	cli_event_debug(g_sigevents, bc->sigtime_id);*/
288057e9
         if (!ok) {
             cli_events_free(jit_ev);
             cli_events_free(interp_ev);
             return CL_EBYTECODE_TESTFAIL;
         }
85a25497
     }
110731e7
     cli_events_free(jit_ev);
     cli_events_free(interp_ev);
63feb6cd
     if (cctx)
6a049897
         cli_event_time_stop(cctx->perf, PERFT_BYTECODE);
f73212dc
     return ret;
f3b2dc9e
 }
 
 uint64_t cli_bytecode_context_getresult_int(struct cli_bc_ctx *ctx)
 {
288057e9
     return *(uint32_t *)ctx->values; /*XXX*/
997a1efe
 }
 
 void cli_bytecode_destroy(struct cli_bc *bc)
 {
     unsigned i, j, k;
a35cfe51
     free(bc->metadata.compiler);
     free(bc->metadata.sigmaker);
997a1efe
 
f6e8bb80
     if (bc->funcs) {
288057e9
         for (i = 0; i < bc->num_func; i++) {
             struct cli_bc_func *f = &bc->funcs[i];
             if (!f)
                 continue;
             free(f->types);
 
             for (j = 0; j < f->numBB; j++) {
                 struct cli_bc_bb *BB = &f->BB[j];
                 for (k = 0; k < BB->numInsts; k++) {
                     struct cli_bc_inst *ii = &BB->insts[k];
                     if (operand_counts[ii->opcode] > 3 ||
                         ii->opcode == OP_BC_CALL_DIRECT || ii->opcode == OP_BC_CALL_API) {
                         free(ii->u.ops.ops);
                         free(ii->u.ops.opsizes);
                     }
                 }
             }
             free(f->BB);
             free(f->allinsts);
             free(f->constants);
         }
         free(bc->funcs);
f6e8bb80
     }
     if (bc->types) {
288057e9
         for (i = NUM_STATIC_TYPES; i < bc->num_types; i++) {
             if (bc->types[i].containedTypes)
                 free(bc->types[i].containedTypes);
         }
         free(bc->types);
f6e8bb80
     }
 
     if (bc->globals) {
288057e9
         for (i = 0; i < bc->num_globals; i++) {
             free(bc->globals[i]);
         }
         free(bc->globals);
f6e8bb80
     }
     if (bc->dbgnodes) {
288057e9
         for (i = 0; i < bc->dbgnode_cnt; i++) {
             for (j = 0; j < bc->dbgnodes[i].numelements; j++) {
                 struct cli_bc_dbgnode_element *el = &bc->dbgnodes[i].elements[j];
                 if (el && el->string)
                     free(el->string);
             }
         }
         free(bc->dbgnodes);
53bd5bb1
     }
ec077929
     free(bc->globaltys);
cf0cd429
     if (bc->uses_apis)
288057e9
         cli_bitset_free(bc->uses_apis);
dcee45cc
     free(bc->lsig);
16c4fcdd
     free(bc->hook_name);
9cbece5c
     free(bc->globalBytes);
5475ec2a
     memset(bc, 0, sizeof(*bc));
997a1efe
 }
 
288057e9
 #define MAP(val)                                                                                                            \
     do {                                                                                                                    \
         operand_t o = val;                                                                                                  \
         if (o & 0x80000000) {                                                                                               \
             o &= 0x7fffffff;                                                                                                \
             if (o > bc->num_globals) {                                                                                      \
                 cli_errmsg("bytecode: global out of range: %u > %u, for instruction %u in function %u\n",                   \
                            o, (unsigned)bc->num_globals, j, i);                                                             \
                 free(map);                                                                                                  \
                 free(gmap);                                                                                                 \
                 return CL_EBYTECODE;                                                                                        \
             }                                                                                                               \
             val = 0x80000000 | gmap[o];                                                                                     \
             break;                                                                                                          \
         }                                                                                                                   \
         if (o >= totValues) {                                                                                               \
             cli_errmsg("bytecode: operand out of range: %u > %u, for instruction %u in function %u\n", o, totValues, j, i); \
             free(map);                                                                                                      \
             free(gmap);                                                                                                     \
             return CL_EBYTECODE;                                                                                            \
         }                                                                                                                   \
         val = map[o];                                                                                                       \
     } while (0)
 
 #define MAPPTR(val)                                                   \
     {                                                                 \
         if ((val < bcfunc->numValues) && bcfunc->types[val] & 0x8000) \
             val = map[val] | 0x40000000;                              \
         else                                                          \
             MAP(val);                                                 \
     }
0d9b99f4
 
52d0d8bc
 static inline int64_t ptr_compose(int32_t id, uint32_t offset)
 {
     uint64_t i = id;
     return (i << 32) | offset;
 }
 
44e13431
 static inline int get_geptypesize(const struct cli_bc *bc, uint16_t tid)
 {
288057e9
     const struct cli_bc_type *ty;
     if (tid >= bc->num_types + 65) {
         cli_errmsg("bytecode: typeid out of range %u >= %u\n", tid, bc->num_types);
         return -1;
     }
     if (tid <= 64) {
         cli_errmsg("bytecode: invalid type for gep (%u)\n", tid);
         return -1;
     }
     ty = &bc->types[tid - 65];
     if (ty->kind != DPointerType) {
         cli_errmsg("bytecode: invalid gep type, must be pointer: %u\n", tid);
         return -1;
     }
     return typesize(bc, ty->containedTypes[0]);
44e13431
 }
 
b1018ea5
 static int calc_gepz(struct cli_bc *bc, struct cli_bc_func *func, uint16_t tid, operand_t op)
 {
     unsigned off = 0, i;
c09f9b29
     uint32_t *gepoff;
b1018ea5
     const struct cli_bc_type *ty;
     if (tid >= bc->num_types + 65) {
288057e9
         cli_errmsg("bytecode: typeid out of range %u >= %u\n", tid, bc->num_types);
         return -1;
b1018ea5
     }
     if (tid <= 65) {
288057e9
         cli_errmsg("bytecode: invalid type for gep (%u)\n", tid);
         return -1;
b1018ea5
     }
     ty = &bc->types[tid - 65];
     if (ty->kind != DPointerType || ty->containedTypes[0] < 65) {
288057e9
         cli_errmsg("bytecode: invalid gep type, must be pointer to nonint: %u\n", tid);
         return -1;
b1018ea5
     }
     ty = &bc->types[ty->containedTypes[0] - 65];
     if (ty->kind != DStructType && ty->kind != DPackedStructType)
288057e9
         return 0;
     gepoff = (uint32_t *)&func->constants[op - func->numValues];
b1018ea5
     if (*gepoff >= ty->numElements) {
288057e9
         cli_errmsg("bytecode: gep offset out of range: %d >= %d\n", (uint32_t)*gepoff, ty->numElements);
         return -1;
b1018ea5
     }
288057e9
     for (i = 0; i < *gepoff; i++) {
         off += typesize(bc, ty->containedTypes[i]);
b1018ea5
     }
     *gepoff = off;
     return 1;
 }
 
6922903a
 static int cli_bytecode_prepare_interpreter(struct cli_bc *bc)
 {
dc200c6b
     unsigned i, j, k;
52d0d8bc
     uint64_t *gmap;
288057e9
     unsigned bcglobalid = cli_apicall_maxglobal - _FIRST_GLOBAL + 2;
     int ret             = CL_SUCCESS;
     bc->numGlobalBytes  = 0;
     gmap                = cli_malloc(bc->num_globals * sizeof(*gmap));
241e7eb1
     if (!gmap) {
288057e9
         cli_errmsg("interpreter: Unable to allocate memory for global map: %zu\n", bc->num_globals * sizeof(*gmap));
241e7eb1
         return CL_EMEM;
     }
288057e9
     for (j = 0; j < bc->num_globals; j++) {
         uint16_t ty    = bc->globaltys[j];
         unsigned align = typealign(bc, ty);
         assert(align);
         bc->numGlobalBytes = (bc->numGlobalBytes + align - 1) & (~(align - 1));
         gmap[j]            = bc->numGlobalBytes;
         bc->numGlobalBytes += typesize(bc, ty);
20327f93
     }
c6c464e5
     if (bc->numGlobalBytes) {
288057e9
         bc->globalBytes = cli_calloc(1, bc->numGlobalBytes);
         if (!bc->globalBytes) {
             cli_errmsg("interpreter: Unable to allocate memory for globalBytes: %u\n", bc->numGlobalBytes);
             free(gmap);
             return CL_EMEM;
         }
c6c464e5
     } else
288057e9
         bc->globalBytes = NULL;
 
     for (j = 0; j < bc->num_globals; j++) {
         struct cli_bc_type *ty;
         if (bc->globaltys[j] < 65)
             continue;
         ty = &bc->types[bc->globaltys[j] - 65];
         switch (ty->kind) {
             case DPointerType: {
                 uint64_t ptr;
                 if (bc->globals[j][1] >= _FIRST_GLOBAL) {
                     ptr = ptr_compose(bc->globals[j][1] - _FIRST_GLOBAL + 1,
                                       bc->globals[j][0]);
                 } else {
                     if (bc->globals[j][1] > bc->num_globals)
                         continue;
                     ptr = ptr_compose(bcglobalid,
                                       gmap[bc->globals[j][1]] + bc->globals[j][0]);
                 }
                 *(uint64_t *)&bc->globalBytes[gmap[j]] = ptr;
                 break;
             }
             case DArrayType: {
                 unsigned elsize, i, off = gmap[j];
                 /* TODO: support other than ints in arrays */
                 elsize = typesize(bc, ty->containedTypes[0]);
                 switch (elsize) {
                     case 1:
                         for (i = 0; i < ty->numElements; i++)
                             bc->globalBytes[off + i] = bc->globals[j][i];
                         break;
                     case 2:
                         for (i = 0; i < ty->numElements; i++)
                             *(uint16_t *)&bc->globalBytes[off + i * 2] = bc->globals[j][i];
                         break;
                     case 4:
                         for (i = 0; i < ty->numElements; i++)
                             *(uint32_t *)&bc->globalBytes[off + i * 4] = bc->globals[j][i];
                         break;
                     case 8:
                         for (i = 0; i < ty->numElements; i++)
                             *(uint64_t *)&bc->globalBytes[off + i * 8] = bc->globals[j][i];
                         break;
                     default:
                         cli_dbgmsg("interpreter: unsupported elsize: %u\n", elsize);
                 }
6a049897
                 break;
288057e9
             }
             default:
                 /*TODO*/
                 if (!bc->globals[j][1])
                     continue; /* null */
                 break;
         }
     }
 
     for (i = 0; i < bc->num_func && ret == CL_SUCCESS; i++) {
         struct cli_bc_func *bcfunc = &bc->funcs[i];
         unsigned totValues         = bcfunc->numValues + bcfunc->numConstants + bc->num_globals;
         unsigned *map              = cli_malloc(sizeof(*map) * (size_t)totValues);
         if (!map) {
             cli_errmsg("interpreter: Unable to allocate memory for map: %zu\n", sizeof(*map) * (size_t)totValues);
             free(gmap);
             return CL_EMEM;
         }
         bcfunc->numBytes = 0;
         for (j = 0; j < bcfunc->numValues; j++) {
             uint16_t ty = bcfunc->types[j];
             unsigned align;
             align = typealign(bc, ty);
             assert(!ty || typesize(bc, ty));
             assert(align);
             bcfunc->numBytes = (bcfunc->numBytes + align - 1) & (~(align - 1));
             map[j]           = bcfunc->numBytes;
             /* printf("%d -> %d, %u\n", j, map[j], typesize(bc, ty)); */
             bcfunc->numBytes += typesize(bc, ty);
             /* TODO: don't allow size 0, it is always a bug! */
         }
         bcfunc->numBytes = (bcfunc->numBytes + 7) & ~7;
         for (j = 0; j < bcfunc->numConstants; j++) {
             map[bcfunc->numValues + j] = bcfunc->numBytes;
             bcfunc->numBytes += 8;
         }
         for (j = 0; j < bcfunc->numInsts && ret == CL_SUCCESS; j++) {
             struct cli_bc_inst *inst = &bcfunc->allinsts[j];
             inst->dest               = map[inst->dest];
             switch (inst->opcode) {
                 case OP_BC_ADD:
                 case OP_BC_SUB:
                 case OP_BC_MUL:
                 case OP_BC_UDIV:
                 case OP_BC_SDIV:
                 case OP_BC_UREM:
                 case OP_BC_SREM:
                 case OP_BC_SHL:
                 case OP_BC_LSHR:
                 case OP_BC_ASHR:
                 case OP_BC_AND:
                 case OP_BC_OR:
                 case OP_BC_XOR:
                 case OP_BC_ICMP_EQ:
                 case OP_BC_ICMP_NE:
                 case OP_BC_ICMP_UGT:
                 case OP_BC_ICMP_UGE:
                 case OP_BC_ICMP_ULT:
                 case OP_BC_ICMP_ULE:
                 case OP_BC_ICMP_SGT:
                 case OP_BC_ICMP_SGE:
                 case OP_BC_ICMP_SLT:
                 case OP_BC_ICMP_SLE:
                 case OP_BC_COPY:
                 case OP_BC_STORE:
                     MAP(inst->u.binop[0]);
                     MAP(inst->u.binop[1]);
                     break;
                 case OP_BC_SEXT:
                 case OP_BC_ZEXT:
                 case OP_BC_TRUNC:
                     MAP(inst->u.cast.source);
                     break;
                 case OP_BC_BRANCH:
                     MAP(inst->u.branch.condition);
                     break;
                 case OP_BC_JMP:
                     break;
                 case OP_BC_RET:
                     MAP(inst->u.unaryop);
                     break;
                 case OP_BC_SELECT:
                     MAP(inst->u.three[0]);
                     MAP(inst->u.three[1]);
                     MAP(inst->u.three[2]);
                     break;
                 case OP_BC_CALL_API: /* fall-through */
                 case OP_BC_CALL_DIRECT: {
                     struct cli_bc_func *target = NULL;
                     if (inst->opcode == OP_BC_CALL_DIRECT) {
                         target = &bc->funcs[inst->u.ops.funcid];
                         if (inst->u.ops.funcid > bc->num_func) {
                             cli_errmsg("bytecode: called function out of range: %u > %u\n", inst->u.ops.funcid, bc->num_func);
                             ret = CL_EBYTECODE;
                         } else if (inst->u.ops.numOps != target->numArgs) {
                             cli_errmsg("bytecode: call operands don't match function prototype\n");
                             ret = CL_EBYTECODE;
                         }
                     } else {
                         /* APIs have at most 2 parameters always */
                         if (inst->u.ops.numOps > 5) {
                             cli_errmsg("bytecode: call operands don't match function prototype\n");
                             ret = CL_EBYTECODE;
                         }
                     }
                     if (ret != CL_SUCCESS)
                         break;
                     if (inst->u.ops.numOps > 0) {
                         inst->u.ops.opsizes = cli_malloc(sizeof(*inst->u.ops.opsizes) * inst->u.ops.numOps);
                         if (!inst->u.ops.opsizes) {
                             cli_errmsg("Out of memory when allocating operand sizes\n");
                             ret = CL_EMEM;
                             break;
                         }
                     } else {
                         inst->u.ops.opsizes = NULL;
                         break;
                     }
                     for (k = 0; k < inst->u.ops.numOps; k++) {
                         MAPPTR(inst->u.ops.ops[k]);
                         if (inst->opcode == OP_BC_CALL_DIRECT)
                             inst->u.ops.opsizes[k] = typesize(bc, target->types[k]);
                         else
                             inst->u.ops.opsizes[k] = 32; /*XXX*/
                     }
                     break;
                 }
                 case OP_BC_LOAD:
                     MAPPTR(inst->u.unaryop);
                     break;
                 case OP_BC_GEP1:
                     if (inst->u.three[1] & 0x80000000 ||
                         bcfunc->types[inst->u.binop[1]] & 0x8000) {
                         cli_errmsg("bytecode: gep1 of alloca is not allowed\n");
                         ret = CL_EBYTECODE;
                     }
                     if (ret != CL_SUCCESS)
                         break;
                     MAP(inst->u.three[1]);
                     MAP(inst->u.three[2]);
44e13431
                     inst->u.three[0] = get_geptypesize(bc, inst->u.three[0]);
cd94be7a
                     if ((int)(inst->u.three[0]) == -1)
288057e9
                         ret = CL_EBYTECODE;
                     break;
                 case OP_BC_GEPZ:
                     /*three[0] is the type*/
                     if (inst->u.three[1] & 0x80000000 ||
                         bcfunc->types[inst->u.three[1]] & 0x8000)
                         inst->interp_op = 5 * (inst->interp_op / 5);
                     else
                         inst->interp_op = 5 * (inst->interp_op / 5) + 3;
                     MAP(inst->u.three[1]);
                     if (calc_gepz(bc, bcfunc, inst->u.three[0], inst->u.three[2]) == -1)
                         ret = CL_EBYTECODE;
                     if (ret == CL_SUCCESS)
                         MAP(inst->u.three[2]);
44e13431
                     break;
288057e9
                     /*		case OP_BC_GEPN:
93e5d4db
 		    *TODO
b1018ea5
 		    break;*/
288057e9
                 case OP_BC_MEMSET:
                 case OP_BC_MEMCPY:
                 case OP_BC_MEMMOVE:
                 case OP_BC_MEMCMP:
                     MAPPTR(inst->u.three[0]);
                     MAPPTR(inst->u.three[1]);
                     MAP(inst->u.three[2]);
                     break;
                 case OP_BC_RET_VOID:
                 case OP_BC_ISBIGENDIAN:
                 case OP_BC_ABORT:
                     /* no operands */
                     break;
                 case OP_BC_BSWAP16:
                 case OP_BC_BSWAP32:
                 case OP_BC_BSWAP64:
                     MAP(inst->u.unaryop);
                     break;
                 case OP_BC_PTRDIFF32:
                     MAPPTR(inst->u.binop[0]);
                     MAPPTR(inst->u.binop[1]);
                     break;
                 case OP_BC_PTRTOINT64:
                     MAPPTR(inst->u.unaryop);
                     break;
                 default:
                     cli_warnmsg("Bytecode: unhandled opcode: %d\n", inst->opcode);
                     ret = CL_EBYTECODE;
             }
         }
         if (map)
             free(map);
6922903a
     }
20327f93
     free(gmap);
6922903a
     bc->state = bc_interp;
6a049897
     return ret;
6922903a
 }
 
a5a19f45
 static int add_selfcheck(struct cli_all_bc *bcs)
 {
     struct cli_bc_func *func;
     struct cli_bc_inst *inst;
     struct cli_bc *bc;
 
288057e9
     bcs->all_bcs = cli_realloc2(bcs->all_bcs, sizeof(*bcs->all_bcs) * (bcs->count + 1));
a5a19f45
     if (!bcs->all_bcs) {
288057e9
         cli_errmsg("cli_loadcbc: Can't allocate memory for bytecode entry\n");
         return CL_EMEM;
a5a19f45
     }
     bc = &bcs->all_bcs[bcs->count++];
     memset(bc, 0, sizeof(*bc));
 
288057e9
     bc->trusted     = 1;
a5a19f45
     bc->num_globals = 1;
288057e9
     bc->globals     = cli_calloc(1, sizeof(*bc->globals));
a5a19f45
     if (!bc->globals) {
288057e9
         cli_errmsg("Failed to allocate memory for globals\n");
         return CL_EMEM;
a5a19f45
     }
     bc->globals[0] = cli_calloc(1, sizeof(*bc->globals[0]));
     if (!bc->globals[0]) {
288057e9
         cli_errmsg("Failed to allocate memory for globals\n");
         return CL_EMEM;
a5a19f45
     }
     bc->globaltys = cli_calloc(1, sizeof(*bc->globaltys));
     if (!bc->globaltys) {
288057e9
         cli_errmsg("Failed to allocate memory for globaltypes\n");
         return CL_EMEM;
a5a19f45
     }
     bc->globaltys[0] = 32;
288057e9
     *bc->globals[0]  = 0;
     bc->id           = ~0;
     bc->kind         = 0;
     bc->num_types    = 5;
     bc->num_func     = 1;
     bc->funcs        = cli_calloc(1, sizeof(*bc->funcs));
a5a19f45
     if (!bc->funcs) {
288057e9
         cli_errmsg("Failed to allocate memory for func\n");
         return CL_EMEM;
a5a19f45
     }
288057e9
     func               = bc->funcs;
     func->numInsts     = 2;
     func->numLocals    = 1;
     func->numValues    = 1;
a5a19f45
     func->numConstants = 1;
288057e9
     func->numBB        = 1;
     func->returnType   = 32;
     func->types        = cli_calloc(1, sizeof(*func->types));
a5a19f45
     if (!func->types) {
288057e9
         cli_errmsg("Failed to allocate memory for types\n");
         return CL_EMEM;
a5a19f45
     }
     func->types[0] = 32;
288057e9
     func->BB       = cli_calloc(1, sizeof(*func->BB));
a5a19f45
     if (!func->BB) {
288057e9
         cli_errmsg("Failed to allocate memory for BB\n");
         return CL_EMEM;
a5a19f45
     }
     func->allinsts = cli_calloc(2, sizeof(*func->allinsts));
     if (!func->allinsts) {
288057e9
         cli_errmsg("Failed to allocate memory for insts\n");
         return CL_EMEM;
a5a19f45
     }
     func->BB->numInsts = 2;
288057e9
     func->BB->insts    = func->allinsts;
     func->constants    = cli_calloc(1, sizeof(*func->constants));
a5a19f45
     if (!func->constants) {
288057e9
         cli_errmsg("Failed to allocate memory for constants\n");
         return CL_EMEM;
a5a19f45
     }
     func->constants[0] = 0xf00d;
288057e9
     inst               = func->allinsts;
a5a19f45
 
288057e9
     inst->opcode        = OP_BC_CALL_API;
     inst->u.ops.numOps  = 1;
a5a19f45
     inst->u.ops.opsizes = NULL;
288057e9
     inst->u.ops.ops     = cli_calloc(1, sizeof(*inst->u.ops.ops));
a5a19f45
     if (!inst->u.ops.ops) {
288057e9
         cli_errmsg("Failed to allocate memory for instructions\n");
         return CL_EMEM;
a5a19f45
     }
     inst->u.ops.ops[0] = 1;
     inst->u.ops.funcid = 18; /* test2 */
288057e9
     inst->dest         = 0;
     inst->type         = 32;
     inst->interp_op    = inst->opcode * 5 + 3;
a5a19f45
 
288057e9
     inst            = &func->allinsts[1];
     inst->opcode    = OP_BC_RET;
     inst->type      = 32;
a5a19f45
     inst->u.unaryop = 0;
288057e9
     inst->interp_op = inst->opcode * 5;
a5a19f45
 
     bc->state = bc_loaded;
     return 0;
 }
 
 static int run_selfcheck(struct cli_all_bc *bcs)
 {
     struct cli_bc_ctx *ctx;
288057e9
     struct cli_bc *bc = &bcs->all_bcs[bcs->count - 1];
a5a19f45
     int rc;
     if (bc->state != bc_jit && bc->state != bc_interp) {
288057e9
         cli_errmsg("Failed to prepare selfcheck bytecode\n");
         return CL_EBYTECODE;
a5a19f45
     }
     ctx = cli_bytecode_context_alloc();
     if (!ctx) {
288057e9
         cli_errmsg("Failed to allocate bytecode context\n");
         return CL_EMEM;
a5a19f45
     }
     cli_bytecode_context_setfuncid(ctx, bc, 0);
 
     cli_dbgmsg("bytecode self test running\n");
71ca3536
     ctx->bytecode_timeout = 0;
288057e9
     rc                    = cli_bytecode_run(bcs, bc, ctx);
a5a19f45
     cli_bytecode_context_destroy(ctx);
     if (rc != CL_SUCCESS) {
288057e9
         cli_errmsg("bytecode self test failed: %s\n",
                    cl_strerror(rc));
a5a19f45
     } else {
288057e9
         cli_dbgmsg("bytecode self test succeeded\n");
a5a19f45
     }
     return rc;
 }
 
 static int selfcheck(int jit, struct cli_bcengine *engine)
 {
     struct cli_all_bc bcs;
     int rc;
 
     memset(&bcs, 0, sizeof(bcs));
     bcs.all_bcs = NULL;
288057e9
     bcs.count   = 0;
     bcs.engine  = engine;
     rc          = add_selfcheck(&bcs);
a5a19f45
     if (rc == CL_SUCCESS) {
288057e9
         if (jit) {
             if (!bcs.engine) {
                 cli_dbgmsg("bytecode: JIT disabled\n");
                 rc = CL_BREAK; /* no JIT - not fatal */
             } else {
                 rc = cli_bytecode_prepare_jit(&bcs);
             }
         } else {
             rc = cli_bytecode_prepare_interpreter(bcs.all_bcs);
         }
         if (rc == CL_SUCCESS)
             rc = run_selfcheck(&bcs);
         if (rc == CL_BREAK)
             rc = CL_SUCCESS;
a5a19f45
     }
     cli_bytecode_destroy(bcs.all_bcs);
     free(bcs.all_bcs);
     cli_bytecode_done_jit(&bcs, 1);
     if (rc != CL_SUCCESS) {
288057e9
         cli_errmsg("Bytecode: failed to run selfcheck in %s mode: %s\n",
                    jit ? "JIT" : "interpreter", cl_strerror(rc));
a5a19f45
     }
     return rc;
 }
 
d5ffa2ac
 static int set_mode(struct cl_engine *engine, enum bytecode_mode mode)
 {
     if (engine->bytecode_mode == mode)
288057e9
         return 0;
d5ffa2ac
     if (engine->bytecode_mode == CL_BYTECODE_MODE_OFF) {
288057e9
         cli_errmsg("bytecode: already turned off, can't turn it on again!\n");
         return -1;
d5ffa2ac
     }
     cli_dbgmsg("Bytecode: mode changed to %d\n", mode);
     if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST) {
288057e9
         if (mode == CL_BYTECODE_MODE_OFF || have_clamjit) {
             cli_errmsg("bytecode: in test mode but JIT/bytecode is about to be disabled: %d\n", mode);
             engine->bytecode_mode = mode;
             return -1;
         }
         return 0;
d5ffa2ac
     }
     if (engine->bytecode_mode == CL_BYTECODE_MODE_JIT) {
288057e9
         cli_errmsg("bytecode: in JIT mode but JIT is about to be disabled: %d\n", mode);
         engine->bytecode_mode = mode;
         return -1;
d5ffa2ac
     }
     engine->bytecode_mode = mode;
     return 0;
 }
 
 /* runs the first bytecode of the specified kind, or the builtin one if no
  * bytecode of that kind is loaded */
288057e9
 static int run_builtin_or_loaded(struct cli_all_bc *bcs, uint8_t kind, const char *builtin_cbc, struct cli_bc_ctx *ctx, const char *desc)
d5ffa2ac
 {
     unsigned i, builtin = 0, rc = 0;
     struct cli_bc *bc = NULL;
 
288057e9
     for (i = 0; i < bcs->count; i++) {
         bc = &bcs->all_bcs[i];
         if (bc->kind == kind)
             break;
d5ffa2ac
     }
     if (i == bcs->count)
288057e9
         bc = NULL;
d5ffa2ac
     if (!bc) {
288057e9
         /* no loaded bytecode found, load the builtin one! */
         struct cli_dbio dbio;
         bc = cli_calloc(1, sizeof(*bc));
         if (!bc) {
             cli_errmsg("Out of memory allocating bytecode\n");
             return CL_EMEM;
         }
         builtin = 1;
 
         memset(&dbio, 0, sizeof(dbio));
         dbio.usebuf = 1;
         dbio.bufpt = dbio.buf = (char *)builtin_cbc;
         dbio.bufsize          = strlen(builtin_cbc) + 1;
         if (!dbio.bufsize || dbio.bufpt[dbio.bufsize - 2] != '\n') {
             cli_errmsg("Invalid builtin bytecode: missing terminator\n");
             free(bc);
             return CL_EMALFDB;
         }
 
         rc = cli_bytecode_load(bc, NULL, &dbio, 1, 0);
         if (rc) {
             cli_errmsg("Failed to load builtin %s bytecode\n", desc);
             free(bc);
             return rc;
         }
d5ffa2ac
     }
     rc = cli_bytecode_prepare_interpreter(bc);
     if (rc) {
288057e9
         cli_errmsg("Failed to prepare %s %s bytecode for interpreter: %s\n",
                    builtin ? "builtin" : "loaded", desc, cl_strerror(rc));
d5ffa2ac
     }
     if (bc->state != bc_interp) {
288057e9
         cli_errmsg("Failed to prepare %s %s bytecode for interpreter\n",
                    builtin ? "builtin" : "loaded", desc);
         rc = CL_EMALFDB;
d5ffa2ac
     }
     if (!rc) {
288057e9
         cli_bytecode_context_setfuncid(ctx, bc, 0);
         cli_dbgmsg("Bytecode: %s running (%s)\n", desc,
                    builtin ? "builtin" : "loaded");
         rc = cli_bytecode_run(bcs, bc, ctx);
d5ffa2ac
     }
     if (rc) {
288057e9
         cli_errmsg("Failed to execute %s %s bytecode: %s\n", builtin ? "builtin" : "loaded",
                    desc, cl_strerror(rc));
d5ffa2ac
     }
     if (builtin) {
288057e9
         cli_bytecode_destroy(bc);
         free(bc);
d5ffa2ac
     }
     return rc;
 }
 
540fc128
 int cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *bcs, unsigned dconfmask)
6922903a
 {
288057e9
     unsigned i, interp = 0, jitok = 0, jitcount = 0;
dc200c6b
     int rc;
d5ffa2ac
     struct cli_bc_ctx *ctx;
a5a19f45
 
7cbc72d3
     if (!bcs->count) {
288057e9
         cli_dbgmsg("No bytecodes loaded, not running builtin test\n");
         return CL_SUCCESS;
7cbc72d3
     }
 
d4d4bce9
     engine->bytecode_mode = CL_BYTECODE_MODE_AUTO;
d5ffa2ac
     cli_detect_environment(&bcs->env);
     switch (bcs->env.arch) {
288057e9
         case arch_i386:
         case arch_x86_64:
             if (!(dconfmask & BYTECODE_JIT_X86)) {
                 cli_dbgmsg("Bytecode: disabled on X86 via DCONF\n");
                 if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
                     return CL_EBYTECODE_TESTFAIL;
             }
             break;
         case arch_ppc32:
         case arch_ppc64:
             if (!(dconfmask & BYTECODE_JIT_PPC)) {
                 cli_dbgmsg("Bytecode: disabled on PPC via DCONF\n");
                 if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
                     return CL_EBYTECODE_TESTFAIL;
             }
             break;
         case arch_arm:
             if (!(dconfmask & BYTECODE_JIT_ARM)) {
                 cli_dbgmsg("Bytecode: disabled on ARM via DCONF\n");
                 if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
                     return CL_EBYTECODE_TESTFAIL;
             }
             break;
         default:
             cli_dbgmsg("Bytecode: JIT not supported on this architecture, falling back\n");
             if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
                 return CL_EBYTECODE_TESTFAIL;
             break;
d5ffa2ac
     }
     cli_dbgmsg("Bytecode: mode is %d\n", engine->bytecode_mode);
a5a19f45
 
d5ffa2ac
     ctx = cli_bytecode_context_alloc();
     if (!ctx) {
288057e9
         cli_errmsg("Bytecode: failed to allocate bytecode context\n");
         return CL_EMEM;
d5ffa2ac
     }
0d4c9946
     rc = run_builtin_or_loaded(bcs, BC_STARTUP, builtin_bc_startup, ctx, "BC_STARTUP");
d5ffa2ac
     if (rc != CL_SUCCESS) {
288057e9
         cli_warnmsg("Bytecode: BC_STARTUP failed to run, disabling ALL bytecodes! Please report to https://bugzilla.clamav.net\n");
         ctx->bytecode_disable_status = 2;
d5ffa2ac
     } else {
288057e9
         cli_dbgmsg("Bytecode: disable status is %d\n", ctx->bytecode_disable_status);
         rc = cli_bytecode_context_getresult_int(ctx);
         /* check magic number, don't use 0 here because it is too easy for a
dbd3ed93
 	 * buggy bytecode to return 0 */
288057e9
         if ((unsigned int)rc != (unsigned int)0xda7aba5e) {
             cli_warnmsg("Bytecode: selftest failed with code %08x. Please report to https://bugzilla.clamav.net\n",
                         rc);
             if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
                 return CL_EBYTECODE_TESTFAIL;
         }
d5ffa2ac
     }
     switch (ctx->bytecode_disable_status) {
288057e9
         case 1:
             if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
                 return CL_EBYTECODE_TESTFAIL;
             break;
         case 2:
             if (set_mode(engine, CL_BYTECODE_MODE_OFF) == -1)
                 return CL_EBYTECODE_TESTFAIL;
             break;
         default:
             break;
d5ffa2ac
     }
     cli_bytecode_context_destroy(ctx);
 
     if (engine->bytecode_mode != CL_BYTECODE_MODE_INTERPRETER &&
288057e9
         engine->bytecode_mode != CL_BYTECODE_MODE_OFF) {
         selfcheck(1, bcs->engine);
         rc = cli_bytecode_prepare_jit(bcs);
         if (rc == CL_SUCCESS) {
             jitok = 1;
             cli_dbgmsg("Bytecode: %u bytecode prepared with JIT\n", bcs->count);
             if (engine->bytecode_mode != CL_BYTECODE_MODE_TEST)
                 return CL_SUCCESS;
         }
         if (engine->bytecode_mode == CL_BYTECODE_MODE_JIT) {
             cli_errmsg("Bytecode: JIT required, but not all bytecodes could be prepared with JIT\n");
             return CL_EMALFDB;
         }
         if (rc && engine->bytecode_mode == CL_BYTECODE_MODE_TEST) {
             cli_errmsg("Bytecode: Test mode, but not all bytecodes could be prepared with JIT\n");
             return CL_EBYTECODE_TESTFAIL;
         }
d5ffa2ac
     } else {
288057e9
         cli_bytecode_done_jit(bcs, 0);
d5ffa2ac
     }
 
     if (!(dconfmask & BYTECODE_INTERPRETER)) {
288057e9
         cli_dbgmsg("Bytecode: needs interpreter, but interpreter is disabled\n");
         if (set_mode(engine, CL_BYTECODE_MODE_OFF) == -1)
             return CL_EBYTECODE_TESTFAIL;
d5ffa2ac
     }
 
     if (engine->bytecode_mode == CL_BYTECODE_MODE_OFF) {
288057e9
         for (i = 0; i < bcs->count; i++)
             bcs->all_bcs[i].state = bc_disabled;
         cli_dbgmsg("Bytecode: ALL bytecodes disabled\n");
         return CL_SUCCESS;
     }
 
     for (i = 0; i < bcs->count; i++) {
         struct cli_bc *bc = &bcs->all_bcs[i];
         if (bc->state == bc_jit) {
             jitcount++;
             if (engine->bytecode_mode != CL_BYTECODE_MODE_TEST)
                 continue;
         }
         if (bc->state == bc_interp) {
             interp++;
             continue;
         }
         rc = cli_bytecode_prepare_interpreter(bc);
         if (rc != CL_SUCCESS) {
             bc->state = bc_disabled;
             cli_warnmsg("Bytecode: %d failed to prepare for interpreter mode\n", bc->id);
             return rc;
         }
         interp++;
6922903a
     }
e86fe7ea
     cli_dbgmsg("Bytecode: %u bytecode prepared with JIT, "
288057e9
                "%u prepared with interpreter, %u total\n",
                jitcount, interp, bcs->count);
d1487222
     return CL_SUCCESS;
6922903a
 }
 
927d0548
 int cli_bytecode_init(struct cli_all_bc *allbc)
6922903a
 {
927d0548
     int ret;
d1487222
     memset(allbc, 0, sizeof(*allbc));
288057e9
     ret = cli_bytecode_init_jit(allbc, 0 /*XXX*/);
927d0548
     cli_dbgmsg("Bytecode initialized in %s mode\n",
288057e9
                allbc->engine ? "JIT" : "interpreter");
927d0548
     allbc->inited = 1;
     return ret;
d1487222
 }
 
 int cli_bytecode_done(struct cli_all_bc *allbc)
 {
a5a19f45
     return cli_bytecode_done_jit(allbc, 0);
6922903a
 }
3b33bd68
 
74b00233
 int cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, fmap_t *map)
a1781898
 {
288057e9
     ctx->fmap           = map;
     ctx->file_size      = map->len + map->offset;
0fa95ef2
     ctx->hooks.filesize = &ctx->file_size;
a1781898
     return 0;
 }
 
762d46e8
 int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
288057e9
                          const struct cli_all_bc *bcs, unsigned bc_idx,
                          const uint32_t *lsigcnt,
                          const uint32_t *lsigsuboff, fmap_t *map)
d38d6dad
 {
     int ret;
     struct cli_bc_ctx ctx;
288057e9
     const struct cli_bc *bc = &bcs->all_bcs[bc_idx - 1];
762d46e8
     struct cli_pe_hook_data pehookdata;
f4e34215
 
288057e9
     if (bc_idx == 0)
60aad52f
         return CL_ENULLARG;
 
d38d6dad
     memset(&ctx, 0, sizeof(ctx));
     cli_bytecode_context_setfuncid(&ctx, bc, 0);
288057e9
     ctx.hooks.match_counts  = lsigcnt;
1c4683ac
     ctx.hooks.match_offsets = lsigsuboff;
92a08a03
     cli_bytecode_context_setctx(&ctx, cctx);
74b00233
     cli_bytecode_context_setfile(&ctx, map);
762d46e8
     if (tinfo && tinfo->status == 1) {
afe940da
         ctx.sections = tinfo->exeinfo.sections;
288057e9
         memset(&pehookdata, 0, sizeof(pehookdata));
         pehookdata.offset    = tinfo->exeinfo.offset;
         pehookdata.ep        = tinfo->exeinfo.ep;
         pehookdata.nsections = tinfo->exeinfo.nsections;
         pehookdata.hdr_size  = tinfo->exeinfo.hdr_size;
         ctx.hooks.pedata     = &pehookdata;
         ctx.resaddr          = tinfo->exeinfo.res_addr;
762d46e8
     }
4abbeb3a
     if (bc->hook_lsig_id) {
288057e9
         cli_dbgmsg("hook lsig id %d matched (bc %d)\n", bc->hook_lsig_id, bc->id);
         /* this is a bytecode for a hook, defer running it until hook is
4abbeb3a
 	 * executed, so that it has all the info for the hook */
288057e9
         if (cctx->hook_lsig_matches)
             cli_bitset_set(cctx->hook_lsig_matches, bc->hook_lsig_id - 1);
         /* save match counts */
         memcpy(&ctx.lsigcnt, lsigcnt, 64 * 4);
         memcpy(&ctx.lsigoff, lsigsuboff, 64 * 4);
         cli_bytecode_context_clear(&ctx);
         return CL_SUCCESS;
4abbeb3a
     }
d38d6dad
 
     cli_dbgmsg("Running bytecode for logical signature match\n");
     ret = cli_bytecode_run(bcs, bc, &ctx);
     if (ret != CL_SUCCESS) {
288057e9
         cli_warnmsg("Bytecode %u failed to run: %s\n", bc->id, cl_strerror(ret));
         cli_bytecode_context_clear(&ctx);
         return CL_SUCCESS;
d38d6dad
     }
     if (ctx.virname) {
d32e0396
         if (cctx->num_viruses == 0) {
             int rc;
             cli_dbgmsg("Bytecode found virus: %s\n", ctx.virname);
             if (!strncmp(ctx.virname, "BC.Heuristics", 13))
                 rc = cli_append_possibly_unwanted(cctx, ctx.virname);
             else
                 rc = cli_append_virus(cctx, ctx.virname);
             cli_bytecode_context_clear(&ctx);
             return rc;
288057e9
         } else {
8650c790
             cli_bytecode_context_clear(&ctx);
d32e0396
             return CL_VIRUS;
         }
d38d6dad
     }
     ret = cli_bytecode_context_getresult_int(&ctx);
ab636570
     cli_dbgmsg("Bytecode %u returned code: %u\n", bc->id, ret);
d38d6dad
     cli_bytecode_context_clear(&ctx);
     return CL_SUCCESS;
 }
ab636570
 
f4e34215
 int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx,
288057e9
                          unsigned id, fmap_t *map)
ab636570
 {
     const unsigned *hooks = engine->hooks[id - _BC_START_HOOKS];
     unsigned i, hooks_cnt = engine->hooks_cnt[id - _BC_START_HOOKS];
     int ret;
8a8dbd59
     unsigned executed = 0, breakflag = 0, errorflag = 0;
ab636570
 
6a049897
     if (!cctx)
         return CL_ENULLARG;
 
ab636570
     cli_dbgmsg("Bytecode executing hook id %u (%u hooks)\n", id, hooks_cnt);
70c222c9
     /* restore match counts */
8a8dbd59
     cli_bytecode_context_setfile(ctx, map);
288057e9
     ctx->hooks.match_counts  = ctx->lsigcnt;
70c222c9
     ctx->hooks.match_offsets = ctx->lsigoff;
288057e9
     for (i = 0; i < hooks_cnt; i++) {
         const struct cli_bc *bc = &engine->bcs.all_bcs[hooks[i]];
         if (bc->lsig) {
             if (!cctx->hook_lsig_matches ||
                 !cli_bitset_test(cctx->hook_lsig_matches, bc->hook_lsig_id - 1))
                 continue;
             cli_dbgmsg("Bytecode: executing bytecode %u (lsig matched)\n", bc->id);
         }
         cli_bytecode_context_setfuncid(ctx, bc, 0);
         ret = cli_bytecode_run(&engine->bcs, bc, ctx);
         executed++;
         if (ret != CL_SUCCESS) {
             cli_warnmsg("Bytecode %u failed to run: %s\n", bc->id, cl_strerror(ret));
             errorflag = 1;
             continue;
         }
         if (ctx->virname) {
             cli_dbgmsg("Bytecode runhook found virus: %s\n", ctx->virname);
             cli_append_virus(cctx, ctx->virname);
             if (!(cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES)) {
                 cli_bytecode_context_clear(ctx);
                 return CL_VIRUS;
             }
             cli_bytecode_context_reset(ctx);
             continue;
         }
         ret = cli_bytecode_context_getresult_int(ctx);
         /* TODO: use prefix here */
         cli_dbgmsg("Bytecode %u returned %u\n", bc->id, ret);
         if (ret == 0xcea5e) {
             cli_dbgmsg("Bytecode set BREAK flag in hook!\n");
             breakflag = 1;
         }
         if (!ret) {
             char *tempfile;
             int fd = cli_bytecode_context_getresult_file(ctx, &tempfile);
             if (fd && fd != -1) {
                 if (cctx->engine->keeptmp)
                     cli_dbgmsg("Bytecode %u unpacked file saved in %s\n",
                                bc->id, tempfile);
                 else
                     cli_dbgmsg("Bytecode %u unpacked file\n", bc->id);
                 lseek(fd, 0, SEEK_SET);
                 cli_dbgmsg("***** Scanning unpacked file ******\n");
                 cctx->recursion++;
                 ret = cli_magic_scandesc(fd, tempfile, cctx);
                 cctx->recursion--;
                 if (!cctx->engine->keeptmp)
                     if (ftruncate(fd, 0) == -1)
                         cli_dbgmsg("ftruncate failed on %d\n", fd);
                 close(fd);
                 if (!cctx->engine->keeptmp) {
                     if (tempfile && cli_unlink(tempfile))
                         ret = CL_EUNLINK;
                 }
                 free(tempfile);
                 if (ret != CL_CLEAN) {
                     if (ret == CL_VIRUS) {
                         cli_dbgmsg("Scanning unpacked file by bytecode %u found a virus\n", bc->id);
                         if (cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES) {
                             cli_bytecode_context_reset(ctx);
                             continue;
                         }
                         cli_bytecode_context_clear(ctx);
                         return ret;
                     }
                 }
                 cli_bytecode_context_reset(ctx);
                 continue;
             }
         }
         cli_bytecode_context_reset(ctx);
ab636570
     }
e86fe7ea
     if (executed)
288057e9
         cli_dbgmsg("Bytecode: executed %u bytecodes for this hook\n", executed);
e86fe7ea
     else
288057e9
         cli_dbgmsg("Bytecode: no logical signature matched, no bytecode executed\n");
6a049897
     if (errorflag && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
288057e9
         return CL_EBYTECODE_TESTFAIL;
f73212dc
     return breakflag ? CL_BREAK : CL_CLEAN;
ab636570
 }
 
236fb136
 int cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data, const struct cli_exe_section *sections)
ab636570
 {
288057e9
     ctx->sections     = sections;
ab636570
     ctx->hooks.pedata = data;
     return 0;
 }
3ae0a76d
 
 void cli_bytecode_context_setctx(struct cli_bc_ctx *ctx, void *cctx)
 {
288057e9
     ctx->ctx              = cctx;
     ctx->bytecode_timeout = ((cli_ctx *)cctx)->engine->bytecode_timeout;
3ae0a76d
 }
a35cfe51
 
 void cli_bytecode_describe(const struct cli_bc *bc)
 {
     char buf[128];
     int cols;
     unsigned i;
     time_t stamp;
     int had;
 
     if (!bc) {
288057e9
         printf("(null bytecode)\n");
         return;
a35cfe51
     }
 
     stamp = bc->metadata.timestamp;
3735fda1
     printf("Bytecode format functionality level: %u\n", bc->metadata.formatlevel);
a35cfe51
     printf("Bytecode metadata:\n\tcompiler version: %s\n",
288057e9
            bc->metadata.compiler ? bc->metadata.compiler : "N/A");
d5ffa2ac
     printf("\tcompiled on: (%d) %s",
288057e9
            (uint32_t)stamp,
            cli_ctime(&stamp, buf, sizeof(buf)));
a35cfe51
     printf("\tcompiled by: %s\n", bc->metadata.sigmaker ? bc->metadata.sigmaker : "N/A");
c0afc517
     /*TODO: parse and display arch name, also take it into account when
       JITing*/
a35cfe51
     printf("\ttarget exclude: %d\n", bc->metadata.targetExclude);
     printf("\tbytecode type: ");
     switch (bc->kind) {
288057e9
         case BC_GENERIC:
             puts("generic, not loadable by clamscan/clamd");
             break;
         case BC_STARTUP:
             puts("run on startup (unique)");
             break;
         case BC_LOGICAL:
             puts("logical only");
             break;
         case BC_PE_UNPACKER:
             puts("PE unpacker hook");
             break;
         case BC_PE_ALL:
             puts("all PE hook");
             break;
         case BC_PRECLASS:
             puts("preclass hook");
             break;
abe33926
         case BC_ELF_UNPACKER:
             puts("ELF unpacker hook");
             break;
1a75f6a5
         case BC_MACHO_UNPACKER:
             puts("Mach-O unpacker hook");
             break;
288057e9
         default:
             printf("Unknown (type %u)", bc->kind);
             break;
a35cfe51
     }
3735fda1
     /* 0 means no limit */
     printf("\tbytecode functionality level: %u - %u\n",
288057e9
            bc->metadata.minfunc, bc->metadata.maxfunc);
a35cfe51
     printf("\tbytecode logical signature: %s\n",
288057e9
            bc->lsig ? bc->lsig : "<none>");
a35cfe51
     printf("\tvirusname prefix: %s\n",
288057e9
            bc->vnameprefix);
a35cfe51
     printf("\tvirusnames: %u\n", bc->vnames_cnt);
     printf("\tbytecode triggered on: ");
     switch (bc->kind) {
288057e9
         case BC_GENERIC:
             puts("N/A (loaded in clambc only)");
             break;
         case BC_LOGICAL:
             puts("files matching logical signature");
             break;
         case BC_PE_UNPACKER:
             if (bc->lsig)
                 puts("PE files matching logical signature (unpacked)");
             else
                 puts("all PE files! (unpacked)");
             break;
         case BC_PDF:
             puts("PDF files");
             break;
         case BC_PE_ALL:
             if (bc->lsig)
                 puts("PE files matching logical signature");
             else
                 puts("all PE files!");
             break;
         case BC_PRECLASS:
             if (bc->lsig)
                 puts("PRECLASS files matching logical signature");
             else
                 puts("all PRECLASS files!");
             break;
abe33926
         case BC_ELF_UNPACKER:
             if (bc->lsig)
                 puts("ELF files matching logical signature (unpacked)");
             else
                 puts("all ELF files! (unpacked)");
             break;
1a75f6a5
         case BC_MACHO_UNPACKER:
             if (bc->lsig)
                 puts("Mach-O files matching logical signature (unpacked)");
             else
                 puts("all Mach-O files! (unpacked)");
             break;
288057e9
         default:
             puts("N/A (unknown type)\n");
             break;
a35cfe51
     }
     printf("\tnumber of functions: %u\n\tnumber of types: %u\n",
288057e9
            bc->num_func, bc->num_types);
5297d09e
     printf("\tnumber of global constants: %u\n", (unsigned)bc->num_globals);
a35cfe51
     printf("\tnumber of debug nodes: %u\n", bc->dbgnode_cnt);
     printf("\tbytecode APIs used:");
     cols = 0; /* remaining */
288057e9
     had  = 0;
     for (i = 0; i < cli_apicall_maxapi; i++) {
         if (cli_bitset_test(bc->uses_apis, i)) {
             unsigned len = strlen(cli_apicalls[i].name);
             if (had)
                 printf(",");
             if (len > (unsigned int)cols) {
                 printf("\n\t");
                 cols = 72;
             }
             printf(" %s", cli_apicalls[i].name);
             had = 1;
             cols -= len;
         }
a35cfe51
     }
     printf("\n");
 }
0ff13b31
 
 const char *bc_tystr[] = {
     "DFunctionType",
     "DPointerType",
     "DStructType",
     "DPackedStructType",
288057e9
     "DArrayType"};
0ff13b31
 
 const char *bc_opstr[] = {
     "OP_BC_NULL",
     "OP_BC_ADD", /* =1*/
     "OP_BC_SUB",
     "OP_BC_MUL",
     "OP_BC_UDIV",
     "OP_BC_SDIV",
     "OP_BC_UREM",
     "OP_BC_SREM",
     "OP_BC_SHL",
     "OP_BC_LSHR",
     "OP_BC_ASHR",
     "OP_BC_AND",
     "OP_BC_OR",
     "OP_BC_XOR",
 
     "OP_BC_TRUNC",
     "OP_BC_SEXT",
     "OP_BC_ZEXT",
 
     "OP_BC_BRANCH",
     "OP_BC_JMP",
     "OP_BC_RET",
     "OP_BC_RET_VOID",
 
     "OP_BC_ICMP_EQ",
     "OP_BC_ICMP_NE",
     "OP_BC_ICMP_UGT",
     "OP_BC_ICMP_UGE",
     "OP_BC_ICMP_ULT",
     "OP_BC_ICMP_ULE",
     "OP_BC_ICMP_SGT",
     "OP_BC_ICMP_SGE",
     "OP_BC_ICMP_SLE",
     "OP_BC_ICMP_SLT",
     "OP_BC_SELECT",
     "OP_BC_CALL_DIRECT",
     "OP_BC_CALL_API",
     "OP_BC_COPY",
     "OP_BC_GEP1",
     "OP_BC_GEPZ",
     "OP_BC_GEPN",
     "OP_BC_STORE",
     "OP_BC_LOAD",
     "OP_BC_MEMSET",
     "OP_BC_MEMCPY",
     "OP_BC_MEMMOVE",
     "OP_BC_MEMCMP",
     "OP_BC_ISBIGENDIAN",
     "OP_BC_ABORT",
     "OP_BC_BSWAP16",
     "OP_BC_BSWAP32",
     "OP_BC_BSWAP64",
     "OP_BC_PTRDIFF32",
     "OP_BC_PTRTOINT64",
     "OP_BC_INVALID" /* last */
 };
 
 extern unsigned cli_numapicalls;
 static void cli_bytetype_helper(const struct cli_bc *bc, unsigned tid)
 {
     unsigned i, j;
0b28c748
     const struct cli_bc_type *ty;
0ff13b31
 
     if (tid & 0x8000) {
         printf("alloc ");
         tid &= 0x7fff;
     }
 
     if (tid < 65) {
         printf("i%d", tid);
         return;
     }
 
     i = tid - 65;
7c9c4fab
     if (i >= bc->num_types) {
7cd9337a
         printf("invalid type");
7c9c4fab
         return;
     }
0b28c748
     ty = &bc->types[i];
0ff13b31
 
     switch (ty->kind) {
288057e9
         case DFunctionType:
0ff13b31
             cli_bytetype_helper(bc, ty->containedTypes[0]);
288057e9
             printf(" func ( ");
             for (j = 1; j < ty->numElements; ++j) {
                 cli_bytetype_helper(bc, ty->containedTypes[0]);
                 printf(" ");
             }
             printf(")");
             break;
         case DPointerType:
0ff13b31
             cli_bytetype_helper(bc, ty->containedTypes[0]);
288057e9
             printf("*");
             break;
         case DStructType:
         case DPackedStructType:
             printf("{ ");
             for (j = 0; j < ty->numElements; ++j) {
                 cli_bytetype_helper(bc, ty->containedTypes[0]);
                 printf(" ");
             }
             printf("}");
             break;
         case DArrayType:
             printf("[");
             printf("%d x ", ty->numElements);
             cli_bytetype_helper(bc, ty->containedTypes[0]);
             printf("]");
             break;
         default:
             printf("unhandled type kind %d, cannot parse", ty->kind);
             break;
0ff13b31
     }
 }
 
 void cli_bytetype_describe(const struct cli_bc *bc)
 {
     unsigned i, tid;
 
288057e9
     printf("found %d extra types of %d total, starting at tid %d\n",
            bc->num_types, 64 + bc->num_types, bc->start_tid);
0ff13b31
 
     printf("TID  KIND                INTERNAL\n");
     printf("------------------------------------------------------------------------\n");
288057e9
     for (i = 0, tid = 65; i < bc->num_types - 1; ++i, ++tid) {
0ff13b31
         printf("%3d: %-20s", tid, bc_tystr[bc->types[i].kind]);
         cli_bytetype_helper(bc, tid);
         printf("\n");
     }
     printf("------------------------------------------------------------------------\n");
 }
 
 void cli_bytevalue_describe(const struct cli_bc *bc, unsigned funcid)
 {
c9a070c9
     unsigned i, total = 0;
0ff13b31
     const struct cli_bc_func *func;
 
     if (funcid >= bc->num_func) {
7cd9337a
         printf("bytecode diagnostic: funcid [%u] outside bytecode numfuncs [%u]\n",
0ff13b31
                funcid, bc->num_func);
         return;
     }
     // globals
fd43d610
     printf("found a total of %zu globals\n", bc->num_globals);
0ff13b31
     printf("GID  ID    VALUE\n");
     printf("------------------------------------------------------------------------\n");
     for (i = 0; i < bc->num_globals; ++i) {
         printf("%3u [%3u]: ", i, i);
         cli_bytetype_helper(bc, bc->globaltys[i]);
         printf(" unknown\n");
     }
     printf("------------------------------------------------------------------------\n");
 
     // arguments and local values
     func = &bc->funcs[funcid];
     printf("found %d values with %d arguments and %d locals\n",
            func->numValues, func->numArgs, func->numLocals);
     printf("VID  ID    VALUE\n");
     printf("------------------------------------------------------------------------\n");
     for (i = 0; i < func->numValues; ++i) {
         printf("%3u [%3u]: ", i, total++);
         cli_bytetype_helper(bc, func->types[i]);
         if (i < func->numArgs)
b2fed43b
             printf(" argument");
0ff13b31
         printf("\n");
     }
     printf("------------------------------------------------------------------------\n");
288057e9
 
0ff13b31
     // constants
     printf("found a total of %d constants\n", func->numConstants);
     printf("CID  ID    VALUE\n");
     printf("------------------------------------------------------------------------\n");
     for (i = 0; i < func->numConstants; ++i) {
653b471b
         printf("%3u [%3u]: " STDu64 "(0x" STDx64 ")\n", i, total++, func->constants[i], func->constants[i]);
0ff13b31
     }
     printf("------------------------------------------------------------------------\n");
     printf("found a total of %u total values\n", total);
     printf("------------------------------------------------------------------------\n");
     return;
 }
 
 void cli_byteinst_describe(const struct cli_bc_inst *inst, unsigned *bbnum)
 {
     unsigned j;
     char inst_str[256];
288057e9
     const struct cli_apicall *api;
0ff13b31
 
     if (inst->opcode > OP_BC_INVALID) {
         printf("opcode %u[%u] of type %u is not implemented yet!",
288057e9
                inst->opcode, inst->interp_op / 5, inst->interp_op % 5);
0ff13b31
         return;
     }
 
288057e9
     snprintf(inst_str, sizeof(inst_str), "%-20s[%-3d/%3d/%3d]", bc_opstr[inst->opcode],
              inst->opcode, inst->interp_op, inst->interp_op % inst->opcode);
0ff13b31
     printf("%-35s", inst_str);
     switch (inst->opcode) {
288057e9
             // binary operations
         case OP_BC_ADD:
             printf("%d = %d + %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_SUB:
             printf("%d = %d - %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_MUL:
             printf("%d = %d * %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_UDIV:
             printf("%d = %d / %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_SDIV:
             printf("%d = %d / %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_UREM:
             printf("%d = %d %% %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_SREM:
             printf("%d = %d %% %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_SHL:
             printf("%d = %d << %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_LSHR:
             printf("%d = %d >> %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ASHR:
             printf("%d = %d >> %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_AND:
             printf("%d = %d & %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_OR:
             printf("%d = %d | %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_XOR:
             printf("%d = %d ^ %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
 
             // casting operations
         case OP_BC_TRUNC:
             printf("%d = %d trunc " STDx64, inst->dest, inst->u.cast.source, inst->u.cast.mask);
             break;
         case OP_BC_SEXT:
             printf("%d = %d sext " STDx64, inst->dest, inst->u.cast.source, inst->u.cast.mask);
             break;
         case OP_BC_ZEXT:
             printf("%d = %d zext " STDx64, inst->dest, inst->u.cast.source, inst->u.cast.mask);
             break;
 
             // control operations (termination instructions)
         case OP_BC_BRANCH:
             printf("br %d ? bb.%d : bb.%d", inst->u.branch.condition,
                    inst->u.branch.br_true, inst->u.branch.br_false);
             (*bbnum)++;
             break;
         case OP_BC_JMP:
             printf("jmp bb.%d", inst->u.jump);
             (*bbnum)++;
             break;
         case OP_BC_RET:
             printf("ret %d", inst->u.unaryop);
             (*bbnum)++;
             break;
         case OP_BC_RET_VOID:
             printf("ret void");
             (*bbnum)++;
             break;
 
             // comparison operations
         case OP_BC_ICMP_EQ:
             printf("%d = (%d == %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_NE:
             printf("%d = (%d != %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_UGT:
             printf("%d = (%d > %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_UGE:
             printf("%d = (%d >= %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_ULT:
             printf("%d = (%d > %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_ULE:
             printf("%d = (%d >= %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_SGT:
             printf("%d = (%d > %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_SGE:
             printf("%d = (%d >= %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_SLE:
             printf("%d = (%d <= %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_ICMP_SLT:
             printf("%d = (%d < %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_SELECT:
             printf("%d = %d ? %d : %d)", inst->dest, inst->u.three[0],
                    inst->u.three[1], inst->u.three[2]);
             break;
 
             // function calling
         case OP_BC_CALL_DIRECT:
             printf("%d = call F.%d (", inst->dest, inst->u.ops.funcid);
             for (j = 0; j < inst->u.ops.numOps; ++j) {
                 if (j == inst->u.ops.numOps - 1) {
                     printf("%d", inst->u.ops.ops[j]);
                 } else {
                     printf("%d, ", inst->u.ops.ops[j]);
                 }
0ff13b31
             }
288057e9
             printf(")");
             break;
         case OP_BC_CALL_API: {
0ff13b31
             if (inst->u.ops.funcid > cli_numapicalls) {
                 printf("apicall FID %d not yet implemented!\n", inst->u.ops.funcid);
                 break;
             }
5b5be2a6
             api = &cli_apicalls[inst->u.ops.funcid];
0ff13b31
             switch (api->kind) {
288057e9
                 case 0:
                     printf("%d = %s[%d] (%d, %d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1]);
                     break;
                 case 1:
                     printf("%d = %s[%d] (p.%d, %d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1]);
                     break;
                 case 2:
                     printf("%d = %s[%d] (%d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0]);
                     break;
                 case 3:
                     printf("p.%d = %s[%d] (%d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0]);
                     break;
                 case 4:
                     printf("%d = %s[%d] (p.%d, %d, %d, %d, %d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1],
                            inst->u.ops.ops[2], inst->u.ops.ops[3], inst->u.ops.ops[4]);
                     break;
                 case 5:
                     printf("%d = %s[%d] ()", inst->dest, api->name,
                            inst->u.ops.funcid);
                     break;
                 case 6:
                     printf("p.%d = %s[%d] (%d, %d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1]);
                     break;
                 case 7:
                     printf("%d = %s[%d] (%d, %d, %d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1],
                            inst->u.ops.ops[2]);
                     break;
                 case 8:
                     printf("%d = %s[%d] (p.%d, %d, p.%d, %d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1],
                            inst->u.ops.ops[2], inst->u.ops.ops[3]);
                     break;
                 case 9:
                     printf("%d = %s[%d] (p.%d, %d, %d)", inst->dest, api->name,
                            inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1],
                            inst->u.ops.ops[2]);
                     break;
                 default:
                     printf("type %u apicalls not yet implemented!\n", api->kind);
                     break;
0ff13b31
             }
288057e9
         } break;
 
             // memory operations
         case OP_BC_COPY:
             printf("cp %d -> %d", inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_GEP1:
             printf("%d = gep1 p.%d + (%d * %d)", inst->dest, inst->u.three[1],
                    inst->u.three[2], inst->u.three[0]);
             break;
         case OP_BC_GEPZ:
             printf("%d = gepz p.%d + (%d)", inst->dest,
                    inst->u.three[1], inst->u.three[2]);
             break;
         case OP_BC_GEPN:
             printf("illegal opcode, impossible");
             break;
         case OP_BC_STORE:
             printf("store %d -> p.%d", inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_LOAD:
             printf("load  %d <- p.%d", inst->dest, inst->u.unaryop);
             break;
 
             // llvm intrinsics
         case OP_BC_MEMSET:
             printf("%d = memset (p.%d, %d, %d)", inst->dest, inst->u.three[0],
                    inst->u.three[1], inst->u.three[2]);
             break;
         case OP_BC_MEMCPY:
             printf("%d = memcpy (p.%d, p.%d, %d)", inst->dest, inst->u.three[0],
                    inst->u.three[1], inst->u.three[2]);
             break;
         case OP_BC_MEMMOVE:
             printf("%d = memmove (p.%d, p.%d, %d)", inst->dest, inst->u.three[0],
                    inst->u.three[1], inst->u.three[2]);
             break;
         case OP_BC_MEMCMP:
             printf("%d = memcmp (p.%d, p.%d, %d)", inst->dest, inst->u.three[0],
                    inst->u.three[1], inst->u.three[2]);
             break;
 
             // utility operations
         case OP_BC_ISBIGENDIAN:
             printf("%d = isbigendian()", inst->dest);
             break;
         case OP_BC_ABORT:
             printf("ABORT!!");
             break;
         case OP_BC_BSWAP16:
             printf("%d = bswap16 %d", inst->dest, inst->u.unaryop);
             break;
         case OP_BC_BSWAP32:
             printf("%d = bswap32 %d", inst->dest, inst->u.unaryop);
             break;
         case OP_BC_BSWAP64:
             printf("%d = bswap64 %d", inst->dest, inst->u.unaryop);
             break;
         case OP_BC_PTRDIFF32:
             printf("%d = ptrdiff32 p.%d p.%d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
             break;
         case OP_BC_PTRTOINT64:
             printf("%d = ptrtoint64 p.%d", inst->dest, inst->u.unaryop);
             break;
         case OP_BC_INVALID: /* last */
             printf("INVALID!!");
             break;
 
         default:
             // redundant check
             printf("opcode %u[%u] of type %u is not implemented yet!",
                    inst->opcode, inst->interp_op / 5, inst->interp_op % 5);
             break;
0ff13b31
     }
 }
 
 void cli_bytefunc_describe(const struct cli_bc *bc, unsigned funcid)
 {
     unsigned i, bbnum, bbpre;
     const struct cli_bc_func *func;
 
     if (funcid >= bc->num_func) {
7cd9337a
         printf("bytecode diagnostic: funcid [%u] outside bytecode numfuncs [%u]\n",
0ff13b31
                funcid, bc->num_func);
         return;
     }
 
     func = &bc->funcs[funcid];
 
     printf("FUNCTION ID: F.%d -> NUMINSTS %d\n", funcid, func->numInsts);
     printf("BB   IDX  OPCODE              [ID /IID/MOD]  INST\n");
     printf("------------------------------------------------------------------------\n");
288057e9
     bbpre = 0;
     bbnum = 0;
0ff13b31
     for (i = 0; i < func->numInsts; ++i) {
         if (bbpre != bbnum) {
             printf("\n");
             bbpre = bbnum;
         }
 
         printf("%3d  %3d  ", bbnum, i);
         cli_byteinst_describe(&func->allinsts[i], &bbnum);
         printf("\n");
     }
     printf("------------------------------------------------------------------------\n");
 }