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

Crashes when UDP/dgram socket is closed during listening event handler #7061

Closed
Matthias247 opened this issue May 30, 2016 · 2 comments
Closed
Assignees
Labels
confirmed-bug Issues with confirmed bugs. dgram Issues and PRs related to the dgram subsystem / UDP.

Comments

@Matthias247
Copy link

  • Version: 4.3, master
  • Platform: Linux 4.4
  • Subsystem: dgram

Node.js crashes when a UDP/dgram socket is closed in the listening event handler.

Output / Stack trace using node.js 4.3:

dgram.js:460
    throw new Error('Not running'); // error message from dgram_legacy.js
    ^

Error: Not running
at Socket._healthCheck (dgram.js:460:11)
at Socket.send (dgram.js:284:8)
at Socket.<anonymous> (dgram.js:299:21)
at Socket.g (events.js:260:16)
at emitNone (events.js:72:20)
at Socket.emit (events.js:166:7)
at startListening (dgram.js:121:10)
at dgram.js:220:7
at nextTickCallbackWith3Args (node.js:452:9)
at process._tickCallback (node.js:358:17)

This seems to happen because node.js attaches an own eventhandler to the listening event which will try to send messages that were enqueued before, but have not been sent (https://github.com/nodejs/node/blob/master/lib/dgram.js#L288-L293). This closure might run after a user event handler which might close the socket (like mine does if it fails to join a multicast group). When it runs and tries to send messages on the already closed socket an exception is thrown which can not be captured by the user, because it's not on his callstack.

What should be done to avoid that:

  • The listening handler should check if the socket is still open before starting to send enqueued messages and should simply return if it's no longer open.
  • The close() function should flush the sendQueue, which would also avoid that the handler does attempt to send anything.

Actually the second part is more part, because it seems that currently the callbacks for those queued sends are also never called if the socket is closed before connecting and because it would already solve the issue. However another sanity check in the listening handler would still be ok.

Workarounds:

  • Don't send anything before listening is emitted
  • Don't close the socket or do any complex logic in the listening handler, but defer the custom logic in a new eventloop tick.

My code for reproduction:

class MulticastSocket {
// ...

open() {
  this._socket = dgram.createSocket(<any>{
    type: 'udp4',
    reuseAddr: true,
  });

  this._socket.once('listening', () => {
    console.log('Multicast socket is listening')     

    try {
      // Try to join a multicast group
      // This might fail depending on the network interfaces of the device
      this._socket.addMembership(this._multicastAddress);
      this._state = 'CONNECTED';
    } catch (e) {
      console.error('Adding multicast group membership failed')        
      this._closeSocket(); // The socket is closed here inside the event listener
    }
  });
}

_closeSocket() {
  if (this._state === 'CLOSED') return false;
  this._state = 'CLOSED';

  if (this._socket) {
  this._socket.removeAllListeners();
  this._socket.close();
  this._socket = null;
}
@mscdex mscdex added the dgram Issues and PRs related to the dgram subsystem / UDP. label May 30, 2016
@Fishrock123
Copy link
Contributor

cc @mcollina

@mcollina mcollina added the confirmed-bug Issues with confirmed bugs. label May 30, 2016
@mcollina
Copy link
Member

Thanks for reporting.

It is a bug, as the sendQueue is not flushed out before calling close if this happens before a 'listening' event handler is specified.
I will try to propose a fix during this week.

@mcollina mcollina self-assigned this May 30, 2016
mcollina added a commit to mcollina/node that referenced this issue May 30, 2016
If the udp socket is not ready and we are accumulating
messages to send, it needs to delay closing the socket when
all messages are flushed.

Fixes: nodejs#7061
cjihrig pushed a commit that referenced this issue Aug 10, 2016
If the udp socket is not ready and we are accumulating
messages to send, it needs to delay closing the socket when
all messages are flushed.

Fixes: #7061
PR-URL: #7066
Reviewed-By: Anna Henningsen <[email protected]>
mcollina added a commit to mcollina/node that referenced this issue Nov 19, 2016
If the udp socket is not ready and we are accumulating
messages to send, it needs to delay closing the socket when
all messages are flushed.

Fixes: nodejs#7061
PR-URL: nodejs#7066
Reviewed-By: Anna Henningsen <[email protected]>
MylesBorins pushed a commit that referenced this issue Nov 22, 2016
If the udp socket is not ready and we are accumulating
messages to send, it needs to delay closing the socket when
all messages are flushed.

Fixes: #7061
PR-URL: #7066
Reviewed-By: Anna Henningsen <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug Issues with confirmed bugs. dgram Issues and PRs related to the dgram subsystem / UDP.
Projects
None yet
Development

No branches or pull requests

4 participants