[njs] Maximum call stack size is limited by 16M.

Igor Sysoev igor at sysoev.ru
Thu Dec 1 16:49:23 UTC 2016


details:   http://hg.nginx.org/njs/rev/6d0d4a92fa02
branches:  
changeset: 269:6d0d4a92fa02
user:      Igor Sysoev <igor at sysoev.ru>
date:      Thu Dec 01 18:56:35 2016 +0300
description:
Maximum call stack size is limited by 16M.

diffstat:

 njs/njs_function.c       |  20 +++++++++++++++-----
 njs/njs_function.h       |  14 ++++----------
 njs/njs_vm.c             |   6 ++++--
 njs/njs_vm.h             |   3 +++
 njs/njscript.c           |   2 +-
 njs/test/njs_unit_test.c |   3 +++
 6 files changed, 30 insertions(+), 18 deletions(-)

diffs (172 lines):

diff -r 86c35adbd3f9 -r 6d0d4a92fa02 njs/njs_function.c
--- a/njs/njs_function.c	Wed Nov 30 14:53:15 2016 +0300
+++ b/njs/njs_function.c	Thu Dec 01 18:56:35 2016 +0300
@@ -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_fun
 }
 
 
+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;
 
diff -r 86c35adbd3f9 -r 6d0d4a92fa02 njs/njs_function.h
--- a/njs/njs_function.h	Wed Nov 30 14:53:15 2016 +0300
+++ b/njs/njs_function.h	Thu Dec 01 18:56:35 2016 +0300
@@ -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 */
 };
 
 
diff -r 86c35adbd3f9 -r 6d0d4a92fa02 njs/njs_vm.c
--- a/njs/njs_vm.c	Wed Nov 30 14:53:15 2016 +0300
+++ b/njs/njs_vm.c	Thu Dec 01 18:56:35 2016 +0300
@@ -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, nj
 
         /* 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);
         }
 
diff -r 86c35adbd3f9 -r 6d0d4a92fa02 njs/njs_vm.h
--- a/njs/njs_vm.h	Wed Nov 30 14:53:15 2016 +0300
+++ b/njs/njs_vm.h	Thu Dec 01 18:56:35 2016 +0300
@@ -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;
diff -r 86c35adbd3f9 -r 6d0d4a92fa02 njs/njscript.c
--- a/njs/njscript.c	Wed Nov 30 14:53:15 2016 +0300
+++ b/njs/njscript.c	Thu Dec 01 18:56:35 2016 +0300
@@ -324,12 +324,12 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache
 
         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,
diff -r 86c35adbd3f9 -r 6d0d4a92fa02 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Wed Nov 30 14:53:15 2016 +0300
+++ b/njs/test/njs_unit_test.c	Thu Dec 01 18:56:35 2016 +0300
@@ -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") },
 



More information about the nginx-devel mailing list