diff options
author | Fedor Indutny <fedor.indutny@gmail.com> | 2014-03-27 21:45:15 +0400 |
---|---|---|
committer | Fedor Indutny <fedor@indutny.com> | 2014-04-11 01:20:43 +0400 |
commit | 4c36f3e7e6108660fc3629d76036e40c1938f639 (patch) | |
tree | 094b5214eeae8c230550402a4fc5da821ec8e52c | |
parent | 525fad473bfafc2f44e1ab591152ca338fe45f2b (diff) | |
download | node-4c36f3e7e6108660fc3629d76036e40c1938f639.tar.gz |
buffer: truncate buffer after string decode
When our estimates for a storage size are higher than the actual length
of decoded data, the destination buffer should be truncated. Otherwise
`Buffer::Length` will give misleading information to C++ layer.
fix #7365
Signed-off-by: Fedor Indutny <fedor@indutny.com>
-rw-r--r-- | lib/buffer.js | 9 | ||||
-rw-r--r-- | src/smalloc.cc | 30 | ||||
-rw-r--r-- | test/simple/test-buffer.js | 11 |
3 files changed, 49 insertions, 1 deletions
diff --git a/lib/buffer.js b/lib/buffer.js index ab16cd8ad..91c13521a 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -23,6 +23,7 @@ var buffer = process.binding('buffer'); var smalloc = process.binding('smalloc'); var util = require('util'); var alloc = smalloc.alloc; +var truncate = smalloc.truncate; var sliceOnto = smalloc.sliceOnto; var kMaxLength = smalloc.kMaxLength; var internal = {}; @@ -79,7 +80,13 @@ function Buffer(subject, encoding) { // In the case of base64 it's possible that the size of the buffer // allocated was slightly too large. In this case we need to rewrite // the length to the actual length written. - this.length = this.write(subject, encoding); + var len = this.write(subject, encoding); + + // Buffer was truncated after decode, realloc internal ExternalArray + if (len !== this.length) { + this.length = len; + truncate(this, this.length); + } } else { if (util.isBuffer(subject)) subject.copy(this, 0, 0, this.length); diff --git a/src/smalloc.cc b/src/smalloc.cc index 1e9cc3621..7b8b3e447 100644 --- a/src/smalloc.cc +++ b/src/smalloc.cc @@ -504,6 +504,35 @@ bool HasExternalData(Environment* env, Local<Object> obj) { } +void AllocTruncate(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + Local<Object> obj = args[0].As<Object>(); + + // can't perform this check in JS + if (!obj->HasIndexedPropertiesInExternalArrayData()) + return env->ThrowTypeError("object has no external array data"); + + char* data = static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData()); + enum ExternalArrayType array_type = + obj->GetIndexedPropertiesExternalArrayDataType(); + int length = obj->GetIndexedPropertiesExternalArrayDataLength(); + + unsigned int new_len = args[1]->Uint32Value(); + if (new_len > kMaxLength) + return env->ThrowRangeError("truncate length is bigger than kMaxLength"); + + if (static_cast<int>(new_len) > length) + return env->ThrowRangeError("truncate length is bigger than current one"); + + obj->SetIndexedPropertiesToExternalArrayData(data, + array_type, + static_cast<int>(new_len)); +} + + + class RetainedAllocInfo: public RetainedObjectInfo { public: explicit RetainedAllocInfo(Handle<Value> wrapper); @@ -572,6 +601,7 @@ void Initialize(Handle<Object> exports, NODE_SET_METHOD(exports, "alloc", Alloc); NODE_SET_METHOD(exports, "dispose", AllocDispose); + NODE_SET_METHOD(exports, "truncate", AllocTruncate); NODE_SET_METHOD(exports, "hasExternalData", HasExternalData); diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index a13bb228f..54dd7de8c 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -1003,3 +1003,14 @@ assert.throws(function () { assert.throws(function () { new SlowBuffer(smalloc.kMaxLength + 1); }, RangeError); + +// Test truncation after decode +var crypto = require('crypto'); + +var b1 = new Buffer('YW55=======', 'base64'); +var b2 = new Buffer('YW55', 'base64'); + +assert.equal( + crypto.createHash('sha1').update(b1).digest('hex'), + crypto.createHash('sha1').update(b2).digest('hex') +); |