]> git.kaiwu.me - njs.git/commitdiff
Change: the default JS stack size is reduced to 64k.
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 10 Nov 2022 17:33:36 +0000 (09:33 -0800)
committerDmitry Volyntsev <xeioex@nginx.com>
Thu, 10 Nov 2022 17:33:36 +0000 (09:33 -0800)
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
src/njs_function.c
src/njs_shell.c
src/njs_vm.c
src/njs_vm.h
src/njs_vmcode.c
src/test/njs_unit_test.c

index c680931e3e9bc178d1f4781584bd1c0a4d1afdc2..b9ae72355ae2c2fa140e1e42167a9639c41d09df 100644 (file)
--- 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
index 7a88e1d76023295e4240d7dfc9eb973545bc31f5..7487e6cf845baebf7d83eca4b63d22aabf27c2c8 100644 (file)
@@ -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);
         }
 
index ba5fb024eba78f712f95bf87ffe9ea6b496ae408..0d3cda32aed020f0d58dbe1a8129b03a2e8a0b65 100644 (file)
@@ -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 <code>         set failure exit code.\n"
         "  -f                disabled denormals mode.\n"
 #ifdef NJS_DEBUG_GENERATOR
         "  -g                enable generator debug.\n"
 #endif
+        "  -j <size>         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 <path>         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':
index 7480b57e15bc338abbeafc6d0a0c3596d596c381..609f97a8bfee655f7786836d65cc2000e645a7b5 100644 (file)
@@ -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;
index da53711665f6f4ad4d3236e3bf86efe79b111b42..485252150a42a53b0d3cdcbe8a2ac6790ad149a7 100644 (file)
@@ -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;
 
index 6afd04137322dd135ce8edaf8d1e634b078f8e1b..86bc86b854512fe3a98cdb7d49cfdd8ca969af4c 100644 (file)
@@ -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);
         }
 
index 7134423ab2916004e91a765a5819fe5742bd6f38..9d8591cd3d46dabc66046c0727c09250d6a47337 100644 (file)
@@ -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);