From 4c36f3e7e6108660fc3629d76036e40c1938f639 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Thu, 27 Mar 2014 21:45:15 +0400 Subject: [PATCH] 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 --- lib/buffer.js | 9 ++++++++- src/smalloc.cc | 30 ++++++++++++++++++++++++++++++ test/simple/test-buffer.js | 11 +++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/lib/buffer.js b/lib/buffer.js index ab16cd8ad7..91c13521ac 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 1e9cc3621a..7b8b3e4473 100644 --- a/src/smalloc.cc +++ b/src/smalloc.cc @@ -504,6 +504,35 @@ bool HasExternalData(Environment* env, Local obj) { } +void AllocTruncate(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(env->isolate()); + + Local obj = args[0].As(); + + // can't perform this check in JS + if (!obj->HasIndexedPropertiesInExternalArrayData()) + return env->ThrowTypeError("object has no external array data"); + + char* data = static_cast(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(new_len) > length) + return env->ThrowRangeError("truncate length is bigger than current one"); + + obj->SetIndexedPropertiesToExternalArrayData(data, + array_type, + static_cast(new_len)); +} + + + class RetainedAllocInfo: public RetainedObjectInfo { public: explicit RetainedAllocInfo(Handle wrapper); @@ -572,6 +601,7 @@ void Initialize(Handle 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 a13bb228f9..54dd7de8cf 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') +);