Repository navigation
fs streams leak fd on invalid argument #35168
Description
Activity
- addedfsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.
on Sep 12, 2020 - changed the title
[-]fs streams don't close fd on invalid argument[/-][+]fs streams leak fd on invalid argument[/+]on Sep 12, 2020 Hi @ronag , I worked out a solution that seems to solve the issue, I just wanted to run my thinking process by you to ensure I am on the right track.
I am catching the error that is thrown from validateInteger, executing fs.close() on the fd variable passed into the ReadStream/WriteStream constructor, then re-throwing the error that was caught.
Let me know if my solution here is incorrect or if you have any advice.
Thanks.
Reacted by Robert Nagy@ronag Should I be handling the error from fs.close callback in any specific way? Or should I be using the synchronous method
@dylanelliott27 create a PR and we can discuss it there
@ronag I don't think this is the right problem to be solved at node level but actually, the program using the createReadStream has the fd leak problem. At least considering the snippet you have put in the description.
I went ahead and checked if we are leaking fd in the case when the path argument is valid and the start argument is invalid. Since in that scenario, the caller has no control over the opened file descriptor. But that's not the case either.
And here's what I did to confirm that:
- Created a file which can't be opened by the user executing the code
- Used following code
const fs = require('fs'); fs.createReadStream("./tmp.txt", { start: 'invalid argument' })If the file was being read before argument validation then it would have thrown permission denied, but the test code threw the "Invalid argument" error.
Hence we can conclude that node is not leaking fd.
I don't quite follow...
Hence we can conclude that node is not leaking fd.
It is leaking a fd since
fs.closeis never invoked on the fd if theReadStreamconstructor throws. My example is when passing an explicitfd.@ronag There's nothing special about
createReadStream. Allfsmethods that accept a file descriptor do not close it when they throw because of bad arguments. For examplefs.write(fd, {wrong: 'object'})also "leaks"fd.@ronag There's nothing special about createReadStream.
There is something special... streams take ownership of the fd and closes it, while e.g.
fs.writedoes not.You could argue either way in regards to what to expect in this specific case. I think it's cleaner if it closes it otherwise you always should wrap
createReadStream/createWriteStreaminto atry/catchwithfs.close, which most users simply won't do. Regardless assumptions should preferably be documented.you always should wrap createReadStream/createWriteStream into a try/catch with fs.close
Not necessarily. You are not supposed to pass invalid arguments to
createReadStreamin the first place. This is a programming error and the reason why asynchronous functions in Node.js can throw synchronous errors in that case. The solution here is to callcreateReadStreamcorrectly, not to wrap it into atry/catch.Reacted by Luigi PincaHm, fair enough.