Skip to content

Commit 4fb9b12

Browse files
TimothyGujasnell
authored andcommitted
src, buffer: do not segfault on out-of-range index
Also add test cases for partial writes and invalid indices. PR-URL: #11927 Fixes: #8724 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 1d82adc commit 4fb9b12

File tree

2 files changed

+75
-16
lines changed

2 files changed

+75
-16
lines changed

src/node_buffer.cc

+20-8
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ void CallbackInfo::WeakCallback(Isolate* isolate) {
169169
// Parse index for external array data.
170170
inline MUST_USE_RESULT bool ParseArrayIndex(Local<Value> arg,
171171
size_t def,
172-
size_t* ret) {
172+
size_t* ret,
173+
size_t needed = 0) {
173174
if (arg->IsUndefined()) {
174175
*ret = def;
175176
return true;
@@ -183,7 +184,7 @@ inline MUST_USE_RESULT bool ParseArrayIndex(Local<Value> arg,
183184
// Check that the result fits in a size_t.
184185
const uint64_t kSizeMax = static_cast<uint64_t>(static_cast<size_t>(-1));
185186
// coverity[pointless_expression]
186-
if (static_cast<uint64_t>(tmp_i) > kSizeMax)
187+
if (static_cast<uint64_t>(tmp_i) > kSizeMax - needed)
187188
return false;
188189

189190
*ret = static_cast<size_t>(tmp_i);
@@ -815,17 +816,28 @@ void WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
815816
CHECK_NE(ts_obj_data, nullptr);
816817

817818
T val = args[1]->NumberValue(env->context()).FromMaybe(0);
818-
size_t offset = args[2]->IntegerValue(env->context()).FromMaybe(0);
819819

820820
size_t memcpy_num = sizeof(T);
821+
size_t offset;
821822

822-
if (should_assert) {
823-
THROW_AND_RETURN_IF_OOB(offset + memcpy_num >= memcpy_num);
824-
THROW_AND_RETURN_IF_OOB(offset + memcpy_num <= ts_obj_length);
823+
// If the offset is negative or larger than the size of the ArrayBuffer,
824+
// throw an error (if needed) and return directly.
825+
if (!ParseArrayIndex(args[2], 0, &offset, memcpy_num) ||
826+
offset >= ts_obj_length) {
827+
if (should_assert)
828+
THROW_AND_RETURN_IF_OOB(false);
829+
return;
825830
}
826831

827-
if (offset + memcpy_num > ts_obj_length)
828-
memcpy_num = ts_obj_length - offset;
832+
// If the offset is too large for the entire value, but small enough to fit
833+
// part of the value, throw an error and return only if should_assert is
834+
// true. Otherwise, write the part of the value that fits.
835+
if (offset + memcpy_num > ts_obj_length) {
836+
if (should_assert)
837+
THROW_AND_RETURN_IF_OOB(false);
838+
else
839+
memcpy_num = ts_obj_length - offset;
840+
}
829841

830842
union NoAlias {
831843
T val;

test/parallel/test-buffer-write-noassert.js

+55-8
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,17 @@ const assert = require('assert');
44

55
// testing buffer write functions
66

7+
const outOfRange = /^RangeError: (?:Index )?out of range(?: index)?$/;
8+
79
function write(funx, args, result, res) {
810
{
911
const buf = Buffer.alloc(9);
1012
assert.strictEqual(buf[funx](...args), result);
1113
assert.deepStrictEqual(buf, res);
1214
}
1315

14-
{
15-
const invalidArgs = Array.from(args);
16-
invalidArgs[1] = -1;
17-
assert.throws(
18-
() => Buffer.alloc(9)[funx](...invalidArgs),
19-
/^RangeError: (?:Index )?out of range(?: index)?$/
20-
);
21-
}
16+
writeInvalidOffset(-1);
17+
writeInvalidOffset(9);
2218

2319
if (!/Int/.test(funx)) {
2420
assert.throws(
@@ -33,6 +29,15 @@ function write(funx, args, result, res) {
3329
assert.deepStrictEqual(buf2, res);
3430
}
3531

32+
function writeInvalidOffset(offset) {
33+
const newArgs = Array.from(args);
34+
newArgs[1] = offset;
35+
assert.throws(() => Buffer.alloc(9)[funx](...newArgs), outOfRange);
36+
37+
const buf = Buffer.alloc(9);
38+
buf[funx](...newArgs, true);
39+
assert.deepStrictEqual(buf, Buffer.alloc(9));
40+
}
3641
}
3742

3843
write('writeInt8', [1, 0], 1, Buffer.from([1, 0, 0, 0, 0, 0, 0, 0, 0]));
@@ -53,3 +58,45 @@ write('writeDoubleBE', [1, 1], 9, Buffer.from([0, 63, 240, 0, 0, 0, 0, 0, 0]));
5358
write('writeDoubleLE', [1, 1], 9, Buffer.from([0, 0, 0, 0, 0, 0, 0, 240, 63]));
5459
write('writeFloatBE', [1, 1], 5, Buffer.from([0, 63, 128, 0, 0, 0, 0, 0, 0]));
5560
write('writeFloatLE', [1, 1], 5, Buffer.from([0, 0, 0, 128, 63, 0, 0, 0, 0]));
61+
62+
function writePartial(funx, args, result, res) {
63+
assert.throws(() => Buffer.alloc(9)[funx](...args), outOfRange);
64+
const buf = Buffer.alloc(9);
65+
assert.strictEqual(buf[funx](...args, true), result);
66+
assert.deepStrictEqual(buf, res);
67+
}
68+
69+
// Test partial writes (cases where the buffer isn't large enough to hold the
70+
// entire value, but is large enough to hold parts of it).
71+
writePartial('writeIntBE', [0x0eadbeef, 6, 4], 10,
72+
Buffer.from([0, 0, 0, 0, 0, 0, 0x0e, 0xad, 0xbe]));
73+
writePartial('writeIntLE', [0x0eadbeef, 6, 4], 10,
74+
Buffer.from([0, 0, 0, 0, 0, 0, 0xef, 0xbe, 0xad]));
75+
writePartial('writeInt16BE', [0x1234, 8], 10,
76+
Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0x12]));
77+
writePartial('writeInt16LE', [0x1234, 8], 10,
78+
Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0x34]));
79+
writePartial('writeInt32BE', [0x0eadbeef, 6], 10,
80+
Buffer.from([0, 0, 0, 0, 0, 0, 0x0e, 0xad, 0xbe]));
81+
writePartial('writeInt32LE', [0x0eadbeef, 6], 10,
82+
Buffer.from([0, 0, 0, 0, 0, 0, 0xef, 0xbe, 0xad]));
83+
writePartial('writeUIntBE', [0xdeadbeef, 6, 4], 10,
84+
Buffer.from([0, 0, 0, 0, 0, 0, 0xde, 0xad, 0xbe]));
85+
writePartial('writeUIntLE', [0xdeadbeef, 6, 4], 10,
86+
Buffer.from([0, 0, 0, 0, 0, 0, 0xef, 0xbe, 0xad]));
87+
writePartial('writeUInt16BE', [0x1234, 8], 10,
88+
Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0x12]));
89+
writePartial('writeUInt16LE', [0x1234, 8], 10,
90+
Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0x34]));
91+
writePartial('writeUInt32BE', [0xdeadbeef, 6], 10,
92+
Buffer.from([0, 0, 0, 0, 0, 0, 0xde, 0xad, 0xbe]));
93+
writePartial('writeUInt32LE', [0xdeadbeef, 6], 10,
94+
Buffer.from([0, 0, 0, 0, 0, 0, 0xef, 0xbe, 0xad]));
95+
writePartial('writeDoubleBE', [1, 2], 10,
96+
Buffer.from([0, 0, 63, 240, 0, 0, 0, 0, 0]));
97+
writePartial('writeDoubleLE', [1, 2], 10,
98+
Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 240]));
99+
writePartial('writeFloatBE', [1, 6], 10,
100+
Buffer.from([0, 0, 0, 0, 0, 0, 63, 128, 0]));
101+
writePartial('writeFloatLE', [1, 6], 10,
102+
Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 128]));

0 commit comments

Comments
 (0)