/* * LANGVM - Language's Virtual Machine * * Author: * Emile van Bergen, emile@evbergen.xs4all.nl * * Permission to redistribute an original or modified version of this program * in source, intermediate or object code form is hereby granted exclusively * under the terms of the GNU General Public License, version 2. Please see the * file COPYING for details, or refer to http://www.gnu.org/copyleft/gpl.html. * * History: * 2001/05/30 - EvB - Created * 2001/10/23 - EvB - Fixed nasty bug caused by value not being copied by an * assignment when the referenced item's value is temporary. * I'm still too lazy to do full-fledged refcounting though. * 2001/10/27 - EvB - Changed moveall to make use of meta_addav() * 2002/03/01 - EvB - Fixed := behaviour where lhs' underlying item is same * as rhs' * 2002/03/20 - EvB - Added nifty feature for DEC2ORD: if the string is not * actually decimal, and the stack item has an underlying * dictionary item, we try to look up the value as a named * constant associated with that string attribute. * 2002/04/23 - EvB - Sanitized conversion operators * - Fixed small ! operator behaviour bug on strings * 2003/12/30 - EvB - Added EXISTS instruction that tests for a resolved * attribute reference * 2004/08/14 - EvB - Added STR2MAC instruction to canonicalize MAC addresses * 2005/02/22 - EvB - Added PAPENCR / PAPDECR instructions * 2005/05/20 - EvB - Added CRYPT instruction, from Geoffrey Hort * 2005/06/01 - EvB - Added HMACMD5 and RANDOM instructions * 2005/06/24 - EvB - Added DICTENC, DICTDEC instructions */ char langvm[] = "LANGVM - Copyright (C) 2001 Emile van Bergen."; /* * INCLUDES & DEFINES */ #include /* For malloc(), strtol() */ #include /* For memset() */ #include /* For write() in printav */ #include /* For time_t, gmtime() and strftime() */ #include /* For touppper(), tolower() */ #include #include #include #ifdef HAVE_CRYPT #include /* For crypt(3) */ #endif #include /* Ahum. We want to dict encode/decode */ #include #include /* * FUNCTIONS */ VM *vm_new(INSN *code, ssize_t len, int stackend, META_AV *reqhead, META_AV *reqtail, META_AV *rephead, META_AV *reptail) { VM *ret; /* Allocate object */ ret = (VM *)malloc(sizeof(VM)); if (!ret) return 0; /* Init members */ ret->ip = code; ret->codeend = (INSN *)((char *)code + len); ret->stk = (META_AV *)malloc(stackend * sizeof(META_AV)); if (!ret->stk) { free(ret); return 0; } memset(ret->stk, 0xee, stackend * sizeof(META_AV)); ret->head[0] = reqhead; ret->head[1] = rephead; ret->tail[0] = reqtail; ret->tail[1] = reptail; ret->stackend = stackend; ret->sp = 0; return ret; } void vm_chgcode(VM *vm, INSN *code, ssize_t len) { vm->ip = code; vm->codeend = (INSN *)((char *)code + len); } void vm_popall(VM *vm) { while(vm->sp > 0) meta_freeavdata(vm->stk + --(vm->sp)); } void vm_del(VM *vm) { if (vm) { if (vm->stk) { vm_popall(vm); free(vm->stk); } free(vm); } } /* Utility functions for complex operators in vm_run */ /* Convert date as in yyyy[mm[dd[hh[mm[ss]]]]] to seconds since Epoch */ META_ORD op_datstr2ord(char *p, META_ORD l) { ssize_t g; struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_year = 1970; tm.tm_mon = 1; tm.tm_mday = 1; (void)( l && (tm.tm_year = meta_atoord(p, MIN(4,l), 0,0, &g, 10), p+=g, l-=g) && l && (tm.tm_mon = meta_atoord(p, MIN(2,l), 0,0, &g, 10), p+=g, l-=g) && l && (tm.tm_mday = meta_atoord(p, MIN(2,l), 0,0, &g, 10), p+=g, l-=g) && l && (tm.tm_hour = meta_atoord(p, MIN(2,l), 0,0, &g, 10), p+=g, l-=g) && l && (tm.tm_min = meta_atoord(p, MIN(2,l), 0,0, &g, 10), p+=g, l-=g) && l && (tm.tm_sec = meta_atoord(p, MIN(2,l), 0,0, &g, 10))); tm.tm_year -= 1900; tm.tm_mon--; return mktime(&tm); } /* Canonicalize MAC addresses, using custom output delimiter. Assumes there's enough room in out to store 12 hex digits + 5 times the output delimiter */ int canmac(char *out, char *in, META_ORD inl, char *od, META_ORD odl) { int n, rl; char *i, *o, c; i = in; o = out; for(n = 0; ; n++) { c = 0; rl = (in + inl) - i; if (rl > 0) { c = meta_atoord(i, MIN(rl, 2), 0, 0, &rl, 16); i += rl; if (i < in + inl && !ISHEX(*i)) i++; } hex(o, &c, 1); o += 2; if (n == 5) break; if (!odl) continue; memcpy(o, od, odl); o += odl; } return o - out; } /* Utility macros for use inside vm_run */ #define NEWSTR(str, len) \ if (!((str) = (char *)malloc((len)))) { \ ret = VM_NOMEM; break; \ } #define CHGSTR(av, str, len) \ meta_freeavdata((av)); \ (av)->p = (str); (av)->l = (len); \ (av)->flags |= AV_FREE_P; #define L (stk[sp-2]) #define R (stk[sp-1]) int vm_run(META *m, VM *vm) { INSN *i, *e; META_AV *stk, *av, *avlst; int ret, sp, fixcnt, dsccnt; META_ORD n; ssize_t g; char *s, *c; time_t t; i = vm->ip; e = vm->codeend; stk = vm->stk; sp = vm->sp; /* Run until something breaks the loop or we're past the code buffer */ for(ret = VM_CONTINUE; i < e; i++) { /* Check # of stack items against fixup and discard counts */ fixcnt = OP_FIXCNT(i->op); dsccnt = OP_DSCCNT(i->op); if (sp < fixcnt || sp < dsccnt) { ret = VM_UNDERFLOW; break; } /* First fixup the required amount of operands */ for( ; fixcnt; fixcnt--) { /* n is first 2, then 1 for a two-term opcode, so av is first the left (deeper) term, then the right */ av = stk + (sp - fixcnt); /* See if this term needs fixing up */ if ((av->flags & AV_UNRESOLVED) && av->i) { /* Yes, so first fixup REQ/REP flag using op. If USEREPVALID is set, the current USEREP overrides the op's default, otherwise USEREP may invert the default or leave it as is */ if ((av->flags & AV_USEREPVALID) == 0) { av->flags ^= OP_USEREP(i->op, fixcnt); } /* See if we want the first or last instance */ if (av->flags & AV_FIRST) { /* First: take first match forwards */ avlst = vm->head[av->flags & AV_USEREP]; for( ; avlst && (avlst->i != av->i || avlst->flags & AV_DEL); avlst = avlst->next); } else { /* Last: take first match backwards */ avlst = vm->tail[av->flags & AV_USEREP]; for( ; avlst && (avlst->i != av->i || avlst->flags & AV_DEL); avlst = avlst->prev); } /* Save reference to the found item, or zero */ av->sub = avlst; av->p = 0; av->l = 0; /* If really found, mark as fixed up */ if (avlst) { av->p = avlst->p; av->l = avlst->l; av->flags &= ~(AV_UNRESOLVED|AV_FREE_P); } } } /* We have our terms in stk[sp-2] (L) and [sp-1] (R/single) */ switch(i->op) { /* * Miscellaneous */ case OP_HALT: ret = VM_HALTED; break; case OP_ABORT: ret = VM_ABORTED; break; case OP_ACCEPT: for(av = vm->head[VM_REP]; av; av = av->next) { if (av->i->spc->nr == C_DS_RAD_PKT && av->i->nr == C_DI_CODE) av->l = C_DV_CODE_ACCEPT; } ret = VM_HALTED; break; case OP_REJECT: for(av = vm->head[VM_REP]; av; av = av->next) { if (av->i->spc->nr == C_DS_RAD_PKT && av->i->nr == C_DI_CODE) av->l = C_DV_CODE_REJECT; if (av->i->spc->nr != C_DS_RAD_ATR || av->i->keeprej) continue; av->flags |= AV_DEL; } ret = VM_HALTED; break; case OP_ACCTRESP: for(av = vm->head[VM_REP]; av; av = av->next) { if (av->i->spc->nr == C_DS_RAD_PKT && av->i->nr == C_DI_CODE) av->l = C_DV_CODE_ACCTRESP; if (av->i->spc->nr != C_DS_RAD_ATR || av->i->keepacct) continue; av->flags |= AV_DEL; } ret = VM_HALTED; break; case OP_NOP: case OP_POP: break; /* * Pushing */ case OP_PUSHINT: if (sp > vm->stackend - 1) { ret = VM_OVERFLOW; break; } stk[sp].i = 0; stk[sp].p = 0; stk[sp].l = i->imm.ord; stk[sp++].flags = 0; break; case OP_PUSHSTR: if (sp > vm->stackend - 1) { ret = VM_OVERFLOW; break; } stk[sp].i = 0; stk[sp].p = (char *)(i + 1); stk[sp].l = i->imm.d.str_len; stk[sp++].flags = 0; i += i->imm.d.disp; break; case OP_PUSHAV: if (sp > vm->stackend - 1) { ret = VM_OVERFLOW; break; } stk[sp].i = i->imm.i.item; stk[sp].p = 0; stk[sp].l = 0; stk[sp++].flags = i->imm.i.flags | AV_UNRESOLVED; break; /* * Unary integer operators */ case OP_NEG: R.l = - R.l; break; case OP_NOT: R.l = ~ R.l; break; case OP_DICTENC: /* This uses the space of the attribute reference on the right to determine how we should encode the request or reply list. This moves the space search to the compiler. Also, the attribute reference's AV_USEREP/AV_USEREQ flag is used to determine the list to be encoded. The reply list is the default. */ meta_freeavdata(&R); R.p = 0; R.l = 0; if (!R.i) break; /* Fixup USEREQ/USEREP flag; opcode doesn't specify an * stack references to fixup, but we do want to have a * correct flag; that is, REP is the default */ if (!(R.flags & AV_USEREPVALID)) R.flags ^= AV_USEREP; /* Build a tree from the encapsulating space */ avlst = 0; meta_buildtree(vm->head[R.flags & AV_USEREP], &avlst, R.i->spc); if (!avlst) break; if (msg_thresh[F_LANG] >= L_DEBUG) { msg(F_LANG, L_DEBUG, "OP_DICTENC: tree for %s list from %s:\n", R.flags & AV_USEREP ? "REP" : "REQ", R.i->spc->name); meta_printavlist(m, avlst, 0); } /* Now encode it */ NEWSTR(s, C_MAX_PKTSIZE); R.l = meta_encode(R.i->spc, s, C_MAX_PKTSIZE, avlst, 0); if (R.l < 0) { R.l = 0; free(s); break; } R.p = s; R.i = 0; R.flags |= AV_FREE_P; break; /* * Binary integer operators */ case OP_MUL: L.l *= R.l; break; case OP_DIV: if (R.l) { L.l /= R.l; break; } ret = VM_INVALIDARG; break; case OP_CIDRMASK: L.l &= 0xffffffff << (32 - R.l); break; case OP_MOD: L.l %= R.l; break; case OP_ADD: L.l += R.l; break; case OP_SUB: L.l -= R.l; break; case OP_SHL: L.l <<= R.l; break; case OP_SHR: L.l >>= R.l; break; case OP_XOR: L.l ^= R.l; break; case OP_AND: L.l &= R.l; break; case OP_OR: L.l |= R.l; break; case OP_GE: L.l = L.l >= R.l; L.i = 0; break; case OP_LE: L.l = L.l <= R.l; L.i = 0; break; case OP_GT: L.l = L.l > R.l; L.i = 0; break; case OP_LT: L.l = L.l < R.l; L.i = 0; break; case OP_EQ: L.l = L.l == R.l; L.i = 0; break; case OP_NE: L.l = L.l != R.l; L.i = 0; break; /* * Unary string operators */ case OP_MD5: NEWSTR(s, 16); md5(s, R.p, R.l); CHGSTR(&R, s, 16); break; case OP_HEX: NEWSTR(s, R.l << 1); hex(s, R.p, R.l); CHGSTR(&R, s, R.l << 1); break; case OP_UPPER: if (!R.l) break; if (R.flags & AV_FREE_P) s = R.p; else { NEWSTR(s, R.l); R.flags |= AV_FREE_P; } for(n = 0; n < R.l; n++) s[n] = toupper(R.p[n]); R.p = s; break; case OP_LOWER: if (!R.l) break; if (R.flags & AV_FREE_P) s = R.p; else { NEWSTR(s, R.l); R.flags |= AV_FREE_P; } for(n = 0; n < R.l; n++) s[n] = tolower(R.p[n]); R.p = s; break; /* * Binary string operators */ case OP_BF: if (L.p && R.p && (s = memchr(L.p, R.p[0], L.l))) L.l = s - L.p; else { meta_freeavdata(&L); L.l = 0; } break; case OP_AF: if (L.p && R.p && (s = memchr(L.p, R.p[0], L.l))) { n = s - L.p + 1; L.l -= n; if (L.flags & AV_FREE_P) s = L.p; else { NEWSTR(s, L.l); L.flags |= AV_FREE_P; } memcpy(s, L.p + n, L.l); L.p = s; } else { meta_freeavdata(&L); L.l = 0; } break; case OP_BL: if (L.p && R.p && (s = memrchr(L.p, R.p[0], L.l))) L.l = s - L.p; else { meta_freeavdata(&L); L.l = 0; } break; case OP_AL: if (L.p && R.p && (s = memrchr(L.p, R.p[0], L.l))) { n = s - L.p + 1; L.l -= n; if (L.flags & AV_FREE_P) s = L.p; else { NEWSTR(s, L.l); L.flags |= AV_FREE_P; } memcpy(s, L.p + n, L.l); L.p = s; } else { meta_freeavdata(&L); L.l = 0; } break; case OP_FO: n = L.l; meta_freeavdata(&L); L = R; R.flags &= ~AV_FREE_P; if (n < 0) n = L.l + n; if (n >= 0 && n < L.l) L.l = n; break; case OP_LO: n = L.l; meta_freeavdata(&L); L = R; R.flags &= ~AV_FREE_P; if (n < 0) n = L.l + n; if (n >= 0 && n < L.l) { if (L.flags & AV_FREE_P) s = L.p; else { NEWSTR(s, n); L.flags |= AV_FREE_P; } memcpy(s, L.p + (L.l - n), n); L.p = s; L.l = n; } break; case OP_XORSTR: if (!L.l || !R.l) break; if (L.flags & AV_FREE_P) s = L.p; else { NEWSTR(s, L.l); L.flags |= AV_FREE_P; } for(n = 0; n < L.l; n++) s[n] = L.p[n] ^ R.p[n % R.l]; L.p = s; break; case OP_CONCAT: if (!R.l) break; if (!L.l) { L = R; break; } NEWSTR(s, L.l + R.l); memcpy(s, L.p, L.l); memcpy(s + L.l, R.p, R.l); CHGSTR(&L, s, L.l + R.l); break; case OP_GESTR: /* FIXME */ meta_freeavdata(&L); L.l = 0; L.i = 0; break; case OP_LESTR: /* FIXME */ meta_freeavdata(&L); L.l = 0; L.i = 0; break; case OP_GTSTR: /* FIXME */ meta_freeavdata(&L); L.l = 0; L.i = 0; break; case OP_LTSTR: /* FIXME */ meta_freeavdata(&L); L.l = 0; L.i = 0; break; case OP_EQSTR: n = L.l == R.l && (!L.l || (L.p && R.p && memcmp(L.p, R.p, L.l) == 0)); meta_freeavdata(&L); L.l = n; L.i = 0; break; case OP_NESTR: n = !(L.l == R.l && (!L.l || (L.p && R.p && memcmp(L.p, R.p, L.l) == 0))); meta_freeavdata(&L); L.l = n; L.i = 0; break; case OP_DICTDEC: /* This uses the space of the attribute reference on the stack to determine how we should decode the string. This moves the space search to the compiler. Also, we use the AV_USEREP/AV_USEREQ flags to determine the destination list, and the AV_FIRST flag to determine where the decoded list should end up. */ /* Decode needs the source data to be persistent. So, neither L.flags & AV_FREE_P must be set, nor, if L.sub is present, L.sub->flags & AV_FREE_P. That means that you can only decode strings that are constant (come from the expression) or strings that come from the original request (not module responses!) this way. For now, that's enough. */ av = 0; if (!R.i || !L.l || (L.flags & AV_FREE_P) || (L.i && L.sub && ((L.sub->flags & AV_FREE_P) || L.sub->p != L.p)) || !(avlst = meta_decode(m, R.i->spc, R.i->vnd, L.p, L.l, &av))) { meta_freeavdata(&L); L.l = 0; L.p = 0; L.i = 0; break; } /* Add avlst/av (head/tail) to proper place */ if (R.flags & AV_FIRST) { /* head */ av->next = vm->head[R.flags & AV_USEREP]; vm->head[R.flags & AV_USEREP] = avlst; if (av->next) av->next->prev = av; if (!vm->tail[R.flags & AV_USEREP]) vm->tail[R.flags & AV_USEREP] = av; break; } /* tail */ avlst->prev = vm->tail[R.flags & AV_USEREP]; vm->tail[R.flags & AV_USEREP] = av; if (avlst->prev) avlst->prev->next = avlst; if (!vm->head[R.flags & AV_USEREP]) vm->head[R.flags & AV_USEREP] = avlst; break; /* * Boolean ops */ case OP_EXISTS: meta_freeavdata(&R); R.l = ! (R.flags & AV_UNRESOLVED); R.p = 0; R.i = 0; break; case OP_BOOLNOT: meta_freeavdata(&R); R.l = ! R.l; R.p = 0; R.i = 0; break; case OP_JMPZ: if (! R.l) i += i->imm.d.disp; break; case OP_JMPNZ: if (R.l) i += i->imm.d.disp; break; /* * Conversion from strings to numbers */ case OP_OCTSTR2ORD: /* oct digits -> ord */ if (!R.l) break; n = meta_atoord(R.p, R.l, 0, 0, 0, 8); meta_freeavdata(&R); R.p = 0; R.l = n; R.i = 0; break; case OP_DECSTR2ORD: /* dec digits -> ord */ if (!R.l) break; n = meta_atoord(R.p, R.l, 0, 0, 0, 10); meta_freeavdata(&R); R.p = 0; R.l = n; R.i = 0; break; case OP_HEXSTR2ORD: /* hex digits -> ord */ if (!R.l) break; n = meta_atoord(R.p, R.l, 0, 0, 0, 16); meta_freeavdata(&R); R.p = 0; R.l = n; R.i = 0; break; case OP_RAWSTR2ORD: /* big-endian binary -> ord */ if (!R.l) break; n = getord(R.p, R.l); meta_freeavdata(&R); R.p = 0; R.l = n; R.i = 0; break; case OP_IPASTR2ORD: /* dotted quad decimal -> ord */ if (!R.l) break; n = meta_atoip(R.p, R.l, 0, 0, 0); meta_freeavdata(&R); R.p = 0; R.l = n; R.i = 0; break; case OP_DATSTR2ORD: /* YYYYMMDDhhmmss -> ord */ if (!R.l) break; n = op_datstr2ord(R.p, R.l); meta_freeavdata(&R); R.p = 0; R.l = n; R.i = 0; break; case OP_INTSTR2ORD: /* digits or named const -> ord */ if (!R.l) break; n = meta_atoord(R.p, R.l, 0, 0, &g, 0); if (!g && R.i) { META_VAL *val; NEWSTR(s, R.l + 1); memcpy(s, R.p, R.l); s[R.l] = 0; val = meta_getvalbyname(0, R.i, s); if (val) n = val->nr; free(s); } meta_freeavdata(&R); R.p = 0; R.l = n; R.i = 0; break; /* * Unary conversion operators from numbers to strings */ case OP_ORD2OCTSTR: /* ord -> oct digits */ NEWSTR(s, n = sizeof(META_ORD) * 3 + 2); n = meta_ordtoa(s, n, 0, 10, R.l); CHGSTR(&R, s, n); R.i = 0; break; case OP_ORD2DECSTR: /* ord -> dec digits */ NEWSTR(s, n = sizeof(META_ORD) * 3 + 2); n = meta_ordtoa(s, n, 0, 10, R.l); CHGSTR(&R, s, n); R.i = 0; break; case OP_ORD2HEXSTR: /* ord -> hex digits */ NEWSTR(s, n = sizeof(META_ORD) * 2 + 2); n = meta_ordtoa(s, n, 0, 16, R.l); CHGSTR(&R, s, n); R.i = 0; break; case OP_ORD2RAWSTR: /* ord -> big-endian binary */ if (R.i && R.i->val_size > 0) { NEWSTR(s, R.i->val_size); putord(s, R.i->val_size, R.l); CHGSTR(&R, s, R.i->val_size); } else { NEWSTR(s, sizeof(META_ORD)); putord(s, sizeof(META_ORD), R.l); CHGSTR(&R, s, sizeof(META_ORD)); } R.i = 0; break; case OP_ORD2IPASTR: /* ord -> dotted quad decimal */ NEWSTR(s, n = 20); n = meta_iptoa(s, n, R.l); CHGSTR(&R, s, n); R.i = 0; break; case OP_ORD2DATSTR: /* ord -> YYYYMMDDhhmmss */ NEWSTR(s, 16); memset(s, 0, 15); t = R.l; strftime(s, 15, "%Y%m%d%H%M%S", localtime(&t)); CHGSTR(&R, s, strlen(s)); R.i = 0; break; case OP_RANDOM: /* ord -> random bytes of length ord */ /* This works both on strings as on integers; if used * on a string, it replaces it with a random string of * the same length and does so in place if it already * was a temporary one. */ if (!R.l) break; if (!R.p || !(R.flags & AV_FREE_P)) { NEWSTR(R.p, R.l); R.flags |= AV_FREE_P; } get_random_data(R.p, R.l); R.i = 0; break; /* * Binary conversion operators from number to string */ case OP_ORD2DFMSTR: /* ord -> strftime formatted date */ if (!R.l) { L.l = 0; break; } NEWSTR(s, 128); memset(s, 0, 128); NEWSTR(c, R.l + 1); memcpy(c, R.p, R.l); c[R.l] = 0; t = L.l; strftime(s, 127, c, localtime(&t)); free(c); CHGSTR(&L, s, strlen(s)); L.i = 0; break; /* * Binary string transformation operators */ case OP_STR2MAC: NEWSTR(s, 13 + R.l * 5); n = canmac(s, L.p, L.l, R.p, R.l); CHGSTR(&L, s, n); break; case OP_PAPENCR: /* Right argument holds secret plus fixed 16-octet * authenticator; left argument holds cleartext. Copy * if not owned by stack item or not enough space. * If we need a new string but do have AV_FREE_P set, * we need to free the old item; hence the full CHGSTR * stanza. Compare PAPDECR. */ if (R.l <= 16) { meta_freeavdata(&L); L.l = 0; break; } if ((L.flags & AV_FREE_P) && !(L.l & 15)) { encrypt_attr_pap(L.p, L.l, L.p, &L.l, R.p, R.l - 16, R.p + R.l - 16, 16); break; } NEWSTR(s, (L.l + 15) & ~ 15); memcpy(s, L.p, L.l); encrypt_attr_pap(L.p, L.l, s, &L.l, R.p, R.l - 16, R.p + R.l - 16, 16); CHGSTR(&L, s, L.l); break; case OP_PAPDECR: /* Right argument holds secret plus fixed 16-octet * authenticator; left argument holds a number of * 16-octet cyphertext chunks. Copy if not owned. */ if (R.l <= 16) { meta_freeavdata(&L); L.l = 0; } if (!L.l || (L.l & 15)) break; if (L.flags & AV_FREE_P) s = L.p; else { NEWSTR(s, L.l); L.flags |= AV_FREE_P; } decrypt_attr_pap(L.p, L.l, s, &L.l, R.p, R.l - 16, R.p + R.l - 16, 16); L.p = s; break; #ifdef HAVE_CRYPT case OP_UNIXCRYPT: /* Calculate a POSIX crypt(3) hash * Left argument holds cleartext to be crypted * First two bytes of right argument holds salt, * which must be at least length 2. */ if (R.l < 2) { meta_freeavdata(&L); L.l = 0; } if (!L.l) break; /* get a temp buffer so we can \0 terminate passwd */ NEWSTR(c, L.l + 1); memcpy(c, L.p, L.l); c[L.l] = '\0'; s = crypt(c, R.p); free(c); /* s is a static data area inside crypt, copy it out */ n = strlen(s); NEWSTR(c, n); memcpy(c, s, n); CHGSTR(&L, c, n); break; #endif case OP_HMACMD5: /* Right argument holds key, left holds message. If * left stack item owns enough memory, overwrite data * in place. Empty messages and keys generate empty * HMACs. */ if (!R.l || !R.p) { meta_freeavdata(&L); L.l = 0; } if (!L.l) break; if ((L.flags & AV_FREE_P) && L.l >= 16) { hmac_md5(L.p, L.p, L.l, R.p, R.l); L.l = 16; break; } NEWSTR(s, 16); hmac_md5(s, L.p, L.l, R.p, R.l); CHGSTR(&L, s, 16); break; /* * Assignment etc. */ case OP_ADDAV: case OP_REPLACEAV: /* If left term is anonymous, skip all this and just copy the right term's *data* to the left term. You won't see this happen often though. */ if (!L.i) { meta_freeavdata(&L); L.l = R.l; L.p = R.p; /* The temporary flag is *moved* from R to L */ L.flags = R.flags; R.flags &= ~AV_FREE_P; break; } /* Add a new item if no underlying item or if ADDAV */ if (i->op == OP_ADDAV || !L.sub) { /* Allocate new item */ av = (META_AV *)malloc(sizeof(META_AV)); if (!av) { ret = VM_NOMEM; break; } memset(av, 0, sizeof(META_AV)); /* Add to list */ meta_addav(&vm->head[L.flags & AV_USEREP], &vm->tail[L.flags & AV_USEREP], L.sub, L.flags & AV_FIRST, av); /* Set things up so that a new fixup would match the newly created item. Also make it look like this fixup actually happened. */ av->i = L.i; L.sub = av; L.l = 0; L.p = 0; L.flags &= ~(AV_UNRESOLVED|AV_FREE_P); } /* When changing the underlying item's data, we copy the *data* from the right term, unless the right term is anonymous and non-temporary (this means its data is immediate). We don't re-use non-temporary pointers from stack items that do have underlying items, as they could point to their underlying items' data, which is freed when the item itself is deleted! */ if (L.sub != R.sub) meta_freeavdata(L.sub); L.sub->l = R.l; if (!R.i && !(R.flags & AV_FREE_P)) { L.sub->p = R.p; } else if (R.l && R.p) { NEWSTR(L.sub->p, R.l); memcpy(L.sub->p, R.p, R.l); L.sub->flags |= AV_FREE_P; } else L.sub->p = 0; /* Now change the left operand as if we would have re-done the fixup at this point. Free any possible temporary data held by the stack item first though */ meta_freeavdata(&L); L.l = L.sub->l; L.p = L.sub->p; L.flags &= ~(AV_UNRESOLVED|AV_FREE_P); break; case OP_POKEAV: /* Return an anonymous zero if we don't succeed */ if (!L.sub || !L.sub->p || !L.sub->l || !R.p || !R.l) { meta_freeavdata(&L); L.sub = 0; L.i = 0; L.p = 0; L.l = 0; L.flags |= AV_UNRESOLVED; break; } memcpy(L.sub->p, R.p, MIN(L.sub->l, R.l)); break; case OP_DELAV: if (!R.sub) { /* If none, return 0. */ meta_freeavdata(&R); /* In case of del 'a' */ R.i = 0; R.p = 0; R.l = 0; break; } /* Set next item's previous field to item before us */ if (R.sub->next) R.sub->next->prev = R.sub->prev; else vm->tail[R.flags & AV_USEREP] = R.sub->prev; /* Set previous item's next field to item after us */ if (R.sub->prev) R.sub->prev->next = R.sub->next; else vm->head[R.flags & AV_USEREP] = R.sub->next; /* Free the underlying item and its temporary data */ meta_freeavdata(R.sub); free(R.sub); /* Return 1, because we deleted something */ R.i = 0; R.p = 0; R.l = 1; break; case OP_DELALLAV: if (sp < 1) { ret = VM_UNDERFLOW; break; } /* Fixup USEREP flag here, default to USEREP set */ if (!(R.flags & AV_USEREPVALID)) R.flags ^= AV_USEREP; /* Already free right operand's data if any */ meta_freeavdata(&R); R.p = 0; R.l = 0; /* Go forward through the list */ for(av = vm->head[R.flags & AV_USEREP]; av; ) { avlst = av->next; if (av->i == R.i) { /* Remove underlying item from list */ if (av->next) av->next->prev = av->prev; else vm->tail[R.flags & AV_USEREP] = av->prev; if (av->prev) av->prev->next = av->next; else vm->head[R.flags & AV_USEREP] = av->next; /* Free underlying item and its data */ meta_freeavdata(av); free(av); /* Increment return value */ R.l++; } av = avlst; } /* Returned int is anonymous */ R.i = 0; break; case OP_MOVEALLAV: if (sp < 1) { ret = VM_UNDERFLOW; break; } /* First free right operand's data if any - this occurs * if you do tricks like all (str . "a"); we only care * about the reference, not the stack item's data. */ meta_freeavdata(&R); R.p = 0; R.l = 0; /* Move all instances of referenced item from the list on which that item is to the other list, preserving their relative order (mainly for eg. Proxy-State). The source list is always walked through from top to bottom, and added to the destination list at the bottom or the top if the F: modifier is used. */ for(av = vm->head[R.flags & AV_USEREP]; av; ) { avlst = av->next; if (av->i == R.i) { /* Remove the item from its environment */ if (av->next) av->next->prev = av->prev; else vm->tail[R.flags & AV_USEREP] = av->prev; if (av->prev) av->prev->next = av->next; else vm->head[R.flags & AV_USEREP] = av->next; /* Add it to the other list */ meta_addav(&vm->head[(R.flags & AV_USEREP) ^ 1], &vm->tail[(R.flags & AV_USEREP) ^ 1], 0, R.flags & AV_FIRST, av); /* Increment return value */ R.l++; } av = avlst; } /* Returned int is anonymous */ R.i = 0; break; case OP_JOINSTR: if (sp < 1) { ret = VM_UNDERFLOW; break; } if (!R.i) break; /* First free right operand's data if any - this occurs * if you do tricks like all (str . "a"); we only care * about the reference, not the stack item's data. */ meta_freeavdata(&R); R.p = 0; R.l = 0; /* First get length of all pairs. This operator breaks * the exception to the 'no late binding' rule (because * of the type switch here), but we can't use the * compiler's autoconversion here because we haven't * got proper lists implemented yet. */ n = 0; if (MT_ISORD(R.i->val_type)) { for(av = vm->head[R.flags & AV_USEREP]; av; av = av->next) if (av->i == R.i) n++; n *= R.i->val_size; } else { for(av = vm->head[R.flags & AV_USEREP]; av; av = av->next) if (av->i == R.i) n += av->l; } if (!n) break; /* Now join everything into s */ NEWSTR(s, n); c = s; if (MT_ISORD(R.i->val_type)) { for(av = vm->head[R.flags & AV_USEREP]; av; av = av->next) if (av->i == R.i) { putord(c, av->i->val_size, av->l); c += av->i->val_size; if (c < s + n) continue; break; } } else { for(av = vm->head[R.flags & AV_USEREP]; av; av = av->next) if (av->i == R.i) { memcpy(c, av->p, av->l); c += av->l; if (c < s + n) continue; break; } } CHGSTR(&R, s, n); R.i = 0; /* must do, because type may mismatch */ break; case OP_CALLIFACE: ret = VM_IFACETRAP; break; default: ret = VM_INVALIDOP; break; } if (ret != VM_CONTINUE) break; /* Now discard the required amount of stack items */ if (sp < dsccnt) { ret = VM_UNDERFLOW; break; } for( ; dsccnt; dsccnt--) meta_freeavdata(stk + (--sp)); } vm->ip = i; vm->sp = sp; return ret; } struct iface *vm_getiface(VM *vm) { struct iface *ret; if (vm && vm->ip && vm->ip->op == OP_CALLIFACE) { ret = vm->ip->imm.iface; vm->ip++; return ret; } return 0; } void vm_dumptrace(VM *vm, META *m, INSN *codestart) { msg(F_LANG, L_ERR, "vm: ip=%d limit=%d\n", vm->ip - codestart, vm->codeend - codestart); msg(F_LANG, L_ERR, "vm: sp=%d limit=%d\n", vm->sp, vm->stackend); msg(F_LANG, L_ERR, "vm: stack contents:\n"); vm_dumpstack(vm, m); msg(F_LANG, L_ERR, "vm: request list:\n"); meta_printavlist(m, vm->head[VM_REQ], 0); msg(F_LANG, L_ERR, "vm: reply list:\n"); meta_printavlist(m, vm->head[VM_REP], 0); } void vm_dumpstack(VM *vm, META *m) { char buf[16]; int n, l; if (vm->sp > 0) { write(2, "(top)", 5); meta_printav(m, vm->stk + vm->sp - 1, 0); for(n = vm->sp - 2; n >= 0; n--) { l = 0; buf[l++] = '('; l += meta_ordtoa(buf + l, 3, 2, 10, n); buf[l++] = ')'; write(2, buf, l); meta_printav(m, vm->stk + n, 0); } } else write(2, "(empty)\n", 8); }