-
Notifications
You must be signed in to change notification settings - Fork 30k
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: throw a consistent range error for length > kMaxLength #10152
buffer: throw a consistent range error for length > kMaxLength #10152
Conversation
Changing error messages is currently Looking at it though it is possible that if the errors varied anyways we may be able to do this as a patch. I'll let others discuss. :D |
@@ -115,6 +115,9 @@ function assertSize(size) { | |||
err = new TypeError('"size" argument must be a number'); | |||
else if (size < 0) | |||
err = new RangeError('"size" argument must not be negative'); | |||
else if (size > binding.kMaxLength) | |||
err = new RangeError('"size" argument must not be larger ' + | |||
'than 0x' + binding.kMaxLength.toString(16)); |
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.
Why hex? It seems like having a base-10 decimal number would be easier to read/understand?
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.
Just trying to be close to the old behavior here(
Line 174 in 9cd44bb
throw new RangeError('Attempt to allocate Buffer larger than maximum ' + |
1c55963
to
b669327
Compare
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.
Looks good so far!
I’d probably like to be cautious and regard this as semver-major
too, unless somebody else feels strongly?
It has to be semver-major given the additional throw. |
The throw always happen when length > kMaxLength, one way or another. This PR just makes it happen earlier in the JS land of Node instead of in the C++ land of V8, and produce a consistent error message. The semver-major-ness I believe come from the change of error messages, though that's very platform-dependent before this PR anyway. (ref: CI results of #9924, as the second and third range error regexp added, more platforms went green) If the error message is not a completely new one but one of the old ones instead(say, |
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 don't think anything needs to be added to test/common.js
, but the rest LGTM.
It should be semver major because the error message changes.
That is intended to be reused in other |
b669327
to
46a6c61
Compare
I've updated the tests since #9924 has been merged. Can I get a CI run? |
Ping. What is the status of this? Is there anything left I can do to get this landed? |
This should be good to go except for the one tiny thing that the commit subject lines should ideally be no longer than 50 characters long. They can also be shortened by the person landing the PR, but since this is semver-major, there should be no hurry to get it into |
Thanks. I will squash the commits and make the subject line cleaner. Maybe it needs another CI? |
a7a0142
to
e3ebd5c
Compare
- Always return the same error message(hopefully more informative) for buffer length > kMaxLength and avoid getting into V8 C++ land for unnecessary checks. - Use accurate RegExp(reusable as `common.bufferMaxSizeMsg`) in tests for this error. - Separate related tests from test-buffer-alloc.
e3ebd5c
to
ec0450e
Compare
Sorry for the spamming, I didn't realize a semver-major PR needs longer to land since this is my first semver-major PR :). I think I will need to add that into #10202 too. |
If you just squashed the commits, it should be fine. But here’s another CI anyway: https://ci.nodejs.org/job/node-test-commit/6618/
There’s no rule saying it needs longer to land, it’s just that it doesn’t really make a difference whether this lands two days earlier or later. :) |
Landed in 3d353c7, thanks! |
- Always return the same error message(hopefully more informative) for buffer length > kMaxLength and avoid getting into V8 C++ land for unnecessary checks. - Use accurate RegExp(reusable as `common.bufferMaxSizeMsg`) in tests for this error. - Separate related tests from test-buffer-alloc. PR-URL: #10152 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
- Always return the same error message(hopefully more informative) for buffer length > kMaxLength and avoid getting into V8 C++ land for unnecessary checks. - Use accurate RegExp(reusable as `common.bufferMaxSizeMsg`) in tests for this error. - Separate related tests from test-buffer-alloc. PR-URL: nodejs#10152 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
- Always return the same error message(hopefully more informative) for buffer length > kMaxLength and avoid getting into V8 C++ land for unnecessary checks. - Use accurate RegExp(reusable as `common.bufferMaxSizeMsg`) in tests for this error. - Separate related tests from test-buffer-alloc. PR-URL: nodejs#10152 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Background
#9924
When trying to make the error messages thrown for buffer length exceeding the maximum limit, CI failed on some platforms and succeeded on others. As @addaleax pointed out the error could be
Array buffer allocation failed
on some machines, while beingInvalid typed array length
on others.Why this happens
When the buffer allocation methods receive a relatively large length as the sole argument, they all go to
createUnsafeBuffer
eventually.And when the length is extrodinarily large, this could throw three types of errors:
RangeError: Invalid array buffer length
v8::internal::Builtin_Impl_ArrayBufferConstructor_ConstructStub
, the stub fornew ArrayBuffer
std::numeric_limits<size_t>::max()
, and v8 can't convert this into asize_t
RangeError: Array buffer allocation failed
v8::internal::Builtin_Impl_ArrayBufferConstructor_ConstructStub
node::MultiplyWithOverflowCheck
, when the byte length(buffer length * 8) that will get passed torealloc
and alike has an overflow assize_t
(i.g.length * 8 > std::numeric_limits<size_t>::max()
). * Or it could fail innode::UncheckedRealloc
-like functions, when the machine don't have enough memory andrealloc
-like calls fail. The limit here depends on the machine's current available memory.RangeError: Invalid typed array length
ArrayBuffer
is created and gets passed tonew FastBuffer
akaUint8Array
.v8::internal::Runtime_TypedArrayInitialize
and thrown there. Since V8 can't handle a typed array with length larger than what a SMI can handle(v8::Smi::kMaxValue
, or justnode::kMaxLength
/buffer.kMaxLength
), when the length is larger than that, this error will be thrown.This is what happens on a 64-bit mac, where
std::numeric_limits<size_t>::max()
is2^64-1
andv8::Smi::kMaxValue
is2^32-1
:But on other platforms, the limits can vary, hence the error message is platform-specific.
What the docs say
Basically the API docs for all the allocation methods say:
What the C++ methods do
node::Buffer::New
andnode::Buffer::Copy
do:What is the old behavior
Before the new buffer implementation came along(#1825), a manual checking is performed and throw a range error in the JS land of Node(as of https://github.com/nodejs/node/blob/9cd44bb2b683446531306bbcff8739fc3526d02c/lib/buffer.js)
What is the issue
v8::Smi::kMaxValue
, the allocation would fail, but we need to get into the C++ land of V8 and go through a lot of steps to throw an error out. This could have been avoided by doing a fast check in the JS land first, as the old implementation did.v8::Smi::kMaxValue
andstd::numeric_limits<size_t>::max()
could be very different on different platforms, so the tests can not test for a consistent error message. But as I understand we are trying to add exact regexp match toassert.throws
in tests, so we need to do a lot of/^RangeError: (Invalid typed array length|Array buffer allocation failed|Invalid array buffer length)$/
. That looks weird.Possible fix
Currently when a large length is passed into the allocation methods, the call path looks like this:
So I believe the most suitable place for this check should be in
assertSize
, and ifassertSize
is added toSlowBuffer()
, all allocations will get checked before they go intonew ArrayBuffer
and enter V8 C++ land.I've only fixed the test that fails on my machine as of now. If @nodejs/buffer think this approach is plausible then I will try to fix more tests with matching regexps.