Skip to content

Commit b13f21c

Browse files
committed
src: fix out-of-bounds write when transcoding odd-length ucs2
Signed-off-by: Nashit-h <nashit@bugqore.com>
1 parent 7a11a9b commit b13f21c

2 files changed

Lines changed: 20 additions & 2 deletions

File tree

src/node_i18n.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,13 @@ MaybeLocal<Object> ToBufferEndian(Environment* env, MaybeStackBuffer<T>* buf) {
123123

124124
void CopySourceBuffer(MaybeStackBuffer<UChar>* dest,
125125
const char* data,
126-
const size_t length,
127126
const size_t length_in_chars) {
128127
dest->AllocateSufficientStorage(length_in_chars);
129128
char* dst = reinterpret_cast<char*>(**dest);
129+
// The destination holds length_in_chars UChar units. Copy that many whole
130+
// units and ignore a trailing odd byte; copying the raw byte length would
131+
// write one byte past the buffer when the source length is not even.
132+
const size_t length = length_in_chars * sizeof(UChar);
130133
memcpy(dst, data, length);
131134
if constexpr (IsBigEndian()) {
132135
CHECK(nbytes::SwapBytes16(dst, length));
@@ -199,7 +202,7 @@ MaybeLocal<Object> TranscodeFromUcs2(Environment* env,
199202
to.set_subst_chars(sub.c_str());
200203

201204
const size_t length_in_chars = source_length / sizeof(UChar);
202-
CopySourceBuffer(&sourcebuf, source, source_length, length_in_chars);
205+
CopySourceBuffer(&sourcebuf, source, length_in_chars);
203206
MaybeStackBuffer<char> destbuf(length_in_chars);
204207
const uint32_t len = ucnv_fromUChars(to.conv(), *destbuf, length_in_chars,
205208
*sourcebuf, length_in_chars, status);

test/parallel/test-icu-transcode.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,18 @@ assert.deepStrictEqual(
8888
{
8989
buffer.transcode(new buffer.Buffer.allocUnsafeSlow(1), 'utf16le', 'ucs2');
9090
}
91+
92+
// An odd-length ucs2 source must only convert whole 2-byte code units and
93+
// leave the trailing byte untouched, without reading or writing past the
94+
// conversion buffer. Lengths are chosen to exercise both the on-stack and the
95+
// heap-allocated code paths.
96+
for (const len of [2049, 4099]) {
97+
const src = Buffer.alloc(len, 0x61);
98+
const wholeUnits = src.subarray(0, len - 1);
99+
for (const to of ['latin1', 'ascii']) {
100+
assert.deepStrictEqual(
101+
buffer.transcode(src, 'utf16le', to),
102+
buffer.transcode(wholeUnits, 'utf16le', to),
103+
`ucs2->${to} odd length ${len}`);
104+
}
105+
}

0 commit comments

Comments
 (0)