[njs] Fixed function constructor for cloned VMs.
Dmitry Volyntsev
xeioex at nginx.com
Wed Sep 29 14:05:22 UTC 2021
details: https://hg.nginx.org/njs/rev/06d4768b37d8
branches:
changeset: 1708:06d4768b37d8
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Wed Sep 29 13:45:26 2021 +0000
description:
Fixed function constructor for cloned VMs.
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.
diffstat:
src/njs_function.c | 4 +++-
src/njs_generator.c | 24 ++++++++++++++----------
src/njs_lexer.c | 5 +++--
src/njs_lexer.h | 2 +-
src/njs_module.c | 2 +-
src/njs_vm.c | 6 ++++--
src/njs_vm.h | 1 +
src/test/njs_unit_test.c | 11 +++++++++++
8 files changed, 38 insertions(+), 17 deletions(-)
diffs (200 lines):
diff -r 6feba0e602ee -r 06d4768b37d8 src/njs_function.c
--- a/src/njs_function.c Fri Sep 17 18:29:40 2021 +0000
+++ b/src/njs_function.c Wed Sep 29 13:45:26 2021 +0000
@@ -1156,7 +1156,8 @@ njs_function_constructor(njs_vm_t *vm, n
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, n
}
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)) {
diff -r 6feba0e602ee -r 06d4768b37d8 src/njs_generator.c
--- a/src/njs_generator.c Fri Sep 17 18:29:40 2021 +0000
+++ b/src/njs_generator.c Wed Sep 29 13:45:26 2021 +0000
@@ -277,8 +277,8 @@ static njs_int_t njs_generate_inc_dec_op
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_
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_
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
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
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;
diff -r 6feba0e602ee -r 06d4768b37d8 src/njs_lexer.c
--- a/src/njs_lexer.c Fri Sep 17 18:29:40 2021 +0000
+++ b/src/njs_lexer.c Wed Sep 29 13:45:26 2021 +0000
@@ -290,7 +290,7 @@ static const njs_lexer_multi_t njs_assi
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->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);
diff -r 6feba0e602ee -r 06d4768b37d8 src/njs_lexer.h
--- a/src/njs_lexer.h Fri Sep 17 18:29:40 2021 +0000
+++ b/src/njs_lexer.h Wed Sep 29 13:45:26 2021 +0000
@@ -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);
diff -r 6feba0e602ee -r 06d4768b37d8 src/njs_module.c
--- a/src/njs_module.c Fri Sep 17 18:29:40 2021 +0000
+++ b/src/njs_module.c Wed Sep 29 13:45:26 2021 +0000
@@ -185,7 +185,7 @@ njs_parser_module(njs_parser_t *parser,
}
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;
}
diff -r 6feba0e602ee -r 06d4768b37d8 src/njs_vm.c
--- a/src/njs_vm.c Fri Sep 17 18:29:40 2021 +0000
+++ b/src/njs_vm.c Wed Sep 29 13:45:26 2021 +0000
@@ -138,7 +138,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
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_lvlhsh_init(&vm->events_hash);
njs_queue_init(&vm->posted_events);
njs_queue_init(&vm->promise_events);
diff -r 6feba0e602ee -r 06d4768b37d8 src/njs_vm.h
--- a/src/njs_vm.h Fri Sep 17 18:29:40 2021 +0000
+++ b/src/njs_vm.h Wed Sep 29 13:45:26 2021 +0000
@@ -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;
diff -r 6feba0e602ee -r 06d4768b37d8 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Fri Sep 17 18:29:40 2021 +0000
+++ b/src/test/njs_unit_test.c Wed Sep 29 13:45:26 2021 +0000
@@ -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") },
};
More information about the nginx-devel
mailing list