bytecode: fix handling of cast and icmp opcodes, improve stack handling.

0.96
Török Edvin 16 years ago
parent 30188fccb9
commit cfec3d90ce
  1. 15
      libclamav/bytecode.c
  2. 253
      libclamav/bytecode_vm.c
  3. 6
      libclamav/others.h

@ -481,10 +481,12 @@ static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char
case OP_SEXT:
case OP_TRUNC:
inst.u.cast.source = readOperand(bcfunc, buffer, &offset, len, &ok);
if (ok) {
/* calculate mask */
inst.u.cast.mask = (1<<bcfunc->allinsts[inst.u.cast.source].type)-1;
}
inst.u.cast.mask = bcfunc->types[inst.u.cast.source];
/* calculate mask */
if (inst.opcode != OP_SEXT)
inst.u.cast.mask = inst.u.cast.mask != 64 ?
(1ull<<inst.u.cast.mask)-1 :
~0ull;
break;
default:
numOp = operand_counts[inst.opcode];
@ -528,7 +530,7 @@ static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char
case OP_ICMP_SGE:
case OP_ICMP_SLE:
case OP_ICMP_SLT:
inst.type = bcfunc->allinsts[inst.u.binop[0]].type;
inst.type = bcfunc->types[inst.u.binop[0]];
break;
}
BB->insts[BB->numInsts++] = inst;
@ -540,7 +542,6 @@ static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char
}
offset++;
}
cli_dbgmsg("Parsed %d instructions\n", BB->numInsts);
if (offset != len) {
cli_errmsg("Trailing garbage in basicblock: %d extra bytes\n",
len-offset);
@ -598,6 +599,8 @@ int cli_bytecode_load(struct cli_bc *bc, FILE *f, struct cli_dbio *dbio)
return rc;
}
if (bb >= bc->funcs[current_func].numBB) {
cli_dbgmsg("Parsed %u BBs, %u instructions\n",
bb, bc->funcs[current_func].numInsts);
state = PARSE_FUNC_HEADER;
current_func++;
}

@ -50,15 +50,6 @@ static int bcfail(const char *msg, long a, long b,
#define CHECK_GT(a,b)
#endif
struct stack_entry {
const struct cli_bc_func *func;
struct cli_bc_value *ret;
struct cli_bc_value *values;
struct cli_bc_bb *bb;
unsigned bb_inst;
};
/* Get the operand of a binary operator, upper bits
* (beyond the size of the operand) may have random values.
* Use this when the active bits of the result of a binop are the same
@ -70,17 +61,23 @@ struct stack_entry {
#define UNOPNOMOD(i) (values[inst->u.binop[i]].v)
/* get the operand of a binary operator, upper bits are cleared */
#define BINOP(i) (BINOPNOMOD(i)&((1 << inst->type)-1))
#define UNOP(x) (UNOPNOMOD(i)&((1 << inst->type)-1))
#define type2mask(t) (inst->type == 64 ? ~0ull : (1ull << inst->type)-1)
#define BINOP(i) (BINOPNOMOD(i)&type2mask(inst->type))
#define UNOP(x) (UNOPNOMOD(i)&typemask(inst->type))
/* get the operand as a signed value.
* Warning: this assumes that result type is same as operand type.
* This is usually true, except for icmp_* and select.
* For icmp_* we fix it up in the loader. */
#define SIGNEXT(a) CLI_SRS(((int64_t)(a)) << (64-inst->type), (64-inst->type))
#define BINOPS(i) SIGNEXT(BINOPNOMOD(i))
#define SIGNEXT(a, from) CLI_SRS(((int64_t)(a)) << (64-(from)), (64-(from)))
#define BINOPS(i) SIGNEXT(BINOPNOMOD(i), inst->type)
#define CASTOP (values[inst->u.cast.source].v& inst->u.cast.mask)
static int jump(const struct cli_bc_func *func, uint16_t bbid, struct cli_bc_bb **bb, const struct cli_bc_inst **inst,
#undef always_inline
#define always_inline
static always_inline int jump(const struct cli_bc_func *func, uint16_t bbid, struct cli_bc_bb **bb, const struct cli_bc_inst **inst,
unsigned *bb_inst)
{
CHECK_GT(func->numBB, bbid);
@ -90,26 +87,164 @@ static int jump(const struct cli_bc_func *func, uint16_t bbid, struct cli_bc_bb
return 0;
}
static struct cli_bc_value *allocate_stack(const struct cli_bc_func *func)
#define STACK_CHUNKSIZE 16384
struct stack_chunk {
struct stack_chunk *prev;
unsigned used;
union {
void *align;
char data[STACK_CHUNKSIZE];
} u;
};
struct stack {
struct stack_chunk* chunk;
uint16_t last_size;
};
static always_inline void* cli_stack_alloc(struct stack *stack, unsigned bytes)
{
struct stack_chunk *chunk = stack->chunk;
uint16_t last_size_off;
/* last_size is stored after data */
/* align bytes to pointer size */
bytes = (bytes + sizeof(uint16_t) + sizeof(void*)) & ~(sizeof(void*)-1);
last_size_off = bytes - 2;
if (chunk && (chunk->used + bytes <= STACK_CHUNKSIZE)) {
/* there is still room in this chunk */
void *ret;
*(uint16_t*)&chunk->u.data[chunk->used + last_size_off] = stack->last_size;
stack->last_size = bytes/sizeof(void*);
ret = chunk->u.data + chunk->used;
chunk->used += bytes;
return ret;
}
if(bytes >= STACK_CHUNKSIZE) {
cli_errmsg("cli_stack_alloc: Attempt to allocate more than STACK_CHUNKSIZE bytes!\n");
return NULL;
}
/* not enough room here, allocate new chunk */
chunk = cli_malloc(sizeof(*stack->chunk));
if (!chunk)
return NULL;
*(uint16_t*)&chunk->u.data[last_size_off] = stack->last_size;
stack->last_size = bytes/sizeof(void*);
chunk->used = bytes;
chunk->prev = stack->chunk;
stack->chunk = chunk;
return chunk->u.data;
}
static always_inline void cli_stack_free(struct stack *stack, void *data)
{
uint16_t last_size;
struct stack_chunk *chunk = stack->chunk;
if (!chunk) {
cli_errmsg("cli_stack_free: stack empty!\n");
return;
}
if ((chunk->u.data + chunk->used) != ((char*)data + stack->last_size*sizeof(void*))) {
cli_errmsg("cli_stack_free: wrong free order: %p, expected %p\n",
data, chunk->u.data + chunk->used - stack->last_size*sizeof(void*));
return;
}
last_size = *(uint16_t*)&chunk->u.data[chunk->used-2];
if (chunk->used < stack->last_size*sizeof(void*)) {
cli_errmsg("cli_stack_free: last_size is corrupt!\n");
return;
}
chunk->used -= stack->last_size*sizeof(void*);
stack->last_size = last_size;
if (!chunk->used) {
stack->chunk = chunk->prev;
free(chunk);
}
}
static void cli_stack_destroy(struct stack *stack)
{
struct stack_chunk *chunk = stack->chunk;
while (chunk) {
stack->chunk = chunk->prev;
free(chunk);
chunk = stack->chunk;
}
}
struct stack_entry {
struct stack_entry *prev;
const struct cli_bc_func *func;
struct cli_bc_value *ret;
struct cli_bc_bb *bb;
unsigned bb_inst;
struct cli_bc_value *values;
};
static always_inline struct stack_entry *allocate_stack(struct stack *stack,
struct stack_entry *prev,
const struct cli_bc_func *func,
const struct cli_bc_func *func_old,
struct cli_bc_value *ret,
struct cli_bc_bb *bb,
unsigned bb_inst)
{
unsigned i;
struct cli_bc_value *values = cli_malloc((func->numValues+func->numConstants)*sizeof(*values));
if (!values)
struct cli_bc_value *values;
const unsigned numValues = func->numValues + func->numConstants;
struct stack_entry *entry = cli_stack_alloc(stack, sizeof(*entry) + sizeof(*values)*numValues);
if (!entry)
return NULL;
for (i=func->numValues;i<func->numValues+func->numConstants;i++)
values[i] = func->constants[i-func->numValues];
return values;
entry->prev = prev;
entry->func = func_old;
entry->ret = ret;
entry->bb = bb;
entry->bb_inst = bb_inst;
/* we allocated room for values right after stack_entry! */
entry->values = values = (struct cli_bc_value*)&entry[1];
memcpy(&values[func->numValues], func->constants,
sizeof(*values)*func->numConstants);
return entry;
}
static always_inline struct stack_entry *pop_stack(struct stack *stack,
struct stack_entry *stack_entry,
const struct cli_bc_func **func,
struct cli_bc_value **ret,
struct cli_bc_bb **bb,
unsigned *bb_inst)
{
void *data;
*func = stack_entry->func;
*ret = stack_entry->ret;
*bb = stack_entry->bb;
*bb_inst = stack_entry->bb_inst;
data = stack_entry;
stack_entry = stack_entry->prev;
cli_stack_free(stack, data);
return stack_entry;
}
int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct cli_bc_func *func, const struct cli_bc_inst *inst)
{
unsigned i, stack_depth=0, bb_inst=0, stop=0, stack_max_depth=0;
uint64_t tmp;
unsigned i, stack_depth=0, bb_inst=0, stop=0 ;
struct cli_bc_func *func2;
struct stack_entry *stack = NULL;
struct stack stack;
struct stack_entry *stack_entry = NULL;
struct cli_bc_bb *bb = NULL;
struct cli_bc_value *values = ctx->values;
struct cli_bc_value *value, *old_values;
memset(&stack, 0, sizeof(stack));
do {
value = &values[inst->dest];
CHECK_GT(func->numValues+func->numConstants, value - values);
@ -126,8 +261,10 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
case OP_UDIV:
{
uint64_t d = BINOP(1);
if (UNLIKELY(!d))
if (UNLIKELY(!d)) {
cli_dbgmsg("bytecode attempted to execute udiv#0\n");
return CL_EBYTECODE;
}
value->v = BINOP(0) / d;
break;
}
@ -135,16 +272,20 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
{
int64_t a = BINOPS(0);
int64_t b = BINOPS(1);
if (UNLIKELY(b == 0 || (b == -1 && a == (-9223372036854775807LL-1LL))))
if (UNLIKELY(b == 0 || (b == -1 && a == (-9223372036854775807LL-1LL)))) {
cli_dbgmsg("bytecode attempted to execute sdiv#0\n");
return CL_EBYTECODE;
}
value->v = a / b;
break;
}
case OP_UREM:
{
uint64_t d = BINOP(1);
if (UNLIKELY(!d))
if (UNLIKELY(!d)) {
cli_dbgmsg("bytecode attempted to execute urem#0\n");
return CL_EBYTECODE;
}
value->v = BINOP(0) % d;
break;
}
@ -152,8 +293,10 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
{
int64_t a = BINOPS(0);
int64_t b = BINOPS(1);
if (UNLIKELY(b == 0 || (b == -1 && (a == -9223372036854775807LL-1LL))))
if (UNLIKELY(b == 0 || (b == -1 && (a == -9223372036854775807LL-1LL)))) {
cli_dbgmsg("bytecode attempted to execute srem#0\n");
return CL_EBYTECODE;
}
value->v = a % b;
break;
}
@ -179,12 +322,13 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
value->v = BINOPNOMOD(0) ^ BINOPNOMOD(1);
break;
case OP_SEXT:
value->v = SIGNEXT(values[inst->u.cast.source].v);
/* mask is number of src bits here, not a mask! */
value->v = SIGNEXT(values[inst->u.cast.source].v, inst->u.cast.mask);
break;
case OP_TRUNC:
/* fall-through */
case OP_ZEXT:
value->v = values[inst->u.cast.source].v & values[inst->u.cast.mask].v;
value->v = CASTOP;
break;
case OP_BRANCH:
stop = jump(func, (values[inst->u.branch.condition].v&1) ?
@ -196,31 +340,13 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
continue;
case OP_RET:
CHECK_GT(stack_depth, 0);
stack_depth--;
value = stack[stack_depth].ret;
func = stack[stack_depth].func;
value->v = values[inst->u.unaryop].v;
old_values = values;
values = stack[stack_depth].values;
stack[stack_depth].values = old_values;
tmp = values[inst->u.unaryop].v;
stack_entry = pop_stack(&stack, stack_entry, &func, &value, &bb,
&bb_inst);
values = stack_entry ? stack_entry->values : ctx->values;
CHECK_GT(func->numValues+func->numConstants, value-values);
CHECK_GT(value-values, -1);
bb = stack[stack_depth].bb;
bb_inst = stack[stack_depth].bb_inst;
if ((stack_depth < stack_max_depth*3/4) || !stack_depth) {
for (i=stack_depth;i<stack_max_depth;i++) {
free(stack[i].values);
}
if (!stack_depth) {
free(stack);
stack = 0;
} else {
stack = cli_realloc2(stack, sizeof(*stack)*stack_depth);
if (!stack)
return CL_EMEM;
}
stack_max_depth = stack_depth;
}
value->v = tmp;
if (!bb) {
stop = CL_BREAK;
continue;
@ -266,29 +392,16 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
func2 = &bc->funcs[inst->u.ops.funcid];
CHECK_EQ(func2->numArgs, inst->u.ops.numOps);
old_values = values;
if (stack_depth+1 > stack_max_depth) {
stack = cli_realloc2(stack, sizeof(*stack)*(stack_depth+1));
if (!stack)
return CL_EMEM;
stack_max_depth = stack_depth+1;
values = allocate_stack(func2);
if (!values)
return CL_EMEM;
} else {
values = stack[stack_depth].values;
}
stack[stack_depth].func = func;
stack[stack_depth].ret = value;
stack[stack_depth].bb = bb;
stack[stack_depth].bb_inst = bb_inst;
stack[stack_depth].values = old_values;
stack_depth++;
//cli_dbgmsg("Executing %d\n", inst->u.ops.funcid);
stack_entry = allocate_stack(&stack, stack_entry, func2, func, value,
bb, bb_inst);
values = stack_entry->values;
// cli_dbgmsg("Executing %d\n", inst->u.ops.funcid);
for (i=0;i<func2->numArgs;i++)
values[i] = old_values[inst->u.ops.ops[i]];
func = func2;
CHECK_GT(func->numBB, 0);
stop = jump(func, 0, &bb, &inst, &bb_inst);
stack_depth++;
continue;
case OP_COPY:
BINOPNOMOD(1) = BINOPNOMOD(0);
@ -303,6 +416,6 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
CHECK_GT(bb->numInsts, bb_inst);
} while (stop == CL_SUCCESS);
free(stack);
cli_stack_destroy(&stack);
return stop == CL_BREAK ? CL_SUCCESS : stop;
}

@ -351,6 +351,12 @@ void cli_errmsg(const char *str, ...);
#define UNLIKELY(cond) (cond)
#endif
#ifdef __GNUC__
#define always_inline inline __attribute__((always_inline))
#else
#define always_inline inline
#endif
#define cli_dbgmsg (!UNLIKELY(cli_debug_flag)) ? (void)0 : cli_dbgmsg_internal
#ifdef __GNUC__

Loading…
Cancel
Save