Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
buffer: truncate buffer after string decode
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
indutny committed Apr 10, 2014
1 parent 525fad4 commit 4c36f3e
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 1 deletion.
9 changes: 8 additions & 1 deletion lib/buffer.js
Expand Up @@ -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 = {};
Expand Down Expand Up @@ -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);
Expand Down
30 changes: 30 additions & 0 deletions src/smalloc.cc
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
11 changes: 11 additions & 0 deletions test/simple/test-buffer.js
Expand Up @@ -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')
);

0 comments on commit 4c36f3e

Please sign in to comment.