Commit message: fix recursion level crash (bb #3706).

Thanks to Stephane Chazelas for the analysis.
CVE: CVE-2011-3627
CWE: CWE-189
Vulnerability description: The bytecode engine in ClamAV before 0.97.3 allows remote attackers to cause a denial of service (crash) via vectors related to "recursion level" and (1) libclamav/bytecode.c and (2) libclamav/bytecode_api.c.

Project: clamav-devel
File: bytecode_api.c

uint8_t* cli_bcapi_pdf_getobj(struct cli_bc_ctx *ctx , int32_t objidx, uint32_t amount)
{
    uint32_t size = cli_bcapi_pdf_getobjsize(ctx, objidx);
    if (amount > size)
	return NULL;
    return fmap_need_off(ctx->fmap, ctx->pdf_objs[objidx].start, amount);
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_iexp(struct cli_bc_ctx *ctx, int32_t a, int32_t b, int32_t c)
{
    double f;
    if (!b)
	return 0x7fffffff;
    f= c*exp((double)a/b);
    return (uint32_t)myround(f);
}

Project: clamav-devel
File: bytecode.c

void cli_bytecode_destroy(struct cli_bc *bc)
{
    unsigned i, j, k;
    free(bc->metadata.compiler);
    free(bc->metadata.sigmaker);

    if (bc->funcs) {
	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);
    }
    if (bc->types) {
	for (i=NUM_STATIC_TYPES;i<bc->num_types;i++) {
	    if (bc->types[i].containedTypes)
		free(bc->types[i].containedTypes);
	}
	free(bc->types);
    }

    if (bc->globals) {
	for (i=0;i<bc->num_globals;i++) {
	    free(bc->globals[i]);
	}
	free(bc->globals);
    }
    if (bc->dbgnodes) {
	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);
    }
    free(bc->globaltys);
    if (bc->uses_apis)
	cli_bitset_free(bc->uses_apis);
    free(bc->lsig);
    free(bc->globalBytes);
    memset(bc, 0, sizeof(*bc));
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_debug_print_uint(struct cli_bc_ctx *ctx, uint32_t a)
{
    cli_event_int(EV, BCEV_DBG_INT, a);
    if (!cli_debug_flag)
	return 0;
    return fprintf(stderr, "%d", a);
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_map_addkey(struct cli_bc_ctx *ctx , const uint8_t* key, int32_t keysize, int32_t id)
{
    struct cli_map *s = get_hashtab(ctx, id);
    if (!s)
	return -1;
    return cli_map_addkey(s, key, keysize);
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_extract_set_container(struct cli_bc_ctx *ctx, uint32_t ftype)
{
    if (ftype > CL_TYPE_IGNORED)
	return -1;
    ctx->containertype = ftype;
    return 0;
}

Project: clamav-devel
File: bytecode.c

static int parseFunctionHeader(struct cli_bc *bc, unsigned fn, unsigned char *buffer)
{
    char ok=1;
    unsigned offset, len, all_locals=0, i;
    struct cli_bc_func *func;

    if (fn >= bc->num_func) {
	cli_errmsg("Found more functions than declared: %u >= %u\n", fn,
		   bc->num_func);
	return CL_EMALFDB;
    }
    func = &bc->funcs[fn];
    len = strlen((const char*)buffer);

    if (buffer[0] != 'A') {
	cli_errmsg("Invalid function arguments header: %c\n", buffer[0]);
	return CL_EMALFDB;
    }
    offset = 1;
    func->numArgs = readFixedNumber(buffer, &offset, len, &ok, 1);
    func->returnType = readTypeID(bc, buffer, &offset, len, &ok);
    if (buffer[offset] != 'L') {
	cli_errmsg("Invalid function locals header: %c\n", buffer[offset]);
	return CL_EMALFDB;
    }
    offset++;
    func->numLocals = readNumber(buffer, &offset, len, &ok);
    if (!ok) {
	cli_errmsg("Invalid number of arguments/locals\n");
	return CL_EMALFDB;
    }
    all_locals = func->numArgs + func->numLocals;
    if (!all_locals) {
	func->types = NULL;
    } else {
	func->types = cli_calloc(all_locals, sizeof(*func->types));
	if (!func->types) {
	    cli_errmsg("Out of memory allocating function arguments\n");
	    return CL_EMEM;
	}
    }
    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;
    }
    if (!ok) {
	cli_errmsg("Invalid local types\n");
	return CL_EMALFDB;
    }
    if (buffer[offset] != 'F') {
	cli_errmsg("Invalid function body header: %c\n", buffer[offset]);
	return CL_EMALFDB;
    }
    offset++;
    func->numInsts = readNumber(buffer, &offset, len, &ok);
    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));
    if (!func->allinsts) {
	cli_errmsg("Out of memory allocating instructions\n");
	return CL_EMEM;
    }
    func->numBB = readNumber(buffer, &offset, len, &ok);
    if (!ok) {
	cli_errmsg("Invalid basic block count\n");
	return CL_EMALFDB;
    }
    func->BB = cli_calloc(func->numBB, sizeof(*func->BB));
    if (!func->BB) {
	cli_errmsg("Out of memory allocating basic blocks\n");
	return CL_EMEM;
    }
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode_api.c

static inline const char* cli_memmem(const char *haystack, unsigned hlen,
				     const unsigned char *needle, unsigned nlen)
{
    const char *p;
    unsigned char c;
    if (!needle || !haystack) {
	return NULL;
    }
    c = *needle++;
    if (nlen == 1)
	return memchr(haystack, c, hlen);

    while (hlen >= nlen) {
	p = haystack;
	haystack = memchr(haystack, c, hlen - nlen + 1);
	if (!haystack)
	    return NULL;
	hlen -= haystack+1 - p;
	p = haystack + 1;
	if (!memcmp(p, needle, nlen-1))
	    return haystack;
	haystack = p;
    }
    return NULL;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_map_setvalue(struct cli_bc_ctx *ctx, const uint8_t* value, int32_t valuesize, int32_t id)
{
    struct cli_map *s = get_hashtab(ctx, id);
    if (!s)
	return -1;
    return cli_map_setvalue(s, value, valuesize);
}

Project: clamav-devel
File: bytecode.c

static unsigned typesize(const struct cli_bc *bc, uint16_t type)
{
    struct cli_bc_type *ty;
    unsigned j;

    type &= 0x7fff;
    if (!type)
	return 0;
    if (type <= 8)
	return 1;
    if (type <= 16)
	return 2;
    if (type <= 32)
	return 4;
    if (type <= 64)
	return 8;
    ty = &bc->types[type-65];
    if (ty->size)
	return ty->size;
    switch (ty->kind) {
	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;
    }
    if (!ty->size && ty->kind != DFunctionType) {
	cli_warnmsg("type %d size is 0\n", type-65);
    }
    return ty->size;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_version_compare(struct cli_bc_ctx *ctx , const uint8_t* lhs, uint32_t lhs_len, 
				  const uint8_t* rhs, uint32_t rhs_len)
{
    unsigned i = 0, j = 0;
    unsigned long li=0, ri=0;
    do {
	while (i < lhs_len && j < rhs_len && lhs[i] == rhs[j] &&
	       !isdigit(lhs[i]) && !isdigit(rhs[j])) {
	    i++; j++;
	}
	if (i == lhs_len && j == rhs_len)
	    return 0;
	if (i == lhs_len)
	    return -1;
	if (j == rhs_len)
	    return 1;
	if (!isdigit(lhs[i]) || !isdigit(rhs[j]))
	    return lhs[i] < rhs[j] ? -1 : 1;
	while (isdigit(lhs[i]) && i < lhs_len)
	    li = 10*li + (lhs[i++] - '0');
	while (isdigit(rhs[j]) && j < rhs_len)
	    ri = 10*ri + (rhs[j++] - '0');
	if (li < ri)
	    return -1;
	if (li > ri)
	    return 1;
    } while (1);
}

Project: clamav-devel
File: bytecode.c

static int parseApis(struct cli_bc *bc, unsigned char *buffer)
{
    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 */

    if (buffer[0] != 'E') {
	cli_errmsg("bytecode: Invalid api header: %c\n", buffer[0]);
	return CL_EMALFDB;
    }

    maxapi = readNumber(buffer, &offset, len, &ok);
    if (!ok)
	return CL_EMALFDB;
    if (maxapi > cli_apicall_maxapi) {
	cli_dbgmsg("bytecode using API %u, but highest API known to libclamav is %u, skipping\n", maxapi, cli_apicall_maxapi);
	return CL_BREAK;
    }
    calls = readNumber(buffer, &offset, len, &ok);
    if (!ok)
	return CL_EMALFDB;
    if (calls > maxapi) {
	cli_errmsg("bytecode: attempting to describe more APIs than max: %u > %u\n", calls, maxapi);
	return CL_EMALFDB;
    }
    bc->uses_apis = cli_bitset_init();
    if (!bc->uses_apis) {
	cli_errmsg("Out of memory allocating apis bitset\n");
	return CL_EMEM;
    }
    apity2ty = cli_calloc(cli_apicall_maxtypes, sizeof(*cli_apicall_types));
    if (!apity2ty) {
	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)
	    return CL_EMALFDB;

	/* APIcall is valid */
	cli_bitset_set(bc->uses_apis, id);
    }
    free(apity2ty); /* free temporary map */
    cli_dbgmsg("bytecode: Parsed %u APIcalls, maxapi %u\n", calls, maxapi);
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_lookupobj(struct cli_bc_ctx *ctx , uint32_t objid)
{
    unsigned i;
    if (!ctx->pdf_phase)
	return -1;
    for (i=0;i<ctx->pdf_nobjs;i++) {
	if (ctx->pdf_objs[i].id == objid)
	    return i;
    }
    return -1;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_seek(struct cli_bc_ctx* ctx, int32_t pos, uint32_t whence)
{
    off_t off;
    if (!ctx->fmap) {
	cli_dbgmsg("bcapi_seek: no fmap\n");
	API_MISUSE();
	return -1;
    }
    switch (whence) {
	case 0:
	    off = pos;
	    break;
	case 1:
	    off = ctx->off + pos;
	    break;
	case 2:
	    off = ctx->file_size + pos;
	    break;
	default:
	    API_MISUSE();
	    cli_dbgmsg("bcapi_seek: invalid whence value\n");
	    return -1;
    }
    if (off < 0 || off > ctx->file_size) {
	cli_dbgmsg("bcapi_seek: out of file: %ld (max %d)\n",
		   off, ctx->file_size);
	return -1;
    }
    cli_event_int(EV, BCEV_OFFSET, off);
    ctx->off = off;
    return off;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_file_find_limit(struct cli_bc_ctx *ctx , const uint8_t* data, uint32_t len, int32_t limit)
{
    char buf[4096];
    fmap_t *map = ctx->fmap;
    uint32_t off = ctx->off;
    int n;

    if (!map || len > sizeof(buf)/4 || len <= 0 || limit <= 0) {
	cli_dbgmsg("bcapi_file_find_limit preconditions not met\n");
	API_MISUSE();
	return -1;
    }

    cli_event_int(EV, BCEV_OFFSET, off);
    cli_event_fastdata(EV, BCEV_FIND, data, len);
    for (;;) {
	const char *p;
	int32_t readlen = sizeof(buf);
	if (off + readlen > limit) {
	    readlen = limit - off;
	    if (readlen < 0)
		return -1;
	}
	n = fmap_readn(map, buf, off, readlen);
	if ((unsigned)n < len || n < 0)
	    return -1;
	p = cli_memmem(buf, n, data, len);
	if (p)
	    return off + p - buf;
	off += n;
    }
    return -1;
}

Project: clamav-devel
File: bytecode.c

static inline char *readData(const unsigned char *p, unsigned *off, unsigned len, char *ok, unsigned *datalen)
{
    unsigned char *dat, *q;
    unsigned l, newoff, i;
    if (p[*off] != '|') {
	cli_errmsg("Data start marker missing: %c\n", p[*off]);
	*ok = 0;
	return NULL;
    }
    (*off)++;
    l = readNumber(p, off, len, ok);
    if (!l || !ok) {
	*datalen = l;
	return NULL;
    }
    newoff = *off + 2*l;
    if (newoff > len) {
	cli_errmsg("Line ended while reading data\n");
	*ok = 0;
	return 0;
    }
    dat = cli_malloc(l);
    if (!dat) {
	cli_errmsg("Cannot allocate memory for data\n");
	*ok = 0;
	return NULL;
    }
    q = dat;
    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;
	    return 0;
	}
	*q++ = (v0&0xf) | ((v1&0xf) << 4);
    }
    *off = newoff;
    *datalen = l;
    return (char*)dat;
}

Project: clamav-devel
File: bytecode.c

static int register_events(cli_events_t *ev)
{
    unsigned i;
    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;
    }
    return 0;
}

Project: clamav-devel
File: bytecode.c

static inline uint64_t readNumber(const unsigned char *p, unsigned *off, unsigned len, char *ok)
{
    uint64_t n=0;
    unsigned i, newoff, lim, p0 = p[*off], shift=0;

    lim = p0 - 0x60;
    if (lim > 0x10) {
	cli_errmsg("Invalid number type: %c\n", p0);
	*ok = 0;
	return 0;
    }
    newoff = *off +lim+1;
    if (newoff > len) {
	cli_errmsg("End of line encountered while reading number\n");
	*ok = 0;
	return 0;
    }

    if (p0 == 0x60) {
	*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;
    }
    *off = newoff;
    return n;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_isin(struct cli_bc_ctx *ctx, int32_t a, int32_t b, int32_t c)
{
    double f;
    if (!b)
	return 0x7fffffff;
    f = c*sin((double)a/b);
    return (int32_t)myround(f);
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_buffer_pipe_new_fromfile(struct cli_bc_ctx *ctx , uint32_t at)
{
    struct bc_buffer *b;
    unsigned n = ctx->nbuffers + 1;

    if (at >= ctx->file_size)
	return -1;

    b = cli_realloc(ctx->buffers, sizeof(*ctx->buffers)*n);
    if (!b) {
	return -1;
    }
    ctx->buffers = b;
    ctx->nbuffers = n;
    b = &b[n-1];

    /* NULL data means read from file at pos read_cursor */
    b->data = NULL;
    b->size = 0;
    b->read_cursor = at;
    b->write_cursor = 0;
    return n-1;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_entropy_buffer(struct cli_bc_ctx *ctx , uint8_t* s, int32_t len)
{
    uint32_t probTable[256];
    unsigned i;
    double entropy = 0;
    double log2 = log(2);

    if (!s || len <= 0)
	return -1;
    memset(probTable, 0, sizeof(probTable));
    for (i=0;i<len;i++) {
	probTable[s[i]]++;
    }
    for (i=0;i<256;i++) {
	double p;
	if (!probTable[i])
	    continue;
	p = (double)probTable[i] / len;
	entropy += -p*log(p)/log2;
    }
    entropy *= 1<<26;
    return (uint32_t)entropy;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_get_offset(struct cli_bc_ctx *ctx , int32_t objidx)
{
    if (!ctx->pdf_phase ||
	objidx >= ctx->pdf_nobjs)
	return -1;
    return ctx->pdf_startoff + ctx->pdf_objs[objidx].start;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_inflate_process(struct cli_bc_ctx *ctx , int32_t id)
{
    int ret;
    unsigned avail_in_orig, avail_out_orig;
    struct bc_inflate *b = get_inflate(ctx, id);
    if (!b || b->from == -1 || b->to == -1)
	return -1;

    b->stream.avail_in = avail_in_orig =
	cli_bcapi_buffer_pipe_read_avail(ctx, b->from);

    b->stream.next_in = cli_bcapi_buffer_pipe_read_get(ctx, b->from,
						       b->stream.avail_in);

    b->stream.avail_out = avail_out_orig =
	cli_bcapi_buffer_pipe_write_avail(ctx, b->to);

    b->stream.next_out = cli_bcapi_buffer_pipe_write_get(ctx, b->to,
							 b->stream.avail_out);

    if (!b->stream.avail_in || !b->stream.avail_out || !b->stream.next_in || !b->stream.next_out)
	return -1;
    /* try hard to extract data, skipping over corrupted data */
    do {
	if (!b->needSync) {
	    ret = inflate(&b->stream, Z_NO_FLUSH);
	    if (ret == Z_DATA_ERROR) {
		cli_dbgmsg("bytecode api: inflate at %lu: %s, trying to recover\n", b->stream.total_in,
			   b->stream.msg);
		b->needSync = 1;
	    }
	}
	if (b->needSync) {
	    ret = inflateSync(&b->stream);
	    if (ret == Z_OK) {
		cli_dbgmsg("bytecode api: successfully recovered inflate stream\n");
		b->needSync = 0;
		continue;
	    }
	}
	break;
    } while (1);
    cli_bcapi_buffer_pipe_read_stopped(ctx, b->from, avail_in_orig - b->stream.avail_in);
    cli_bcapi_buffer_pipe_write_stopped(ctx, b->to, avail_out_orig - b->stream.avail_out);

    if (ret == Z_MEM_ERROR) {
	cli_dbgmsg("bytecode api: out of memory!\n");
	cli_bcapi_inflate_done(ctx, id);
	return ret;
    }
    if (ret == Z_STREAM_END) {
	cli_bcapi_inflate_done(ctx, id);
    }
    if (ret == Z_BUF_ERROR) {
	cli_dbgmsg("bytecode api: buffer error!\n");
    }

    return ret;
}

Project: clamav-devel
File: bytecode.c

static int16_t get_optype(const struct cli_bc_func *bcfunc, operand_t op)
{
    if (op >= bcfunc->numArgs + bcfunc->numLocals)
	return 0;
    return bcfunc->types[op]&0x7fff;
}

Project: clamav-devel
File: bytecode_api.c

static struct bc_buffer *get_buffer(struct cli_bc_ctx *ctx, int32_t id)
{
    if (!ctx->buffers || id < 0 || id >= ctx->nbuffers) {
	cli_dbgmsg("bytecode api: invalid buffer id %u\n", id);
	return NULL;
    }
    return &ctx->buffers[id];
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_done(struct cli_all_bc *allbc)
{
    return cli_bytecode_done_jit(allbc, 0);
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_get_dumpedobjid(struct cli_bc_ctx *ctx)
{
    if (ctx->pdf_phase != PDF_PHASE_POSTDUMP)
	return -1;
    return ctx->pdf_dumpedid;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_engine_dconf_level(struct cli_bc_ctx *ctx)
{
    return CL_FLEVEL_DCONF;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_trace_ptr(struct cli_bc_ctx *ctx, const uint8_t* ptr, uint32_t dummy)
{
    if (LIKELY(ctx->trace_level < trace_val))
	return 0;
    if (ctx->trace_level&0x80) {
	if ((ctx->trace_level&0x7f) < trace_param)
	    return 0;
	ctx->trace(ctx, trace_param);
    }
    if (ctx->trace_ptr)
	ctx->trace_ptr(ctx, ptr);
    return 0;
}

Project: clamav-devel
File: bytecode.c

static inline unsigned readFixedNumber(const unsigned char *p, unsigned *off,
				       unsigned len, char *ok, unsigned width)
{
    unsigned i, n=0, shift=0;
    unsigned newoff = *off + width;
    if (newoff > len) {
	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;
    }
    *off = newoff;
    return n;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_hashset_new(struct cli_bc_ctx *ctx )
{
    unsigned  n = ctx->nhashsets+1;
    struct cli_hashset *s = cli_realloc(ctx->hashsets, sizeof(*ctx->hashsets)*n);
    if (!s) {
	cli_event_error_oom(EV, 0);
	return -1;
    }
    ctx->hashsets = s;
    ctx->nhashsets = n;
    s = &s[n-1];
    cli_hashset_init(s, 16, 80);
    return n-1;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_getobjid(struct cli_bc_ctx *ctx , int32_t objidx)
{
    if (!ctx->pdf_phase ||
	objidx >= ctx->pdf_nobjs)
	return -1;
    return ctx->pdf_objs[objidx].id;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_read(struct cli_bc_ctx* ctx, uint8_t *data, int32_t size)
{
    int n;
    if (!ctx->fmap) {
	API_MISUSE();
	return -1;
    }
    if (size < 0 || size > CLI_MAX_ALLOCATION) {
	cli_warnmsg("bytecode: negative read size: %d\n", size);
	API_MISUSE();
	return -1;
    }
    n = fmap_readn(ctx->fmap, data, ctx->off, size);
    if (n <= 0) {
	cli_dbgmsg("bcapi_read: fmap_readn failed (requested %d)\n", size);
	cli_event_count(EV, BCEV_READ_ERR);
	return n;
    }
    cli_event_int(EV, BCEV_OFFSET, ctx->off);
    cli_event_fastdata(EV, BCEV_READ, data, size);
    //cli_event_data(EV, BCEV_READ, data, n);
    ctx->off += n;
    return n;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_memstr(struct cli_bc_ctx *ctx, const uint8_t* h, int32_t hs,
			 const uint8_t*n, int32_t ns)
{
    const uint8_t *s;
    if (!h || !n || hs < 0 || ns < 0) {
	API_MISUSE();
	return -1;
    }
    cli_event_fastdata(EV, BCEV_MEM_1, h, hs);
    cli_event_fastdata(EV, BCEV_MEM_2, n, ns);
    s = (const uint8_t*) cli_memstr((const char*)h, hs, (const char*)n, ns);
    if (!s)
	return -1;
    return s - h;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_debug_print_str(struct cli_bc_ctx *ctx, const uint8_t *str, uint32_t len)
{
    cli_event_fastdata(EV, BCEV_DBG_STR, str, strlen((const char*)str));
    cli_dbgmsg("bytecode debug: %s\n", str);
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_file_byteat(struct cli_bc_ctx *ctx, uint32_t off)
{
    unsigned char c;
    if (!ctx->fmap) {
	cli_dbgmsg("bcapi_file_byteat: no fmap\n");
	return -1;
    }
    cli_event_int(EV, BCEV_OFFSET, off);
    if (fmap_readn(ctx->fmap, &c, off, 1) != 1) {
	cli_dbgmsg("bcapi_file_byteat: fmap_readn failed at %u\n", off);
	return -1;
    }
    return c;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *bcs, unsigned dconfmask)
{
    unsigned i, interp = 0, jitok = 0, jitcount=0;
    int rc;
    struct cli_bc_ctx *ctx;

    if (!bcs->count) {
	cli_dbgmsg("No bytecodes loaded, not running builtin test\n");
	return CL_SUCCESS;
    }

    cli_detect_environment(&bcs->env);
    switch (bcs->env.arch) {
	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;
    }
    cli_dbgmsg("Bytecode: mode is %d\n", engine->bytecode_mode);

    ctx = cli_bytecode_context_alloc();
    if (!ctx) {
	cli_errmsg("Bytecode: failed to allocate bytecode context\n");
	return CL_EMEM;
    }
    rc = run_builtin_or_loaded(bcs, BC_STARTUP, builtin_bc_startup, ctx, "BC_STARTUP");
    if (rc != CL_SUCCESS) {
	cli_warnmsg("Bytecode: BC_STARTUP failed to run, disabling ALL bytecodes! Please report to http://bugs.clamav.net\n");
	ctx->bytecode_disable_status = 2;
    } else {
	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
	 * buggy bytecode to return 0 */
	if (rc != 0xda7aba5e) {
	    cli_warnmsg("Bytecode: selftest failed with code %08x. Please report to http://bugs.clamav.net\n",
			rc);
	    if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
		return CL_EBYTECODE_TESTFAIL;
	}
    }
    switch (ctx->bytecode_disable_status) {
	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;
    }
    cli_bytecode_context_destroy(ctx);


    if (engine->bytecode_mode != CL_BYTECODE_MODE_INTERPRETER &&
	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;
	}
    } else {
	cli_bytecode_done_jit(bcs, 0);
    }

    if (!(dconfmask & BYTECODE_INTERPRETER)) {
	cli_dbgmsg("Bytecode: needs interpreter, but interpreter is disabled\n");
	if (set_mode(engine, CL_BYTECODE_MODE_OFF) == -1)
	    return CL_EBYTECODE_TESTFAIL;
    }

    if (engine->bytecode_mode == CL_BYTECODE_MODE_OFF) {
	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++;
    }
    cli_dbgmsg("Bytecode: %u bytecode prepared with JIT, "
	       "%u prepared with interpreter, %u total\n", jitcount, interp, bcs->count);
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *bc, unsigned funcid)
{
    unsigned i, s=0;
    const struct cli_bc_func *func;
    if (funcid >= bc->num_func) {
	cli_errmsg("bytecode: function ID doesn't exist: %u\n", funcid);
	return CL_EARG;
    }
    func = ctx->func = &bc->funcs[funcid];
    ctx->bc = bc;
    ctx->numParams = func->numArgs;
    ctx->funcid = funcid;
    if (func->numArgs) {
	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;
    ctx->values = cli_malloc(s);
    if (!ctx->values) {
	cli_errmsg("bytecode: error allocating memory for parameters\n");
	return CL_EMEM;
    }
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_inflate_done(struct cli_bc_ctx *ctx , int32_t id)
{
    int ret;
    struct bc_inflate *b = get_inflate(ctx, id);
    if (!b || b->from == -1 || b->to == -1)
	return -1;
    ret = inflateEnd(&b->stream);
    if (ret == Z_STREAM_ERROR)
	cli_dbgmsg("bytecode api: inflateEnd: %s\n", b->stream.msg);
    b->from = b->to = -1;
    return ret;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_hashset_done(struct cli_bc_ctx *ctx , int32_t id)
{
    struct cli_hashset *s = get_hashset(ctx, id);
    if (!s)
	return -1;
    cli_hashset_destroy(s);
    if (id == ctx->nhashsets-1) {
	ctx->nhashsets--;
	if (!ctx->nhashsets) {
	    free(ctx->hashsets);
	    ctx->hashsets = NULL;
	} else {
	    s = cli_realloc(ctx->hashsets, ctx->nhashsets*sizeof(*s));
	    if (s)
		ctx->hashsets = s;
	}
    }
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

int cli_bytecode_context_setpdf(struct cli_bc_ctx *ctx, unsigned phase,
				unsigned nobjs,
				struct pdf_obj *objs, uint32_t *pdf_flags,
				uint32_t pdfsize, uint32_t pdfstartoff)
{
    ctx->pdf_nobjs = nobjs;
    ctx->pdf_objs = objs;
    ctx->pdf_flags = pdf_flags;
    ctx->pdf_size = pdfsize;
    ctx->pdf_startoff = pdfstartoff;
    ctx->pdf_phase = phase;
    return 0;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx,
			 unsigned id, fmap_t *map, const char **virname)
{
    const unsigned *hooks = engine->hooks[id - _BC_START_HOOKS];
    unsigned i, hooks_cnt = engine->hooks_cnt[id - _BC_START_HOOKS];
    int ret;
    unsigned executed = 0, breakflag = 0, errorflag = 0;

    cli_dbgmsg("Bytecode executing hook id %u (%u hooks)\n", id, hooks_cnt);
    /* restore match counts */
    cli_bytecode_context_setfile(ctx, map);
    ctx->hooks.match_counts = ctx->lsigcnt;
    ctx->hooks.match_offsets = ctx->lsigoff;
    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 found virus: %s\n", ctx->virname);
	    if (virname)
		*virname = ctx->virname;
	    cli_bytecode_context_clear(ctx);
	    return CL_VIRUS;
	}
	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 && 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, cctx);
		cctx->recursion--;
		if (!cctx || !cctx->engine->keeptmp)
		    if (ftruncate(fd, 0) == -1)
			cli_dbgmsg("ftruncate failed on %d\n", fd);
		close(fd);
		if (!cctx || !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);
		    cli_bytecode_context_clear(ctx);
		    return ret;
		}
		cli_bytecode_context_reset(ctx);
		continue;
	    }
	}
	cli_bytecode_context_reset(ctx);
    }
    if (executed)
	cli_dbgmsg("Bytecode: executed %u bytecodes for this hook\n", executed);
    else
	cli_dbgmsg("Bytecode: no logical signature matched, no bytecode executed\n");
    if (errorflag && cctx && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
	return CL_EBYTECODE_TESTFAIL;
    return breakflag ? CL_BREAK : CL_CLEAN;
}

Project: clamav-devel
File: bytecode.c

static int run_selfcheck(struct cli_all_bc *bcs)
{
    struct cli_bc_ctx *ctx;
    struct cli_bc *bc = &bcs->all_bcs[bcs->count-1];
    int rc;
    if (bc->state != bc_jit && bc->state != bc_interp) {
	cli_errmsg("Failed to prepare selfcheck bytecode\n");
	return CL_EBYTECODE;
    }
    ctx = cli_bytecode_context_alloc();
    if (!ctx) {
	cli_errmsg("Failed to allocate bytecode context\n");
	return CL_EMEM;
    }
    cli_bytecode_context_setfuncid(ctx, bc, 0);

    cli_dbgmsg("bytecode self test running\n");
    ctx->bytecode_timeout = 0;
    rc = cli_bytecode_run(bcs, bc, ctx);
    cli_bytecode_context_destroy(ctx);
    if (rc != CL_SUCCESS) {
	cli_errmsg("bytecode self test failed: %s\n",
		   cl_strerror(rc));
    } else {
	cli_dbgmsg("bytecode self test succeeded\n");
    }
    return rc;
}

Project: clamav-devel
File: bytecode.c

static void parseType(struct cli_bc *bc, struct cli_bc_type *ty,
		      unsigned char *buffer, unsigned *off, unsigned len,
		      char *ok)
{
    unsigned j;

    ty->numElements = readNumber(buffer, off, len, ok);
    if (!*ok) {
	cli_errmsg("Error parsing type\n");
	*ok = 0;
	return;
    }
    ty->containedTypes = cli_malloc(sizeof(*ty->containedTypes)*ty->numElements);
    if (!ty->containedTypes) {
	cli_errmsg("Out of memory allocating %u types\n", ty->numElements);
	*ok = 0;
	return;
    }
    for (j=0;j<ty->numElements;j++) {
	ty->containedTypes[j] = readTypeID(bc, buffer, off, len, ok);
    }
}

Project: clamav-devel
File: bytecode.c

static int calc_gepz(struct cli_bc *bc, struct cli_bc_func *func, uint16_t tid, operand_t op)
{
    unsigned off = 0, i;
    uint32_t *gepoff;
    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 <= 65) {
	cli_errmsg("bytecode: invalid type for gep (%u)\n", tid);
	return -1;
    }
    ty = &bc->types[tid - 65];
    if (ty->kind != DPointerType || ty->containedTypes[0] < 65) {
	cli_errmsg("bytecode: invalid gep type, must be pointer to nonint: %u\n", tid);
	return -1;
    }
    ty = &bc->types[ty->containedTypes[0] - 65];
    if (ty->kind != DStructType && ty->kind != DPackedStructType)
	return 0;
    gepoff = (uint32_t*)&func->constants[op - func->numValues];
    if (*gepoff >= ty->numElements) {
	cli_errmsg("bytecode: gep offset out of range: %d >= %d\n",(uint32_t)*gepoff, ty->numElements);
	return -1;
    }
    for (i=0;i<*gepoff;i++) {
	off += typesize(bc, ty->containedTypes[i]);
    }
    *gepoff = off;
    return 1;
}

Project: clamav-devel
File: bytecode.c

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;
    bcs.count = 0;
    bcs.engine = engine;
    rc = add_selfcheck(&bcs);
    if (rc == CL_SUCCESS) {
	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;
    }
    cli_bytecode_destroy(bcs.all_bcs);
    free(bcs.all_bcs);
    cli_bytecode_done_jit(&bcs, 1);
    if (rc != CL_SUCCESS) {
	cli_errmsg("Bytecode: failed to run selfcheck in %s mode: %s\n",
		   jit ? "JIT" : "interpreter", cl_strerror(rc));
    }
    return rc;
}

Project: clamav-devel
File: bytecode.c

static int parseMD(struct cli_bc *bc, unsigned char *buffer)
{
    unsigned offset = 1, len = strlen((const char*)buffer);
    unsigned numMD, i, b;
    char ok = 1;
    if (buffer[0] != 'D')
	return CL_EMALFDB;
    numMD = readNumber(buffer, &offset, len, &ok);
    if (!ok) {
	cli_errmsg("Unable to parse number of MD nodes\n");
	return CL_EMALFDB;
    }
    b = bc->dbgnode_cnt;
    bc->dbgnode_cnt += numMD;
    bc->dbgnodes = cli_realloc(bc->dbgnodes, bc->dbgnode_cnt * sizeof(*bc->dbgnodes));
    if (!bc->dbgnodes)
	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;
	    }
	}
    }
    cli_dbgmsg("bytecode: Parsed %u nodes total\n", bc->dbgnode_cnt);
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode.c

static int cli_bytecode_prepare_interpreter(struct cli_bc *bc)
{
    unsigned i, j, k;
    uint64_t *gmap;
    unsigned bcglobalid = cli_apicall_maxglobal - _FIRST_GLOBAL+2;
    bc->numGlobalBytes = 0;
    gmap = cli_malloc(bc->num_globals*sizeof(*gmap));
    if (!gmap)
	return CL_EMEM;
    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);
    }
    if (bc->numGlobalBytes) {
	bc->globalBytes = cli_calloc(1, bc->numGlobalBytes);
	if (!bc->globalBytes)
	    return CL_EMEM;
    } else
	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);
		    }
		    break;
		}
	    default:
		/*TODO*/
		if (!bc->globals[j][1])
		    continue; /* null */
		break;
	}
    }

    for (i=0;i<bc->num_func;i++) {
	struct cli_bc_func *bcfunc = &bc->funcs[i];
	unsigned totValues = bcfunc->numValues + bcfunc->numConstants + bc->num_globals;
	unsigned *map = cli_malloc(sizeof(*map)*totValues);
	if (!map)
	    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;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);
			    return CL_EBYTECODE;
			}
			if (inst->u.ops.numOps != target->numArgs) {
			    cli_errmsg("bytecode: call operands don't match function prototype\n");
			    return 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");
			    return CL_EBYTECODE;
			}
		    }
		    if (inst->u.ops.numOps) {
			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");
			    return CL_EMEM;
			}
		    } else
			inst->u.ops.opsizes = NULL;
		    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");
                      return CL_EBYTECODE;
                    }
		    MAP(inst->u.three[1]);
		    MAP(inst->u.three[2]);
                    inst->u.three[0] = get_geptypesize(bc, inst->u.three[0]);
                    if (inst->u.three[0] == -1)
                      return 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)
			return CL_EBYTECODE;
		    MAP(inst->u.three[2]);
		    break;
/*		case OP_BC_GEPN:
		    *TODO 
		    break;*/
		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);
		    return CL_EBYTECODE;
	    }
	}
	free(map);
    }
    free(gmap);
    bc->state = bc_interp;
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_debug_print_str_start(struct cli_bc_ctx *ctx , const uint8_t* s, uint32_t len)
{
    if (!s || len <= 0)
	return -1;
    cli_event_fastdata(EV, BCEV_DBG_STR, s, len);
    cli_dbgmsg("bytecode debug: %.*s", len, s);
    return 0;
}

Project: clamav-devel
File: bytecode.c

static inline funcid_t readFuncID(struct cli_bc *bc, unsigned char *p,
				  unsigned *off, unsigned len, char *ok)
{
    funcid_t id = readNumber(p, off, len, ok)-1;
    if (*ok && id >= bc->num_func) {
	cli_errmsg("Called function out of range: %u >= %u\n", id, bc->num_func);
	*ok = 0;
	return ~0;
    }
    return id;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_trace_source(struct cli_bc_ctx *ctx, const uint8_t *file, uint32_t line)
{
    if (LIKELY(ctx->trace_level < trace_line))
	return 0;
    if (ctx->file != (const char*)file || ctx->line != line) {
	ctx->col = 0;
	ctx->file =(const char*)file ? (const char*)file : "??";
	ctx->line = line;
    }
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_buffer_pipe_read_avail(struct cli_bc_ctx *ctx , int32_t id)
{
    struct bc_buffer *b = get_buffer(ctx, id);
    if (!b)
	return 0;
    if (b->data) {
	if (b->write_cursor <= b->read_cursor)
	    return 0;
	return b->write_cursor - b->read_cursor;
    }
    if (!ctx->fmap || b->read_cursor >= ctx->file_size)
	return 0;
    if (b->read_cursor + BUFSIZ <= ctx->file_size)
	return BUFSIZ;
    return ctx->file_size - b->read_cursor;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_trace_value(struct cli_bc_ctx *ctx, const uint8_t* name, uint32_t value)
{
    if (LIKELY(ctx->trace_level < trace_val))
	return 0;
    if (ctx->trace_level&0x80) {
	if ((ctx->trace_level&0x7f) < trace_param)
	    return 0;
	ctx->trace(ctx, trace_param);
    }
    if (ctx->trace_val && name)
	ctx->trace_val(ctx, (const char*)name, value);
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_get_file_reliability(struct cli_bc_ctx *ctx )
{
    cli_ctx *cctx = (cli_ctx*)ctx->ctx;
    return cctx ? cctx->corrupted_input : 3;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_engine_db_options(struct cli_bc_ctx *ctx)
{
    cli_ctx *cctx = (cli_ctx*)ctx->ctx;
    return cctx->engine->dboptions;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_load(struct cli_bc *bc, FILE *f, struct cli_dbio *dbio, int trust)
{
    unsigned row = 0, current_func = 0, bb=0;
    char *buffer;
    unsigned linelength=0;
    char firstbuf[FILEBUFF];
    enum parse_state state;
    int rc, end=0;

    memset(bc, 0, sizeof(*bc));
    cli_dbgmsg("Loading %s bytecode\n", trust ? "trusted" : "untrusted");
    bc->trusted = trust;
    if (!f && !dbio) {
	cli_errmsg("Unable to load bytecode (null file)\n");
	return CL_ENULLARG;
    }
    if (!cli_dbgets(firstbuf, FILEBUFF, f, dbio)) {
	cli_errmsg("Unable to load bytecode (empty file)\n");
	return CL_EMALFDB;
    }
    cli_chomp(firstbuf);
    rc = parseHeader(bc, (unsigned char*)firstbuf, &linelength);
    state = PARSE_BC_LSIG;
    if (rc == CL_BREAK) {
	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;
    }
    if (rc != CL_SUCCESS) {
	cli_errmsg("Error at bytecode line %u\n", row);
	return rc;
    }
    buffer = cli_malloc(linelength);
    if (!buffer) {
	cli_errmsg("Out of memory allocating line of length %u\n", linelength);
	return CL_EMEM;
    }
    while (cli_dbgets(buffer, linelength, f, dbio) && !end) {
	cli_chomp(buffer);
	row++;
	switch (state) {
	    case PARSE_BC_LSIG:
		rc = parseLSig(bc, 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_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:
                if (*buffer == 'S') {
		    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
		 * */
		if (buffer[0] == 'S')
		    end = 1;
		/* noop parse, but we need to use dbgets with dynamic buffer,
		 * otherwise we get 'Line too long for provided buffer' */
		break;
	}
    }
    free(buffer);
    cli_dbgmsg("Parsed %d functions\n", current_func);
    if (current_func != bc->num_func && bc->state != bc_skip) {
	cli_errmsg("Loaded less functions than declared: %u vs. %u\n",
		   current_func, bc->num_func);
	return CL_EMALFDB;
    }
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_hex2ui(struct cli_bc_ctx *ctx, uint32_t ah, uint32_t bh)
{
    char result = 0;
    unsigned char in[2];
    in[0] = ah;
    in[1] = bh;

    if (cli_hex2str_to((const char*)in, &result, 2) == -1)
	return -1;
    return result;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_get_obj_num(struct cli_bc_ctx *ctx)
{
    if (!ctx->pdf_phase)
	return -1;
    return ctx->pdf_nobjs;
}

Project: clamav-devel
File: bytecode.c

static bbid_t readBBID(struct cli_bc_func *func, const unsigned char *buffer, unsigned *off, unsigned len, char *ok) {
    unsigned id = readNumber(buffer, off, len, ok);
    if (!id || id >= func->numBB) {
	cli_errmsg("Basic block ID out of range: %u\n", id);
	*ok = 0;
    }
    if (!*ok)
	return ~0;
    return id;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64_t c)
{
    if (i >= ctx->numParams) {
	cli_errmsg("bytecode: param index out of bounds: %u\n", i);
	return CL_EARG;
    }
    if (!type_isint(ctx->func->types[i])) {
	cli_errmsg("bytecode: parameter type mismatch\n");
	return CL_EARG;
    }
    switch (ctx->opsizes[i]) {
	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;
    }
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode.c

static inline int get_geptypesize(const struct cli_bc *bc, uint16_t tid)
{
  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]);
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_bytecode_rt_error(struct cli_bc_ctx *ctx , int32_t id)
{
    int32_t line = id >> 8;
    int32_t col = id&0xff;
    cli_warnmsg("Bytecode runtime error at line %u, col %u\n", line, col);
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

uint8_t* cli_bcapi_malloc(struct cli_bc_ctx *ctx, uint32_t size)
{
    void *v;
#if USE_MPOOL
    if (!ctx->mpool) {
	ctx->mpool = mpool_create();
	if (!ctx->mpool) {
	    cli_dbgmsg("bytecode: mpool_create failed!\n");
	    cli_event_error_oom(EV, 0);
	    return NULL;
	}
    }
    v = mpool_malloc(ctx->mpool, size);
#else
    /* TODO: implement using a list of pointers we allocated! */
    cli_errmsg("cli_bcapi_malloc not implemented for systems without mmap yet!\n");
    v = cli_malloc(size);
#endif
    if (!v)
	cli_event_error_oom(EV, size);
    return v;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_inflate_init(struct cli_bc_ctx *ctx, int32_t from, int32_t to, int32_t windowBits)
{
    int ret;
    z_stream stream;
    struct bc_inflate *b;
    unsigned n = ctx->ninflates + 1;
    if (!get_buffer(ctx, from) || !get_buffer(ctx, to)) {
	cli_dbgmsg("bytecode api: inflate_init: invalid buffers!\n");
	return -1;
    }
    memset(&stream, 0, sizeof(stream));
    ret = inflateInit2(&stream, windowBits);
    switch (ret) {
	case Z_MEM_ERROR:
	    cli_dbgmsg("bytecode api: inflateInit2: out of memory!\n");
	    return -1;
	case Z_VERSION_ERROR:
	    cli_dbgmsg("bytecode api: inflateinit2: zlib version error!\n");
	    return -1;
	case Z_STREAM_ERROR:
	    cli_dbgmsg("bytecode api: inflateinit2: zlib stream error!\n");
	    return -1;
	case Z_OK:
	    break;
	default:
	    cli_dbgmsg("bytecode api: inflateInit2: unknown error %d\n", ret);
	    return -1;
    }

    b = cli_realloc(ctx->inflates, sizeof(*ctx->inflates)*n);
    if (!b) {
	inflateEnd(&stream);
	return -1;
    }
    ctx->inflates = b;
    ctx->ninflates = n;
    b = &b[n-1];

    b->from = from;
    b->to = to;
    b->needSync = 0;
    memcpy(&b->stream, &stream, sizeof(stream));
    return n-1;
}

Project: clamav-devel
File: bytecode.c

static inline funcid_t readAPIFuncID(struct cli_bc *bc, unsigned char *p,
				     unsigned *off, unsigned len, char *ok)
{
    funcid_t id = readNumber(p, off, len, ok)-1;
    if (*ok && !cli_bitset_test(bc->uses_apis, id)) {
	cli_errmsg("Called undeclared API function: %u\n", id);
	*ok = 0;
	return ~0;
    }
    return id;
}

Project: clamav-devel
File: bytecode_api.c

static inline double myround(double a)
{
    if (a < 0)
	return a-0.5;
    return a+0.5;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_fill_buffer(struct cli_bc_ctx *ctx, uint8_t* buf,
			      uint32_t buflen, uint32_t filled,
			      uint32_t pos, uint32_t fill)
{
    int32_t res, remaining, tofill;
    if (!buf || !buflen || buflen > CLI_MAX_ALLOCATION || filled > buflen) {
	cli_dbgmsg("fill_buffer1\n");
	API_MISUSE();
	return -1;
    }
    if (ctx->off >= ctx->file_size) {
	cli_dbgmsg("fill_buffer2\n");
	API_MISUSE();
	return 0;
    }
    remaining = filled - pos;
    if (remaining) {
	if (!CLI_ISCONTAINED(buf, buflen, buf+pos, remaining)) {
	    cli_dbgmsg("fill_buffer3\n");
	    API_MISUSE();
	    return -1;
	}
	memmove(buf, buf+pos, remaining);
    }
    tofill = buflen - remaining;
    if (!CLI_ISCONTAINED(buf, buflen, buf+remaining, tofill)) {
	cli_dbgmsg("fill_buffer4\n");
	API_MISUSE();
	return -1;
    }
    res = cli_bcapi_read(ctx, buf+remaining, tofill);
    if (res <= 0) {
	cli_dbgmsg("fill_buffer5\n");
	API_MISUSE();
	return res;
    }
    return remaining + res;
}

Project: clamav-devel
File: bytecode.c

static inline operand_t readOperand(struct cli_bc_func *func, unsigned char *p,
				    unsigned *off, unsigned len, char *ok)
{
    uint64_t v;
    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.
	 * This is needed on big-endian machines, because constants are always
	 * read as u64, but accesed as one of these types: u8, u16, u32, u64 */
	*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++;
    }
    v = readNumber(p, off, len, ok);
    if (!*ok)
	return MAX_OP;
    if (v >= func->numValues) {
	cli_errmsg("Operand index exceeds bounds: %u >= %u!\n", (unsigned)v, (unsigned)func->numValues);
	*ok = 0;
	return MAX_OP;
    }
    return v;
}

Project: clamav-devel
File: bytecode_api.c

static struct cli_map *get_hashtab(struct cli_bc_ctx *ctx, int32_t id)
{
    if (id < 0 || id >= ctx->nmaps || !ctx->maps)
	return NULL;
    return &ctx->maps[id];
}

Project: clamav-devel
File: bytecode.c

static int parseGlobals(struct cli_bc *bc, unsigned char *buffer)
{
    unsigned i, offset = 1, len = strlen((const char*)buffer), numglobals;
    unsigned maxglobal;
    char ok=1;

    if (buffer[0] != 'G') {
	cli_errmsg("bytecode: Invalid globals header: %c\n", buffer[0]);
	return CL_EMALFDB;
    }
    maxglobal = readNumber(buffer, &offset, len, &ok);
    if (maxglobal > cli_apicall_maxglobal) {
	cli_dbgmsg("bytecode using global %u, but highest global known to libclamav is %u, skipping\n", maxglobal, cli_apicall_maxglobal);
	return CL_BREAK;
    }
    numglobals = readNumber(buffer, &offset, len, &ok);
    bc->globals = cli_calloc(numglobals, sizeof(*bc->globals));
    if (!bc->globals) {
	cli_errmsg("bytecode: OOM allocating memory for %u globals\n", numglobals);
	return CL_EMEM;
    }
    bc->globaltys = cli_calloc(numglobals, sizeof(*bc->globaltys));
    if (!bc->globaltys) {
	cli_errmsg("bytecode: OOM allocating memory for %u global types\n", numglobals);
	return CL_EMEM;
    }
    bc->num_globals = numglobals;
    if (!ok)
	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);
    }
    if (!ok)
	return CL_EMALFDB;
    if (offset != len) {
	cli_errmsg("Trailing garbage in globals: %d extra bytes\n",
		   len-offset);
	return CL_EMALFDB;
    }
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_context_getresult_file(struct cli_bc_ctx *ctx, char **tempfilename)
{
    int fd;
    *tempfilename = ctx->tempfile;
    fd  = ctx->outfd;
    ctx->tempfile = NULL;
    ctx->outfd = 0;
    return fd;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_extract_new(struct cli_bc_ctx *ctx, int32_t id)
{
    cli_ctx *cctx;
    int res = -1;

    cli_event_count(EV, BCEV_EXTRACTED);
    cli_dbgmsg("previous tempfile had %u bytes\n", ctx->written);
    if (!ctx->written)
	return 0;
    if (ctx->ctx && cli_updatelimits(ctx->ctx, ctx->written))
	return -1;
    ctx->written = 0;
    lseek(ctx->outfd, 0, SEEK_SET);
    cli_dbgmsg("bytecode: scanning extracted file %s\n", ctx->tempfile);
    cctx = (cli_ctx*)ctx->ctx;
    if (cctx) {
	cli_file_t current = cctx->container_type;
	if (ctx->containertype != CL_TYPE_ANY)
	    cctx->container_type = ctx->containertype;
	cctx->recursion++;
	res = cli_magic_scandesc(ctx->outfd, cctx);
	cctx->recursion--;
	cctx->container_type = current;
	if (res == CL_VIRUS) {
	    if (cctx->virname)
		ctx->virname = *cctx->virname;
	    ctx->found = 1;
	}
    }
    if ((cctx && cctx->engine->keeptmp) ||
	(ftruncate(ctx->outfd, 0) == -1)) {

	close(ctx->outfd);
	if (!(cctx && cctx->engine->keeptmp) && ctx->tempfile)
	    cli_unlink(ctx->tempfile);
	free(ctx->tempfile);
	ctx->tempfile = NULL;
	ctx->outfd = 0;
    }
    cli_dbgmsg("bytecode: extracting new file with id %u\n", id);
    return res;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_set_flags(struct cli_bc_ctx *ctx , int32_t flags)
{
    if (!ctx->pdf_phase)
	return -1;
    cli_dbgmsg("cli_pdf: bytecode set_flags %08x -> %08x\n",
	       *ctx->pdf_flags,
	       flags);
    *ctx->pdf_flags = flags;
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_file_find(struct cli_bc_ctx *ctx, const uint8_t* data, uint32_t len)
{
    fmap_t *map = ctx->fmap;
    if (!map || len <= 0) {
	cli_dbgmsg("bcapi_file_find preconditions not met\n");
	API_MISUSE();
	return -1;
    }
    return cli_bcapi_file_find_limit(ctx, data, len, map->len);
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_test1(struct cli_bc_ctx *ctx, uint32_t a, uint32_t b)
{
    return (a==0xf00dbeef && b==0xbeeff00d) ? 0x12345678 : 0x55;
}

Project: clamav-devel
File: bytecode.c

static int set_mode(struct cl_engine *engine, enum bytecode_mode mode)
{
    if (engine->bytecode_mode == mode)
	return 0;
    if (engine->bytecode_mode == CL_BYTECODE_MODE_OFF) {
	cli_errmsg("bytecode: already turned off, can't turn it on again!\n");
	return -1;
    }
    cli_dbgmsg("Bytecode: mode changed to %d\n", mode);
    if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST) {
	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;
    }
    if (engine->bytecode_mode == CL_BYTECODE_MODE_JIT) {
	cli_errmsg("bytecode: in JIT mode but JIT is about to be disabled: %d\n", mode);
	engine->bytecode_mode = mode;
	return -1;
    }
    engine->bytecode_mode = mode;
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_pe_rawaddr(struct cli_bc_ctx *ctx, uint32_t rva)
{
  uint32_t ret;
  unsigned err = 0;
  const struct cli_pe_hook_data *pe = ctx->hooks.pedata;
  ret = cli_rawaddr(rva, ctx->sections, pe->nsections, &err,
		    ctx->file_size, pe->hdr_size);
  if (err) {
    cli_dbgmsg("bcapi_pe_rawaddr invalid rva: %u\n", rva);
    return PE_INVALID_RVA;
  }
  return ret;
}

Project: clamav-devel
File: bytecode.c

static void add_static_types(struct cli_bc *bc)
{
    unsigned i;
    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;
    }
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_getobjflags(struct cli_bc_ctx *ctx , int32_t objidx)
{
    if (!ctx->pdf_phase ||
	objidx >= ctx->pdf_nobjs)
	return -1;
    return ctx->pdf_objs[objidx].flags;
}

Project: clamav-devel
File: bytecode.c

uint64_t cli_bytecode_context_getresult_int(struct cli_bc_ctx *ctx)
{
    return *(uint32_t*)ctx->values;/*XXX*/
}

Project: clamav-devel
File: bytecode.c

void cli_bytecode_describe(const struct cli_bc *bc)
{
    char buf[128];
    int cols;
    unsigned i;
    time_t stamp;
    int had;

    if (!bc) {
	printf("(null bytecode)\n");
	return;
    }

    stamp = bc->metadata.timestamp;
    printf("Bytecode format functionality level: %u\n", bc->metadata.formatlevel);
    printf("Bytecode metadata:\n\tcompiler version: %s\n",
	   bc->metadata.compiler ? bc->metadata.compiler : "N/A");
    printf("\tcompiled on: (%d) %s",
	   (uint32_t)stamp,
	   cli_ctime(&stamp, buf, sizeof(buf)));
    printf("\tcompiled by: %s\n", bc->metadata.sigmaker ? bc->metadata.sigmaker : "N/A");
    /*TODO: parse and display arch name, also take it into account when
      JITing*/
    printf("\ttarget exclude: %d\n", bc->metadata.targetExclude);
    printf("\tbytecode type: ");
    switch (bc->kind) {
	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 hook");
	    break;
	default:
	    printf("Unknown (type %u)", bc->kind);
	    break;
    }
    /* 0 means no limit */
    printf("\tbytecode functionality level: %u - %u\n",
	   bc->metadata.minfunc, bc->metadata.maxfunc);
    printf("\tbytecode logical signature: %s\n",
	       bc->lsig ? bc->lsig : "<none>");
    printf("\tvirusname prefix: %s\n",
	   bc->vnameprefix);
    printf("\tvirusnames: %u\n", bc->vnames_cnt);
    printf("\tbytecode triggered on: ");
    switch (bc->kind) {
	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;
	default:
	    puts("N/A (unknown type)\n");
	    break;
    }
    printf("\tnumber of functions: %u\n\tnumber of types: %u\n",
	   bc->num_func, bc->num_types);
    printf("\tnumber of global constants: %u\n", (unsigned)bc->num_globals);
    printf("\tnumber of debug nodes: %u\n", bc->dbgnode_cnt);
    printf("\tbytecode APIs used:");
    cols = 0; /* remaining */
    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 > cols) {
		printf("\n\t");
		cols = 72;
	    }
	    printf(" %s", cli_apicalls[i].name);
	    had = 1;
	    cols -= len;
	}
    }
    printf("\n");
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *data, unsigned datalen)
{
    cli_errmsg("Pointer parameters are not implemented yet!\n");
    return CL_EARG;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_buffer_pipe_new(struct cli_bc_ctx *ctx, uint32_t size)
{
    unsigned char *data;
    struct bc_buffer *b;
    unsigned n = ctx->nbuffers + 1;

    data = cli_calloc(1, size);
    if (!data)
	return -1;
    b = cli_realloc(ctx->buffers, sizeof(*ctx->buffers)*n);
    if (!b) {
	free(data);
	return -1;
    }
    ctx->buffers = b;
    ctx->nbuffers = n;
    b = &b[n-1];

    b->data = data;
    b->size = size;
    b->write_cursor = b->read_cursor = 0;
    return n-1;
}

Project: clamav-devel
File: bytecode_api.c

void cli_bytecode_context_set_trace(struct cli_bc_ctx* ctx, unsigned level,
				    bc_dbg_callback_trace trace,
				    bc_dbg_callback_trace_op trace_op,
				    bc_dbg_callback_trace_val trace_val,
				    bc_dbg_callback_trace_ptr trace_ptr)
{
    ctx->trace = trace;
    ctx->trace_op = trace_op;
    ctx->trace_val = trace_val;
    ctx->trace_ptr = trace_ptr;
    ctx->trace_level = level;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_map_done(struct cli_bc_ctx *ctx , int32_t id)
{
    struct cli_map *s = get_hashtab(ctx, id);
    if (!s)
	return -1;
    cli_map_delete(s);
    if (id == ctx->nmaps-1) {
	ctx->nmaps--;
	if (!ctx->nmaps) {
	    free(ctx->maps);
	    ctx->maps = NULL;
	} else {
	    s = cli_realloc(ctx->maps, ctx->nmaps*(sizeof(*s)));
	    if (s)
		ctx->maps = s;
	}
    }
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_buffer_pipe_read_stopped(struct cli_bc_ctx *ctx , int32_t id, uint32_t amount)
{
    struct bc_buffer *b = get_buffer(ctx, id);
    if (!b)
	return -1;
    if (b->data) {
	if (b->write_cursor <= b->read_cursor)
	    return -1;
	if (b->read_cursor + amount > b->write_cursor)
	    b->read_cursor = b->write_cursor;
	else
	    b->read_cursor += amount;
	if (b->read_cursor >= b->size &&
	    b->write_cursor >= b->size)
	    b->read_cursor = b->write_cursor = 0;
	return 0;
    }
    b->read_cursor += amount;
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_debug_print_str_nonl(struct cli_bc_ctx *ctx , const uint8_t* s, uint32_t len)
{
    if (!s || len <= 0)
	return -1;
    if (!cli_debug_flag)
	return 0;
    return fwrite(s, 1, len, stderr);
}

Project: clamav-devel
File: bytecode.c

static void context_safe(struct cli_bc_ctx *ctx)
{
    /* make sure these are never NULL */
    if (!ctx->hooks.kind)
	ctx->hooks.kind = &nokind;
    if (!ctx->hooks.match_counts)
	ctx->hooks.match_counts = nomatch;
    if (!ctx->hooks.match_offsets)
	ctx->hooks.match_counts = nooffsets;
    if (!ctx->hooks.filesize)
	ctx->hooks.filesize = &nofilesize;
    if (!ctx->hooks.pedata)
	ctx->hooks.pedata = &nopedata;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_write(struct cli_bc_ctx *ctx, uint8_t*data, int32_t len)
{
    char err[128];
    int32_t res;

    cli_ctx *cctx = (cli_ctx*)ctx->ctx;
    if (len < 0) {
	cli_warnmsg("Bytecode API: called with negative length!\n");
	API_MISUSE();
	return -1;
    }
    if (!ctx->outfd) {
	ctx->tempfile = cli_gentemp(cctx ? cctx->engine->tmpdir : NULL);
	if (!ctx->tempfile) {
	    cli_dbgmsg("Bytecode API: Unable to allocate memory for tempfile\n");
	    cli_event_error_oom(EV, 0);
	    return -1;
	}
	ctx->outfd = open(ctx->tempfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600);
	if (ctx->outfd == -1) {
	    ctx->outfd = 0;
	    cli_warnmsg("Bytecode API: Can't create file %s: %s\n", ctx->tempfile, cli_strerror(errno, err, sizeof(err)));
	    cli_event_error_str(EV, "cli_bcapi_write: Can't create temporary file");
	    free(ctx->tempfile);
	    return -1;
	}
	cli_dbgmsg("bytecode opened new tempfile: %s\n", ctx->tempfile);
    }

    cli_event_fastdata(ctx->bc_events, BCEV_WRITE, data, len);
    if (cli_checklimits("bytecode api", cctx, ctx->written + len, 0, 0))
	return -1;
    res = cli_writen(ctx->outfd, data, len);
    if (res > 0) ctx->written += res;
    if (res == -1) {
	cli_warnmsg("Bytecode API: write failed: %s\n", cli_strerror(errno, err, sizeof(err)));
	cli_event_error_str(EV, "cli_bcapi_write: write failed");
    }
    return res;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_map_getvaluesize(struct cli_bc_ctx *ctx, int32_t id)
{
    struct cli_map *s = get_hashtab(ctx, id);
    if (!s)
	return -1;
    return cli_map_getvalue_size(s);
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_disable_jit_if(struct cli_bc_ctx *ctx , const int8_t* reason, uint32_t len, uint32_t cond)
{
    if (ctx->bc->kind != BC_STARTUP) {
	cli_dbgmsg("Bytecode must be BC_STARTUP to call disable_jit_if\n");
	return -1;
    }
    if (!cond)
	return ctx->bytecode_disable_status;
    if (*reason == '^')
	cli_warnmsg("Bytecode: disabling JIT because %s\n", reason+1);
    else
	cli_dbgmsg("Bytecode: disabling JIT because %s\n", reason);
    if (ctx->bytecode_disable_status != 2) /* no reenabling */
	ctx->bytecode_disable_status = 1;
    return ctx->bytecode_disable_status;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_engine_functionality_level(struct cli_bc_ctx *ctx)
{
    return cl_retflevel();
}

Project: clamav-devel
File: bytecode.c

void cli_bytecode_context_destroy(struct cli_bc_ctx *ctx)
{
   cli_bytecode_context_clear(ctx);
   free(ctx);
}

Project: clamav-devel
File: bytecode_api.c

static struct cli_hashset *get_hashset(struct cli_bc_ctx *ctx, int32_t id)
{
    if (id < 0 || id >= ctx->nhashsets || !ctx->hashsets) {
	API_MISUSE();
	return NULL;
    }
    return &ctx->hashsets[id];
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_get_pe_section(struct cli_bc_ctx *ctx, struct cli_exe_section* section, uint32_t num)
{
    if (num < ctx->hooks.pedata->nsections) {
	memcpy(section, &ctx->sections[num], sizeof(*section));
	return 0;
    }
    return -1;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_map_remove(struct cli_bc_ctx *ctx , const uint8_t* key, int32_t keysize, int32_t id)
{
    struct cli_map *s = get_hashtab(ctx, id);
    if (!s)
	return -1;
    return cli_map_removekey(s, key, keysize);
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_ilog2(struct cli_bc_ctx *ctx, uint32_t a, uint32_t b)
{
    double f;
    if (!b)
	return 0x7fffffff;
    /* log(a/b) is -32..32, so 2^26*32=2^31 covers the entire range of int32 */
    f = (1<<26)*log((double)a / b) / log(2);
    return (int32_t)myround(f);
}

Project: clamav-devel
File: bytecode.c

static inline int64_t ptr_compose(int32_t id, uint32_t offset)
{
    uint64_t i = id;
    return (i << 32) | offset;
}

Project: clamav-devel
File: bytecode_api.c

static int check_bits(uint32_t query, uint32_t value, uint8_t shift, uint8_t mask)
{
    uint8_t q = (query >> shift)&mask;
    uint8_t v = (value >> shift)&mask;
    /* q == mask -> ANY */
    if (q == v || q == mask)
	return 1;
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_disable_bytecode_if(struct cli_bc_ctx *ctx , const int8_t* reason, uint32_t len, uint32_t cond)
{
    if (ctx->bc->kind != BC_STARTUP) {
	cli_dbgmsg("Bytecode must be BC_STARTUP to call disable_bytecode_if\n");
	return -1;
    }
    if (!cond)
	return ctx->bytecode_disable_status;
    if (*reason == '^')
	cli_warnmsg("Bytecode: disabling completely because %s\n", reason+1);
    else
	cli_dbgmsg("Bytecode: disabling completely because %s\n", reason);
    ctx->bytecode_disable_status = 2;
    return ctx->bytecode_disable_status;
}

Project: clamav-devel
File: bytecode.c

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)
{
    unsigned i, builtin = 0, rc = 0;
    struct cli_bc *bc = NULL;

    for (i=0;i<bcs->count;i++) {
	bc = &bcs->all_bcs[i];
	if (bc->kind == kind)
	    break;
    }
    if (i == bcs->count)
	bc = NULL;
    if (!bc) {
	/* 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);
	if (rc) {
	    cli_errmsg("Failed to load builtin %s bytecode\n", desc);
	    free(bc);
	    return rc;
	}
    }
    rc = cli_bytecode_prepare_interpreter(bc);
    if (rc) {
	cli_errmsg("Failed to prepare %s %s bytecode for interpreter: %s\n",
		   builtin ? "builtin" : "loaded", desc, cl_strerror(rc));
    }
    if (bc->state != bc_interp) {
	cli_errmsg("Failed to prepare %s %s bytecode for interpreter\n",
		   builtin ? "builtin" : "loaded", desc);
	rc = CL_EMALFDB;
    }
    if (!rc) {
	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);
    }
    if (rc) {
	cli_errmsg("Failed to execute %s %s bytecode: %s\n",builtin ? "builtin":"loaded",
		   desc, cl_strerror(rc));
    }
    if (builtin) {
	cli_bytecode_destroy(bc);
	free(bc);
    }
    return rc;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_atoi(struct cli_bc_ctx *ctx, const uint8_t* str, int32_t len)
{
    int32_t number = 0;
    const uint8_t *end = str + len;
    while (isspace(*str) && str < end) str++;
    if (str == end)
	return -1;/* all spaces */
    if (*str == '+') str++;
    if (str == end)
	return -1;/* all spaces and +*/
    if (*str == '-')
	return -1;/* only positive numbers */
    if (!isdigit(*str))
	return -1;
    while (isdigit(*str) && str < end) {
	number = number*10 + (*str - '0');
    }
    return number;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_trace_directory(struct cli_bc_ctx *ctx, const uint8_t* dir, uint32_t dummy)
{
    if (LIKELY(!ctx->trace_level))
	return 0;
    ctx->directory = (const char*)dir ? (const char*)dir : "";
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_setobjflags(struct cli_bc_ctx *ctx , int32_t objidx, int32_t flags)
{
    if (!ctx->pdf_phase ||
	objidx >= ctx->pdf_nobjs)
	return -1;
    cli_dbgmsg("cli_pdf: bytecode setobjflags %08x -> %08x\n",
	       ctx->pdf_objs[objidx].flags,
	       flags);
    ctx->pdf_objs[objidx].flags = flags;
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_trace_scope(struct cli_bc_ctx *ctx, const uint8_t *scope, uint32_t scopeid)
{
    if (LIKELY(!ctx->trace_level))
	return 0;
    if (ctx->scope != (const char*)scope) {
	ctx->scope = (const char*)scope ? (const char*)scope : "?";
	ctx->scopeid = scopeid;
	ctx->trace_level |= 0x80;/* temporarely increase level to print params */
    } else if ((ctx->trace_level >= trace_scope) && ctx->scopeid != scopeid) {
	ctx->scopeid = scopeid;
	ctx->trace_level |= 0x40;/* temporarely increase level to print location */
    }
    return 0;
}

Project: clamav-devel
File: bytecode.c

static int types_equal(const struct cli_bc *bc, uint16_t *apity2ty, uint16_t tid, uint16_t apitid)
{
    unsigned i;
    const struct cli_bc_type *ty = &bc->types[tid - 65];
    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. */
     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 */
     }
    return 1;
}

Project: clamav-devel
File: bytecode.c

static int add_selfcheck(struct cli_all_bc *bcs)
{
    struct cli_bc_func *func;
    struct cli_bc_inst *inst;
    struct cli_bc *bc;

    bcs->all_bcs = cli_realloc2(bcs->all_bcs, sizeof(*bcs->all_bcs)*(bcs->count+1));
    if (!bcs->all_bcs) {
	cli_errmsg("cli_loadcbc: Can't allocate memory for bytecode entry\n");
	return CL_EMEM;
    }
    bc = &bcs->all_bcs[bcs->count++];
    memset(bc, 0, sizeof(*bc));

    bc->trusted = 1;
    bc->num_globals = 1;
    bc->globals = cli_calloc(1, sizeof(*bc->globals));
    if (!bc->globals) {
	cli_errmsg("Failed to allocate memory for globals\n");
	return CL_EMEM;
    }
    bc->globals[0] = cli_calloc(1, sizeof(*bc->globals[0]));
    if (!bc->globals[0]) {
	cli_errmsg("Failed to allocate memory for globals\n");
	return CL_EMEM;
    }
    bc->globaltys = cli_calloc(1, sizeof(*bc->globaltys));
    if (!bc->globaltys) {
	cli_errmsg("Failed to allocate memory for globaltypes\n");
	return CL_EMEM;
    }
    bc->globaltys[0] = 32;
    *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));
    if (!bc->funcs) {
	cli_errmsg("Failed to allocate memory for func\n");
	return CL_EMEM;
    }
    func = bc->funcs;
    func->numInsts = 2;
    func->numLocals = 1;
    func->numValues = 1;
    func->numConstants = 1;
    func->numBB = 1;
    func->returnType = 32;
    func->types = cli_calloc(1, sizeof(*func->types));
    if (!func->types) {
	cli_errmsg("Failed to allocate memory for types\n");
	return CL_EMEM;
    }
    func->types[0] = 32;
    func->BB = cli_calloc(1, sizeof(*func->BB));
    if (!func->BB) {
	cli_errmsg("Failed to allocate memory for BB\n");
	return CL_EMEM;
    }
    func->allinsts = cli_calloc(2, sizeof(*func->allinsts));
    if (!func->allinsts) {
	cli_errmsg("Failed to allocate memory for insts\n");
	return CL_EMEM;
    }
    func->BB->numInsts = 2;
    func->BB->insts = func->allinsts;
    func->constants = cli_calloc(1, sizeof(*func->constants));
    if (!func->constants) {
	cli_errmsg("Failed to allocate memory for constants\n");
	return CL_EMEM;
    }
    func->constants[0] = 0xf00d;
    inst = func->allinsts;

    inst->opcode = OP_BC_CALL_API;
    inst->u.ops.numOps = 1;
    inst->u.ops.opsizes = NULL;
    inst->u.ops.ops = cli_calloc(1, sizeof(*inst->u.ops.ops));
    if (!inst->u.ops.ops) {
	cli_errmsg("Failed to allocate memory for instructions\n");
	return CL_EMEM;
    }
    inst->u.ops.ops[0] = 1;
    inst->u.ops.funcid = 18; /* test2 */
    inst->dest = 0;
    inst->type = 32;
    inst->interp_op = inst->opcode* 5 + 3;

    inst = &func->allinsts[1];
    inst->opcode = OP_BC_RET;
    inst->type = 32;
    inst->u.unaryop = 0;
    inst->interp_op = inst->opcode* 5;

    bc->state = bc_loaded;
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_check_platform(struct cli_bc_ctx *ctx , uint32_t a, uint32_t b , uint32_t c)
{
    unsigned ret =
	check_bits(a, ctx->env->platform_id_a, 24, 0xff) &&
	check_bits(a, ctx->env->platform_id_a, 20, 0xf) &&
	check_bits(a, ctx->env->platform_id_a, 16, 0xf) &&
	check_bits(a, ctx->env->platform_id_a, 8, 0xff) &&
	check_bits(a, ctx->env->platform_id_a, 0, 0xff) &&
	check_bits(b, ctx->env->platform_id_b, 28, 0xf) &&
	check_bits(b, ctx->env->platform_id_b, 24, 0xf) &&
	check_bits(b, ctx->env->platform_id_b, 16, 0xff) &&
	check_bits(b, ctx->env->platform_id_b, 8, 0xff) &&
	check_bits(b, ctx->env->platform_id_b, 0, 0xff) &&
	check_bits(c, ctx->env->platform_id_c, 24, 0xff) &&
	check_bits(c, ctx->env->platform_id_c, 16, 0xff) &&
	check_bits(c, ctx->env->platform_id_c, 8, 0xff) &&
	check_bits(c, ctx->env->platform_id_c, 0, 0xff);
    if (ret) {
	cli_dbgmsg("check_platform(0x%08x,0x%08x,0x%08x) = match\n",a,b,c);
    }
    return ret;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_setvirusname(struct cli_bc_ctx* ctx, const uint8_t *name, uint32_t len)
{
    ctx->virname = (const char*)name;
    return 0;
}

Project: clamav-devel
File: bytecode.c

static unsigned typealign(const struct cli_bc *bc, uint16_t type)
{
    type &= 0x7fff;
    if (type <= 64) {
	unsigned size = typesize(bc, type);
	return size ? size : 1;
    }
    return bc->types[type-65].align;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_init(struct cli_all_bc *allbc)
{
    int ret;
    memset(allbc, 0, sizeof(*allbc));
    ret = cli_bytecode_init_jit(allbc, 0/*XXX*/);
    cli_dbgmsg("Bytecode initialized in %s mode\n",
	       allbc->engine ? "JIT" : "interpreter");
    allbc->inited = 1;
    return ret;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_read_number(struct cli_bc_ctx *ctx, uint32_t radix)
{
    unsigned i;
    char *p;
    int32_t result;

    if ((radix != 10 && radix != 16) || !ctx->fmap)
	return -1;
    cli_event_int(EV, BCEV_OFFSET, ctx->off);
    while ((p = fmap_need_off_once(ctx->fmap, ctx->off, BUF))) {
	for (i=0;i<BUF;i++) {
	    if (p[i] >= '0' && p[i] <= '9') {
		char *endptr;
		p = fmap_need_ptr_once(ctx->fmap, p+i, 16);
		if (!p)
		    return -1;
		result = strtoul(p, &endptr, radix);
		ctx->off += i + (endptr - p);
		return result;
	    }
	}
	ctx->off += BUF;
    }
    return -1;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_hashset_empty(struct cli_bc_ctx *ctx, int32_t id)
{
    struct cli_hashset *s = get_hashset(ctx, id);
    return !s->count;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_trace_op(struct cli_bc_ctx *ctx, const uint8_t *op, uint32_t col)
{
    if (LIKELY(ctx->trace_level < trace_col))
	return 0;
    if (ctx->trace_level&0xc0) {
	ctx->col = col;
	/* func/scope changed and they needed param/location event */
	ctx->trace(ctx, (ctx->trace_level&0x80) ? trace_func : trace_scope);
	ctx->trace_level &= ~0xc0;
    }
    if (LIKELY(ctx->trace_level < trace_col))
	return 0;
    if (ctx->col != col) {
	ctx->col = col;
	ctx->trace(ctx, trace_col);
    } else {
	ctx->trace(ctx, trace_line);
    }
    if (LIKELY(ctx->trace_level < trace_op))
	return 0;
    if (ctx->trace_op && op)
	ctx->trace_op(ctx, (const char*)op);
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_pdf_getobjsize(struct cli_bc_ctx *ctx , int32_t objidx)
{
    if (!ctx->pdf_phase ||
	objidx >= ctx->pdf_nobjs ||
	ctx->pdf_phase == PDF_PHASE_POSTDUMP /* map is obj itself, no access to pdf anymore */
       )
	return 0;
    if (objidx + 1 == ctx->pdf_nobjs)
	return ctx->pdf_size - ctx->pdf_objs[objidx].start;
    return ctx->pdf_objs[objidx+1].start - ctx->pdf_objs[objidx].start - 4;
}

Project: clamav-devel
File: bytecode.c

static inline int type_isint(uint16_t type)
{
    return type > 0 && type <= 64;
}

Project: clamav-devel
File: bytecode.c

static int parseLSig(struct cli_bc *bc, char *buffer)
{
    const char *prefix;
    char *vnames, *vend = strchr(buffer, ';');
    if (vend) {
	bc->lsig = cli_strdup(buffer);
	*vend++ = '\0';
	prefix = buffer;
	vnames = strchr(vend, '{');
    } else {
	/* Not a logical signature, but we still have a virusname */
	bc->lsig = NULL;
    }

    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
			 const struct cli_all_bc *bcs, unsigned bc_idx,
			 const char **virname, const uint32_t* lsigcnt,
			 const uint32_t *lsigsuboff, fmap_t *map)
{
    int ret;
    struct cli_bc_ctx ctx;
    const struct cli_bc *bc = &bcs->all_bcs[bc_idx-1];
    struct cli_pe_hook_data pehookdata;

    memset(&ctx, 0, sizeof(ctx));
    cli_bytecode_context_setfuncid(&ctx, bc, 0);
    ctx.hooks.match_counts = lsigcnt;
    ctx.hooks.match_offsets = lsigsuboff;
    cli_bytecode_context_setctx(&ctx, cctx);
    cli_bytecode_context_setfile(&ctx, map);
    if (tinfo && tinfo->status == 1) {
	ctx.sections = tinfo->exeinfo.section;
	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;
    }
    if (bc->hook_lsig_id) {
	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
	 * executed, so that it has all the info for the hook */
	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;
    }

    cli_dbgmsg("Running bytecode for logical signature match\n");
    ret = cli_bytecode_run(bcs, bc, &ctx);
    if (ret != CL_SUCCESS) {
	cli_warnmsg("Bytcode %u failed to run: %s\n", bc->id, cl_strerror(ret));
	cli_bytecode_context_clear(&ctx);
	return CL_SUCCESS;
    }
    if (ctx.virname) {
	int rc;
	cli_dbgmsg("Bytecode found virus: %s\n", ctx.virname);
	if (virname)
	    *virname = ctx.virname;
	if (!strncmp(*virname, "BC.Heuristics", 13))
	    rc = cli_found_possibly_unwanted(cctx);
	else
	    rc = CL_VIRUS;
	cli_bytecode_context_clear(&ctx);
	return rc;
    }
    ret = cli_bytecode_context_getresult_int(&ctx);
    cli_dbgmsg("Bytecode %u returned code: %u\n", bc->id, ret);
    cli_bytecode_context_clear(&ctx);
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_test2(struct cli_bc_ctx *ctx, uint32_t a)
{
    return a == 0xf00d ? 0xd00f : 0x5555;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_disasm_x86(struct cli_bc_ctx *ctx, struct DISASM_RESULT *res, uint32_t len)
{
    int n;
    const unsigned char *buf;
    const unsigned char* next;
    if (!res || !ctx->fmap || ctx->off >= ctx->fmap->len) {
	API_MISUSE();
	return -1;
    }
    /* 32 should be longest instr we support decoding.
     * When we'll support mmx/sse instructions this should be updated! */
    n = MIN(32, ctx->fmap->len - ctx->off);
    buf = fmap_need_off_once(ctx->fmap, ctx->off, n);
    next = cli_disasm_one(buf, n, res, 0);
    if (!next) {
	cli_dbgmsg("bcapi_disasm: failed\n");
	cli_event_count(EV, BCEV_DISASM_FAIL);
	return -1;
    }
    return ctx->off + next - buf;
}

Project: clamav-devel
File: bytecode_api.c

static struct bc_jsnorm *get_jsnorm(struct cli_bc_ctx *ctx, int32_t id)
{
    if (id < 0 || id >= ctx->njsnorms || !ctx->jsnorms)
	return NULL;
    return &ctx->jsnorms[id];
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_hashset_contains(struct cli_bc_ctx *ctx , int32_t id, uint32_t key)
{
    struct cli_hashset *s = get_hashset(ctx, id);
    if (!s)
	return -1;
    return cli_hashset_contains(s, key);
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_jsnorm_done(struct cli_bc_ctx *ctx , int32_t id)
{
    struct bc_jsnorm *b = get_jsnorm(ctx, id);
    if (!b || b->from == -1)
	return -1;
    if (ctx->ctx && cli_updatelimits(ctx->ctx, ctx->jsnormwritten))
	return -1;
    ctx->jsnormwritten = 0;
    cli_js_parse_done(b->state);
    cli_js_output(b->state, ctx->jsnormdir);
    cli_js_destroy(b->state);
    b->from = -1;
    return 0;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, fmap_t *map)
{
    ctx->fmap = map;
    ctx->file_size = map->len + map->offset;
    ctx->hooks.filesize = &ctx->file_size;
    return 0;
}

Project: clamav-devel
File: bytecode.c

static uint16_t type_components(struct cli_bc *bc, uint16_t id, char *ok)
{
    unsigned i, sum=0;
    const struct cli_bc_type *ty;
    if (id <= 64)
	return 1;
    ty = &bc->types[id-65];
    /* TODO: protect against recursive types */
    switch (ty->kind) {
	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;
    }
}

Project: clamav-devel
File: bytecode.c

static inline char *readString(const unsigned char *p, unsigned *off, unsigned len, char *ok)
{
    unsigned stringlen;
    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;
    }
    return str;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_jsnorm_init(struct cli_bc_ctx *ctx, int32_t from)
{
    struct parser_state *state;
    struct bc_jsnorm *b;
    unsigned  n = ctx->njsnorms + 1;
    if (!get_buffer(ctx, from)) {
	cli_dbgmsg("bytecode api: jsnorm_init: invalid buffers!\n");
	return -1;
    }
    state = cli_js_init();
    if (!state)
	return -1;
    b = cli_realloc(ctx->jsnorms, sizeof(*ctx->jsnorms)*n);
    if (!b) {
	cli_js_destroy(state);
	return -1;
    }
    ctx->jsnorms = b;
    ctx->njsnorms = n;
    b = &b[n-1];
    b->from = from;
    b->state = state;
    if (!ctx->jsnormdir) {
	cli_ctx *cctx = (cli_ctx*)ctx->ctx;
	ctx->jsnormdir = cli_gentemp(cctx ? cctx->engine->tmpdir : NULL);
	if (ctx->jsnormdir) {
	    if (mkdir(ctx->jsnormdir, 0700)) {
		cli_dbgmsg("js: can't create temp dir %s\n", ctx->jsnormdir);
		free(ctx->jsnormdir);
		return CL_ETMPDIR;
	    }
	}
    }
    return n-1;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_hashset_add(struct cli_bc_ctx *ctx , int32_t id, uint32_t key)
{
    struct cli_hashset *s = get_hashset(ctx, id);
    if (!s)
	return -1;
    return cli_hashset_addkey(s, key);
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_map_new(struct cli_bc_ctx *ctx, int32_t keysize, int32_t valuesize)
{
    unsigned n = ctx->nmaps+1;
    struct cli_map *s;
    if (!keysize)
	return -1;
    s = cli_realloc(ctx->maps, sizeof(*ctx->maps)*n);
    if (!s)
	return -1;
    ctx->maps = s;
    ctx->nmaps = n;
    s = &s[n-1];
    cli_map_init(s, keysize, valuesize, 16);
    return n-1;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_get_environment(struct cli_bc_ctx *ctx , struct cli_environment* env, uint32_t len)
{
    if (len > sizeof(*env)) {
	cli_dbgmsg("cli_bcapi_get_environment len %u > %lu\n", len, sizeof(*env));
	return -1;
    }
    memcpy(env, ctx->env, len);
    return 0;
}

Project: clamav-devel
File: bytecode.c

static int parseHeader(struct cli_bc *bc, unsigned char *buffer, unsigned *linelength)
{
    uint64_t magic1;
    unsigned magic2;
    char ok = 1;
    unsigned offset, len, flevel;
    char *pos;

    if (strncmp((const char*)buffer, BC_HEADER, sizeof(BC_HEADER)-1)) {
	cli_errmsg("Missing file magic in bytecode");
	return CL_EMALFDB;
    }
    offset = sizeof(BC_HEADER)-1;
    len = strlen((const char*)buffer);
    bc->metadata.formatlevel = readNumber(buffer, &offset, len, &ok);
    if (!ok) {
	cli_errmsg("Unable to parse (format) functionality level in bytecode header\n");
	return CL_EMALFDB;
    }
    /* we support 2 bytecode formats */
    if (bc->metadata.formatlevel != BC_FORMAT_096 &&
	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;
    }
    /* Optimistic parsing, check for error only at the end.*/
    bc->metadata.timestamp = readNumber(buffer, &offset, len, &ok);
    bc->metadata.sigmaker = readString(buffer, &offset, len, &ok);
    bc->metadata.targetExclude = readNumber(buffer, &offset, len, &ok);
    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();
    /* 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)) {
      cli_dbgmsg("Skipping bytecode with (engine) functionality level %u-%u (current %u)\n",
                 bc->metadata.minfunc, bc->metadata.maxfunc, flevel);
      return CL_BREAK;
    }
    bc->metadata.maxresource = readNumber(buffer, &offset, len, &ok);
    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;
    if (!ok) {
	cli_errmsg("Invalid bytecode header at %u\n", offset);
	return CL_EMALFDB;
    }
    magic1 = readNumber(buffer, &offset, len, &ok);
    magic2 = readFixedNumber(buffer, &offset, len, &ok, 2);
    if (!ok || magic1 != 0x53e5493e9f3d1c30ull || magic2 != 42) {
      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;
    }
    if (buffer[offset] != ':') {
	cli_errmsg("Expected : but found: %c\n", buffer[offset]);
	return CL_EMALFDB;
    }
    offset++;
    *linelength = strtol((const char*)buffer+offset, &pos, 10);
    if (*pos != '\0') {
	cli_errmsg("Invalid number: %s\n", buffer+offset);
	return CL_EMALFDB;
    }

    bc->funcs = cli_calloc(bc->num_func, sizeof(*bc->funcs));
    if (!bc->funcs) {
	cli_errmsg("Out of memory allocating %u functions\n", bc->num_func);
	return CL_EMEM;
    }
    bc->types = cli_calloc(bc->num_types, sizeof(*bc->types));
    if (!bc->types) {
	cli_errmsg("Out of memory allocating %u types\n", bc->num_types);
	return CL_EMEM;
    }
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode.c

static int parseTypes(struct cli_bc *bc, unsigned char *buffer)
{
    unsigned i, offset = 1, len = strlen((const char*)buffer);
    char ok=1;

    if (buffer[0] != 'T') {
	cli_errmsg("Invalid function types header: %c\n", buffer[0]);
	return CL_EMALFDB;
    }
    bc->start_tid = readFixedNumber(buffer, &offset, len, &ok, 2);
    if (bc->start_tid != BC_START_TID) {
	cli_warnmsg("Type start id mismatch: %u != %u\n", bc->start_tid,
		    BC_START_TID);
	return CL_BREAK;
    }
    add_static_types(bc);
    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]);
	}
    }
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode.c

static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx)
{
    unsigned i;

    free(ctx->opsizes);
    ctx->opsizes = NULL;

    free(ctx->values);
    ctx->values = NULL;

    free(ctx->operands);
    ctx->operands = NULL;

    if (ctx->outfd) {
	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;
    }
    if (ctx->jsnormdir) {
	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) {
		    lseek(fd, 0, SEEK_SET);
		    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;
    }
    ctx->numParams = 0;
    ctx->funcid = 0;
    /* don't touch fmap, file_size, and hooks, sections, ctx, timeout, pdf* */
    ctx->off = 0;
    ctx->written = 0;
    ctx->jsnormwritten = 0;
#if USE_MPOOL
    if (ctx->mpool) {
	mpool_destroy(ctx->mpool);
	ctx->mpool = NULL;
    }
#else
    /*TODO: implement for no-mmap case too*/
#endif
    for (i=0;i<ctx->ninflates;i++)
	cli_bcapi_inflate_done(ctx, i);
    free(ctx->inflates);
    ctx->inflates = NULL;
    ctx->ninflates = 0;

    for (i=0;i<ctx->nbuffers;i++)
	cli_bcapi_buffer_pipe_done(ctx, i);
    free(ctx->buffers);
    ctx->buffers = NULL;
    ctx->nbuffers = 0;

    for (i=0;i<ctx->nhashsets;i++)
	cli_bcapi_hashset_done(ctx, i);
    free(ctx->hashsets);
    ctx->hashsets = NULL;
    ctx->nhashsets = 0;

    for (i=0;i<ctx->njsnorms;i++)
	cli_bcapi_jsnorm_done(ctx, i);
    free(ctx->jsnorms);
    ctx->jsnorms = NULL;
    ctx->njsnorms = 0;
    ctx->jsnormdir = NULL;

    for (i=0;i<ctx->nmaps;i++)
	cli_bcapi_map_done(ctx, i);
    free(ctx->maps);
    ctx->maps = NULL;
    ctx->nmaps = 0;

    ctx->containertype = CL_TYPE_ANY;
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_input_switch(struct cli_bc_ctx *ctx , int32_t extracted_file)
{
    fmap_t *map;
    if (ctx->extracted_file_input == extracted_file)
	return 0;
    if (!extracted_file) {
	cli_dbgmsg("bytecode api: input switched back to main file\n");
	ctx->fmap = ctx->save_map;
	ctx->extracted_file_input = 0;
	return 0;
    }
    if (ctx->outfd < 0)
	return -1;
    map = fmap(ctx->outfd, 0, 0);
    if (!map) {
	cli_warnmsg("can't mmap() extracted temporary file %s\n", ctx->tempfile);
	return -1;
    }
    ctx->save_map = ctx->fmap;
    cli_bytecode_context_setfile(ctx, map);
    ctx->extracted_file_input = 1;
    cli_dbgmsg("bytecode api: input switched to extracted file\n");
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_hashset_remove(struct cli_bc_ctx *ctx , int32_t id, uint32_t key)
{
    struct cli_hashset *s = get_hashset(ctx, id);
    if (!s)
	return -1;
    return cli_hashset_removekey(s, key);
}

Project: clamav-devel
File: bytecode.c

static void readConstant(struct cli_bc *bc, unsigned i, unsigned comp,
			 unsigned char *buffer, unsigned *offset,
			 unsigned len, char *ok)
{
    unsigned j=0;
    if (*ok && buffer[*offset] == 0x40 &&
	buffer [*offset+1] == 0x60) {
	/* zero initializer */
	memset(bc->globals[i], 0, sizeof(*bc->globals[0])*comp);
	(*offset)+=2;
	return;
    }
    while (*ok && buffer[*offset] != 0x60) {
	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);
    }
    if (*ok && j != comp) {
	cli_errmsg("bytecode: constant has too few subcomponents: %u < %u\n", j, comp);
	*ok = 0;
    }
    (*offset)++;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_get_flags(struct cli_bc_ctx *ctx)
{
    if (!ctx->pdf_phase)
	return -1;
    return *ctx->pdf_flags;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_jsnorm_process(struct cli_bc_ctx *ctx, int32_t id)
{
    unsigned avail;
    unsigned char *in;
    cli_ctx *cctx = ctx->ctx;
    struct bc_jsnorm *b = get_jsnorm(ctx, id);
    if (!b || b->from == -1 || !b->state)
	return -1;

    avail = cli_bcapi_buffer_pipe_read_avail(ctx, b->from);
    in = cli_bcapi_buffer_pipe_read_get(ctx, b->from, avail);
    if (!avail || !in)
	return -1;
    if (cctx && cli_checklimits("bytecode js api", cctx, ctx->jsnormwritten + avail, 0, 0))
	return -1;
    cli_bcapi_buffer_pipe_read_stopped(ctx, b->from, avail);
    cli_js_process_buffer(b->state, (char*)in, avail);
    return 0;
}

Project: clamav-devel
File: bytecode.c

void cli_bytecode_context_setctx(struct cli_bc_ctx *ctx, void *cctx)
{
    ctx->ctx = cctx;
    ctx->bytecode_timeout = ((cli_ctx*)cctx)->engine->bytecode_timeout;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, struct cli_bc_ctx *ctx)
{
    int ret = CL_SUCCESS;
    struct cli_bc_inst inst;
    struct cli_bc_func func;
    cli_events_t *jit_ev = NULL, *interp_ev = NULL;

    int test_mode = 0;
    cli_ctx *cctx =(cli_ctx*)ctx->ctx;
    if (cctx && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
	test_mode = 1;

    if (!ctx || !ctx->bc || !ctx->func)
	return CL_ENULLARG;
    if (ctx->numParams && (!ctx->values || !ctx->operands))
	return CL_ENULLARG;
    if (bc->state == bc_loaded) {
	cli_errmsg("bytecode has to be prepared either for interpreter or JIT!\n");
	return CL_EARG;
    }
    if (bc->state == bc_disabled) {
	cli_dbgmsg("bytecode triggered but running bytecodes is disabled\n");
	return CL_SUCCESS;
    }
    if (cctx)
	cli_event_time_start(cctx->perf, PERFT_BYTECODE);
    ctx->env = &bcs->env;
    context_safe(ctx);
    if (test_mode) {
	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;
	}
    }
    if (bc->state == bc_interp || test_mode) {
	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 interpeter 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
	*/
	if (ctx->outfd)
	    cli_bcapi_extract_new(ctx, -1);
    }
    if (bc->state == bc_jit || test_mode) {
	if (test_mode) {
	    ctx->off = 0;
	}
	ctx->bc_events = jit_ev;
	cli_dbgmsg("Bytecode %u: executing in JIT mode\n", bc->id);

	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);

	cli_event_int(jit_ev, BCEV_EXEC_RETURNVALUE, ret);
	cli_event_string(jit_ev, BCEV_VIRUSNAME, ctx->virname);

	/* need to be called here to catch any extracted but not yet scanned files
	*/
	if (ctx->outfd)
	    cli_bcapi_extract_new(ctx, -1);
    }

    if (test_mode) {
	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);
	cli_event_debug(interp_ev, BCEV_EXEC_TIME);*/
	if (!ok) {
	    cli_events_free(jit_ev);
	    cli_events_free(interp_ev);
	    return CL_EBYTECODE_TESTFAIL;
	}
    }
    cli_events_free(jit_ev);
    cli_events_free(interp_ev);
    if (cctx)
	cli_event_time_stop(cctx->perf, PERFT_BYTECODE);
    return ret;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_pdf_get_phase(struct cli_bc_ctx *ctx)
{
    return ctx->pdf_phase;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_buffer_pipe_done(struct cli_bc_ctx *ctx , int32_t id)
{
    struct bc_buffer *b = get_buffer(ctx, id);
    if (!b)
	return -1;
    free(b->data);
    b->data = NULL;
    return -0;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_running_on_jit(struct cli_bc_ctx *ctx )
{
    ctx->no_diff = 1;
    return ctx->on_jit;
}

Project: clamav-devel
File: bytecode.c

static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char *buffer)
{
    char ok=1;
    unsigned offset, len, i, last = 0;
    struct cli_bc_bb *BB;
    struct cli_bc_func *bcfunc = &bc->funcs[func];
    struct cli_bc_inst inst;

    if (bb >= bcfunc->numBB) {
	cli_errmsg("Found too many basic blocks\n");
	return CL_EMALFDB;
    }

    BB = &bcfunc->BB[bb];
    len = strlen((const char*) buffer);
    if (buffer[0] != 'B') {
	cli_errmsg("Invalid basic block header: %c\n", buffer[0]);
	return CL_EMALFDB;
    }
    offset = 1;
    BB->numInsts = 0;
    BB->insts = &bcfunc->allinsts[bcfunc->insn_idx];
    while (!last) {
	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++;
    }
    if (buffer[offset] == 'D') {
	unsigned num;
	offset += 3;
	if (offset >= len)
	    return CL_EMALFDB;
	num = 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)
	    return CL_EMEM;
	for (i=0;i<num;i++) {
	    bcfunc->dbgnodes[i] = readNumber(buffer, &offset, len, &ok);
	    if (!ok)
		return CL_EMALFDB;
	}
    }
    if (offset != len) {
	cli_errmsg("Trailing garbage in basicblock: %d extra bytes\n",
		   len-offset);
	return CL_EMALFDB;
    }
    bcfunc->numBytes = 0;
    bcfunc->insn_idx += BB->numInsts;
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_buffer_pipe_write_stopped(struct cli_bc_ctx *ctx , int32_t id, uint32_t size)
{
    struct bc_buffer *b = get_buffer(ctx, id);
    if (!b || !b->data)
	return -1;
    if (b->write_cursor + size >= b->size)
	b->write_cursor = b->size;
    else
	b->write_cursor += size;
    return 0;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_icos(struct cli_bc_ctx *ctx, int32_t a, int32_t b, int32_t c)
{
    double f;
    if (!b)
	return 0x7fffffff;
    f = c*cos((double)a/b);
    return (int32_t)myround(f);
}

Project: clamav-devel
File: bytecode_api.c

uint8_t* cli_bcapi_buffer_pipe_read_get(struct cli_bc_ctx *ctx , int32_t id, uint32_t size)
{
    struct bc_buffer *b = get_buffer(ctx, id);
    if (!b || size > cli_bcapi_buffer_pipe_read_avail(ctx, id) || !size)
	return NULL;
    if (b->data)
	return b->data + b->read_cursor;
    return fmap_need_off(ctx->fmap, b->read_cursor, size);
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_engine_scan_options(struct cli_bc_ctx *ctx)
{
    cli_ctx *cctx = (cli_ctx*)ctx->ctx;
    return cctx->options;
}

Project: clamav-devel
File: bytecode_api.c

uint32_t cli_bcapi_buffer_pipe_write_avail(struct cli_bc_ctx *ctx, int32_t id)
{
    struct bc_buffer *b = get_buffer(ctx, id);
    if (!b)
	return 0;
    if (!b->data)
	return 0;
    if (b->write_cursor >= b->size)
	return 0;
    return b->size - b->write_cursor;
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data, const struct cli_exe_section *sections)
{
    ctx->sections = sections;
    ctx->hooks.pedata = data;
    return 0;
}

Project: clamav-devel
File: bytecode.c

static uint16_t readTypeID(struct cli_bc *bc, unsigned char *buffer,
			   unsigned *offset, unsigned len, char *ok)
{
    uint64_t t = readNumber(buffer, offset, len, ok);
    if (!ok)
	return ~0;
    if (t >= bc->num_types + bc->start_tid) {
	cli_errmsg("Invalid type id: %llu\n", (unsigned long long)t);
	*ok = 0;
	return ~0;
    }
    return t;
}

Project: clamav-devel
File: bytecode_api.c

uint8_t* cli_bcapi_buffer_pipe_write_get(struct cli_bc_ctx *ctx, int32_t id, uint32_t size)
{
    struct bc_buffer *b = get_buffer(ctx, id);
    if (!b || size > cli_bcapi_buffer_pipe_write_avail(ctx, id) || !size)
	return NULL;
    if (!b->data)
	return NULL;
    return b->data + b->write_cursor;
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_map_find(struct cli_bc_ctx *ctx , const uint8_t* key, int32_t keysize, int32_t id)
{
    struct cli_map *s = get_hashtab(ctx, id);
    if (!s)
	return -1;
    return cli_map_find(s, key, keysize);
}

Project: clamav-devel
File: bytecode_api.c

static struct bc_inflate *get_inflate(struct cli_bc_ctx *ctx, int32_t id)
{
    if (id < 0 || id >= ctx->ninflates || !ctx->inflates)
	return NULL;
    return &ctx->inflates[id];
}

Project: clamav-devel
File: bytecode_api.c

int32_t cli_bcapi_ipow(struct cli_bc_ctx *ctx, int32_t a, int32_t b, int32_t c)
{
    if (!a && b < 0)
	return 0x7fffffff;
    return (int32_t)myround(c*pow(a,b));
}

Project: clamav-devel
File: bytecode_api.c

uint8_t* cli_bcapi_map_getvalue(struct cli_bc_ctx *ctx , int32_t id, int32_t valuesize)
{
    struct cli_map *s = get_hashtab(ctx, id);
    if (!s)
	return NULL;
    if (cli_map_getvalue_size(s) != valuesize)
	return NULL;
    return cli_map_getvalue(s);
}

Project: clamav-devel
File: bytecode.c

int cli_bytecode_context_clear(struct cli_bc_ctx *ctx)
{
    cli_bytecode_context_reset(ctx);
    memset(ctx, 0, sizeof(*ctx));
    return CL_SUCCESS;
}

Project: clamav-devel
File: bytecode.c

struct cli_bc_ctx *cli_bytecode_context_alloc(void)
{
    struct cli_bc_ctx *ctx = cli_calloc(1, sizeof(*ctx));
    ctx->bytecode_timeout = 60000;
    cli_bytecode_context_reset(ctx);
    return ctx;
}