-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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
V8 Fatal Error when trying to convert a buffered integer to string #649
Comments
|
I understand what's wrong, haha. I'm just wondering why its giving a scary V8 error like that instead of a generic Javascript error. In Node 0.10.31:
|
@kevinmartin well, it does have an extraordinary length. > var buf = new Buffer(Date.now())
undefined
> buf.length
878611666 |
this issue reminds me of similar crash I had last year on node. I just retried it on iojs and it is still crashing. test.js : var fs = require('fs');
var zlib = require('zlib');
fs.readFile('./Compound_006625001_006650000.sdf.gz', function (err, content) {
zlib.gunzip(content, function (err, sdf) {
sdf.toString();
});
}); The gzipped file : ftp://ftp.ncbi.nlm.nih.gov/pubchem/Compound/CURRENT-Full/SDF/Compound_006625001_006650000.sdf.gz > iojs test.js
#
# Fatal error in ../deps/v8/src/handles.h, line 48
# CHECK(location_ != NULL) failed
#
==== C stack trace ===============================
1: V8_Fatal
2: v8::String::NewFromUtf8(v8::Isolate*, char const*, v8::String::NewStringType, int)
3: node::StringBytes::Encode(v8::Isolate*, char const*, unsigned long, node::encoding)
4: node::Buffer::Utf8Slice(v8::FunctionCallbackInfo<v8::Value> const&)
5: v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&))
6: ??
7: ??
zsh: illegal hardware instruction (core dumped) iojs test.js The uncompressed file is valid utf-8. |
There is a logic error in lib/buffer.js: the length argument to the constructor gets coerced to uint32 with In short, confirmed. :-) |
I think @bnoordhuis found a bug, but I don't think it was this bug. The We just have a plain Buffer (for which |
See #657 for a proposed mitigation. It's not perfect because the real issue lies in V8, it aborts when internal operations OOM. For example, |
V8 bug report: https://code.google.com/p/v8/issues/detail?id=3853 |
I've been poking around V8 and I do believe I understand the motivation for aborting. When you apply this patch: diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc
index 88d3c88..04f0295 100644
--- a/deps/v8/src/api.cc
+++ b/deps/v8/src/api.cc
@@ -5464,10 +5464,14 @@ inline Local<String> NewString(Isolate* v8_isolate,
ENTER_V8(isolate);
if (length == -1) length = StringLength(data);
// We do not expect this to fail. Change this if it does.
- i::Handle<i::String> result = NewString(
+ i::MaybeHandle<i::String> maybe_result = NewString(
isolate->factory(),
type,
- i::Vector<const Char>(data, length)).ToHandleChecked();
+ i::Vector<const Char>(data, length));
+ if (maybe_result.is_null()) {
+ return Local<String>();
+ }
+ i::Handle<i::String> result = maybe_result.ToHandleChecked();
if (type == String::kUndetectableString) {
result->MarkAsUndetectable();
} And run diff --git a/lib/buffer.js b/lib/buffer.js
index 41de85e..c0340f6 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -231,35 +231,48 @@ Buffer.prototype.toString = function(encoding, start, end) {
if (end <= start) return '';
while (true) {
+ let result;
switch (encoding) {
case 'hex':
- return this.hexSlice(start, end);
+ result = this.hexSlice(start, end);
+ break;
case 'utf8':
case 'utf-8':
- return this.utf8Slice(start, end);
+ result = this.utf8Slice(start, end);
+ break;
case 'ascii':
- return this.asciiSlice(start, end);
+ result = this.asciiSlice(start, end);
+ break;
case 'binary':
- return this.binarySlice(start, end);
+ result = this.binarySlice(start, end);
+ break;
case 'base64':
- return this.base64Slice(start, end);
+ result = this.base64Slice(start, end);
+ break;
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
- return this.ucs2Slice(start, end);
+ result = this.ucs2Slice(start, end);
+ break;
default:
if (loweredCase)
throw new TypeError('Unknown encoding: ' + encoding);
encoding = (encoding + '').toLowerCase();
loweredCase = true;
+ continue;
}
}
+
+ if (result === undefined)
+ throw new Error('Failed to convert buffer to string. Not enough memory?');
+
+ return result;
}
};
That same test fails with:
I speculate that there simply isn't enough memory left to create the exception object. |
Love the irony of not having enough memory to build an oom error, I know in .NET at least they pre allocate their oom so they can better grantee they can throw it later if needed. Not sure how relevant that is though. |
@meandmycode I believe V8 used to do something similar. That they moved away from that approach probably means it wasn't bulletproof. I remember some discussion about it on v8-users or v8-dev but I can't find the thread now. |
@bnoordhuis What memory are we talking about ? Certainly not the system's RAM, since I have more than 6GB free when I try to convert my buffer to string. |
#657 landed. I'm leaving this issue open pending https://code.google.com/p/v8/issues/detail?id=3853. |
Fixed by v8/v8@8aae1b3 |
The default/utf-8 |
@petkaantonov Externalized strings need special handling by the GC so I'm not sure if it would be a win for small strings. I can also see it making fragmentation of the C/C++ heap worse. The current 1+ MB cutoff point may be on the high side but some careful benchmarking is in order to find out what a better cutoff point is. |
@petkaantonov External strings can only be one or two byte strings. Thus excluding the possibility of an external UTF8 string. |
Also, the cutoff point that's currently in there was chosen after meticulous benchmarking. Here's a basic graph showing what happens to allocation time as string size increases: http://trevnorris.github.io/nodeconf.eu/#/17 |
True, but internal strings aren't UTF-8 either, V8 always decodes them to ASCII or UTF-16. |
Yes, we are not gaining anything by outsourcing this work to v8. We can do it ourselves and make it external. |
Sure, but do we really want to implement our own encoding parser so we can externalize all strings? |
We don't have to implement our own, even though it's a fun task. |
- pass a regexp to assert.throws()
pass a regexp to assert.throws() PR-URL: #9924 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
pass a regexp to assert.throws() PR-URL: #9924 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
pass a regexp to assert.throws() PR-URL: nodejs#9924 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
pass a regexp to assert.throws() PR-URL: #9924 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
IO.js Version 1.0.4
The text was updated successfully, but these errors were encountered: