Skip to content

Crash deserializing IPC message using advanced serialization #34797

Description

@novemberborn
  • Version: 14.8.0
  • Platform: MacOS
  • Subsystem: child_process

What steps will reproduce the bug?

I start a child process using fork() and { serialization: 'advanced' }. In the child process I synchronously call process.send() until it returns false. I keep process.channel referenced until all my send() callbacks have been called. Every so often, the main process crashes. Other times it exits gracefully, though it does not log all received messages. Presumably because the child process exits without flushing its IPC buffer. That's manageable and probably not a bug.

main.js:

const childProcess = require('child_process')

const channel = childProcess.fork('./child.js', [], { serialization: 'advanced' })
channel.on('message', message => {
  console.error('main:', message.count)
})

child.js:

// Keep the process alive until all messages have been sent.
process.channel.ref()

let pending = 0
const drain = () => {
  if (--pending === 0) {
    console.error('all messages sent')
    if (!process.connected) {
      console.error('already disconnected')
    } else {
      console.error('unref channel')
      // Allow the process to exit.
      process.channel.unref()
    }
  }
}

// Fill up any internal buffers.
const filler = Buffer.alloc(2 ** 12, 1)

// Enqueue as many messages as possible until we're told to back off.
let count = 0
let ok
do {
  pending++
  ok = process.send({count: ++count, filler}, drain)
  console.error('child:', count, ok)
} while (ok)

And then run node main.js.

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

It's intermittent.

What is the expected behavior?

The main process does not crash.

What do you see instead?

The main process crashes with the following error:

internal/child_process/serialization.js:69
      deserializer.readHeader();
                   ^

Error: Unable to deserialize cloned data due to invalid or unsupported version.
    at parseChannelMessages (internal/child_process/serialization.js:69:20)
    at parseChannelMessages.next (<anonymous>)
    at Pipe.channel.onread (internal/child_process.js:595:18)

Additional information

If I modify child.process to schedule sends using setImmediate() there is no crash, and the main process receives all messages:

let count = 0
do {
  pending++
  setImmediate(() => {
    process.send({count: ++count, filler}, drain)
    console.error('child:', count)
  })
} while (pending < 100)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    child_processIssues and PRs related to the child_process subsystem.confirmed-bugIssues and PRs for confirmed bugs.v8 moduleIssues and PRs related to the node:v8 module.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions