Skip to content

Commit 09eb588

Browse files
santigimenoaddaleax
authored andcommitted
child_process: fix handleless NODE_HANDLE handling
It is possible that `recvmsg()` may return an error on ancillary data reception when receiving a `NODE_HANDLE` message (for example `MSG_CTRUNC`). This would end up, if the handle type was `net.Socket`, on a `message` event with a non null but invalid `sendHandle`. To improve the situation, send a `NODE_HANDLE_NACK` that'll cause the sending process to retransmit the message again. In case the same message is retransmitted 3 times without success, close the handle and print a warning. PR-URL: #13235 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 820d97d commit 09eb588

File tree

1 file changed

+44
-16
lines changed

1 file changed

+44
-16
lines changed

‎lib/internal/child_process.js‎

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const errnoException = util._errnoException;
2323
constSocketListSend=SocketList.SocketListSend;
2424
constSocketListReceive=SocketList.SocketListReceive;
2525

26+
constMAX_HANDLE_RETRANSMISSIONS=3;
27+
2628
// this object contain function to convert TCP objects to native handle objects
2729
// and back again.
2830
consthandleConversion={
@@ -88,17 +90,18 @@ const handleConversion ={
8890
returnhandle;
8991
},
9092

91-
postSend: function(handle,options,target){
93+
postSend: function(message,handle,options,callback,target){
9294
// Store the handle after successfully sending it, so it can be closed
9395
// when the NODE_HANDLE_ACK is received. If the handle could not be sent,
9496
// just close it.
9597
if(handle&&!options.keepOpen){
9698
if(target){
97-
// There can only be one _pendingHandle as passing handles are
99+
// There can only be one _pendingMessage as passing handles are
98100
// processed one at a time: handles are stored in _handleQueue while
99101
// waiting for the NODE_HANDLE_ACK of the current passing handle.
100-
assert(!target._pendingHandle);
101-
target._pendingHandle=handle;
102+
assert(!target._pendingMessage);
103+
target._pendingMessage=
104+
{ callback, message, handle, options,retransmissions: 0};
102105
}else{
103106
handle.close();
104107
}
@@ -249,6 +252,11 @@ function getHandleWrapType(stream){
249252
returnfalse;
250253
}
251254

255+
functionclosePendingHandle(target){
256+
target._pendingMessage.handle.close();
257+
target._pendingMessage=null;
258+
}
259+
252260

253261
ChildProcess.prototype.spawn=function(options){
254262
varipc;
@@ -434,7 +442,7 @@ function setupChannel(target, channel){
434442
});
435443

436444
target._handleQueue=null;
437-
target._pendingHandle=null;
445+
target._pendingMessage=null;
438446

439447
constcontrol=newControl(channel);
440448

@@ -490,16 +498,31 @@ function setupChannel(target, channel){
490498
// handlers will go through this
491499
target.on('internalMessage',function(message,handle){
492500
// Once acknowledged - continue sending handles.
493-
if(message.cmd==='NODE_HANDLE_ACK'){
494-
if(target._pendingHandle){
495-
target._pendingHandle.close();
496-
target._pendingHandle=null;
501+
if(message.cmd==='NODE_HANDLE_ACK'||
502+
message.cmd==='NODE_HANDLE_NACK'){
503+
504+
if(target._pendingMessage){
505+
if(message.cmd==='NODE_HANDLE_ACK'){
506+
closePendingHandle(target);
507+
}elseif(target._pendingMessage.retransmissions++===
508+
MAX_HANDLE_RETRANSMISSIONS){
509+
closePendingHandle(target);
510+
process.emitWarning('Handle did not reach the receiving process '+
511+
'correctly','SentHandleNotReceivedWarning');
512+
}
497513
}
498514

499515
assert(Array.isArray(target._handleQueue));
500516
varqueue=target._handleQueue;
501517
target._handleQueue=null;
502518

519+
if(target._pendingMessage){
520+
target._send(target._pendingMessage.message,
521+
target._pendingMessage.handle,
522+
target._pendingMessage.options,
523+
target._pendingMessage.callback);
524+
}
525+
503526
for(vari=0;i<queue.length;i++){
504527
varargs=queue[i];
505528
target._send(args.message,args.handle,args.options,args.callback);
@@ -514,6 +537,12 @@ function setupChannel(target, channel){
514537

515538
if(message.cmd!=='NODE_HANDLE')return;
516539

540+
// It is possible that the handle is not received because of some error on
541+
// ancillary data reception such as MSG_CTRUNC. In this case, report the
542+
// sender about it by sending a NODE_HANDLE_NACK message.
543+
if(!handle)
544+
returntarget._send({cmd: 'NODE_HANDLE_NACK'},null,true);
545+
517546
// Acknowledge handle receival. Don't emit error events (for example if
518547
// the other side has disconnected) because this call to send() is not
519548
// initiated by the user and it shouldn't be fatal to be unable to ACK
@@ -624,7 +653,8 @@ function setupChannel(target, channel){
624653
net._setSimultaneousAccepts(handle);
625654
}
626655
}elseif(this._handleQueue&&
627-
!(message&&message.cmd==='NODE_HANDLE_ACK')){
656+
!(message&&(message.cmd==='NODE_HANDLE_ACK'||
657+
message.cmd==='NODE_HANDLE_NACK'))){
628658
// Queue request anyway to avoid out-of-order messages.
629659
this._handleQueue.push({
630660
callback: callback,
@@ -646,7 +676,7 @@ function setupChannel(target, channel){
646676
if(!this._handleQueue)
647677
this._handleQueue=[];
648678
if(obj&&obj.postSend)
649-
obj.postSend(handle,options,target);
679+
obj.postSend(message,handle,options,callback,target);
650680
}
651681

652682
if(req.async){
@@ -662,7 +692,7 @@ function setupChannel(target, channel){
662692
}else{
663693
// Cleanup handle on error
664694
if(obj&&obj.postSend)
665-
obj.postSend(handle,options);
695+
obj.postSend(message,handle,options,callback);
666696

667697
if(!options.swallowErrors){
668698
constex=errnoException(err,'write');
@@ -711,10 +741,8 @@ function setupChannel(target, channel){
711741
// This marks the fact that the channel is actually disconnected.
712742
this.channel=null;
713743

714-
if(this._pendingHandle){
715-
this._pendingHandle.close();
716-
this._pendingHandle=null;
717-
}
744+
if(this._pendingMessage)
745+
closePendingHandle(this);
718746

719747
varfired=false;
720748
functionfinish(){

0 commit comments

Comments
(0)