-
Notifications
You must be signed in to change notification settings - Fork 29.7k
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
No abort on require() with null bytes #8277
Conversation
1d13b57
to
8a011d3
Compare
The changes to node_file.cc don't look correct to me at all. What error does uv_fs_read() return? If it's UV_EBADF, it means you did something wrong and it should fail. |
lib/fs.js
Outdated
@@ -6,6 +6,7 @@ | |||
const constants = process.binding('constants').fs; | |||
const util = require('util'); | |||
const pathModule = require('path'); | |||
const internalFs = require('internal/fs'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unfortunately, if I have understood the situation correctly, a breaking change à la #6413?
Blocked by #6413. |
@bnoordhuis if This matches the behaviour before 1bbf8d0 in try {
var jsonPath = path.resolve(requestPath, 'package.json');
var json = fs.readFileSync(jsonPath, 'utf8');
} catch (e) {
return false;
} i.e. this is equivalent to: const json = internalModuleReadFile(path._makeLong(jsonPath));
if (json === undefined) {
return false;
} |
I should also note that this is failing on all machines in CI but is fine on my machine so I suspect there's something wonky going on with git. Will try again too. I'll also get rid of the |
@rvagg Imo there is nothing preventing landing #6413 (or it's alternative) for v7.0, as graceful-fs@3 was updated to not trigger the warning, and hence gulp is fine. See #6413 (comment). I don't think that we should introduce more hacks because of that at the moment. If that won't happen for some reason, then duplicating would be sufficient as a work-around. It should also be fine for v4.x. |
OK, well given the current status what I might do is adjust this PR to duplicate, then introduce a new PR that undoes the duplication that can land when it's unblocked, that way the original can go through to v4 and the new can update for v7+. |
That doesn't answer my question on why it's failing. What error does it return? |
I haven't cared so far because it's irelevant for the sake of restoring previous functionality. There is no Testing it now with |
I have no idea why this is failing on CI but working fine for me locally on both OSX and Linux. I'm guessing it's a fixture thing. https://ci.nodejs.org/job/node-test-commit/4782/ is the latest attempt if anyone else wants to poke at it. |
ed9877d
to
39db98c
Compare
Figured out CI, it's cause I was running with a Here's CI: https://ci.nodejs.org/job/node-test-commit/4788/ PTAL |
@@ -130,6 +130,9 @@ function assertEncoding(encoding) { | |||
} | |||
} | |||
|
|||
// This is duplicated in module.js and needs to be moved to internal/fs.js | |||
// once it is OK again to include internal/ resources in fs.js. | |||
// See: https://github.com/nodejs/node/pull/6413 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#6413 should land on Monday.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see a follow-up #8292, thanks.
Okay, that means you need to do the null byte check before passing it on to C++ land. Either that or it should be uv_fs_open() that gets (and deals with) the error, not uv_fs_read(). The change as it is would cause divergence across platforms. FreeBSD allows reading from a file descriptor that refers to a directory, other Unices don't.
The changes to node_file.cc look like daubing on more code to make the problem go away without really understanding what the problem is in the first place. More or less confirmed by the above. |
@bnoordhuis I still don't quite get where the problem is, and I entirely disagree with your assessment of "without really understanding what the problem is", so I'll explain that.
It is, that's what this addition is for: Module._findPath = function(request, paths, isMain) {
+ nullCheck(request); That will There may be additional errors, however, I haven't introduced tests for any but consider this. The previous incarnation before the optimisations were introduced did: try {
var jsonPath = path.resolve(requestPath, 'package.json');
var json = fs.readFileSync(jsonPath, 'utf8');
} catch (e) {
return false;
} i.e. catching any error thrown by fs. The new version effectively did this with if (fd < 0) {
return;
} which ends up being caught at by the replacement of the try/catch: const json = internalModuleReadFile(path._makeLong(jsonPath));
if (json === undefined) {
return false;
} but any errors occuring within - CHECK_GE(numchars, 0);
+ if (numchars < 0) {
+ break;
+ } followed by: + if (numchars < 0) {
+ args.GetReturnValue().Set(Undefined(env->isolate()));
+ } else { which perhaps could be just a So, previously, errors encountered by I believe this is consistent with the previous behaviour which used So, there are essentially two problems being fixed here, and two commits to fix them. One is to get the |
If I understand you correctly, the node_file.cc changes fix the case of trying to read a package.json that is a directory, not a file, but going by your description that's accidental more than intentional? ( The changes also don't fix the freebsd/non-freebsd divergence but that is ameliorated by the fact that the directory entry is unlikely to be valid JSON. :-) I've filed #8307 because I think this is a separate issue. |
There's no accidental fix here and you're still missing the point I think. The two things being fixed here are the null bytes check not being in place and leading to an abort, any \u0000 in require can crash a program. It's a stretch but I could concoct a scenario where this couod be a DoS vector if require depends at all on user input (bad practice of course but still). The second is the fact that all the usual cases of fs read failure lead to an abort in the new require path. The is-directory failure I mentioned is simply one that can come up by using the test cases we have (edit: the test cases introduced here if you remove the null bytes check, i.e. null bytes can result in trying to read a directory in some cases on some platforms but the null bytes check doesn't even let it it get that far), not something I have actually experienced. I don't understand what the hesitation is here, it seems that there is a suggestion that I somehow don't understand what I'm fixing (an assessment I don't agree with) and therefore it's not good enough. I don't get what couod be controversial about fixing this. In lieu of making forward progress here I'd prefer to just revert the commit(s) that introduced this, I'll put those in when I have time so that option can be considered too. Having an abort() in the path of reasonable failure cases should not be acceptable. They should be reserved for the unexpected and surprising (to us). |
start = 3; // Skip UTF-8 BOM. | ||
} | ||
if (numchars < 0) { | ||
args.GetReturnValue().Set(Undefined(env->isolate())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than returning undefined
.... can we throw here? Yes, I know that's semver-major but it's better than an abort and it's likely more correct than undefined
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would short circuit the require lookup algorithm which may potentially find a working match in a further iteration. We can do it but it's not a small breaking change and IMO would be unnecessarily disruptive for little gain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See also the comment above the function:
// Used to speed up module loading. Returns the contents of the file as
// a string or undefined when the file cannot be opened. The speedup
// comes from not creating Error objects on failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 works for me.
On Sunday, September 4, 2016, Anna Henningsen [email protected]
wrote:
In src/node_file.cc
#8277 (comment):@@ -566,17 +569,21 @@ static void InternalModuleReadFile(const FunctionCallbackInfo& args) {
CHECK_EQ(0, uv_fs_close(loop, &close_req, fd, nullptr));
uv_fs_req_cleanup(&close_req);
- size_t start = 0;
- if (chars.size() >= 3 && 0 == memcmp(&chars[0], "\xEF\xBB\xBF", 3)) {
- start = 3; // Skip UTF-8 BOM.
- }
- if (numchars < 0) {
- args.GetReturnValue().Set(Undefined(env->isolate()));
See also the comment above the function:
// Used to speed up module loading. Returns the contents of the file as// a string or undefined when the file cannot be opened. The speedup// comes from not creating Error objects on failure.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/nodejs/node/pull/8277/files/39db98ca5e20b1c28b5058c7b049747324d955bd#r77449453,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAa2eWcj_tAWKN-vBdhw8Czm5djPYWULks5qmpA1gaJpZM4Jt1xz
.
c133999
to
83c7a88
Compare
ping @rvagg @bnoordhuis ... |
Needs a rebase first, 3 out of 4 touched files conflict. However, I'm not convinced this is a) the right fix, and b) necessary. The threat scenario seems to be |
ping @rvagg Are you still working on this or have we found a different solution? |
Closing due to lack of forward progress. We can reopen if necessary. |
Still needs resolution, just need to find a path forward that satisfies the concerns here, tricky to navigate it seems but really needs attention. If someone else wants to pick this up in the meantime then please do. |
I believe @bnoordhuis had been dabbling with this lately. I'll verify |
You said something similar in #14905 (comment), but it doesn't seem to be clear to anyone else. Quoting @bnoordhuis: Why are nul bytes in a file relevant? |
@cjihrig one thing is to make sure to understand those are two different issues. |
@saper I am not sure what you mean with "unpack JSON file containing null". There is no JSON file in the tests and as I pointed out before (#14905 (comment)) the tests work fine and Node.js is not crashing anymore since #14854 landed. |
@saper as far as I see it this is not needed at all anymore. If you can come up with a test case that fails I would ask you to post that. Otherwise there is little to do. |
InternalModuleReadFile()
returningUndefined
so we don't get anabort()
whenuv_fs_read()
raises an error (currently it's only on\u0000
that I can find an error but perhaps there are others).nullBytes()
check inreadPackage()
so we get a proper error rather than a truncated one. This is also consistent with the behaviour ofrequire()
on null bytes prior to the introduction of the fast-path reads that now exist in v4.x+.This comes from 1bbf8d0 which is in v4.x and v6.x. In v0.12 and prior you'd get a normal "null bytes not allowed" error thrown when using a
require()
involving\u0000
.