support_v8_sandboxed_pointers.patch 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
  2. From: Jeremy Rose <[email protected]>
  3. Date: Tue, 21 Jun 2022 10:04:21 -0700
  4. Subject: support V8 sandboxed pointers
  5. This refactors several allocators to allocate within the V8 memory cage,
  6. allowing them to be compatible with the V8_SANDBOXED_POINTERS feature.
  7. diff --git a/src/api/environment.cc b/src/api/environment.cc
  8. index 5fc1b6f2446d7c786024eb60800e2edab613dcd1..f59abcb21d64b910d8d42eb23c03109f62558813 100644
  9. --- a/src/api/environment.cc
  10. +++ b/src/api/environment.cc
  11. @@ -101,6 +101,14 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
  12. return result;
  13. }
  14. +NodeArrayBufferAllocator::NodeArrayBufferAllocator() {
  15. + zero_fill_field_ = static_cast<uint32_t*>(allocator_->Allocate(sizeof(*zero_fill_field_)));
  16. +}
  17. +
  18. +NodeArrayBufferAllocator::~NodeArrayBufferAllocator() {
  19. + allocator_->Free(zero_fill_field_, sizeof(*zero_fill_field_));
  20. +}
  21. +
  22. void* NodeArrayBufferAllocator::Allocate(size_t size) {
  23. void* ret;
  24. if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
  25. diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc
  26. index 33ffbbb85d05f5356183e3aa1ca23707c5629b5d..008d212ebe25b0022020379aa08963c12828940c 100644
  27. --- a/src/crypto/crypto_dh.cc
  28. +++ b/src/crypto/crypto_dh.cc
  29. @@ -51,6 +51,25 @@ void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
  30. namespace {
  31. MaybeLocal<Value> DataPointerToBuffer(Environment* env,
  32. ncrypto::DataPointer&& data) {
  33. +#if defined(V8_ENABLE_SANDBOX)
  34. + std::unique_ptr<v8::BackingStore> backing;
  35. + if (data.size() > 0) {
  36. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  37. + void* v8_data = allocator->Allocate(data.size());
  38. + CHECK(v8_data);
  39. + memcpy(v8_data, data.get(), data.size());
  40. + backing = ArrayBuffer::NewBackingStore(
  41. + v8_data,
  42. + data.size(),
  43. + [](void* data, size_t length, void*) {
  44. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  45. + allocator->Free(data, length);
  46. + }, nullptr);
  47. + } else {
  48. + NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
  49. + backing = v8::ArrayBuffer::NewBackingStore(env->isolate(), data.size());
  50. + }
  51. +#else
  52. auto backing = ArrayBuffer::NewBackingStore(
  53. data.get(),
  54. data.size(),
  55. @@ -59,6 +78,7 @@ MaybeLocal<Value> DataPointerToBuffer(Environment* env,
  56. },
  57. nullptr);
  58. data.release();
  59. +#endif
  60. auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
  61. return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Value>());
  62. diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
  63. index ee81048caab4ccfe26ea9e677782c9c955d162a9..643c9d31dc2737faa2ec51684ffceb35a1014a58 100644
  64. --- a/src/crypto/crypto_util.cc
  65. +++ b/src/crypto/crypto_util.cc
  66. @@ -326,10 +326,35 @@ ByteSource& ByteSource::operator=(ByteSource&& other) noexcept {
  67. return *this;
  68. }
  69. -std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore() {
  70. +std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore(Environment* env) {
  71. // It's ok for allocated_data_ to be nullptr but
  72. // only if size_ is zero.
  73. CHECK_IMPLIES(size_ > 0, allocated_data_ != nullptr);
  74. +#if defined(V8_ENABLE_SANDBOX)
  75. + // When V8 sandboxed pointers are enabled, we have to copy into the memory
  76. + // cage. We still want to ensure we erase the data on free though, so
  77. + // provide a custom deleter that calls OPENSSL_cleanse.
  78. + if (!size())
  79. + return ArrayBuffer::NewBackingStore(env->isolate(), 0);
  80. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  81. + void* v8_data = allocator->Allocate(size());
  82. + CHECK(v8_data);
  83. + memcpy(v8_data, allocated_data_, size());
  84. + OPENSSL_clear_free(allocated_data_, size());
  85. + std::unique_ptr<BackingStore> ptr = ArrayBuffer::NewBackingStore(
  86. + v8_data,
  87. + size(),
  88. + [](void* data, size_t length, void*) {
  89. + OPENSSL_cleanse(data, length);
  90. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  91. + allocator->Free(data, length);
  92. + }, nullptr);
  93. + CHECK(ptr);
  94. + allocated_data_ = nullptr;
  95. + data_ = nullptr;
  96. + size_ = 0;
  97. + return ptr;
  98. +#else
  99. std::unique_ptr<BackingStore> ptr = ArrayBuffer::NewBackingStore(
  100. allocated_data_,
  101. size(),
  102. @@ -341,10 +366,11 @@ std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore() {
  103. data_ = nullptr;
  104. size_ = 0;
  105. return ptr;
  106. +#endif // defined(V8_ENABLE_SANDBOX)
  107. }
  108. Local<ArrayBuffer> ByteSource::ToArrayBuffer(Environment* env) {
  109. - std::unique_ptr<BackingStore> store = ReleaseToBackingStore();
  110. + std::unique_ptr<BackingStore> store = ReleaseToBackingStore(env);
  111. return ArrayBuffer::New(env->isolate(), std::move(store));
  112. }
  113. @@ -641,6 +667,16 @@ namespace {
  114. // in which case this has the same semantics as
  115. // using OPENSSL_malloc. However, if the secure heap is
  116. // initialized, SecureBuffer will automatically use it.
  117. +#if defined(V8_ENABLE_SANDBOX)
  118. +// When V8 sandboxed pointers are enabled, the secure heap cannot be used as
  119. +// all ArrayBuffers must be allocated inside the V8 memory cage.
  120. +void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
  121. + CHECK(args[0]->IsUint32());
  122. + uint32_t len = args[0].As<Uint32>()->Value();
  123. + Local<ArrayBuffer> buffer = ArrayBuffer::New(args.GetIsolate(), len);
  124. + args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len));
  125. +}
  126. +#else
  127. void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
  128. CHECK(args[0]->IsUint32());
  129. Environment* env = Environment::GetCurrent(args);
  130. @@ -662,6 +698,7 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
  131. Local<ArrayBuffer> buffer = ArrayBuffer::New(env->isolate(), store);
  132. args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len));
  133. }
  134. +#endif // defined(V8_ENABLE_SANDBOX)
  135. void SecureHeapUsed(const FunctionCallbackInfo<Value>& args) {
  136. #ifndef OPENSSL_IS_BORINGSSL
  137. diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h
  138. index 922e77091d72172ed6cfc5e8477901e3608396c5..16a236c69caed6d60248c7973531a95adc8f2edb 100644
  139. --- a/src/crypto/crypto_util.h
  140. +++ b/src/crypto/crypto_util.h
  141. @@ -268,7 +268,7 @@ class ByteSource {
  142. // Creates a v8::BackingStore that takes over responsibility for
  143. // any allocated data. The ByteSource will be reset with size = 0
  144. // after being called.
  145. - std::unique_ptr<v8::BackingStore> ReleaseToBackingStore();
  146. + std::unique_ptr<v8::BackingStore> ReleaseToBackingStore(Environment* env);
  147. v8::Local<v8::ArrayBuffer> ToArrayBuffer(Environment* env);
  148. diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc
  149. index af2f953f0388dbce326b0c519de3883552f8f009..7fb34057a486914dd886ec4d3d23be90cccb4fea 100644
  150. --- a/src/crypto/crypto_x509.cc
  151. +++ b/src/crypto/crypto_x509.cc
  152. @@ -174,6 +174,19 @@ MaybeLocal<Value> ToV8Value(Local<Context> context, const BIOPointer& bio) {
  153. MaybeLocal<Value> ToBuffer(Environment* env, BIOPointer* bio) {
  154. if (bio == nullptr || !*bio) return {};
  155. BUF_MEM* mem = *bio;
  156. +#if defined(V8_ENABLE_SANDBOX)
  157. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  158. + void* v8_data = allocator->Allocate(mem->length);
  159. + CHECK(v8_data);
  160. + memcpy(v8_data, mem->data, mem->length);
  161. + std::unique_ptr<v8::BackingStore> backing = ArrayBuffer::NewBackingStore(
  162. + v8_data,
  163. + mem->length,
  164. + [](void* data, size_t length, void*) {
  165. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  166. + allocator->Free(data, length);
  167. + }, nullptr);
  168. +#else
  169. auto backing = ArrayBuffer::NewBackingStore(
  170. mem->data,
  171. mem->length,
  172. @@ -181,6 +194,8 @@ MaybeLocal<Value> ToBuffer(Environment* env, BIOPointer* bio) {
  173. BIOPointer free_me(static_cast<BIO*>(data));
  174. },
  175. bio->release());
  176. +#endif
  177. +
  178. auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
  179. Local<Value> ret;
  180. if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&ret)) return {};
  181. diff --git a/src/node_i18n.cc b/src/node_i18n.cc
  182. index 43bb68351bf0a68285e464601013bbdbd5d5df5f..4126bbff080548c91154a6dcc437359c82a6c771 100644
  183. --- a/src/node_i18n.cc
  184. +++ b/src/node_i18n.cc
  185. @@ -107,7 +107,7 @@ namespace {
  186. template <typename T>
  187. MaybeLocal<Object> ToBufferEndian(Environment* env, MaybeStackBuffer<T>* buf) {
  188. - MaybeLocal<Object> ret = Buffer::New(env, buf);
  189. + MaybeLocal<Object> ret = Buffer::Copy(env, reinterpret_cast<char*>(buf->out()), buf->length() * sizeof(T));
  190. if (ret.IsEmpty())
  191. return ret;
  192. @@ -184,7 +184,7 @@ MaybeLocal<Object> TranscodeLatin1ToUcs2(Environment* env,
  193. return {};
  194. }
  195. - return Buffer::New(env, &destbuf);
  196. + return Buffer::Copy(env, reinterpret_cast<char*>(destbuf.out()), destbuf.length() * sizeof(UChar));
  197. }
  198. MaybeLocal<Object> TranscodeFromUcs2(Environment* env,
  199. @@ -229,7 +229,7 @@ MaybeLocal<Object> TranscodeUcs2FromUtf8(Environment* env,
  200. return {};
  201. }
  202. - return Buffer::New(env, &destbuf);
  203. + return Buffer::Copy(env, reinterpret_cast<char*>(destbuf.out()), destbuf.length() * sizeof(UChar));
  204. }
  205. MaybeLocal<Object> TranscodeUtf8FromUcs2(Environment* env,
  206. @@ -253,7 +253,7 @@ MaybeLocal<Object> TranscodeUtf8FromUcs2(Environment* env,
  207. return {};
  208. }
  209. - return Buffer::New(env, &destbuf);
  210. + return Buffer::Copy(env, reinterpret_cast<char*>(destbuf.out()), destbuf.length() * sizeof(char));
  211. }
  212. constexpr const char* EncodingName(const enum encoding encoding) {
  213. diff --git a/src/node_internals.h b/src/node_internals.h
  214. index fe2d25decd883085e4c3f368ab4acc01a7f66f6e..bcd5c87afcff9c56429443363c63fc8079521451 100644
  215. --- a/src/node_internals.h
  216. +++ b/src/node_internals.h
  217. @@ -117,7 +117,9 @@ v8::Maybe<bool> InitializePrimordials(v8::Local<v8::Context> context);
  218. class NodeArrayBufferAllocator : public ArrayBufferAllocator {
  219. public:
  220. - inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
  221. + NodeArrayBufferAllocator();
  222. + ~NodeArrayBufferAllocator() override;
  223. + inline uint32_t* zero_fill_field() { return zero_fill_field_; }
  224. void* Allocate(size_t size) override; // Defined in src/node.cc
  225. void* AllocateUninitialized(size_t size) override;
  226. @@ -135,7 +137,7 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
  227. }
  228. private:
  229. - uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
  230. + uint32_t* zero_fill_field_ = nullptr; // Boolean but exposed as uint32 to JS land.
  231. std::atomic<size_t> total_mem_usage_ {0};
  232. // Delegate to V8's allocator for compatibility with the V8 memory cage.
  233. diff --git a/src/node_serdes.cc b/src/node_serdes.cc
  234. index 7a70997bc024efa4f3ff4cabe30d5e88dcc7bc78..438d6e581a2ee55216f9a9945204b07824fc28a0 100644
  235. --- a/src/node_serdes.cc
  236. +++ b/src/node_serdes.cc
  237. @@ -29,6 +29,11 @@ using v8::ValueSerializer;
  238. namespace serdes {
  239. +v8::ArrayBuffer::Allocator* GetAllocator() {
  240. + static v8::ArrayBuffer::Allocator* allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
  241. + return allocator;
  242. +}
  243. +
  244. class SerializerContext : public BaseObject,
  245. public ValueSerializer::Delegate {
  246. public:
  247. @@ -37,10 +42,15 @@ class SerializerContext : public BaseObject,
  248. ~SerializerContext() override = default;
  249. + // v8::ValueSerializer::Delegate
  250. void ThrowDataCloneError(Local<String> message) override;
  251. Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override;
  252. Maybe<uint32_t> GetSharedArrayBufferId(
  253. Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override;
  254. + void* ReallocateBufferMemory(void* old_buffer,
  255. + size_t old_length,
  256. + size_t* new_length) override;
  257. + void FreeBufferMemory(void* buffer) override;
  258. static void SetTreatArrayBufferViewsAsHostObjects(
  259. const FunctionCallbackInfo<Value>& args);
  260. @@ -61,6 +71,7 @@ class SerializerContext : public BaseObject,
  261. private:
  262. ValueSerializer serializer_;
  263. + size_t last_length_ = 0;
  264. };
  265. class DeserializerContext : public BaseObject,
  266. @@ -144,6 +155,24 @@ Maybe<uint32_t> SerializerContext::GetSharedArrayBufferId(
  267. return id.ToLocalChecked()->Uint32Value(env()->context());
  268. }
  269. +void* SerializerContext::ReallocateBufferMemory(void* old_buffer,
  270. + size_t requested_size,
  271. + size_t* new_length) {
  272. + *new_length = std::max(static_cast<size_t>(4096), requested_size);
  273. + if (old_buffer) {
  274. + void* ret = GetAllocator()->Reallocate(old_buffer, last_length_, *new_length);
  275. + last_length_ = *new_length;
  276. + return ret;
  277. + } else {
  278. + last_length_ = *new_length;
  279. + return GetAllocator()->Allocate(*new_length);
  280. + }
  281. +}
  282. +
  283. +void SerializerContext::FreeBufferMemory(void* buffer) {
  284. + GetAllocator()->Free(buffer, last_length_);
  285. +}
  286. +
  287. Maybe<bool> SerializerContext::WriteHostObject(Isolate* isolate,
  288. Local<Object> input) {
  289. MaybeLocal<Value> ret;
  290. @@ -209,9 +238,14 @@ void SerializerContext::ReleaseBuffer(const FunctionCallbackInfo<Value>& args) {
  291. // Note: Both ValueSerializer and this Buffer::New() variant use malloc()
  292. // as the underlying allocator.
  293. std::pair<uint8_t*, size_t> ret = ctx->serializer_.Release();
  294. - auto buf = Buffer::New(ctx->env(),
  295. - reinterpret_cast<char*>(ret.first),
  296. - ret.second);
  297. + std::unique_ptr<v8::BackingStore> bs =
  298. + v8::ArrayBuffer::NewBackingStore(reinterpret_cast<char*>(ret.first), ret.second,
  299. + [](void* data, size_t length, void* deleter_data) {
  300. + if (data) GetAllocator()->Free(reinterpret_cast<char*>(data), length);
  301. + }, nullptr);
  302. + Local<ArrayBuffer> ab = v8::ArrayBuffer::New(ctx->env()->isolate(), std::move(bs));
  303. +
  304. + auto buf = Buffer::New(ctx->env(), ab, 0, ret.second);
  305. if (!buf.IsEmpty()) {
  306. args.GetReturnValue().Set(buf.ToLocalChecked());
  307. diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc
  308. index 9787b14352753c5e0f8dc2b90093680e7cd10f1a..31af9e62396368af1b81f8841a705fd313df2b9f 100644
  309. --- a/src/node_trace_events.cc
  310. +++ b/src/node_trace_events.cc
  311. @@ -132,12 +132,28 @@ static void GetCategoryEnabledBuffer(const FunctionCallbackInfo<Value>& args) {
  312. const uint8_t* enabled_pointer =
  313. TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_name.out());
  314. uint8_t* enabled_pointer_cast = const_cast<uint8_t*>(enabled_pointer);
  315. + uint8_t size = sizeof(*enabled_pointer_cast);
  316. +#if defined(V8_ENABLE_SANDBOX)
  317. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  318. + void* v8_data = allocator->Allocate(size);
  319. + CHECK(v8_data);
  320. + memcpy(v8_data, enabled_pointer_cast, size);
  321. + std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
  322. + v8_data,
  323. + size,
  324. + [](void* data, size_t length, void*) {
  325. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  326. + allocator->Free(data, length);
  327. + }, nullptr);
  328. +#else
  329. std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
  330. enabled_pointer_cast,
  331. - sizeof(*enabled_pointer_cast),
  332. + size,
  333. [](void*, size_t, void*) {},
  334. nullptr);
  335. +#endif
  336. +
  337. auto ab = ArrayBuffer::New(isolate, std::move(bs));
  338. v8::Local<Uint8Array> u8 = v8::Uint8Array::New(ab, 0, 1);