-
Notifications
You must be signed in to change notification settings - Fork 30.2k
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: remove noAssert argument #12119
Conversation
@@ -43,12 +43,16 @@ assert.throws(function() { | |||
Buffer.from(new AB()); | |||
}, TypeError); | |||
|
|||
// write{Double,Float}{LE,BE} with noAssert should not crash, cf. #3766 | |||
// https://github.com/nodejs/node/issues/3766 |
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.
Since this GitHub issue is specifically about behavior when noAssert
is true, I think the URL/comment can be removed.
b.writeDoubleLE(11.11, 0, true); | ||
b.writeDoubleBE(11.11, 0, true); | ||
assert.throws(() => b.writeFloatLE(11.11, 0), | ||
/RangeError: out of range index/); |
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 think these four tests can be removed if you want, as they were a regression test for a crash when noAssert
was true
. No noAssert
means no regression possible. :-D
assert.throws(() => b.writeFloatLE(11.11, 0), | ||
/RangeError: out of range index/); | ||
assert.throws(() => b.writeFloatBE(11.11, 0), | ||
/RangeError: out of range index/); |
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.
If you decide to keep these four tests, though, please add a ^
and $
to the start and end respectively of the RegExp.
-1 If |
The security mailing list gets emails from people who think they found a security flaw or a denial of service when in fact they are passing $some_truthy_value for noAssert. It happens frequently enough that it stands out. I'd say that fact alone suggests it's a poorly designed API that causes real-world issues. I also don't really buy the performance argument. When you use the built-in functions you do it for convenience. You don't really care if it takes 50 ns or 100 ns because if you did, you would use hand-coded versions. (That said, see #8724 (comment) - I'm fairly sure the use of Compromise: remove noAssert and add |
I think the noAssert flag is very suspicious, and given the simple conditional check, its hard to see why its 3.5x slower. As a positional arg, Aside: I really dislike the use of "safe" in APIs, it doesn't give any clue as to meaning. I'd prefer something more descriptive, like "unchecked". Even better would be to more all the APIs into a different module |
It would be interesting to find an actual module that uses noAssert, maybe one of the DB procotols, and benchmark its real-world code with/without the check to see actual performance impact. |
Given that this change would cause code that currently works successfully when |
That takes too long, I don't have the patience to sit that out. Middle ground: make the OOB checks in src/node_buffer.cc unconditional. That should be uncontroversial because "code that currently works successfully" doesn't apply to code that violates memory safety. |
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.
Other arguments aside, the code changes LGTM.
I could live with that approach. |
src/node_buffer.cc
Outdated
else | ||
memcpy_num = ts_obj_length - offset; | ||
THROW_AND_RETURN_IF_OOB(false); | ||
memcpy_num = ts_obj_length - offset; | ||
} |
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.
Uhh, the memcpy_num
assignment is dead code since THROW_AND_RETURN_IF_OOB
will always execute because of false
.
@bnoordhuis Is this still something you'd like to move forward? Can you rebase and maybe address @TimothyGu's comment?
|
976b0ae
to
8532503
Compare
PTAL, significantly watered-down changeset uploaded. :-/ |
test/parallel/test-buffer-read.js
Outdated
/* eslint-disable no-restricted-syntax */ | ||
for (const noAssert of [true, false]) { | ||
// Out of bounds. | ||
assert.throws(() => Buffer.from(1).readFloatBE(0, noAssert)); |
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.
If I'm not mistaken, this and the subsequent lines are throwing on Buffer.from()
(because it can't accept a number as an argument) and never getting to readFloatBE()
. (Maybe create the Buffer external to the assert.throws()
to avoid that possibility in future refactoring etc.?)
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.
Good catch, this was hiding a real bug.
Verify that the receiver is a buffer object before attempting to write out a floating point value. Fixes: nodejs#8724
8532503
to
676ddbb
Compare
Check bounds and throw an exception when reading floating point values from a buffer. Before this commit, the process would fail a CHECK and terminate when `noAssert=true`. Fixes: nodejs#8724
676ddbb
to
2b3e2a2
Compare
@TimothyGu Does this LGTY now? |
Ping @TimothyGu |
No response. Please take another look as your comment seems to be addressed.
@nodejs/tsc PTAL. This is semver-major and needs further reviews. |
Does this require some modifications to buffer.md to explain when |
(Also: This only affects the C++ checks, but there are checks in JS-land that still respect |
|
||
inline uint64_t Add64Clamp(uint64_t x, uint64_t y) { | ||
return x + y >= x ? x + y : static_cast<uint64_t>(-1); | ||
} |
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 function seems unused.
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 added it for parity (I had plans to use it for something else) but I can remove it.
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 would prefer not to add functions that have no functionality are unused.
Ping @bnoordhuis |
Ping @bnoordhuis This is ready to land as soon as my comment is addressed. This could also be done while landing if you are fine with that. |
There's a -1 from @mscdex so this might need to be escalated to TSC if that isn't resolved. This might need a doc update in I would like to see this land. |
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.
LGTM with a doc update.
@Trott thanks for pointing that out. I missed that. And good that you also caught the doc update :-) @bnoordhuis @mscdex There will be a small performance penalty but if it truly has a 3.5 times impact, something must be wrong with V8. I think it makes sense to a) rerun the benchmark with current master and b) improve the benchmark by removing |
I wonder if the benchmark is faulty. I can understand seeing a performance degradation with buffers/buffer-read.js millions=1 type="DoubleBE" buffer="fast" noAssert="false" -13.81 % *** 2.686215e-10
buffers/buffer-read.js millions=1 type="DoubleBE" buffer="fast" noAssert="true" -17.24 % *** 7.214802e-22
buffers/buffer-read.js millions=1 type="DoubleBE" buffer="slow" noAssert="false" -13.80 % *** 4.725621e-14
buffers/buffer-read.js millions=1 type="DoubleBE" buffer="slow" noAssert="true" -15.80 % *** 6.746154e-15
buffers/buffer-read.js millions=1 type="DoubleLE" buffer="fast" noAssert="false" -14.59 % *** 2.411344e-15
buffers/buffer-read.js millions=1 type="DoubleLE" buffer="fast" noAssert="true" -18.60 % *** 8.171223e-16
buffers/buffer-read.js millions=1 type="DoubleLE" buffer="slow" noAssert="false" -17.89 % *** 1.790047e-19
buffers/buffer-read.js millions=1 type="DoubleLE" buffer="slow" noAssert="true" -16.12 % *** 3.988442e-13
buffers/buffer-read.js millions=1 type="FloatBE" buffer="fast" noAssert="false" -16.16 % *** 1.482242e-15
buffers/buffer-read.js millions=1 type="FloatBE" buffer="fast" noAssert="true" -15.38 % *** 2.969582e-23
buffers/buffer-read.js millions=1 type="FloatBE" buffer="slow" noAssert="false" -14.22 % *** 1.242668e-13
buffers/buffer-read.js millions=1 type="FloatBE" buffer="slow" noAssert="true" -17.66 % *** 6.550393e-24
buffers/buffer-read.js millions=1 type="FloatLE" buffer="fast" noAssert="false" -12.73 % *** 2.119630e-08
buffers/buffer-read.js millions=1 type="FloatLE" buffer="fast" noAssert="true" -16.95 % *** 1.742243e-24
buffers/buffer-read.js millions=1 type="FloatLE" buffer="slow" noAssert="false" -12.40 % *** 1.328481e-09
buffers/buffer-read.js millions=1 type="FloatLE" buffer="slow" noAssert="true" -18.43 % *** 2.863973e-24 Pinging @psmarshall on the benchmark. [Update: I think I see now why |
From experimenting locally it looks like the regression is caused by the call to |
Ping @bnoordhuis |
Ping @bnoordhuis again |
I'll probably try a different tack, by moving the functions to JS. See #17775. |
@bnoordhuis even though #17775 got merged, I guess #8724 is still unresolved and it still makes sense to remove the |
Ping @bnoordhuis |
Superseded by #18395 |
noAssert=true
is intrinsically unsafe. Always-on checking doescause a performance regression but some of that can be regained by
open-coding the checks (which this pull request doesn't do but can
be done at a later time if so desired.)
What's more, I don't think performance is ultra-critical for these
methods. If you want to squeeze out every last ounce of oomph, you
don't call out to generic kitchen-sink methods, you open-code loads
and stores directly in your parser or generator. I can see no good
reason for leaving it in and that is why this commit takes it out.
Refs: #8724 (#8724 (comment) is relevant vis-a-vis performance.)