]> git.kaiwu.me - njs.git/commitdiff
Fixed function constructor for cloned VMs.
authorDmitry Volyntsev <xeioex@nginx.com>
Wed, 29 Sep 2021 13:45:26 +0000 (13:45 +0000)
committerDmitry Volyntsev <xeioex@nginx.com>
Wed, 29 Sep 2021 13:45:26 +0000 (13:45 +0000)
Previously a shared "keywords_hash" and "values_hash" were used while
compiling functions in runtime.  This led to populating a shared hash
with elements allocated in a cloned VM.  Which resulted in
heap-use-after-free when next cloned VM accesses the shared hashes.

src/njs_function.c
src/njs_generator.c
src/njs_lexer.c
src/njs_lexer.h
src/njs_module.c
src/njs_vm.c
src/njs_vm.h
src/test/njs_unit_test.c

index 087889cdc67825d7d00a58ba385c54cc61820a2b..e61f4e62c7990acef51ec5b3bead8e187e210a41 100644 (file)
@@ -1156,7 +1156,8 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     file = njs_str_value("runtime");
 
-    ret = njs_lexer_init(vm, &lexer, &file, str.start, str.start + str.length);
+    ret = njs_lexer_init(vm, &lexer, &file, str.start, str.start + str.length,
+                         1);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -1206,6 +1207,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     njs_memzero(&generator, sizeof(njs_generator_t));
+    generator.runtime = 1;
 
     code = njs_generate_scope(vm, &generator, scope, &njs_entry_anonymous);
     if (njs_slow_path(code == NULL)) {
index f2e3bde9e430176b21f7c2cbc079ca655b9bc5e7..e0fb30d0a89db1045db7e42061a57d845443158c 100644 (file)
@@ -277,8 +277,8 @@ static njs_int_t njs_generate_inc_dec_operation_prop(njs_vm_t *vm,
 static njs_int_t njs_generate_function_declaration(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_function_scope(njs_vm_t *vm,
-    njs_function_lambda_t *lambda, njs_parser_node_t *node,
-    const njs_str_t *name, njs_uint_t depth);
+    njs_generator_t *generator, njs_function_lambda_t *lambda,
+    njs_parser_node_t *node, const njs_str_t *name);
 static njs_int_t njs_generate_scope_end(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 static int64_t njs_generate_lambda_variables(njs_vm_t *vm,
@@ -3056,8 +3056,8 @@ njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator,
         return NJS_ERROR;
     }
 
-    ret = njs_generate_function_scope(vm, lambda, node, &lex_entry->name,
-                                      generator->depth);
+    ret = njs_generate_function_scope(vm, generator, lambda, node,
+                                      &lex_entry->name);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -3093,7 +3093,7 @@ njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
 
     name = module ? &njs_entry_module : &njs_entry_anonymous;
 
-    ret = njs_generate_function_scope(vm, lambda, node, name, generator->depth);
+    ret = njs_generate_function_scope(vm, generator, lambda, node, name);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -3594,8 +3594,8 @@ njs_generate_function_declaration(njs_vm_t *vm, njs_generator_t *generator,
         return NJS_ERROR;
     }
 
-    ret = njs_generate_function_scope(vm, lambda, node, &lex_entry->name,
-                                      generator->depth);
+    ret = njs_generate_function_scope(vm, generator, lambda, node,
+                                      &lex_entry->name);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -3617,23 +3617,27 @@ njs_generate_function_declaration(njs_vm_t *vm, njs_generator_t *generator,
 
 
 static njs_int_t
-njs_generate_function_scope(njs_vm_t *vm, njs_function_lambda_t *lambda,
-    njs_parser_node_t *node, const njs_str_t *name, njs_uint_t depth)
+njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev,
+    njs_function_lambda_t *lambda, njs_parser_node_t *node,
+    const njs_str_t *name)
 {
     njs_arr_t          *arr;
     njs_bool_t         module;
+    njs_uint_t         depth;
     njs_vm_code_t      *code;
     njs_generator_t    generator;
     njs_parser_node_t  *file_node;
 
-    njs_memzero(&generator, sizeof(njs_generator_t));
+    depth = prev->depth;
 
     if (++depth >= NJS_FUNCTION_MAX_DEPTH) {
         njs_range_error(vm, "Maximum function nesting depth exceeded");
         return NJS_ERROR;
     }
 
+    njs_memzero(&generator, sizeof(njs_generator_t));
     generator.depth = depth;
+    generator.runtime = prev->runtime;
 
     node = node->right;
 
index 4a04022631d807b27654c2ef85c1fab7d77966f6..7c1a8270e848e91488dea4d8160936e22d6209a9 100644 (file)
@@ -290,7 +290,7 @@ static const njs_lexer_multi_t  njs_assignment_token[] = {
 
 njs_int_t
 njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file,
-    u_char *start, u_char *end)
+    u_char *start, u_char *end, njs_uint_t runtime)
 {
     njs_memzero(lexer, sizeof(njs_lexer_t));
 
@@ -298,7 +298,8 @@ njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file,
     lexer->start = start;
     lexer->end = end;
     lexer->line = 1;
-    lexer->keywords_hash = &vm->shared->keywords_hash;
+    lexer->keywords_hash = (runtime) ? &vm->keywords_hash
+                                     : &vm->shared->keywords_hash;
     lexer->mem_pool = vm->mem_pool;
 
     njs_queue_init(&lexer->preread);
index e3bd1dc8dfdf4c8c68a9bcdf42fa7c398d372f61..c62c70c3faa000b1ad61595958d133cdef771054 100644 (file)
@@ -271,7 +271,7 @@ typedef struct {
 
 
 njs_int_t njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file,
-    u_char *start, u_char *end);
+    u_char *start, u_char *end, njs_uint_t runtime);
 
 njs_lexer_token_t *njs_lexer_token(njs_lexer_t *lexer,
     njs_bool_t with_end_line);
index fb67b9bfd48ad7f9c5083fba9b96580eef3607b7..829a175af54e3aea7dbcc22b8b48f9e096635255 100644 (file)
@@ -185,7 +185,7 @@ njs_parser_module(njs_parser_t *parser, njs_lexer_token_t *token,
     }
 
     ret = njs_lexer_init(parser->vm, &temp->lexer, &info.file, text.start,
-                         text.start + text.length);
+                         text.start + text.length, 0);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
index 1f07d32ab0f747900cf7f31f78ef12273e5ec7c0..834e0a66402ef73e2eaade685be56d87bc74d478 100644 (file)
@@ -138,7 +138,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
         njs_module_reset(vm);
     }
 
-    ret = njs_lexer_init(vm, &lexer, &vm->options.file, *start, end);
+    ret = njs_lexer_init(vm, &lexer, &vm->options.file, *start, end, 0);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -317,9 +317,11 @@ njs_vm_init(njs_vm_t *vm)
         return NJS_ERROR;
     }
 
+    njs_lvlhsh_init(&vm->values_hash);
+    njs_lvlhsh_init(&vm->keywords_hash);
     njs_lvlhsh_init(&vm->modules_hash);
-
     njs_lvlhsh_init(&vm->events_hash);
+
     njs_queue_init(&vm->posted_events);
     njs_queue_init(&vm->promise_events);
 
index 2efa859d8b3f5cfc2850e32ce5e29f393ff87498..c4f2fe20006279565c1e62b4df5f07f1e2388fc4 100644 (file)
@@ -147,6 +147,7 @@ struct njs_vm_s {
     njs_frame_t              *active_frame;
 
     njs_rbtree_t             *variables_hash;
+    njs_lvlhsh_t             keywords_hash;
     njs_lvlhsh_t             values_hash;
 
     njs_arr_t                *modules;
index 72d0181746a8de880defd60ddac6ffdc24a04a31..c34413fa7ad79551b8d2b79244dda6d3cd2a2c9f 100644 (file)
@@ -21028,6 +21028,17 @@ static njs_unit_test_t  njs_shared_test[] =
       njs_str("TypeError: \"path\" must be a string or Buffer\n"
               "    at fs.readFileSync (native)\n"
               "    at main (:1)\n") },
+
+    { njs_str("var f = new Function('return 1;'); f();"),
+      njs_str("1") },
+
+    { njs_str("var sum = new Function('a', 'b', 'return a + b');"
+              "sum(2, 4);"),
+      njs_str("6") },
+
+    { njs_str("var sum = new Function('a, b', 'return a + b');"
+              "sum(2, 4);"),
+      njs_str("6") },
 };