summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFedor Indutny <fedor.indutny@gmail.com>2014-03-27 21:45:15 +0400
committerFedor Indutny <fedor@indutny.com>2014-04-11 01:20:43 +0400
commit4c36f3e7e6108660fc3629d76036e40c1938f639 (patch)
tree094b5214eeae8c230550402a4fc5da821ec8e52c
parent525fad473bfafc2f44e1ab591152ca338fe45f2b (diff)
downloadnode-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.js9
-rw-r--r--src/smalloc.cc30
-rw-r--r--test/simple/test-buffer.js11
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')
+);