diff --git a/include/vm/fzl_vm.h b/include/vm/fzl_vm.h index cdd820f..92fedd7 100644 --- a/include/vm/fzl_vm.h +++ b/include/vm/fzl_vm.h @@ -36,7 +36,7 @@ typedef struct fzl_vm_t *fzl_mkvm(const char *filepath); // Load and run code -int fzl_vm_run(fzl_vm_t *vm); +int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter); // Get last VM error message char *fzl_vm_geterror(); diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index b3ab61c..3537808 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -14,10 +14,7 @@ #include char last_err[256]; -size_t last_jmp_ip = 0; - -// NOTE: add `--max-iter` option for limit -size_t max_iter_limit = 100000; +size_t last_jmp_ip = 0; size_t total_jmp_on_same = 0; static char *expect_hdr = "FZL\0\1"; @@ -279,7 +276,7 @@ void print_err_data(fzl_vm_t *vm) printf("\t> [%zd] %lu\n", i, vm->cstack[i]); } -int fzl_vm_run(fzl_vm_t *vm) +int fzl_vm_run(fzl_vm_t *vm, bool allow_infinite, uint64_t max_iter) { if (!vm) { @@ -673,19 +670,24 @@ int fzl_vm_run(fzl_vm_t *vm) "runtime error - expected JMP addr"); return -1; } - uint64_t jmp_ip = vm->ops[++vm->ip]; - if (jmp_ip == last_jmp_ip) - total_jmp_on_same++; - else - total_jmp_on_same = 0; - if (total_jmp_on_same > max_iter_limit) + uint64_t jmp_ip = vm->ops[++vm->ip]; + if (!allow_infinite) { - snprintf(last_err, sizeof(last_err), - "runtime error - exceeded max loop jumps (infinite loop?)\n" - "If you thinks there's mistake, change jumps limit with " - "--max-iter=N"); - return -1; + if (jmp_ip == last_jmp_ip) + total_jmp_on_same++; + else + total_jmp_on_same = 0; + + if (total_jmp_on_same > max_iter) + { + snprintf( + last_err, sizeof(last_err), + "runtime error - exceeded max loop jumps (infinite loop?)\n" + "If you thinks there's mistake, change jumps limit with " + "--max-iter=N"); + return -1; + } } vm->ip = jmp_ip - 1; diff --git a/src/vm/main.c b/src/vm/main.c index 8be35f6..62dbef8 100644 --- a/src/vm/main.c +++ b/src/vm/main.c @@ -1,5 +1,7 @@ +#include #include #include +#include #include #include #include @@ -10,14 +12,18 @@ typedef struct char *filepath; bool help; bool debug; + bool allow_infinite; + uint64_t max_iter; } run_args_t; void print_hint() { printf("Usage: fz [flags] \n"); printf("Flags:\n"); - printf("\t-h\t--help\tprint usage information\n"); - printf("\t-d\t--debug\tdisplay program opcodes\n"); + printf("\t-h\t--help\t\t\tprint usage information\n"); + printf("\t-d\t--debug\t\t\tdisplay program opcodes\n"); + printf("\t\t--allow-infinite\tallow infinite loops\n"); + printf("\t\t--max-iter N\t\trestrict loops to maximum N iterations\n"); } // Parse passed arguments and build args setup structure @@ -29,10 +35,12 @@ run_args_t *parse_args(int argc, char **argv) return NULL; } - run_args_t *args = malloc(sizeof(run_args_t)); - args->filepath = NULL; - args->help = false; - args->debug = false; + run_args_t *args = malloc(sizeof(run_args_t)); + args->filepath = NULL; + args->help = false; + args->debug = false; + args->allow_infinite = false; + args->max_iter = 100000; for (int i = 0; i < argc; i++) { @@ -46,9 +54,33 @@ run_args_t *parse_args(int argc, char **argv) } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) args->debug = true; + else if (strcmp(argv[i], "--allow-infinite") == 0) + args->allow_infinite = true; + else if (strcmp(argv[i], "--max-iter") == 0) + { + char *n = argv[++i]; + if (!n || argc <= i || !(n[0] >= '0' && n[0] <= '9')) + { + printf("%sError%s: --max-iter options requires number after it\n", + FZL_CERROR, FZL_CRST); + free(args); + return NULL; + } + + uint64_t iters = strtol(n, NULL, 10); + if (iters < 1) + { + printf("%sError%s: '--max-iter 0' is not allowed\n", FZL_CERROR, + FZL_CRST); + free(args); + return NULL; + } + + args->max_iter = iters; + } else { - printf("Error: unknown flag '%s'.\n", argv[i]); + printf("%sError%s: unknown flag '%s'.\n", FZL_CERROR, FZL_CRST, argv[i]); printf("Use 'fz --help' to read the usage\n"); free(args); return NULL; @@ -57,7 +89,8 @@ run_args_t *parse_args(int argc, char **argv) if (argv[argc - 1][0] == '-') { - printf("Error: last argument MUST be a file path\n"); + printf("%sError%s: last argument MUST be a file path\n", FZL_CERROR, + FZL_CRST); printf("Use 'fz --help' for help\n"); free(args); return NULL; @@ -83,7 +116,7 @@ int main(int argc, char **argv) fzl_vm_t *vm = fzl_mkvm(args->filepath); if (!vm) { - printf("Error: %s\n", fzl_vm_geterror()); + printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, fzl_vm_geterror()); free(args); return -1; } @@ -92,12 +125,12 @@ int main(int argc, char **argv) fzl_vm_print_ops(vm); else { - int result = fzl_vm_run(vm); + int result = fzl_vm_run(vm, args->allow_infinite, args->max_iter); if (result != 0) { char *err = fzl_vm_geterror(); if (err && err[0]) - printf("Error: %s\n", err); + printf("%sError%s: %s\n", FZL_CERROR, FZL_CRST, err); free(args); fzl_freevm(vm);