-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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: segfault writing values with noAssert=true #8724
Comments
If it doesn’t benchmark horribly, I think we should do that. |
any idea what the cost of permanently turning on |
It performs about the same. This is master with noAssert=true:
Master with noAssert=false. Omitting noAssert benchmarks the same:
And this is with noAssert removed, see patch below:
Patch: diff --git a/lib/buffer.js b/lib/buffer.js
index 876bdbe..d3e48a1 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -1241,13 +1241,8 @@ Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
};
-Buffer.prototype.writeFloatLE = function writeFloatLE(val, offset, noAssert) {
- val = +val;
- offset = offset >>> 0;
- if (!noAssert)
- binding.writeFloatLE(this, val, offset);
- else
- binding.writeFloatLE(this, val, offset, true);
+Buffer.prototype.writeFloatLE = function writeFloatLE(val, offset) {
+ binding.writeFloatLE(this, +val, offset >>> 0);
return offset + 4;
};
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index 4baa8d9..027b963 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -817,12 +817,7 @@ template <typename T, enum Endianness endianness>
void WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- bool should_assert = args.Length() < 4;
-
- if (should_assert) {
- THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
- }
-
+ THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
Local<Uint8Array> ts_obj = args[0].As<Uint8Array>();
ArrayBuffer::Contents ts_obj_c = ts_obj->Buffer()->GetContents();
const size_t ts_obj_offset = ts_obj->ByteOffset();
@@ -837,10 +832,8 @@ void WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
size_t memcpy_num = sizeof(T);
- if (should_assert) {
- CHECK_NOT_OOB(offset + memcpy_num >= memcpy_num);
- CHECK_NOT_OOB(offset + memcpy_num <= ts_obj_length);
- }
+ CHECK_NOT_OOB(offset + memcpy_num >= memcpy_num);
+ CHECK_NOT_OOB(offset + memcpy_num <= ts_obj_length);
if (offset + memcpy_num > ts_obj_length)
memcpy_num = ts_obj_length - offset; |
This is just about writing floats and doubles, right? |
Also add test cases for partial writes and invalid indices. Fixes: nodejs#8724
I'm reopening this. I can't detect any measurable difference between noAssert=true and removing noAssert altogether, neither for integers nor floating-point numbers, so I think we should just get rid of the flag. |
@bnoordhuis could you post your benchmark results for ints? I still get a significant difference with |
Okay, I'm able to reproduce with So, I see about a 3.5x performance improvement with noAssert=true. With some clever open coding I can bring that back to 2.7x. Something is fishy though: the open-coded version is only 5% slower than a direct I also can't reproduce the gap outside the benchmark with e.g. the test case below; in fact, it's a little slower with the current implementation. I'm starting to think the benchmark is suspect. Current version: Buffer.prototype.writeUInt8 = function(value, offset, noAssert) {
value = +value;
offset = offset >>> 0;
if (!noAssert)
checkInt(this, value, offset, 1, 0xff, 0);
this[offset] = value;
return offset + 1;
}; Open-coded version: function failUInt8(that, value, offset) {
if (value >= 256)
throw new TypeError('"value" argument is out of bounds');
if (offset >= that.length)
throw new RangeError('Index out of range');
}
Buffer.prototype.writeUInt8 = function(value, offset) {
value = value >>> 0;
offset = offset >>> 0;
if (value >= 256 || offset >= this.length)
failUInt8(this, value, offset);
this[offset] = value;
return offset + 1;
}; Direct version: Buffer.prototype.writeUInt8 = function(value, offset) {
this[offset] = value;
}; Simple test:
|
Also add test cases for partial writes and invalid indices. PR-URL: nodejs/node#11927 Fixes: nodejs/node#8724 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
Does TurboFan/Ignition change anything here in either direction as far as performance of our code that handles |
Verify that the receiver is a buffer object before attempting to write out a floating point value. Fixes: nodejs#8724
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
This ports the Buffer#write(Double|Float)(B|L)E functions to JS. This fixes a security issue concerning type confusion and fixes another possible crash in combination with `noAssert`. In addition to that it will also significantly improve the write performance. Fixes: nodejs#12179 Fixes: nodejs#8724
This ports the Buffer#write(Double|Float)(B|L)E functions to JS. This fixes a security issue concerning type confusion and fixes another possible crash in combination with `noAssert`. In addition to that it will also significantly improve the write performance. Fixes: nodejs#12179 Fixes: nodejs#8724 PR-URL: nodejs#18395 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reported by @guidovranken. Test case:
The documentation says this:
IOW, it's technically allowed for node.js to crash but whether that's actually a good idea is something reasonable people can disagree on. Anyone have opinions on either:
noAssert
; i.e., always checking the inputs, orThe text was updated successfully, but these errors were encountered: