src: redo unaligned access workaround
Introduce two-byte overloads of node::Encode() and StringBytes::Encode() that ensure that the input is suitably aligned. Revisits commit 535fec8 from yesterday.
This commit is contained in:
parent
a60056df3c
commit
56fde66c46
12
src/node.cc
12
src/node.cc
@ -1219,13 +1219,15 @@ enum encoding ParseEncoding(Isolate* isolate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Local<Value> Encode(Isolate* isolate,
|
Local<Value> Encode(Isolate* isolate,
|
||||||
const void* buf,
|
const char* buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
enum encoding encoding) {
|
enum encoding encoding) {
|
||||||
return StringBytes::Encode(isolate,
|
CHECK_NE(encoding, UCS2);
|
||||||
static_cast<const char*>(buf),
|
return StringBytes::Encode(isolate, buf, len, encoding);
|
||||||
len,
|
}
|
||||||
encoding);
|
|
||||||
|
Local<Value> Encode(Isolate* isolate, const uint16_t* buf, size_t len) {
|
||||||
|
return StringBytes::Encode(isolate, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns -1 if the handle was not valid for decoding
|
// Returns -1 if the handle was not valid for decoding
|
||||||
|
18
src/node.h
18
src/node.h
@ -142,6 +142,7 @@ NODE_EXTERN v8::Handle<v8::Value> MakeCallback(
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifndef NODE_STRINGIFY
|
#ifndef NODE_STRINGIFY
|
||||||
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
|
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
|
||||||
@ -267,16 +268,29 @@ NODE_DEPRECATED("Use FatalException(isolate, ...)",
|
|||||||
return FatalException(v8::Isolate::GetCurrent(), try_catch);
|
return FatalException(v8::Isolate::GetCurrent(), try_catch);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Don't call with encoding=UCS2.
|
||||||
NODE_EXTERN v8::Local<v8::Value> Encode(v8::Isolate* isolate,
|
NODE_EXTERN v8::Local<v8::Value> Encode(v8::Isolate* isolate,
|
||||||
const void* buf,
|
const char* buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
enum encoding encoding = BINARY);
|
enum encoding encoding = BINARY);
|
||||||
|
|
||||||
|
NODE_EXTERN v8::Local<v8::Value> Encode(v8::Isolate* isolate,
|
||||||
|
const uint16_t* buf,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
NODE_DEPRECATED("Use Encode(isolate, ...)",
|
NODE_DEPRECATED("Use Encode(isolate, ...)",
|
||||||
inline v8::Local<v8::Value> Encode(
|
inline v8::Local<v8::Value> Encode(
|
||||||
const void* buf,
|
const void* buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
enum encoding encoding = BINARY) {
|
enum encoding encoding = BINARY) {
|
||||||
return Encode(v8::Isolate::GetCurrent(), buf, len, encoding);
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
|
if (encoding == UCS2) {
|
||||||
|
assert(reinterpret_cast<uintptr_t>(buf) % sizeof(uint16_t) == 0 &&
|
||||||
|
"UCS2 buffer must be aligned on two-byte boundary.");
|
||||||
|
const uint16_t* that = static_cast<const uint16_t*>(buf);
|
||||||
|
return Encode(isolate, that, len / sizeof(*that));
|
||||||
|
}
|
||||||
|
return Encode(isolate, static_cast<const char*>(buf), len, encoding);
|
||||||
})
|
})
|
||||||
|
|
||||||
// Returns -1 if the handle was not valid for decoding
|
// Returns -1 if the handle was not valid for decoding
|
||||||
|
@ -264,6 +264,40 @@ void StringSlice(const FunctionCallbackInfo<Value>& args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void StringSlice<UCS2>(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
|
ARGS_THIS(args.This())
|
||||||
|
SLICE_START_END(args[0], args[1], obj_length)
|
||||||
|
length /= 2;
|
||||||
|
|
||||||
|
const char* data = obj_data + start;
|
||||||
|
const uint16_t* buf;
|
||||||
|
bool release = false;
|
||||||
|
|
||||||
|
if (reinterpret_cast<uintptr_t>(data) % sizeof(*buf) == 0) {
|
||||||
|
buf = reinterpret_cast<const uint16_t*>(data);
|
||||||
|
} else {
|
||||||
|
// Make a copy to avoid unaligned accesses in v8::String::NewFromTwoByte().
|
||||||
|
uint16_t* copy = new uint16_t[length];
|
||||||
|
for (size_t i = 0, k = 0; i < length; i += 1, k += 2) {
|
||||||
|
// Assumes that the input is little endian.
|
||||||
|
const uint8_t lo = static_cast<uint8_t>(data[k + 0]);
|
||||||
|
const uint8_t hi = static_cast<uint8_t>(data[k + 1]);
|
||||||
|
copy[i] = lo | hi << 8;
|
||||||
|
}
|
||||||
|
buf = copy;
|
||||||
|
release = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(StringBytes::Encode(env->isolate(), buf, length));
|
||||||
|
|
||||||
|
if (release)
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void BinarySlice(const FunctionCallbackInfo<Value>& args) {
|
void BinarySlice(const FunctionCallbackInfo<Value>& args) {
|
||||||
StringSlice<BINARY>(args);
|
StringSlice<BINARY>(args);
|
||||||
}
|
}
|
||||||
|
@ -1447,8 +1447,8 @@ void SSLWrap<Base>::GetSession(const FunctionCallbackInfo<Value>& args) {
|
|||||||
int slen = i2d_SSL_SESSION(sess, nullptr);
|
int slen = i2d_SSL_SESSION(sess, nullptr);
|
||||||
CHECK_GT(slen, 0);
|
CHECK_GT(slen, 0);
|
||||||
|
|
||||||
unsigned char* sbuf = new unsigned char[slen];
|
char* sbuf = new char[slen];
|
||||||
unsigned char* p = sbuf;
|
unsigned char* p = reinterpret_cast<unsigned char*>(sbuf);
|
||||||
i2d_SSL_SESSION(sess, &p);
|
i2d_SSL_SESSION(sess, &p);
|
||||||
args.GetReturnValue().Set(Encode(env->isolate(), sbuf, slen, BUFFER));
|
args.GetReturnValue().Set(Encode(env->isolate(), sbuf, slen, BUFFER));
|
||||||
delete[] sbuf;
|
delete[] sbuf;
|
||||||
|
@ -690,6 +690,7 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
|
|||||||
enum encoding encoding) {
|
enum encoding encoding) {
|
||||||
EscapableHandleScope scope(isolate);
|
EscapableHandleScope scope(isolate);
|
||||||
|
|
||||||
|
CHECK_NE(encoding, UCS2);
|
||||||
CHECK_LE(buflen, Buffer::kMaxLength);
|
CHECK_LE(buflen, Buffer::kMaxLength);
|
||||||
if (!buflen && encoding != BUFFER)
|
if (!buflen && encoding != BUFFER)
|
||||||
return scope.Escape(String::Empty(isolate));
|
return scope.Escape(String::Empty(isolate));
|
||||||
@ -747,32 +748,6 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case UCS2: {
|
|
||||||
// Node's "ucs2" encoding expects LE character data inside a
|
|
||||||
// Buffer, so we need to reorder on BE platforms. See
|
|
||||||
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
|
|
||||||
// encoding specification. Note that we have to make a copy
|
|
||||||
// to avoid pointer aliasing and unaligned access, something
|
|
||||||
// that we can't guarantee by just reinterpret_casting |buf|.
|
|
||||||
size_t srclen = buflen / 2;
|
|
||||||
uint16_t* src = new uint16_t[srclen];
|
|
||||||
for (size_t i = 0, k = 0; i < srclen; i += 1, k += 2) {
|
|
||||||
const uint8_t lo = static_cast<uint8_t>(buf[k + 0]);
|
|
||||||
const uint8_t hi = static_cast<uint8_t>(buf[k + 1]);
|
|
||||||
src[i] = lo | hi << 8;
|
|
||||||
}
|
|
||||||
if (buflen < EXTERN_APEX) {
|
|
||||||
val = String::NewFromTwoByte(isolate,
|
|
||||||
src,
|
|
||||||
String::kNormalString,
|
|
||||||
srclen);
|
|
||||||
} else {
|
|
||||||
val = ExternTwoByteString::NewFromCopy(isolate, src, srclen);
|
|
||||||
}
|
|
||||||
delete[] src;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case HEX: {
|
case HEX: {
|
||||||
size_t dlen = buflen * 2;
|
size_t dlen = buflen * 2;
|
||||||
char* dst = new char[dlen];
|
char* dst = new char[dlen];
|
||||||
@ -796,4 +771,39 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
|
|||||||
return scope.Escape(val);
|
return scope.Escape(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Local<Value> StringBytes::Encode(Isolate* isolate,
|
||||||
|
const uint16_t* buf,
|
||||||
|
size_t buflen) {
|
||||||
|
const uint16_t* src = buf;
|
||||||
|
|
||||||
|
// Node's "ucs2" encoding expects LE character data inside a
|
||||||
|
// Buffer, so we need to reorder on BE platforms. See
|
||||||
|
// http://nodejs.org/api/buffer.html regarding Node's "ucs2"
|
||||||
|
// encoding specification.
|
||||||
|
if (IsBigEndian()) {
|
||||||
|
// Inefficient, see StringSlice<UCS2>() in src/node_buffer.cc;
|
||||||
|
// this is potentially the second copy of the actual input.
|
||||||
|
uint16_t* copy = new uint16_t[buflen];
|
||||||
|
for (size_t i = 0; i < buflen; i += 1)
|
||||||
|
copy[i] = buf[i] << 8 | buf[i] >> 8;
|
||||||
|
src = copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<String> val;
|
||||||
|
if (buflen < EXTERN_APEX) {
|
||||||
|
val = String::NewFromTwoByte(isolate,
|
||||||
|
src,
|
||||||
|
String::kNormalString,
|
||||||
|
buflen);
|
||||||
|
} else {
|
||||||
|
val = ExternTwoByteString::NewFromCopy(isolate, src, buflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src != buf)
|
||||||
|
delete[] src;
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@ -71,11 +71,16 @@ class StringBytes {
|
|||||||
int* chars_written = nullptr);
|
int* chars_written = nullptr);
|
||||||
|
|
||||||
// Take the bytes in the src, and turn it into a Buffer or String.
|
// Take the bytes in the src, and turn it into a Buffer or String.
|
||||||
|
// Don't call with encoding=UCS2.
|
||||||
static v8::Local<v8::Value> Encode(v8::Isolate* isolate,
|
static v8::Local<v8::Value> Encode(v8::Isolate* isolate,
|
||||||
const char* buf,
|
const char* buf,
|
||||||
size_t buflen,
|
size_t buflen,
|
||||||
enum encoding encoding);
|
enum encoding encoding);
|
||||||
|
|
||||||
|
static v8::Local<v8::Value> Encode(v8::Isolate* isolate,
|
||||||
|
const uint16_t* buf,
|
||||||
|
size_t buflen);
|
||||||
|
|
||||||
// Deprecated legacy interface
|
// Deprecated legacy interface
|
||||||
|
|
||||||
NODE_DEPRECATED("Use IsValidString(isolate, ...)",
|
NODE_DEPRECATED("Use IsValidString(isolate, ...)",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user