Browse Source

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>
v0.11.13-release
Fedor Indutny 11 years ago
committed by Fedor Indutny
parent
commit
4c36f3e7e6
  1. 9
      lib/buffer.js
  2. 30
      src/smalloc.cc
  3. 11
      test/simple/test-buffer.js

9
lib/buffer.js

@ -23,6 +23,7 @@ var buffer = process.binding('buffer');
var smalloc = process.binding('smalloc'); var smalloc = process.binding('smalloc');
var util = require('util'); var util = require('util');
var alloc = smalloc.alloc; var alloc = smalloc.alloc;
var truncate = smalloc.truncate;
var sliceOnto = smalloc.sliceOnto; var sliceOnto = smalloc.sliceOnto;
var kMaxLength = smalloc.kMaxLength; var kMaxLength = smalloc.kMaxLength;
var internal = {}; var internal = {};
@ -79,7 +80,13 @@ function Buffer(subject, encoding) {
// In the case of base64 it's possible that the size of the buffer // 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 // allocated was slightly too large. In this case we need to rewrite
// the length to the actual length written. // 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 { } else {
if (util.isBuffer(subject)) if (util.isBuffer(subject))
subject.copy(this, 0, 0, this.length); subject.copy(this, 0, 0, this.length);

30
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 { class RetainedAllocInfo: public RetainedObjectInfo {
public: public:
explicit RetainedAllocInfo(Handle<Value> wrapper); explicit RetainedAllocInfo(Handle<Value> wrapper);
@ -572,6 +601,7 @@ void Initialize(Handle<Object> exports,
NODE_SET_METHOD(exports, "alloc", Alloc); NODE_SET_METHOD(exports, "alloc", Alloc);
NODE_SET_METHOD(exports, "dispose", AllocDispose); NODE_SET_METHOD(exports, "dispose", AllocDispose);
NODE_SET_METHOD(exports, "truncate", AllocTruncate);
NODE_SET_METHOD(exports, "hasExternalData", HasExternalData); NODE_SET_METHOD(exports, "hasExternalData", HasExternalData);

11
test/simple/test-buffer.js

@ -1003,3 +1003,14 @@ assert.throws(function () {
assert.throws(function () { assert.throws(function () {
new SlowBuffer(smalloc.kMaxLength + 1); new SlowBuffer(smalloc.kMaxLength + 1);
}, RangeError); }, 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')
);

Loading…
Cancel
Save