-
Notifications
You must be signed in to change notification settings - Fork 30.3k
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
Closed socket leads to undefined values in socket.remote* properties #48061
Comments
As you say, the entry for From a correctness perspective, it's working as intended. The properties are intentionally lazy-loaded for performance reasons and I don't expect we'll change that. |
Can I take this? |
Fixes: nodejs#48061 PR-URL: nodejs#48139 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Rich Trott <rtrott@gmail.com>
Fixes: nodejs#48061 PR-URL: nodejs#48139 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Rich Trott <rtrott@gmail.com>
Fixes: nodejs#48061 PR-URL: nodejs#48139 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Rich Trott <rtrott@gmail.com>
Fixes: nodejs#48061 PR-URL: nodejs#48139 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Rich Trott <rtrott@gmail.com>
Version
v19.5.0
Platform
Microsoft Windows NT 10.0.19045.0 x64
Subsystem
net
What steps will reproduce the bug?
Run the following Node.js code:
Then, in a separate shell, run the following command:
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
Expected the output:
Remote port is: <port number>
The output should include the port that the client used to connect to the server.
What do you see instead?
Remote port is: undefined
Additional information
The code that handles the
_getpeername
attribute of sockets in the filenet.js
is incharge of retrieving a peername object with data about the socket in use. This object is used for retrieving the attributesremoteAddress
,remotePort
andremoteFamily
. In 2015 the code of the function was updated in a way that will allow these properties to still be accessed even after the socket is closed. The logic of the fix was as follows:this._peername
has a valuethis._peername
has a value already, return itthis._handle
has a valuethis._handle
has a value, usethis._handle
to createthis._peername
The code of the function has changed since 2015, but the main idea of this logic remains. The reason that before this fix the above properties weren’t available after the socket closed is that the previous code checked
this._handle
before checkingthis._peername
. Since after the socket closesthis._handle
becomesnull
, the_getpeername
function will return an empty object immediately. The fix to the code changed the order, and assumingthis._peername
exists, it will return it even after the socket closes. The fix assumes thatthis._peername
was created sometime before the socket was closed.The assumption that
this._peername
always exists before closing the socket was found to be not necessarily true. In certain cases, the socket may be closed before the first time_getpeername
is called. In such cases, boththis._peername
andthis._handle
will benull
, causing the function to return an empty object.The code provided above in the reproduction steps is one example for this kind of behavior. As shown above, the
remotePort
property isundefined
. The reason for that is that the underlying HTTP server received a valid HTTP request, followed by the character “1”, which is not a valid request. The server calls ouronrequest
function with the valid request as an argument. Then, theonrequest
code calls an async function. This call allows Node to do some other logic. In the meantime, the server has read the “1” character and decided that it is a bad request. It sends a “400 Bad Request” response to the client and closes the socket. Then, the async function returns, allowing the rest of theonrequest
function to run. The code tries to readsocket.remotePort
, but since this is the first time the code tries to reference this property,this._peername
is stillnull
. Since the socket has already been destroyed, alsothis._handle
isnull
, causing the_getpeername
function to return an empty object.It is important to note that the current documentation of node documents the fact that
remoteAddress
may beundefined
despite the code fixes that were supposed to fix this issue, but this fact is not documented forremotePort
orremoteFamily
, which may beundefined
too.Developers might assume that the
socket.remote*
properties will always have valid values. Anundefined
value in these properties may lead to unexpected application behavior or errors.Real-World Use Cases
This bug may affect many projects that assume
socket.remote*
are always available, even after closing the socket. We searched for other projects that may be impacted by the bug. One example we found was in the project hapi. We found an issue and a commit that fixes it. The issue addresses a case whereremoteAddress
isundefined
. Although the flow in their code doesn’t necessarily match the pattern we discussed (await and then reference toremoteAddress
), they did suffer from a similar problem without even knowing that it may be a bug in the core of Node.js. We encountered another project that is affected by this bug, causing a denial of service vulnerability, and reported it to them.The text was updated successfully, but these errors were encountered: