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

Closed socket leads to undefined values in socket.remote* properties #48061

Closed
orihjfrog opened this issue May 18, 2023 · 2 comments · Fixed by #48139
Closed

Closed socket leads to undefined values in socket.remote* properties #48061

orihjfrog opened this issue May 18, 2023 · 2 comments · Fixed by #48139
Labels
doc Issues and PRs related to the documentations. net Issues and PRs related to the net subsystem.

Comments

@orihjfrog
Copy link

orihjfrog commented May 18, 2023

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:

import * as http from 'http';

async function a() {
    return true;
}
async function onrequest(req, res) {
    const socket = req.socket;
    await a();
    console.log("Remote port is: " + socket.remotePort)
}
const server = http.createServer()

server.on('request', onrequest)

server.listen(12345)

Then, in a separate shell, run the following command:

curl -X GET "http://127.0.0.1:12345" -H "Content-Length:" --data "1"

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 file net.js is incharge of retrieving a peername object with data about the socket in use. This object is used for retrieving the attributes remoteAddress, remotePort and remoteFamily. 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:

  • Check if this._peername has a value
  • If this._peername has a value already, return it
  • If it doesn’t have a value, check if this._handle has a value
  • If this._handle has a value, use this._handle to create this._peername
  • If it doesn’t have a value, return an empty object

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 checking this._peername. Since after the socket closes this._handle becomes null, the _getpeername function will return an empty object immediately. The fix to the code changed the order, and assuming this._peername exists, it will return it even after the socket closes. The fix assumes that this._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, both this._peername and this._handle will be null, 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 is undefined. 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 our onrequest function with the valid request as an argument. Then, the onrequest 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 the onrequest function to run. The code tries to read socket.remotePort, but since this is the first time the code tries to reference this property, this._peername is still null. Since the socket has already been destroyed, also this._handle is null, 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 be undefined despite the code fixes that were supposed to fix this issue, but this fact is not documented for remotePort or remoteFamily, which may be undefined too.

Developers might assume that the socket.remote* properties will always have valid values. An undefined 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 where remoteAddress is undefined. Although the flow in their code doesn’t necessarily match the pattern we discussed (await and then reference to remoteAddress), 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.

@VoltrexKeyva VoltrexKeyva added the net Issues and PRs related to the net subsystem. label May 19, 2023
@bnoordhuis
Copy link
Member

As you say, the entry for socket.remoteAddress mentions that it can be undefined. The entries for the other properties should be updated to say the same thing.

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.

@bnoordhuis bnoordhuis added the doc Issues and PRs related to the documentations. label May 19, 2023
@Basa198
Copy link
Contributor

Basa198 commented May 23, 2023

Can I take this?
I will add in the docs that socket.remotePort and socket.remoteFamily may be undefined as well

nodejs-github-bot pushed a commit that referenced this issue May 25, 2023
Fixes: #48061
PR-URL: #48139
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Rich Trott <rtrott@gmail.com>
marco-ippolito pushed a commit to marco-ippolito/node that referenced this issue May 26, 2023
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>
targos pushed a commit that referenced this issue May 30, 2023
Fixes: #48061
PR-URL: #48139
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Rich Trott <rtrott@gmail.com>
danielleadams pushed a commit that referenced this issue Jul 6, 2023
Fixes: #48061
PR-URL: #48139
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Rich Trott <rtrott@gmail.com>
MoLow pushed a commit to MoLow/node that referenced this issue Jul 6, 2023
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>
Ceres6 pushed a commit to Ceres6/node that referenced this issue Aug 14, 2023
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>
Ceres6 pushed a commit to Ceres6/node that referenced this issue Aug 14, 2023
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
doc Issues and PRs related to the documentations. net Issues and PRs related to the net subsystem.
Projects
None yet
4 participants