From 78e5656a619106571ba73c29558617092b7cb919 Mon Sep 17 00:00:00 2001 From: Carsten Haitzler Date: Tue, 19 Oct 2004 09:36:35 +0000 Subject: [PATCH] instruction limiter on the amx lets u limit function runs in instruction count SVN revision: 11905 --- legacy/embryo/src/bin/embryo_main.c | 30 +++++--- legacy/embryo/src/lib/Embryo.h | 11 +-- legacy/embryo/src/lib/embryo_amx.c | 95 ++++++++++++++++++++++++-- legacy/embryo/src/lib/embryo_private.h | 23 ++++--- 4 files changed, 133 insertions(+), 26 deletions(-) diff --git a/legacy/embryo/src/bin/embryo_main.c b/legacy/embryo/src/bin/embryo_main.c index 990f1acbee..d37c4c63dc 100644 --- a/legacy/embryo/src/bin/embryo_main.c +++ b/legacy/embryo/src/bin/embryo_main.c @@ -209,6 +209,7 @@ main(int argc,char *argv[]) int r = EMBRYO_PROGRAM_OK; int err; int args = 0; + int instruct = 0; char *file = NULL; char *func = NULL; @@ -273,23 +274,36 @@ main(int argc,char *argv[]) } } r = EMBRYO_PROGRAM_OK; + fn = EMBRYO_FUNCTION_MAIN; if (func) { fn = embryo_program_function_find(ep, func); - if (fn != EMBRYO_FUNCTION_NONE) - { - while ((r = embryo_program_run(ep, fn)) == EMBRYO_PROGRAM_SLEEP); - } - else + if (fn == EMBRYO_FUNCTION_NONE) { printf("Unable to find public function %s()\n" "Executing main() instead\n", func); - while ((r = embryo_program_run(ep, EMBRYO_FUNCTION_MAIN)) == EMBRYO_PROGRAM_SLEEP); + fn = EMBRYO_FUNCTION_MAIN; } } - else + embryo_program_max_cycle_run_set(ep, 100000000); + for (;;) { - while ((r = embryo_program_run(ep, EMBRYO_FUNCTION_MAIN)) == EMBRYO_PROGRAM_SLEEP); + r = embryo_program_run(ep, fn); + if (r == EMBRYO_PROGRAM_SLEEP) + { + fn = EMBRYO_FUNCTION_CONT; + printf("SLEEP INSTRUCTION!\n"); + continue; + } + else if (r == EMBRYO_PROGRAM_TOOLONG) + { + fn = EMBRYO_FUNCTION_CONT; + instruct++; + printf("Executed %i00 million instructions!\n", instruct); + continue; + } + else + break; } embryo_program_vm_pop(ep); if (r == EMBRYO_PROGRAM_FAIL) diff --git a/legacy/embryo/src/lib/Embryo.h b/legacy/embryo/src/lib/Embryo.h index 4dfffaa544..d0a4eefab9 100644 --- a/legacy/embryo/src/lib/Embryo.h +++ b/legacy/embryo/src/lib/Embryo.h @@ -43,10 +43,11 @@ extern "C" { /** An invalid cell reference */ #define EMBRYO_CELL_NONE 0x7fffffff /* program run return values */ -#define EMBRYO_PROGRAM_OK 1 -#define EMBRYO_PROGRAM_SLEEP 2 -#define EMBRYO_PROGRAM_BUSY 3 -#define EMBRYO_PROGRAM_FAIL 0 +#define EMBRYO_PROGRAM_OK 1 +#define EMBRYO_PROGRAM_SLEEP 2 +#define EMBRYO_PROGRAM_BUSY 3 +#define EMBRYO_PROGRAM_TOOLONG 4 +#define EMBRYO_PROGRAM_FAIL 0 typedef unsigned int Embryo_UCell; typedef int Embryo_Cell; @@ -94,6 +95,8 @@ extern "C" { int embryo_program_recursion_get(Embryo_Program *ep); int embryo_program_run(Embryo_Program *ep, Embryo_Function func); Embryo_Cell embryo_program_return_value_get(Embryo_Program *ep); + void embryo_program_max_cycle_run_set(Embryo_Program *ep, int max); + int embryo_program_max_cycle_run_get(Embryo_Program *ep); int embryo_parameter_cell_push(Embryo_Program *ep, Embryo_Cell cell); int embryo_parameter_string_push(Embryo_Program *ep, char *str); int embryo_parameter_cell_array_push(Embryo_Program *ep, Embryo_Cell *cells, int num); diff --git a/legacy/embryo/src/lib/embryo_amx.c b/legacy/embryo/src/lib/embryo_amx.c index 04e3a9c966..2afee870e7 100644 --- a/legacy/embryo/src/lib/embryo_amx.c +++ b/legacy/embryo/src/lib/embryo_amx.c @@ -1013,6 +1013,8 @@ embryo_program_recursion_get(Embryo_Program *ep) * @return @c EMBRYO_PROGRAM_OK on success. @c EMBRYO_PROGRAM_SLEEP if the * program is halted by the Small @c sleep call. * @c EMBRYO_PROGRAM_FAIL if there is an error. + * @c EMBRYO_PROGRAM_TOOLONG if the program executes for longer than + * it is allowed to in abstract machine instruction count. * @ingroup Embryo_Run_Group */ int @@ -1028,6 +1030,8 @@ embryo_program_run(Embryo_Program *ep, Embryo_Function fn) unsigned char op; Embryo_Cell offs; int num; + int max_run_cycles; + int cycle_count; #ifdef EMBRYO_EXEC_JUMPTABLE /* we limit the jumptable to 256 elements. why? above we forced "op" to be * a unsigned char - that means 256 max values. we limit opcode overflow @@ -1346,10 +1350,19 @@ embryo_program_run(Embryo_Program *ep, Embryo_Function fn) /* track recursion depth */ ep->run_count++; - + + max_run_cycles = ep->max_run_cycles; /* start running */ - for (;;) + for (cycle_count = 0;;) { + if (max_run_cycles > 0) + { + if (cycle_count >= max_run_cycles) + { + TOOLONG(ep); + } + cycle_count++; + } op = (Embryo_Opcode)*cip++; SWITCH(op); CASE(EMBRYO_OP_LOAD_PRI); @@ -2106,10 +2119,9 @@ embryo_program_run(Embryo_Program *ep, Embryo_Function fn) #endif SWITCHEND; } + ep->max_run_cycles = max_run_cycles; ep->run_count--; - ep->hea = hea_start; - return EMBRYO_PROGRAM_OK; } @@ -2128,6 +2140,81 @@ embryo_program_return_value_get(Embryo_Program *ep) return ep->retval; } +/** + * Sets the maximum number of abstract machine cycles any given program run + * can execute before being put to sleep and returning. + * + * @param ep The given program. + * @param max The number of machine cycles as a limit. + * + * This sets the maximum number of abstract machine (virtual machine) + * instructions that a single run of an embryo function (even if its main) + * can use before embryo embryo_program_run() reutrns with the value + * EMBRYO_PROGRAM_TOOLONG. If the function fully executes within this number + * of cycles, embryo_program_run() will return as normal with either + * EMBRYO_PROGRAM_OK, EMBRYO_PROGRAM_FAIL or EMBRYO_PROGRAM_SLEEP. If the + * run exceeds this instruction count, then EMBRYO_PROGRAM_TOOLONG will be + * returned indicating the program exceeded its run count. If the app wishes + * to continue running this anyway - it is free to process its own events or + * whatever it wants and continue the function by calling + * embryo_program_run(program, EMBRYO_FUNCTION_CONT); which will start the + * run again until the instruction count is reached. This can keep being done + * to allow the calling program to still be able to control things outside the + * embryo function being called. If the maximum run cycle count is 0 then the + * program is allowed to run forever only returning when it is done. + * + * It is important to note that abstract machine cycles are NOT the same as + * the host machine cpu cycles. They are not fixed in runtime per cycle, so + * this is more of a helper tool than a way to HARD-FORCE a script to only + * run for a specific period of time. If the cycle count is set to something + * low like 5000 or 1000, then every 1000 (or 5000) cycles control will be + * returned to the calling process where it can check a timer to see if a + * physical runtime limit has been elapsed and then abort runing further + * assuming a "runaway script" or keep continuing the script run. This + * limits resolution to only that many cycles which do not take a determined + * amount of time to execute, as this varies from cpu to cpu and also depends + * on how loaded the system is. Making the max cycle run too low will + * impact performance requiring the abstract machine to do setup and teardown + * cycles too often comapred to cycles actually executed. + * + * Also note it does NOT include nested abstract machines. IF this abstract + * machine run calls embryo script that calls a native function that in turn + * calls more embryo script, then the 2nd (and so on) levels are not included + * in this run count. They can set their own max instruction count values + * separately. + * + * The default max cycle run value is 0 in any program until set with this + * function. + * + * @ingroup Embryo_Run_Group + */ +void +embryo_program_max_cycle_run_set(Embryo_Program *ep, int max) +{ + if (!ep) return; + if (max < 0) max = 0; + ep->max_run_cycles = max; +} + +/** + * Retreives the maximum number of abstract machine cycles a program is allowed + * to run. + * @param ep The given program. + * @return The number of cycles a run cycle is allowed to run for this + * program. + * + * This returns the value set by embryo_program_max_cycle_run_set(). See + * embryo_program_max_cycle_run_set() for more information. + * + * @ingroup Embryo_Run_Group + */ +int +embryo_program_max_cycle_run_get(Embryo_Program *ep) +{ + if (!ep) return 0; + return ep->max_run_cycles; +} + /** * @defgroup Embryo_Parameter_Group Function Parameter Functions * diff --git a/legacy/embryo/src/lib/embryo_private.h b/legacy/embryo/src/lib/embryo_private.h index f0f43ce583..2955f1f6c5 100644 --- a/legacy/embryo/src/lib/embryo_private.h +++ b/legacy/embryo/src/lib/embryo_private.h @@ -189,16 +189,17 @@ __entryswap32(*((unsigned int *)(entry) + 1))) \ #define EMBRYO_MAGIC 0xf1e0 /* magic byte pattern */ #define EMBRYO_FLAG_COMPACT 0x04 /* compact encoding */ #define EMBRYO_FLAG_RELOC 0x8000 /* jump/call addresses relocated */ -#define GETPARAM(v) (v = *(Embryo_Cell *)cip++) -#define PUSH(v) (stk -= sizeof(Embryo_Cell), *(Embryo_Cell *)(data + (int)stk) = v) -#define POP(v) (v = *(Embryo_Cell *)(data + (int)stk), stk += sizeof(Embryo_Cell)) -#define ABORT(ep,v) {(ep)->stk = reset_stk; (ep)->hea = reset_hea; ep->run_count--; ep->error = v; return 0;} -#define OK(ep,v) {(ep)->stk = reset_stk; (ep)->hea = reset_hea; ep->run_count--; ep->error = v; return 1;} -#define STKMARGIN ((Embryo_Cell)(16 * sizeof(Embryo_Cell))) -#define CHKMARGIN() if ((hea + STKMARGIN) > stk) {ep->error = EMBRYO_ERROR_STACKERR; return 0;} -#define CHKSTACK() if (stk > ep->stp) {ep->run_count--; ep->error = EMBRYO_ERROR_STACKLOW; return 0;} -#define CHKHEAP() if (hea < ep->hlw) {ep->run_count--; ep->error = EMBRYO_ERROR_HEAPLOW; return 0;} -#define CHKMEM(x) if ((((x) >= hea) && ((x) < stk)) || ((Embryo_UCell)(x) >= (Embryo_UCell)ep->stp)) ABORT(ep, EMBRYO_ERROR_MEMACCESS); +#define GETPARAM(v) (v = *(Embryo_Cell *)cip++) +#define PUSH(v) (stk -= sizeof(Embryo_Cell), *(Embryo_Cell *)(data + (int)stk) = v) +#define POP(v) (v = *(Embryo_Cell *)(data + (int)stk), stk += sizeof(Embryo_Cell)) +#define ABORT(ep,v) {(ep)->stk = reset_stk; (ep)->hea = reset_hea; (ep)->run_count--; ep->error = v; (ep)->max_run_cycles = max_run_cycles; return EMBRYO_PROGRAM_FAIL;} +#define OK(ep,v) {(ep)->stk = reset_stk; (ep)->hea = reset_hea; (ep)->run_count--; ep->error = v; (ep)->max_run_cycles = max_run_cycles; return EMBRYO_PROGRAM_OK;} +#define TOOLONG(ep) {(ep)->pri = pri; (ep)->cip = (Embryo_Cell)((unsigned char *)cip - code); (ep)->alt = alt; (ep)->frm = frm; (ep)->stk = stk; (ep)->hea = hea; (ep)->reset_stk = reset_stk; (ep)->reset_hea = reset_hea; (ep)->run_count--; (ep)->max_run_cycles = max_run_cycles; return EMBRYO_PROGRAM_TOOLONG;} +#define STKMARGIN ((Embryo_Cell)(16 * sizeof(Embryo_Cell))) +#define CHKMARGIN() if ((hea + STKMARGIN) > stk) {ep->error = EMBRYO_ERROR_STACKERR; return 0;} +#define CHKSTACK() if (stk > ep->stp) {ep->run_count--; ep->error = EMBRYO_ERROR_STACKLOW; return 0;} +#define CHKHEAP() if (hea < ep->hlw) {ep->run_count--; ep->error = EMBRYO_ERROR_HEAPLOW; return 0;} +#define CHKMEM(x) if ((((x) >= hea) && ((x) < stk)) || ((Embryo_UCell)(x) >= (Embryo_UCell)ep->stp)) ABORT(ep, EMBRYO_ERROR_MEMACCESS); typedef struct _Embryo_Param Embryo_Param; typedef struct _Embryo_Header Embryo_Header; @@ -249,6 +250,8 @@ struct _Embryo_Program int run_count; + int max_run_cycles; + void *data; };