diff --git a/lib/utils.js b/lib/utils.js index 45c2cd2b..f4433261 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -270,6 +270,9 @@ function BufferPool(len) { } BufferPool.prototype.alloc = function (len) { + if (len < 0) { + throw new Error('negative length'); + } var maxLen = this._len; if (len > maxLen) { return newBuffer(len); @@ -448,6 +451,8 @@ function Tap(buf, pos) { */ Tap.prototype.isValid = function () { return this.pos <= this.buf.length; }; +Tap.prototype._invalidate = function () { this.pos = this.buf.length + 1; }; + // Read, skip, write methods. // // These should fail silently when the buffer overflows. Note this is only @@ -522,7 +527,7 @@ Tap.prototype.readFloat = function () { var pos = this.pos; this.pos += 4; if (this.pos > buf.length) { - return; + return 0; } return this.buf.readFloatLE(pos); }; @@ -544,7 +549,7 @@ Tap.prototype.readDouble = function () { var pos = this.pos; this.pos += 8; if (this.pos > buf.length) { - return; + return 0; } return this.buf.readDoubleLE(pos); }; @@ -585,11 +590,20 @@ Tap.prototype.writeFixed = function (buf, len) { }; Tap.prototype.readBytes = function () { - return this.readFixed(this.readLong()); + var len = this.readLong(); + if (len < 0) { + this._invalidate(); + return; + } + return this.readFixed(len); }; Tap.prototype.skipBytes = function () { var len = this.readLong(); + if (len < 0) { + this._invalidate(); + return; + } this.pos += len; }; @@ -604,6 +618,10 @@ if (typeof Buffer.prototype.utf8Slice == 'function') { // Use this optimized function when available. Tap.prototype.readString = function () { var len = this.readLong(); + if (len < 0) { + this._invalidate(); + return ''; + } var pos = this.pos; var buf = this.buf; this.pos += len; @@ -615,6 +633,10 @@ if (typeof Buffer.prototype.utf8Slice == 'function') { } else { Tap.prototype.readString = function () { var len = this.readLong(); + if (len < 0) { + this._invalidate(); + return ''; + } var pos = this.pos; var buf = this.buf; this.pos += len; @@ -627,6 +649,10 @@ if (typeof Buffer.prototype.utf8Slice == 'function') { Tap.prototype.skipString = function () { var len = this.readLong(); + if (len < 0) { + this._invalidate(); + return; + } this.pos += len; }; @@ -872,6 +898,7 @@ module.exports = { toMap: toMap, singleIndexOf: singleIndexOf, hasDuplicates: hasDuplicates, + BufferPool: BufferPool, Lcg: Lcg, OrderedQueue: OrderedQueue, Tap: Tap diff --git a/package.json b/package.json index c4f590b9..6a8a3639 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "avsc", - "version": "5.4.14", + "version": "5.4.15", "description": "Avro for JavaScript", "homepage": "https://github.com/mtth/avsc", "keywords": [ diff --git a/test/test_utils.js b/test/test_utils.js index 325ff857..528e63b0 100644 --- a/test/test_utils.js +++ b/test/test_utils.js @@ -143,6 +143,23 @@ suite('utils', function () { }); + suite('Tap', function () { + + var BufferPool = utils.BufferPool; + + test('alloc negative length', function () { + var pool = new BufferPool(16); + assert.throws(function () { pool.alloc(-1); }); + }); + + test('alloc beyond pool size', function () { + var pool = new BufferPool(4); + assert.equal(pool.alloc(3).length, 3); + assert.equal(pool.alloc(2).length, 2); + }); + + }); + suite('Tap', function () { var Tap = utils.Tap;