From 5e9a6d568d8c5f963aaa08937de6d4002d9108e3 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Thu, 10 Nov 2022 09:33:36 -0800 Subject: [PATCH] Change: the default JS stack size is reduced to 64k. After 86784a68e8c8 (Computed goto) the size of a stack frame njs_vmcode_interpreter() when -O0 is specified during compilation increased significantly. This might cause system stack overflow for very deep recursive calls because system stack is exausted faster than JS stack. It is possible now to specify JS stack size for CLI. --- src/njs.h | 2 ++ src/njs_function.c | 6 +++--- src/njs_shell.c | 18 ++++++++++++++++-- src/njs_vm.c | 3 +++ src/njs_vm.h | 4 ++-- src/njs_vmcode.c | 2 +- src/test/njs_unit_test.c | 2 +- 7 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/njs.h b/src/njs.h index c680931e..b9ae7235 100644 --- a/src/njs.h +++ b/src/njs.h @@ -254,6 +254,8 @@ typedef struct { char **argv; njs_uint_t argc; + njs_uint_t max_stack_size; + njs_log_level_t log_level; #define NJS_VM_OPT_UNHANDLED_REJECTION_IGNORE 0 diff --git a/src/njs_function.c b/src/njs_function.c index 7a88e1d7..7487e6cf 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -464,7 +464,7 @@ njs_function_frame_alloc(njs_vm_t *vm, size_t size) spare_size = size + NJS_FRAME_SPARE_SIZE; spare_size = njs_align_size(spare_size, NJS_FRAME_SPARE_SIZE); - if (vm->stack_size + spare_size > NJS_MAX_STACK_SIZE) { + if (spare_size > vm->spare_stack_size) { njs_range_error(vm, "Maximum call stack size exceeded"); return NULL; } @@ -476,7 +476,7 @@ njs_function_frame_alloc(njs_vm_t *vm, size_t size) } chunk_size = spare_size; - vm->stack_size += spare_size; + vm->spare_stack_size -= spare_size; } njs_memzero(frame, sizeof(njs_native_frame_t)); @@ -702,7 +702,7 @@ njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *native) /* GC: free frame->local, etc. */ if (native->size != 0) { - vm->stack_size -= native->size; + vm->spare_stack_size += native->size; njs_mp_free(vm->mem_pool, native); } diff --git a/src/njs_shell.c b/src/njs_shell.c index ba5fb024..0d3cda32 100644 --- a/src/njs_shell.c +++ b/src/njs_shell.c @@ -39,6 +39,7 @@ typedef struct { uint8_t opcode_debug; uint8_t generator_debug; int exit_code; + int stack_size; char *file; char *command; @@ -292,6 +293,10 @@ main(int argc, char **argv) vm_options.ast = opts.ast; vm_options.unhandled_rejection = opts.unhandled_rejection; + if (opts.stack_size != 0) { + vm_options.max_stack_size = opts.stack_size; + } + #ifdef NJS_HAVE_READLINE if (opts.interactive) { @@ -343,15 +348,16 @@ njs_options_parse(njs_opts_t *opts, int argc, char **argv) " -a print AST.\n" " -c specify the command to execute.\n" " -d print disassembled code.\n" - " -e set failure exit code.\n" + " -e set failure exit code.\n" " -f disabled denormals mode.\n" #ifdef NJS_DEBUG_GENERATOR " -g enable generator debug.\n" #endif + " -j set the maximum stack size in bytes.\n" #ifdef NJS_DEBUG_OPCODE " -o enable opcode debug.\n" #endif - " -p set path prefix for modules.\n" + " -p set path prefix for modules.\n" " -q disable interactive introduction prompt.\n" " -r ignore unhandled promise rejection.\n" " -s sandbox mode.\n" @@ -432,6 +438,14 @@ njs_options_parse(njs_opts_t *opts, int argc, char **argv) opts->generator_debug = 1; break; #endif + case 'j': + if (++i < argc) { + opts->stack_size = atoi(argv[i]); + break; + } + + njs_stderror("option \"-j\" requires argument\n"); + return NJS_ERROR; #ifdef NJS_DEBUG_OPCODE case 'o': diff --git a/src/njs_vm.c b/src/njs_vm.c index 7480b57e..609f97a8 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -26,6 +26,7 @@ njs_vm_opt_init(njs_vm_opt_t *options) njs_memzero(options, sizeof(njs_vm_opt_t)); options->log_level = NJS_LOG_LEVEL_INFO; + options->max_stack_size = NJS_MAX_STACK_SIZE; } @@ -71,6 +72,8 @@ njs_vm_create(njs_vm_opt_t *options) vm->external = options->external; + vm->spare_stack_size = options->max_stack_size; + vm->trace.level = NJS_LEVEL_TRACE; vm->trace.size = 2048; vm->trace.data = vm; diff --git a/src/njs_vm.h b/src/njs_vm.h index da537116..48525215 100644 --- a/src/njs_vm.h +++ b/src/njs_vm.h @@ -8,7 +8,7 @@ #define _NJS_VM_H_INCLUDED_ -#define NJS_MAX_STACK_SIZE (256 * 1024) +#define NJS_MAX_STACK_SIZE (64 * 1024) typedef struct njs_frame_s njs_frame_t; @@ -160,7 +160,7 @@ struct njs_vm_s { njs_mp_t *mem_pool; u_char *start; - size_t stack_size; + size_t spare_stack_size; njs_vm_shared_t *shared; diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 6afd0413..86bc86b8 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -1837,7 +1837,7 @@ error: njs_vm_scopes_restore(vm, native, previous); if (native->size != 0) { - vm->stack_size -= native->size; + vm->spare_stack_size += native->size; njs_mp_free(vm->mem_pool, native); } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 7134423a..9d8591cd 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -23218,7 +23218,7 @@ njs_vm_value_test(njs_unit_test_t unused[], size_t num, njs_str_t *name, for (i = 0; i < njs_nitems(tests); i++) { - memset(&options, 0, sizeof(njs_vm_opt_t)); + njs_vm_opt_init(&options); options.init = 1; vm = njs_vm_create(&options); -- 2.47.3