Skip to content

Commit c1ce423

Browse files
indutnyrvagg
authored andcommitted
string_bytes: fix unaligned write in UCS2
Support unaligned output buffer when writing out UCS2 in `StringBytes::Write`. Fix: #2457 PR-URL: #2480 Reviewed-By: Trevor Norris <[email protected]>
1 parent 56a2ae9 commit c1ce423

File tree

2 files changed

+75
-12
lines changed

2 files changed

+75
-12
lines changed

src/string_bytes.cc

+66-12
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,46 @@ bool StringBytes::GetExternalParts(Isolate* isolate,
293293
}
294294

295295

296+
size_t StringBytes::WriteUCS2(char* buf,
297+
size_t buflen,
298+
size_t nbytes,
299+
const char* data,
300+
Local<String> str,
301+
int flags,
302+
size_t* chars_written) {
303+
uint16_t* const dst = reinterpret_cast<uint16_t*>(buf);
304+
305+
size_t max_chars = (buflen / sizeof(*dst));
306+
size_t nchars;
307+
size_t alignment = reinterpret_cast<uintptr_t>(dst) % sizeof(*dst);
308+
if (alignment == 0) {
309+
nchars = str->Write(dst, 0, max_chars, flags);
310+
*chars_written = nchars;
311+
return nchars * sizeof(*dst);
312+
}
313+
314+
uint16_t* aligned_dst =
315+
reinterpret_cast<uint16_t*>(buf + sizeof(*dst) - alignment);
316+
ASSERT_EQ(reinterpret_cast<uintptr_t>(aligned_dst) % sizeof(*dst), 0);
317+
318+
// Write all but the last char
319+
nchars = str->Write(aligned_dst, 0, max_chars - 1, flags);
320+
321+
// Shift everything to unaligned-left
322+
memmove(dst, aligned_dst, nchars * sizeof(*dst));
323+
324+
// One more char to be written
325+
uint16_t last;
326+
if (nchars == max_chars - 1 && str->Write(&last, nchars, 1, flags) != 0) {
327+
memcpy(buf + nchars * sizeof(*dst), &last, sizeof(last));
328+
nchars++;
329+
}
330+
331+
*chars_written = nchars;
332+
return nchars * sizeof(*dst);
333+
}
334+
335+
296336
size_t StringBytes::Write(Isolate* isolate,
297337
char* buf,
298338
size_t buflen,
@@ -334,26 +374,40 @@ size_t StringBytes::Write(Isolate* isolate,
334374
break;
335375

336376
case UCS2: {
337-
uint16_t* const dst = reinterpret_cast<uint16_t*>(buf);
338377
size_t nchars;
378+
339379
if (is_extern && !str->IsOneByte()) {
340380
memcpy(buf, data, nbytes);
341-
nchars = nbytes / sizeof(*dst);
381+
nchars = nbytes / sizeof(uint16_t);
342382
} else {
343-
nchars = buflen / sizeof(*dst);
344-
nchars = str->Write(dst, 0, nchars, flags);
345-
nbytes = nchars * sizeof(*dst);
383+
nbytes = WriteUCS2(buf, buflen, nbytes, data, str, flags, &nchars);
346384
}
347-
if (IsBigEndian()) {
348-
// Node's "ucs2" encoding wants LE character data stored in
349-
// the Buffer, so we need to reorder on BE platforms. See
350-
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
351-
// encoding specification
385+
if (chars_written != nullptr)
386+
*chars_written = nchars;
387+
388+
if (!IsBigEndian())
389+
break;
390+
391+
// Node's "ucs2" encoding wants LE character data stored in
392+
// the Buffer, so we need to reorder on BE platforms. See
393+
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
394+
// encoding specification
395+
396+
const bool is_aligned =
397+
reinterpret_cast<uintptr_t>(buf) % sizeof(uint16_t);
398+
if (is_aligned) {
399+
uint16_t* const dst = reinterpret_cast<uint16_t*>(buf);
352400
for (size_t i = 0; i < nchars; i++)
353401
dst[i] = dst[i] << 8 | dst[i] >> 8;
402+
break;
403+
}
404+
405+
ASSERT_EQ(sizeof(uint16_t), 2);
406+
for (size_t i = 0; i < nchars; i++) {
407+
char tmp = buf[i * 2];
408+
buf[i * 2] = buf[i * 2 + 1];
409+
buf[i * 2 + 1] = tmp;
354410
}
355-
if (chars_written != nullptr)
356-
*chars_written = nchars;
357411
break;
358412
}
359413

src/string_bytes.h

+9
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ class StringBytes {
151151
enum encoding encoding) {
152152
return Encode(v8::Isolate::GetCurrent(), buf, buflen, encoding);
153153
})
154+
155+
private:
156+
static size_t WriteUCS2(char* buf,
157+
size_t buflen,
158+
size_t nbytes,
159+
const char* data,
160+
v8::Local<v8::String> str,
161+
int flags,
162+
size_t* chars_written);
154163
};
155164

156165
} // namespace node

0 commit comments

Comments
 (0)