Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Maximum call stack size exceeded" causes a crash in "async_hooks" module #37989

Open
zyscoder opened this issue Mar 30, 2021 · 5 comments
Open
Labels
async_hooks Issues and PRs related to the async hooks subsystem.

Comments

@zyscoder
Copy link

What steps will reproduce the bug?

Setup a node instance,

» node

and run the following javascript code.

async_hooks.createHook({after:new async_hooks.AsyncResource('str').bind(()=>{})}).enable();

Then the node instance crashes.

How often does it reproduce? Is there a required condition?

This abort can always be triggered following the steps above.

What is the expected behavior?

If any error occurs, an exception or other similar error-reporting stuff should be thrown. There is no reason to abort the whole node process.

What do you see instead?

» node
Welcome to Node.js v14.15.1.
Type ".help" for more information.
> async_hooks.createHook({after:new async_hooks.AsyncResource('str').bind(()=>{})}).enable();
AsyncHook {
  [Symbol(init)]: undefined,
  [Symbol(before)]: undefined,
  [Symbol(after)]: [Function: bound runInAsyncScope] {
    asyncResource: AsyncResource {
      [Symbol(async_id_symbol)]: 26,
      [Symbol(trigger_async_id_symbol)]: 5,
      [Symbol(destroyed)]: [Object]
    }
  },
  [Symbol(destroy)]: undefined,
  [Symbol(promiseResolve)]: undefined
}
> RangeError: Maximum call stack size exceeded
    at process._rawDebug (internal/process/per_thread.js:53:30)
    at fatalError (internal/async_hooks.js:159:13)
    at emitHook (internal/async_hooks.js:234:5)
    at emitAfterScript (internal/async_hooks.js:480:5)
    at AsyncResource.runInAsyncScope (async_hooks.js:198:9)
    at emitHook (internal/async_hooks.js:230:38)
    at emitAfterScript (internal/async_hooks.js:480:5)
    at AsyncResource.runInAsyncScope (async_hooks.js:198:9)
    at emitHook (internal/async_hooks.js:230:38)
    at emitAfterScript (internal/async_hooks.js:480:5)                                                                                                                                                                                                                                           

Additional information

@zyscoder
Copy link
Author

The following js code can also trigger "Maximum call stack size exceeded":

 `async_hooks.createHook({before:new async_hooks.AsyncResource('str').bind(()=>{})}).enable();`
» node
Welcome to Node.js v14.15.1.
Type ".help" for more information.
> async_hooks.createHook({before:new async_hooks.AsyncResource('str').bind(()=>{})}).enable();
AsyncHook {
  [Symbol(init)]: undefined,
  [Symbol(before)]: [Function: bound runInAsyncScope] {
    asyncResource: AsyncResource {
      [Symbol(async_id_symbol)]: 26,
      [Symbol(trigger_async_id_symbol)]: 5,
      [Symbol(destroyed)]: [Object]
    }
  },
  [Symbol(after)]: undefined,
  [Symbol(destroy)]: undefined,
  [Symbol(promiseResolve)]: undefined
}
> RangeError: Maximum call stack size exceeded
    at emitHook (internal/async_hooks.js:234:5)
    at emitBeforeScript (internal/async_hooks.js:474:5)
    at AsyncResource.runInAsyncScope (async_hooks.js:188:5)
    at emitHook (internal/async_hooks.js:230:38)
    at emitBeforeScript (internal/async_hooks.js:474:5)
    at AsyncResource.runInAsyncScope (async_hooks.js:188:5)
    at emitHook (internal/async_hooks.js:230:38)
    at emitBeforeScript (internal/async_hooks.js:474:5)
    at AsyncResource.runInAsyncScope (async_hooks.js:188:5)
    at emitHook (internal/async_hooks.js:230:38)

@Flarna
Copy link
Member

Flarna commented Mar 31, 2021

If any error occurs, an exception or other similar error-reporting stuff should be thrown.

Well, there is a RangeError thrown. it's not catched in your code.
But if you put your before/after handler within a try/catch a real crash happens which is described in #36079

@Flarna Flarna added the async_hooks Issues and PRs related to the async hooks subsystem. label Mar 31, 2021
@zyscoder
Copy link
Author

zyscoder commented Mar 31, 2021

Well, there is a RangeError thrown. it's not catched in your code.

I'm not sure what you mean but the node instance still exits (abnormally) although there has been a RangeError thrown. I don't know if this is a normal behavior.

@Flarna
Copy link
Member

Flarna commented Mar 31, 2021

If any AsyncHook callbacks throw, the application will print the stack trace and exit see https://nodejs.org/dist/latest-v14.x/docs/api/async_hooks.html#async_hooks_error_handling

@PhakornKiong
Copy link
Contributor

PhakornKiong commented Mar 31, 2021

@zyscoder

Let me try to chip in on this,

You can try to run this in Nodejs REPL and see

let codeA = `function debug2(...args){fs.writeFileSync(1, \`\${util.format(...args)}\n\`, { flag: 'a' });}async_hooks.createHook({init(asyncId, type, triggerAsyncId, resource) {debug2('init:  ',resource);},after(asyncId) {debug2(asyncId);new async_hooks.AsyncResource('str').bind(() => {});},}).enable();`;

eval(codeA)

Which is actually just a very simple function

function debug2(...args) {
  fs.writeFileSync(1, `${util.format(...args)}\n`, { flag: 'a' });
}
async_hooks
  .createHook({
    init(asyncId, type, triggerAsyncId, resource) {
      debug2('init:  ', resource);
    },
    after(asyncId) {
      debug2(asyncId);
      new async_hooks.AsyncResource('str').bind(() => {});
    },
  })
  .enable();

This would produce (a snippet of it since it keeps repeating)

 init:   <ref *1> Timeout {
  _idleTimeout: 15,
  _idlePrev: [Circular *1],
  _idleNext: [Circular *1],
  _idleStart: null,
  _onTimeout: [Function: flushHistory],
  _timerArgs: undefined,
  _repeat: null,
  _destroyed: false,
  [Symbol(refed)]: true,
  [Symbol(kHasPrimitive)]: false,
  [Symbol(asyncId)]: 73,
  [Symbol(triggerId)]: 5
}
init:   {
  callback: [Function: maybeReadMore_],
  args: [
    ReadStream {
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: null,
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 4,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: null,
      _server: null,
init:   {aw: true,
  callback: [Function: afterWriteTick],
  args: [ 0,
    { [Symbol(async_id_symbol)]: 5,
      count: 1,Handle)]: [TTY],
      cb: [Function: nop],]: false,
      stream: [WriteStream],Size)]: 0,
      state: [WritableState]l,
    } [Symbol(kBuffer)]: null,

I presumed the REPL is ran with some async stuff like setTimout, process.nextTick() that is causing StackOverflow.

Any reason why you would want to do this in REPL?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
async_hooks Issues and PRs related to the async hooks subsystem.
Projects
None yet
Development

No branches or pull requests

3 participants