Skip to content

Commit 2b3e2a2

Browse files
committed
src: check bounds when reading floating point
Check bounds and throw an exception when reading floating point values from a buffer. Before this commit, the process would fail a CHECK and terminate when `noAssert=true`. Fixes: nodejs#8724
1 parent 8d8f4dd commit 2b3e2a2

File tree

2 files changed

+80
-3
lines changed

2 files changed

+80
-3
lines changed

src/node_buffer.cc

+8-3
Original file line numberDiff line numberDiff line change
@@ -718,11 +718,16 @@ static inline void Swizzle(char* start, unsigned int len) {
718718

719719
template <typename T, enum Endianness endianness>
720720
void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
721-
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
721+
auto env = Environment::GetCurrent(args);
722+
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
722723
SPREAD_BUFFER_ARG(args[0], ts_obj);
723724

724-
uint32_t offset = args[1]->Uint32Value();
725-
CHECK_LE(offset + sizeof(T), ts_obj_length);
725+
uint32_t offset;
726+
if (!args[1]->Uint32Value(env->context()).To(&offset))
727+
return; // Exception pending.
728+
729+
if (ts_obj_length < Add32Clamp(offset, sizeof(T)))
730+
return env->ThrowRangeError("Index out of range");
726731

727732
union NoAlias {
728733
T val;

test/parallel/test-buffer-read.js

+72
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,75 @@ assert.throws(() => Buffer.allocUnsafe(8).readFloatLE(-1), RangeError);
140140
assert.strictEqual(buf.readIntLE(0, 6), 0x060504030201);
141141
assert.strictEqual(buf.readIntBE(0, 6), 0x010203040506);
142142
}
143+
144+
// https://github.com/nodejs/node/issues/8724 - specific to methods dealing
145+
// with floating point types because they call out to C++ code.
146+
{
147+
// ERR_INDEX_OUT_OF_RANGE is optional, exceptions from the binding layer
148+
// don't have it.
149+
const re = /^RangeError( \[ERR_INDEX_OUT_OF_RANGE\])?: Index out of range$/;
150+
const buf = Buffer.alloc(0);
151+
assert.throws(() => buf.readFloatBE(0), re);
152+
assert.throws(() => buf.readFloatLE(0), re);
153+
assert.throws(() => buf.readDoubleBE(0), re);
154+
assert.throws(() => buf.readDoubleLE(0), re);
155+
for (const noAssert of [true, false]) {
156+
assert.throws(() => buf.readFloatBE(0, noAssert), re);
157+
assert.throws(() => buf.readFloatLE(0, noAssert), re);
158+
assert.throws(() => buf.readDoubleBE(0, noAssert), re);
159+
assert.throws(() => buf.readDoubleLE(0, noAssert), re);
160+
}
161+
}
162+
163+
{
164+
const { readFloatBE, readFloatLE, readDoubleBE, readDoubleLE } =
165+
Buffer.prototype;
166+
const re = /^TypeError: argument should be a Buffer$/;
167+
assert.throws(() => readFloatBE(0, true), re);
168+
assert.throws(() => readFloatLE(0, true), re);
169+
assert.throws(() => readDoubleBE(0, true), re);
170+
assert.throws(() => readDoubleLE(0, true), re);
171+
}
172+
173+
{
174+
const { readFloatBE, readFloatLE, readDoubleBE, readDoubleLE } =
175+
Buffer.prototype;
176+
const re = /^TypeError: Cannot read property 'length' of undefined$/;
177+
assert.throws(() => readFloatBE(0), re);
178+
assert.throws(() => readFloatLE(0), re);
179+
assert.throws(() => readDoubleBE(0), re);
180+
assert.throws(() => readDoubleLE(0), re);
181+
assert.throws(() => readFloatBE(0, false), re);
182+
assert.throws(() => readFloatLE(0, false), re);
183+
assert.throws(() => readDoubleBE(0, false), re);
184+
assert.throws(() => readDoubleLE(0, false), re);
185+
}
186+
187+
{
188+
const re =
189+
new RegExp('^TypeError: Method get TypedArray\\.prototype\\.length ' +
190+
'called on incompatible receiver \\[object Object\\]$');
191+
assert.throws(() => Buffer.prototype.readFloatBE(0), re);
192+
assert.throws(() => Buffer.prototype.readFloatLE(0), re);
193+
assert.throws(() => Buffer.prototype.readDoubleBE(0), re);
194+
assert.throws(() => Buffer.prototype.readDoubleLE(0), re);
195+
assert.throws(() => Buffer.prototype.readFloatBE(0, false), re);
196+
assert.throws(() => Buffer.prototype.readFloatLE(0, false), re);
197+
assert.throws(() => Buffer.prototype.readDoubleBE(0, false), re);
198+
assert.throws(() => Buffer.prototype.readDoubleLE(0, false), re);
199+
}
200+
201+
for (const noAssert of [true, false]) {
202+
const re = /^TypeError: argument should be a Buffer$/;
203+
// Wrong receiver.
204+
assert.throws(() => Buffer.prototype.readFloatBE.call({}, 0, noAssert), re);
205+
assert.throws(() => Buffer.prototype.readFloatLE.call({}, 0, noAssert), re);
206+
assert.throws(() => Buffer.prototype.readDoubleBE.call({}, 0, noAssert), re);
207+
assert.throws(() => Buffer.prototype.readDoubleLE.call({}, 0, noAssert), re);
208+
if (noAssert === false) continue; // noAssert=false is tested above.
209+
// Non-method call.
210+
assert.throws(() => Buffer.prototype.readFloatBE(0, noAssert), re);
211+
assert.throws(() => Buffer.prototype.readFloatLE(0, noAssert), re);
212+
assert.throws(() => Buffer.prototype.readDoubleBE(0, noAssert), re);
213+
assert.throws(() => Buffer.prototype.readDoubleLE(0, noAssert), re);
214+
}

0 commit comments

Comments
 (0)