-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
buffer: prevent failed assertions #1922
buffer: prevent failed assertions #1922
Conversation
This commit adds assert() calls in JS, which prevents non-buffer instances from triggering failed C++ assertions (killing the process). They will now die with an AssertionError. Buffer.prototype.copy had to be wrapped in JS to allow for the assertion, with a modification to the arguments passed. Fixes: nodejs#1485
@@ -335,6 +336,14 @@ Buffer.byteLength = byteLength; | |||
|
|||
// toString(encoding, start=0, end=buffer.length) | |||
Buffer.prototype.toString = function(encoding, start, end) { | |||
// .toString() can be called automatically on non-Buffers. |
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.
How?
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.
+Buffer.prototype
, it isn't "supported" but it was decided that it shouldn't kill the process. I can add that to the comment, if you'd like.
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.
Yes, and some more detail on what "automatically" means would be great...
The fix seems overcomplicated in any case. Why throw for arguments.length !== 0?
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.
To get here you'd have to call toString.{call,apply}()
or to copy it to the prototype of another constructor, etc. Either way you're hosed. Like @domenic mentions, just throw a TypeError
if the instanceof
check fails.
TypeErrors are more conventional here (used in ES and the web for the same purpose). I'd suggest that instead of AssertionErrors. That also avoids any crazy back-compat breaks that could possibly occur from your change of some of the errors from TypeErrors to AssertionErrors. |
Since you're trying to be overly safe and all, it's worth mentioning that to get past these checks you just have to mess with What I'm getting at is it's almost always possible to get around the checks in place. We just have to determine where we draw the line, and I'm not sure checking the |
@trevnorris I can stop the type checking at |
I completely agree that the application should not abort, but I have to draw the line at usage matching or relatively close to the blessed API. Only because there are many ways I know of to cause an abort by doing strange things with the blessed API, and checking all of them would be near impossible. My gauge is the level of effort necessary to cause it. Doing funky things with the prototype then making an explicit call to a custom method is outside that scope. With that said, I agree that the |
also add comments
Thank you @trevnorris & @domenic, I have pushed another commit to refine it to just |
LGTM. Guess we should go ahead and run CI for consistency. https://jenkins-iojs.nodesource.com/job/iojs+any-pr+multi/786/ |
@@ -335,6 +335,14 @@ Buffer.byteLength = byteLength; | |||
|
|||
// toString(encoding, start=0, end=buffer.length) | |||
Buffer.prototype.toString = function(encoding, start, end) { | |||
// .toString() can be called by JS on prototype (Buffer.prototype + ''). | |||
if (!(this instanceof Buffer)) { | |||
if (arguments.length === 0) |
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.
I still don't understand the purpose of this check at all. Why not always return O.p.toString when used on a non-buffer.
If we can get away with it without performance costs, I'd still prefer to avoid crashes in favor of TypeErrors, even for bizarre abuses. I think this should be doable though---just have the C++ code that extracts the Not a big deal though. |
@domenic Was thinking something similar. The I am planning on doing this in another PR so I can make sure to maintain forwards compatibility. |
Will HasInstance() work with subclassing? I.e. will subclass instances pass the test correctly? (I don't know how HasInstance works really.) What you really want to verify is "are the internal structures that C++ is going to rely on present." That is why I was thinking IsTypedArray. But HasInstance might be just as good or better. |
Currently |
@trevnorris should I continue with this PR then? or would you like to just keep it with yours? |
@brendanashworth feel bad about doing that and scrapping all the work you've done here. Wasn't until I was considering the implementation consequences of your patch that I realized what needs to happen. If you're really alright with it, I'll create a patch. If you can C++ I think the change would be pretty straight forward. |
@trevnorris no, please, feel free. I like your approach better anyways. I can C, but I can neither C++ nor v8, so you'd probably do a much better job than I would. I'll close this for now, thanks. |
Thanks much for your work here. Wouldn't have realized what was necessary otherwise. In that case I'll throw for all improper usages except toString(). |
cross-ref: #2012 |
If an object's prototype is munged it's possible to bypass the instanceof check and cause the application to abort. Instead now use HasInstance() to verify that the object is a Buffer, and throw if not. This check will not work for JS only methods. So while the application won't abort, it also won't throw. In order to properly throw in all cases with toString() the JS optimization of checking that length is zero has been removed. In its place the native methods will now return early if a zero length string is detected. Ref: #1486 Ref: #1922 Fixes: #1485 PR-URL: #2012 Reviewed-By: Ben Noordhuis <[email protected]>
If an object's prototype is munged it's possible to bypass the instanceof check and cause the application to abort. Instead now use HasInstance() to verify that the object is a Buffer, and throw if not. This check will not work for JS only methods. So while the application won't abort, it also won't throw. In order to properly throw in all cases with toString() the JS optimization of checking that length is zero has been removed. In its place the native methods will now return early if a zero length string is detected. Ref: nodejs#1486 Ref: nodejs#1922 Fixes: nodejs#1485 PR-URL: nodejs#2012 Reviewed-By: Ben Noordhuis <[email protected]>
This commit adds
assert()
calls in JS, which prevents non-bufferinstances from triggering failed C++ assertions (killing the process).
They will now die with an
AssertionError
.Buffer.prototype.copy
had tobe wrapped in JS to allow for the assertion, with a modification to the
arguments passed.
Fixes: #1485
Follows #1486.