[njs] Fixed potential memory corruption in njs_function_frame_invoke().
Dmitry Volyntsev
xeioex at nginx.com
Mon Jun 15 15:27:13 UTC 2020
details: https://hg.nginx.org/njs/rev/cf85ed422623
branches:
changeset: 1430:cf85ed422623
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Mon Jun 15 15:26:40 2020 +0000
description:
Fixed potential memory corruption in njs_function_frame_invoke().
It is a bug to cast vm->top_frame which is (njs_native_frame_t *) type,
to (njs_frame_t *) in general context. The cast is allowed only for the
lambda frames.
The bug did not manifest itself previously because native frames were
allocated with NJS_NATIVE_FRAME_SIZE, which is sizeof(njs_value_t) aligned
size of njs_native_frame_t (on 64bit platform are 80 and 72 bytes
respectively). njs_frame_t contains njs_native_frame_t as a first field.
The frame->retval assignment for native frames resulted in 8 padding bytes
were used as a storage space for the retval field.
The bug becomes visible when the size of njs_native_frame_t changes.
The issue was introduced in 540f03725df2.
diffstat:
src/njs_function.c | 24 +++++++++++-------------
src/njs_function.h | 15 ++++-----------
src/njs_promise.c | 18 +++++++++---------
src/njs_vm.c | 14 ++++++++------
src/njs_vm.h | 2 +-
src/njs_vmcode.c | 10 +++++-----
6 files changed, 38 insertions(+), 45 deletions(-)
diffs (294 lines):
diff -r b33402f10e82 -r cf85ed422623 src/njs_function.c
--- a/src/njs_function.c Sun May 31 08:45:41 2020 +0300
+++ b/src/njs_function.c Mon Jun 15 15:26:40 2020 +0000
@@ -677,13 +677,11 @@ njs_function_native_call(njs_vm_t *vm)
{
njs_int_t ret;
njs_value_t *value;
- njs_frame_t *frame;
njs_function_t *function, *target;
njs_native_frame_t *native, *previous;
njs_function_native_t call;
native = vm->top_frame;
- frame = (njs_frame_t *) native;
function = native->function;
if (njs_fast_path(function->bound == NULL)) {
@@ -711,10 +709,10 @@ njs_function_native_call(njs_vm_t *vm)
previous = njs_function_previous_frame(native);
- njs_vm_scopes_restore(vm, frame, previous);
+ njs_vm_scopes_restore(vm, native, previous);
if (!native->skip) {
- value = njs_vmcode_operand(vm, frame->retval);
+ value = njs_vmcode_operand(vm, native->retval);
/*
* GC: value external/internal++ depending
* on vm->retval and retval type
@@ -1035,10 +1033,10 @@ static njs_int_t
njs_function_prototype_call(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
- njs_int_t ret;
- njs_frame_t *frame;
- njs_function_t *function;
- const njs_value_t *this;
+ njs_int_t ret;
+ njs_function_t *function;
+ const njs_value_t *this;
+ njs_native_frame_t *frame;
if (!njs_is_function(&args[0])) {
njs_type_error(vm, "\"this\" argument is not a function");
@@ -1054,13 +1052,13 @@ njs_function_prototype_call(njs_vm_t *vm
nargs = 0;
}
- frame = (njs_frame_t *) vm->top_frame;
+ frame = vm->top_frame;
+
+ /* Skip the "call" method frame. */
+ frame->skip = 1;
function = njs_function(&args[0]);
- /* Skip the "call" method frame. */
- vm->top_frame->skip = 1;
-
ret = njs_function_frame(vm, function, this, &args[2], nargs, 0);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
@@ -1144,7 +1142,7 @@ activate:
return ret;
}
- ret = njs_function_frame_invoke(vm, frame->retval);
+ ret = njs_function_frame_invoke(vm, frame->native.retval);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
diff -r b33402f10e82 -r cf85ed422623 src/njs_function.h
--- a/src/njs_function.h Sun May 31 08:45:41 2020 +0300
+++ b/src/njs_function.h Mon Jun 15 15:26:40 2020 +0000
@@ -39,10 +39,6 @@ struct njs_function_lambda_s {
njs_align_size(sizeof(njs_frame_t) + closures * sizeof(njs_closure_t *), \
sizeof(njs_value_t))
-/* The retval field is not used in the global frame. */
-#define NJS_GLOBAL_FRAME_SIZE \
- njs_align_size(offsetof(njs_frame_t, retval), sizeof(njs_value_t))
-
#define NJS_FRAME_SPARE_SIZE 512
@@ -68,6 +64,7 @@ struct njs_native_frame_s {
njs_object_t *arguments_object;
njs_exception_t exception;
+ njs_index_t retval;
uint32_t size;
uint32_t free_size;
@@ -84,9 +81,6 @@ struct njs_native_frame_s {
struct njs_frame_s {
njs_native_frame_t native;
- njs_index_t retval;
-
- u_char *return_address;
njs_frame_t *previous_active_frame;
njs_value_t *local;
@@ -170,13 +164,12 @@ njs_function_previous_frame(njs_native_f
njs_inline njs_int_t
njs_function_frame_invoke(njs_vm_t *vm, njs_index_t retval)
{
- njs_frame_t *frame;
+ njs_native_frame_t *frame;
- frame = (njs_frame_t *) vm->top_frame;
-
+ frame = vm->top_frame;
frame->retval = retval;
- if (frame->native.function->native) {
+ if (frame->function->native) {
return njs_function_native_call(vm);
} else {
diff -r b33402f10e82 -r cf85ed422623 src/njs_promise.c
--- a/src/njs_promise.c Sun May 31 08:45:41 2020 +0300
+++ b/src/njs_promise.c Mon Jun 15 15:26:40 2020 +0000
@@ -559,16 +559,16 @@ njs_promise_resolve_function(njs_vm_t *v
njs_index_t unused)
{
njs_int_t ret;
- njs_frame_t *active_frame;
njs_value_t *resolution, error, then, arguments[3];
njs_promise_t *promise;
njs_function_t *function;
+ njs_native_frame_t *active_frame;
njs_promise_context_t *context;
static const njs_value_t string_then = njs_string("then");
- active_frame = (njs_frame_t *) vm->top_frame;
- context = active_frame->native.function->context;
+ active_frame = vm->top_frame;
+ context = active_frame->function->context;
promise = njs_promise(&context->promise);
if (*context->resolved_ref) {
@@ -715,12 +715,12 @@ static njs_int_t
njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused)
{
- njs_frame_t *active_frame;
njs_value_t *value;
+ njs_native_frame_t *active_frame;
njs_promise_context_t *context;
- active_frame = (njs_frame_t *) vm->top_frame;
- context = active_frame->native.function->context;
+ active_frame = vm->top_frame;
+ context = active_frame->function->context;
if (*context->resolved_ref) {
njs_vm_retval_set(vm, &njs_value_undefined);
@@ -995,12 +995,12 @@ njs_promise_then_finally_function(njs_vm
{
njs_int_t ret;
njs_value_t value, retval;
- njs_frame_t *frame;
njs_promise_t *promise;
+ njs_native_frame_t *frame;
njs_promise_context_t *context;
- frame = (njs_frame_t *) vm->top_frame;
- context = frame->native.function->context;
+ frame = vm->top_frame;
+ context = frame->function->context;
ret = njs_function_call(vm, njs_function(&context->finally),
&njs_value_undefined, args, 0, &retval);
diff -r b33402f10e82 -r cf85ed422623 src/njs_vm.c
--- a/src/njs_vm.c Sun May 31 08:45:41 2020 +0300
+++ b/src/njs_vm.c Mon Jun 15 15:26:40 2020 +0000
@@ -286,7 +286,7 @@ njs_vm_init(njs_vm_t *vm)
scope_size = vm->scope_size + NJS_INDEX_GLOBAL_OFFSET;
- size = NJS_GLOBAL_FRAME_SIZE + scope_size + NJS_FRAME_SPARE_SIZE;
+ size = njs_frame_size(0) + scope_size + NJS_FRAME_SPARE_SIZE;
size = njs_align_size(size, NJS_FRAME_SPARE_SIZE);
frame = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
@@ -294,15 +294,15 @@ njs_vm_init(njs_vm_t *vm)
return NJS_ERROR;
}
- njs_memzero(frame, NJS_GLOBAL_FRAME_SIZE);
+ njs_memzero(frame, njs_frame_size(0));
vm->top_frame = &frame->native;
vm->active_frame = frame;
frame->native.size = size;
- frame->native.free_size = size - (NJS_GLOBAL_FRAME_SIZE + scope_size);
+ frame->native.free_size = size - (njs_frame_size(0) + scope_size);
- values = (u_char *) frame + NJS_GLOBAL_FRAME_SIZE;
+ values = (u_char *) frame + njs_frame_size(0);
frame->native.free = values + scope_size;
@@ -357,11 +357,12 @@ njs_vm_invoke(njs_vm_t *vm, njs_function
void
-njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
+njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *native,
njs_native_frame_t *previous)
{
njs_uint_t n, nesting;
njs_value_t *args;
+ njs_frame_t *frame;
njs_closure_t **closures;
njs_function_t *function;
@@ -376,7 +377,7 @@ njs_vm_scopes_restore(njs_vm_t *vm, njs_
vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = args;
- function = frame->native.function;
+ function = native->function;
if (function->native) {
return;
@@ -386,6 +387,7 @@ njs_vm_scopes_restore(njs_vm_t *vm, njs_
/* GC: release function closures. */
}
+ frame = (njs_frame_t *) native;
frame = frame->previous_active_frame;
vm->active_frame = frame;
diff -r b33402f10e82 -r cf85ed422623 src/njs_vm.h
--- a/src/njs_vm.h Sun May 31 08:45:41 2020 +0300
+++ b/src/njs_vm.h Mon Jun 15 15:26:40 2020 +0000
@@ -286,7 +286,7 @@ struct njs_vm_shared_s {
};
-void njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
+void njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *frame,
njs_native_frame_t *previous);
njs_int_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
njs_native_frame_t *native_frame);
diff -r b33402f10e82 -r cf85ed422623 src/njs_vmcode.c
--- a/src/njs_vmcode.c Sun May 31 08:45:41 2020 +0300
+++ b/src/njs_vmcode.c Mon Jun 15 15:26:40 2020 +0000
@@ -705,13 +705,13 @@ next:
previous = njs_function_previous_frame(&frame->native);
- njs_vm_scopes_restore(vm, frame, previous);
+ njs_vm_scopes_restore(vm, &frame->native, previous);
/*
* If a retval is in a callee arguments scope it
* must be in the previous callee arguments scope.
*/
- retval = njs_vmcode_operand(vm, frame->retval);
+ retval = njs_vmcode_operand(vm, frame->native.retval);
/*
* GC: value external/internal++ depending on
@@ -923,7 +923,7 @@ error:
lambda_call = (frame == vm->active_frame);
- njs_vm_scopes_restore(vm, frame, previous);
+ njs_vm_scopes_restore(vm, &frame->native, previous);
if (frame->native.size != 0) {
vm->stack_size -= frame->native.size;
@@ -1687,13 +1687,13 @@ njs_vmcode_return(njs_vm_t *vm, njs_valu
previous = njs_function_previous_frame(&frame->native);
- njs_vm_scopes_restore(vm, frame, previous);
+ njs_vm_scopes_restore(vm, &frame->native, previous);
/*
* If a retval is in a callee arguments scope it
* must be in the previous callee arguments scope.
*/
- retval = njs_vmcode_operand(vm, frame->retval);
+ retval = njs_vmcode_operand(vm, frame->native.retval);
/* GC: value external/internal++ depending on value and retval type */
*retval = *value;
More information about the nginx-devel
mailing list