Njs: Segfault when using .call.call construction.

Created on 23 Apr 2019  路  16Comments  路  Source: nginx/njs

> eval.call.call(function(){return String})
Segmentation fault
> URIError.apply.apply(RegExp)
Segmentation fault
> isNaN.apply.call(isNaN)
Segmentation fault
bug fuzzer

All 16 comments

@drsm

See the below code.

function.apply(thisArg, [argsArray])
var a = {};
a.foo = function() {
    console.log(this === a); // true
}
a.foo();
URIError.apply.apply(undefined)

I tried in nodejs. It throws TypeError: URIError.apply.apply is not a function. Why?

  1. Is URIError.apply.apply the same as URIError.apply is?
console.log(URIError.apply.apply === URIError.apply);  // In nodejs, true
  1. With apply function. What is the value of this?
URIError.apply.apply(undefined);

Is URIError.apply or undefined?

deleted.

@hongzhidao

Is URIError.apply.apply the same as URIError.apply is?

Yes, they are both equal to Function.prototype.apply.

With apply function. What is the value of this?

URIError.apply.apply(undefined);

this = URIError.apply thisArg = undefined

see apply

@hongzhidao

I tried in nodejs. It throws TypeError: URIError.apply.apply is not a function. Why?

Yes, error message is confusing there:

> var fn = Function.prototype.apply;
undefined
> fn()
Thrown:
TypeError: fn is not a function
> var o = { f: fn }
undefined
> o.f()
Thrown:
TypeError: o.f is not a function
> var f = function() { console.log('f'); }
undefined
> f.x = fn
[Function: apply]
> f.x()
f
undefined

@drsm

Two questions.
1.

console.log(typeof(eval.call));  // function
eval.call.call(1);
          ^

TypeError: eval.call is not a function

As eval.call is a function, why can't we use eval.call.call(1)?

  1. Is there any difference between eval.call(Number) and eval.call.call(Number)?

@xeioex did you find these recent issues by test262?

@hongzhidao

there is a bad confusing error message in nodejs:

root@node:~# eshost -x 'eval.call.call(1)'
#### ch

TypeError: Function.prototype.call: 'this' is not a Function object

#### jsc

TypeError: 1 is not a function

#### sm

TypeError: Function.prototype.call called on incompatible number

#### v8

TypeError: eval.call.call is not a function

#### xs

TypeError: ?.call: this is no Function instance

eval.call.call(1);

The function object eval.call === Function.prototype.call is called on the primitive value 1
the same:

> var x = Function.prototype.call.bind(1);
undefined
> x()
Thrown:
TypeError: x is not a function
> typeof x
'function'

Is there any difference between eval.call(Number) and eval.call.call(Number)?

  1. eval is called on Number
> Number.eval = eval
[Function: eval]
> Number.eval()
undefined
> eval.bind(Number)()
undefined
  1. eval.call === Function.prototype.call is called on Number
> Function.prototype.call.bind(Number)()
0
> eval.call.call.call(Number) 
0
> eval.call.call.call.call(Number) 
0
> eval.call === eval.call.call.call
true

the_object.the_function(arguments)
the_function.call(the_object, arguments)
the_function.apply(the_object, [arguments])

@xeioex

  1. Fixed function prototype call and apply. (Still have problem, ignore this)
deleted.
  1. Help test and add unit tests, please. @drsm @xeioex
    (I'm not familiar with call/apply)

  2. What's the actual return value of xxx.call(...)? For example.

eval.call.call(function() { return 1 });

BTW, according to my understanding.
Function.prototype.call always call this as a function.
So, eval.call(...) will call the eval function since this is object eval with property call.
eval.call.call(foo) will call foo since this is bound to foo.
Right?

@hongzhidao

Function.prototype.call always call this as a function.
So, eval.call(...) will call the eval function since this is object eval with property call.
eval.call.call(foo) will call foo since this is bound to foo.
Right?

Yes.

@hongzhidao

>> var x = function() { console.log('x'); }
undefined
>> eval.call(x)
InternalError: Not implemented
    at eval (native)
    at Function.prototype.call (native)
    at main (native)

>> eval.call.call(x) // should call x there
[Function]
>> x.call()
x
undefined
>> x()
x
undefined

here is the test:

> var fn = Function.prototype.call;
undefined
> fn.call(() => 1)
1
> fn.call(fn, () => 1)
1
> fn.call(fn, fn, () => 1)
1

deleted.

@xeioex @drsm

Here are two patches. (done)

  1. Improved njs_function_native_call().
  2. Fixed function prototype call and apply.
# HG changeset patch
# User hongzhidao <[email protected]>
# Date 1556279874 -28800
# Node ID 73effc1318a90da6cf8fa0204c2318e0733d2bff
# Parent  43dc900bc914b1d8d2eaf36008f8690d21c54447
Improved njs_function_native_call().

diff -r 43dc900bc914 -r 73effc1318a9 njs/njs_function.c
--- a/njs/njs_function.c    Thu Apr 25 19:50:20 2019 +0300
+++ b/njs/njs_function.c    Fri Apr 26 19:57:54 2019 +0800
@@ -566,7 +566,7 @@ njs_function_lambda_call(njs_vm_t *vm, n
 njs_ret_t
 njs_function_native_call(njs_vm_t *vm, njs_function_native_t native,
     njs_value_t *args, uint8_t *args_types, nxt_uint_t nargs,
-    njs_index_t retval)
+    njs_index_t retval, u_char *return_address)
 {
     njs_ret_t           ret;
     njs_value_t         *value;
@@ -617,6 +617,8 @@ njs_function_native_call(njs_vm_t *vm, n
             *value = vm->retval;
         }

+        vm->current = return_address;
+
         njs_function_frame_free(vm, frame);

         return NXT_OK;
diff -r 43dc900bc914 -r 73effc1318a9 njs/njs_function.h
--- a/njs/njs_function.h    Thu Apr 25 19:50:20 2019 +0300
+++ b/njs/njs_function.h    Fri Apr 26 19:57:54 2019 +0800
@@ -173,7 +173,7 @@ njs_ret_t njs_function_lambda_call(njs_v
     u_char *return_address);
 njs_ret_t njs_function_native_call(njs_vm_t *vm, njs_function_native_t native,
     njs_value_t *args, uint8_t *args_types, nxt_uint_t nargs,
-    njs_index_t retval);
+    njs_index_t retval, u_char *return_address);
 void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame);


diff -r 43dc900bc914 -r 73effc1318a9 njs/njs_vm.c
--- a/njs/njs_vm.c  Thu Apr 25 19:50:20 2019 +0300
+++ b/njs/njs_vm.c  Fri Apr 26 19:57:54 2019 +0800
@@ -2047,7 +2047,8 @@ njs_vmcode_function_call(njs_vm_t *vm, n
             ret = njs_function_native_call(vm, function->u.native,
                                            frame->arguments,
                                            function->args_types, frame->nargs,
-                                           (njs_index_t) retval);
+                                           (njs_index_t) retval,
+                                           return_address);
         }

     } else {
@@ -2055,16 +2056,7 @@ njs_vmcode_function_call(njs_vm_t *vm, n
                                        return_address);
     }

-    switch (ret) {
-    case NXT_OK:
-        return sizeof(njs_vmcode_function_call_t);
-
-    case NJS_APPLIED:
-        return 0;
-
-    default:
-        return ret;
-    }
+    return (ret == NJS_APPLIED) ? 0 : ret;
 }


@@ -2304,19 +2296,9 @@ njs_vmcode_continuation(njs_vm_t *vm, nj

     ret = njs_function_native_call(vm, cont->function, frame->arguments,
                                    cont->args_types, frame->nargs,
-                                   cont->retval);
-
-    switch (ret) {
-    case NXT_OK:
-        vm->current = return_address;
-        /* Fall through. */
-
-    case NJS_APPLIED:
-        return 0;
-
-    default:
-        return ret;
-    }
+                                   cont->retval, return_address);
+
+    return (ret == NJS_APPLIED) ? 0 : ret;
 }
# HG changeset patch
# User hongzhidao <[email protected]>
# Date 1556281097 -28800
# Node ID 893b82c75279cd114b45093b81f20889c691162f
# Parent  73effc1318a90da6cf8fa0204c2318e0733d2bff
Fixed function prototype call and apply.

diff -r 73effc1318a9 -r 893b82c75279 njs/njs_function.c
--- a/njs/njs_function.c    Fri Apr 26 19:57:54 2019 +0800
+++ b/njs/njs_function.c    Fri Apr 26 20:18:17 2019 +0800
@@ -31,7 +31,6 @@ njs_function_alloc(njs_vm_t *vm, njs_fun
         goto fail;
     }

-
     /*
      * nxt_mp_zalloc() does also:
      *   nxt_lvlhsh_init(&function->object.hash);
@@ -312,6 +311,10 @@ njs_function_native_frame(njs_vm_t *vm,
     frame->nargs = function->args_offset + nargs;
     frame->ctor = ctor;

+    if (continuation_size > 0) {
+        frame->continuation = njs_vm_continuation(vm);
+    }
+
     value = (njs_value_t *) (njs_continuation(frame) + continuation_size);
     frame->arguments = value;

@@ -770,6 +773,10 @@ njs_function_frame_free(njs_vm_t *vm, nj
     do {
         previous = frame->previous;

+        if (frame->continuation != NULL) {
+            vm->current = frame->continuation->return_address;
+        }
+
         /* GC: free frame->local, etc. */

         if (frame->size != 0) {
diff -r 73effc1318a9 -r 893b82c75279 njs/njs_function.h
--- a/njs/njs_function.h    Fri Apr 26 19:57:54 2019 +0800
+++ b/njs/njs_function.h    Fri Apr 26 20:18:17 2019 +0800
@@ -104,6 +104,8 @@ struct njs_native_frame_s {
     njs_function_t                 *function;
     njs_native_frame_t             *previous;

+    njs_continuation_t             *continuation;
+
     njs_value_t                    *arguments;
     njs_object_t                   *arguments_object;

diff -r 73effc1318a9 -r 893b82c75279 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c  Fri Apr 26 19:57:54 2019 +0800
+++ b/njs/test/njs_unit_test.c  Fri Apr 26 20:18:17 2019 +0800
@@ -6039,6 +6039,21 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("function f (x){ return x**2}; f(2\n)"),
       nxt_string("4") },

+    { nxt_string("var fn = Function.prototype.call; fn.call(() => 1)"),
+      nxt_string("1") },
+
+    { nxt_string("var fn = Function.prototype.call; fn.call(fn, () => 1)"),
+      nxt_string("1") },
+
+    { nxt_string("var fn = Function.prototype.call; fn.call(fn, fn, () => 1)"),
+      nxt_string("1") },
+
+    { nxt_string("eval.call.call(Number)"),
+      nxt_string("0") },
+
+    { nxt_string("URIError.apply.apply(RegExp)"),
+      nxt_string("/(?:)/") },
+
     /* Recursive factorial. */

     { nxt_string("function f(a) {"

@hongzhidao

Works fine for me, thanks!

>> var fn = Function.prototype.apply, x = (...a) => [this === x, a.join()]; fn.call(x, x, [1,2,3])
[
 false,
 '1,2,3'
]
>> var fn = Function.prototype.apply, x = function(...a) { return [this === x, a.join()] }; fn.call(x, x, [1,2,3])
[
 true,
 '1,2,3'
]

@xeioex wait a moment.
For the second patch, I think it's still not ideal.

@xeioex

Here's the final version of this patch.

# HG changeset patch
# User hongzhidao <[email protected]>
# Date 1556279874 -28800
# Node ID 73effc1318a90da6cf8fa0204c2318e0733d2bff
# Parent  43dc900bc914b1d8d2eaf36008f8690d21c54447
Improved njs_function_native_call().

diff -r 43dc900bc914 -r 73effc1318a9 njs/njs_function.c
--- a/njs/njs_function.c    Thu Apr 25 19:50:20 2019 +0300
+++ b/njs/njs_function.c    Fri Apr 26 19:57:54 2019 +0800
@@ -566,7 +566,7 @@ njs_function_lambda_call(njs_vm_t *vm, n
 njs_ret_t
 njs_function_native_call(njs_vm_t *vm, njs_function_native_t native,
     njs_value_t *args, uint8_t *args_types, nxt_uint_t nargs,
-    njs_index_t retval)
+    njs_index_t retval, u_char *return_address)
 {
     njs_ret_t           ret;
     njs_value_t         *value;
@@ -617,6 +617,8 @@ njs_function_native_call(njs_vm_t *vm, n
             *value = vm->retval;
         }

+        vm->current = return_address;
+
         njs_function_frame_free(vm, frame);

         return NXT_OK;
diff -r 43dc900bc914 -r 73effc1318a9 njs/njs_function.h
--- a/njs/njs_function.h    Thu Apr 25 19:50:20 2019 +0300
+++ b/njs/njs_function.h    Fri Apr 26 19:57:54 2019 +0800
@@ -173,7 +173,7 @@ njs_ret_t njs_function_lambda_call(njs_v
     u_char *return_address);
 njs_ret_t njs_function_native_call(njs_vm_t *vm, njs_function_native_t native,
     njs_value_t *args, uint8_t *args_types, nxt_uint_t nargs,
-    njs_index_t retval);
+    njs_index_t retval, u_char *return_address);
 void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame);


diff -r 43dc900bc914 -r 73effc1318a9 njs/njs_vm.c
--- a/njs/njs_vm.c  Thu Apr 25 19:50:20 2019 +0300
+++ b/njs/njs_vm.c  Fri Apr 26 19:57:54 2019 +0800
@@ -2047,7 +2047,8 @@ njs_vmcode_function_call(njs_vm_t *vm, n
             ret = njs_function_native_call(vm, function->u.native,
                                            frame->arguments,
                                            function->args_types, frame->nargs,
-                                           (njs_index_t) retval);
+                                           (njs_index_t) retval,
+                                           return_address);
         }

     } else {
@@ -2055,16 +2056,7 @@ njs_vmcode_function_call(njs_vm_t *vm, n
                                        return_address);
     }

-    switch (ret) {
-    case NXT_OK:
-        return sizeof(njs_vmcode_function_call_t);
-
-    case NJS_APPLIED:
-        return 0;
-
-    default:
-        return ret;
-    }
+    return (ret == NJS_APPLIED) ? 0 : ret;
 }


@@ -2304,19 +2296,9 @@ njs_vmcode_continuation(njs_vm_t *vm, nj

     ret = njs_function_native_call(vm, cont->function, frame->arguments,
                                    cont->args_types, frame->nargs,
-                                   cont->retval);
-
-    switch (ret) {
-    case NXT_OK:
-        vm->current = return_address;
-        /* Fall through. */
-
-    case NJS_APPLIED:
-        return 0;
-
-    default:
-        return ret;
-    }
+                                   cont->retval, return_address);
+
+    return (ret == NJS_APPLIED) ? 0 : ret;
 }


# HG changeset patch
# User hongzhidao <[email protected]>
# Date 1556285404 -28800
# Node ID f2e74147ee5474e10a16159c45a876c92a4775a0
# Parent  73effc1318a90da6cf8fa0204c2318e0733d2bff
Improved njs_vm_continuation().

diff -r 73effc1318a9 -r f2e74147ee54 njs/njs_function.c
--- a/njs/njs_function.c    Fri Apr 26 19:57:54 2019 +0800
+++ b/njs/njs_function.c    Fri Apr 26 21:30:04 2019 +0800
@@ -294,6 +294,7 @@ njs_function_native_frame(njs_vm_t *vm,
     size_t continuation_size, nxt_bool_t ctor)
 {
     size_t              size;
+    u_char              *continuation;
     nxt_uint_t          n;
     njs_value_t         *value, *bound;
     njs_native_frame_t  *frame;
@@ -312,7 +313,13 @@ njs_function_native_frame(njs_vm_t *vm,
     frame->nargs = function->args_offset + nargs;
     frame->ctor = ctor;

-    value = (njs_value_t *) (njs_continuation(frame) + continuation_size);
+    continuation = (u_char *) frame + NJS_NATIVE_FRAME_SIZE;
+
+    if (continuation_size > 0) {
+        frame->continuation = (njs_continuation_t *) continuation;
+    }
+
+    value = (njs_value_t *) (continuation + continuation_size);
     frame->arguments = value;

     bound = function->bound;
@@ -770,6 +777,10 @@ njs_function_frame_free(njs_vm_t *vm, nj
     do {
         previous = frame->previous;

+        if (frame->continuation != NULL) {
+            vm->current = frame->continuation->return_address;
+        }
+
         /* GC: free frame->local, etc. */

         if (frame->size != 0) {
diff -r 73effc1318a9 -r f2e74147ee54 njs/njs_function.h
--- a/njs/njs_function.h    Fri Apr 26 19:57:54 2019 +0800
+++ b/njs/njs_function.h    Fri Apr 26 21:30:04 2019 +0800
@@ -66,10 +66,7 @@ typedef struct {


 #define njs_vm_continuation(vm)                                               \
-    (void *) njs_continuation((vm)->top_frame)
-
-#define njs_continuation(frame)                                               \
-    ((u_char *) frame + NJS_NATIVE_FRAME_SIZE)
+    (void *) ((vm)->top_frame->continuation)

 #define njs_continuation_size(size)                                           \
     nxt_align_size(sizeof(size), sizeof(njs_value_t))
@@ -104,6 +101,8 @@ struct njs_native_frame_s {
     njs_function_t                 *function;
     njs_native_frame_t             *previous;

+    njs_continuation_t             *continuation;
+
     njs_value_t                    *arguments;
     njs_object_t                   *arguments_object;

diff -r 73effc1318a9 -r f2e74147ee54 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c  Fri Apr 26 19:57:54 2019 +0800
+++ b/njs/test/njs_unit_test.c  Fri Apr 26 21:30:04 2019 +0800
@@ -6039,6 +6039,21 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("function f (x){ return x**2}; f(2\n)"),
       nxt_string("4") },

+    { nxt_string("var fn = Function.prototype.call; fn.call(() => 1)"),
+      nxt_string("1") },
+
+    { nxt_string("var fn = Function.prototype.call; fn.call(fn, () => 1)"),
+      nxt_string("1") },
+
+    { nxt_string("var fn = Function.prototype.call; fn.call(fn, fn, () => 1)"),
+      nxt_string("1") },
+
+    { nxt_string("eval.call.call(Number)"),
+      nxt_string("0") },
+
+    { nxt_string("URIError.apply.apply(RegExp)"),
+      nxt_string("/(?:)/") },
+
     /* Recursive factorial. */

     { nxt_string("function f(a) {"

@drsm thanks for your help :)

Was this page helpful?
0 / 5 - 0 ratings