From ad48705bf1f04b4221a5f5b07715ac48b3160d53 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Mon, 21 Feb 2022 16:52:59 +0000 Subject: [PATCH] Fixed frame allocation from an awaited frame. njs_function_frame_save() is used to save the awaited frame when "await" instruction is encountered. The saving was done as a memcpy() of existing runtime frame. njs_function_frame_alloc() is used to alloc a new function frame, this function tries to use a spare preallocated memory from the previous frame first. Previously, this function might result in "use-after-free" when invoked from a restored frame saved with njs_function_frame_save(). Because njs_function_frame_save() left pointers to the spare memory of the original frame which may be already free when saved frame is restored. The fix is to erase fields for the spare memory from the saved frame. This closes #469 issue on Github. --- src/njs_function.c | 4 ++++ test/js/async_recursive_large.t.js | 26 ++++++++++++++++++++++++++ test/js/async_recursive_mid.t.js | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 test/js/async_recursive_large.t.js diff --git a/src/njs_function.c b/src/njs_function.c index c9d4d973..78857e01 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -811,9 +811,13 @@ njs_function_frame_save(njs_vm_t *vm, njs_frame_t *frame, u_char *pc) njs_native_frame_t *active, *native; *frame = *vm->active_frame; + frame->previous_active_frame = NULL; native = &frame->native; + native->size = 0; + native->free = NULL; + native->free_size = 0; active = &vm->active_frame->native; value_count = njs_function_frame_value_count(active); diff --git a/test/js/async_recursive_large.t.js b/test/js/async_recursive_large.t.js new file mode 100644 index 00000000..423e8d01 --- /dev/null +++ b/test/js/async_recursive_large.t.js @@ -0,0 +1,26 @@ +/*--- +includes: [compareArray.js] +flags: [async] +---*/ + +let stages = []; + +async function f(v) { + if (v == 1000) { + return; + } + + stages.push(`f>${v}`); + + await "X"; + + await f(v + 1); + + stages.push(`f<${v}`); +} + +f(0) +.then(v => { + assert.sameValue(stages.length, 2000); +}) +.then($DONE, $DONE); diff --git a/test/js/async_recursive_mid.t.js b/test/js/async_recursive_mid.t.js index 4d3a9fd1..6b677962 100644 --- a/test/js/async_recursive_mid.t.js +++ b/test/js/async_recursive_mid.t.js @@ -6,7 +6,7 @@ flags: [async] let stages = []; async function f(v) { - if (v == 3) { + if (v == 1000) { return; } -- 2.47.3