]> git.kaiwu.me - njs.git/commitdiff
Maximum call stack size is limited by 16M.
authorIgor Sysoev <igor@sysoev.ru>
Thu, 1 Dec 2016 15:56:35 +0000 (18:56 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Thu, 1 Dec 2016 15:56:35 +0000 (18:56 +0300)
njs/njs_function.c
njs/njs_function.h
njs/njs_vm.c
njs/njs_vm.h
njs/njscript.c
njs/test/njs_unit_test.c

index 83ac0800192d8ad3eca39b1d326e463e8db27f18..eaab0a7bfb755ae8590b5f8b433a7a1e8c5db7e9 100644 (file)
@@ -16,6 +16,7 @@
 #include <nxt_mem_cache_pool.h>
 #include <njscript.h>
 #include <njs_vm.h>
+#include <njs_string.h>
 #include <njs_object.h>
 #include <njs_array.h>
 #include <njs_function.h>
@@ -199,35 +200,44 @@ njs_function_frame(njs_vm_t *vm, njs_function_t *function, njs_value_t *this,
 }
 
 
+static const njs_value_t  njs_exception_stack_size_exceeded =
+    njs_long_string("RangeError: Maximum call stack size exceeded");
+
+
 nxt_noinline njs_native_frame_t *
 njs_function_frame_alloc(njs_vm_t *vm, size_t size)
 {
-    size_t              spare_size;
-    uint8_t             first;
+    size_t              spare_size, chunk_size;
     njs_native_frame_t  *frame;
 
     spare_size = vm->frame->free_size;
 
     if (nxt_fast_path(size <= spare_size)) {
         frame = (njs_native_frame_t *) vm->frame->free;
-        first = 0;
+        chunk_size = 0;
 
     } else {
         spare_size = size + NJS_FRAME_SPARE_SIZE;
         spare_size = nxt_align_size(spare_size, NJS_FRAME_SPARE_SIZE);
 
+        if (vm->stack_size + spare_size > NJS_MAX_STACK_SIZE) {
+            vm->exception = &njs_exception_stack_size_exceeded;
+            return NULL;
+        }
+
         frame = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
                                     spare_size);
         if (nxt_slow_path(frame == NULL)) {
             return NULL;
         }
 
-        first = 1;
+        chunk_size = spare_size;
+        vm->stack_size += spare_size;
     }
 
     memset(frame, 0, sizeof(njs_native_frame_t));
 
-    frame->first = first;
+    frame->size = chunk_size;
     frame->free_size = spare_size - size;
     frame->free = (u_char *) frame + size;
 
index 9fdda918ae893c0d61a588a95fc32eda7d0c7eb6..1f79bbd79267b5f850118a0f1eea142694b4cb48 100644 (file)
@@ -89,30 +89,24 @@ struct njs_native_frame_s {
 
     njs_exception_t                exception;
 
+    uint32_t                       size;
     uint32_t                       free_size;
     uint32_t                       nargs;
 
     /* Function is called as constructor with "new" keyword. */
     uint8_t                        ctor;              /* 1 bit  */
 
-    /*
-     * The first frame in chunk.
-     * 7 bits are just to possibly initialize first and skip
-     * fields with one operation.
-     */
-    uint8_t                        first:7;           /* 1 bit  */
-
     /* Skip the Function.call() and Function.apply() methods frames. */
-    uint8_t                        skip:1;            /* 1 bit  */
+    uint8_t                        skip;              /* 1 bit  */
 
     /* A number of trap tries, it can be no more than three. */
-    uint8_t                        trap_tries:2;      /* 2 bits */
+    uint8_t                        trap_tries;        /* 2 bits */
 
     /*
      * The first operand in trap is reference to original value,
      * it is used to increment or decrement this value.
      */
-    uint8_t                        trap_reference:1;  /* 1 bit */
+    uint8_t                        trap_reference  /* 1 bit */
 };
 
 
index e1a0557d6404e852415a85d2e0f43cde41204031..8be5ecf49752558dc51183ab2f2e76e0c02a51ae 100644 (file)
@@ -277,7 +277,8 @@ start:
             vm->scopes[NJS_SCOPE_LOCAL] = frame->prev_local;
             vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
 
-            if (frame->native.first) {
+            if (frame->native.size != 0) {
+                vm->stack_size -= frame->native.size;
                 nxt_mem_cache_free(vm->mem_cache_pool, frame);
             }
         }
@@ -2673,7 +2674,8 @@ njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame)
 
         /* GC: free frame->local, etc. */
 
-        if (frame->first) {
+        if (frame->size != 0) {
+            vm->stack_size -= frame->size;
             nxt_mem_cache_free(vm->mem_cache_pool, frame);
         }
 
index af185ca66c08b8707ac9b2fa078610dab0c6d815..29cfd75ab4506b763b068a0869a91c2b30e8575a 100644 (file)
@@ -12,6 +12,8 @@
 #include <nxt_regex.h>
 
 
+#define NJS_MAX_STACK_SIZE       (16 * 1024 * 1024)
+
 /*
  * Negative return values handled by nJSVM interpreter as special events.
  * The values must be in range from -1 to -11, because -12 is minimal jump
@@ -829,6 +831,7 @@ struct njs_vm_s {
 
     njs_value_t              *global_scope;
     size_t                   scope_size;
+    size_t                   stack_size;
 
     njs_vm_shared_t          *shared;
     njs_parser_t             *parser;
index 6c530d4e2486691433606448cac654ebb4643a6b..049fbce22ac6dfdccc509e894452a2afe2c6ee67 100644 (file)
@@ -324,12 +324,12 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external)
 
         nvm->frame = &frame->native;
 
+        frame->native.size = size;
         frame->native.free_size = size - (NJS_GLOBAL_FRAME_SIZE + scope_size);
 
         values = (u_char *) frame + NJS_GLOBAL_FRAME_SIZE;
 
         frame->native.free = values + scope_size;
-        frame->native.first = 1;
 
         nvm->scopes[NJS_SCOPE_GLOBAL] = (njs_value_t *) values;
         memcpy(values + NJS_INDEX_GLOBAL_OFFSET, vm->global_scope,
index 92a332caf191aa65515cc5a0ddc0a64ee2ace94e..18888f3d23f48a16b7249f3574500a354f825831 100644 (file)
@@ -3759,6 +3759,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("return"),
       nxt_string("SyntaxError: Illegal return statement in 1") },
 
+    { nxt_string("function f() { return f() } f()"),
+      nxt_string("RangeError: Maximum call stack size exceeded") },
+
     { nxt_string("function () { } f()"),
       nxt_string("SyntaxError: Unexpected token \"(\" in 1") },