support_v8_sandboxed_pointers.patch 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 33edf44239a7ec13ebee2f926b1e7f9b8d0b83fe..11f8b8dea7bf4119c2d99ee451e3f1bc7bde8902 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_util.cc b/src/crypto/crypto_util.cc
  26. index 3034b114e081e2b32dd5b71653927a41af7d48df..49b0175c219d75dd3a038687f353b2428fbdf62b 100644
  27. --- a/src/crypto/crypto_util.cc
  28. +++ b/src/crypto/crypto_util.cc
  29. @@ -348,10 +348,35 @@ ByteSource& ByteSource::operator=(ByteSource&& other) noexcept {
  30. return *this;
  31. }
  32. -std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore() {
  33. +std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore(Environment* env) {
  34. // It's ok for allocated_data_ to be nullptr but
  35. // only if size_ is zero.
  36. CHECK_IMPLIES(size_ > 0, allocated_data_ != nullptr);
  37. +#if defined(V8_ENABLE_SANDBOX)
  38. + // When V8 sandboxed pointers are enabled, we have to copy into the memory
  39. + // cage. We still want to ensure we erase the data on free though, so
  40. + // provide a custom deleter that calls OPENSSL_cleanse.
  41. + if (!size())
  42. + return ArrayBuffer::NewBackingStore(env->isolate(), 0);
  43. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  44. + void* v8_data = allocator->Allocate(size());
  45. + CHECK(v8_data);
  46. + memcpy(v8_data, allocated_data_, size());
  47. + OPENSSL_clear_free(allocated_data_, size());
  48. + std::unique_ptr<BackingStore> ptr = ArrayBuffer::NewBackingStore(
  49. + v8_data,
  50. + size(),
  51. + [](void* data, size_t length, void*) {
  52. + OPENSSL_cleanse(data, length);
  53. + std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
  54. + allocator->Free(data, length);
  55. + }, nullptr);
  56. + CHECK(ptr);
  57. + allocated_data_ = nullptr;
  58. + data_ = nullptr;
  59. + size_ = 0;
  60. + return ptr;
  61. +#else
  62. std::unique_ptr<BackingStore> ptr = ArrayBuffer::NewBackingStore(
  63. allocated_data_,
  64. size(),
  65. @@ -363,10 +388,11 @@ std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore() {
  66. data_ = nullptr;
  67. size_ = 0;
  68. return ptr;
  69. +#endif // defined(V8_ENABLE_SANDBOX)
  70. }
  71. Local<ArrayBuffer> ByteSource::ToArrayBuffer(Environment* env) {
  72. - std::unique_ptr<BackingStore> store = ReleaseToBackingStore();
  73. + std::unique_ptr<BackingStore> store = ReleaseToBackingStore(env);
  74. return ArrayBuffer::New(env->isolate(), std::move(store));
  75. }
  76. @@ -702,6 +728,16 @@ namespace {
  77. // in which case this has the same semantics as
  78. // using OPENSSL_malloc. However, if the secure heap is
  79. // initialized, SecureBuffer will automatically use it.
  80. +#if defined(V8_ENABLE_SANDBOX)
  81. +// When V8 sandboxed pointers are enabled, the secure heap cannot be used as
  82. +// all ArrayBuffers must be allocated inside the V8 memory cage.
  83. +void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
  84. + CHECK(args[0]->IsUint32());
  85. + uint32_t len = args[0].As<Uint32>()->Value();
  86. + Local<ArrayBuffer> buffer = ArrayBuffer::New(args.GetIsolate(), len);
  87. + args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len));
  88. +}
  89. +#else
  90. void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
  91. CHECK(args[0]->IsUint32());
  92. Environment* env = Environment::GetCurrent(args);
  93. @@ -723,6 +759,7 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
  94. Local<ArrayBuffer> buffer = ArrayBuffer::New(env->isolate(), store);
  95. args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len));
  96. }
  97. +#endif // defined(V8_ENABLE_SANDBOX)
  98. void SecureHeapUsed(const FunctionCallbackInfo<Value>& args) {
  99. #ifndef OPENSSL_IS_BORINGSSL
  100. diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h
  101. index 1ce5f35a70a7c855796cc96d4201cc996907cf8f..be700c953d55b35bd2fee7fd872efd9ed2600c63 100644
  102. --- a/src/crypto/crypto_util.h
  103. +++ b/src/crypto/crypto_util.h
  104. @@ -280,7 +280,7 @@ class ByteSource {
  105. // Creates a v8::BackingStore that takes over responsibility for
  106. // any allocated data. The ByteSource will be reset with size = 0
  107. // after being called.
  108. - std::unique_ptr<v8::BackingStore> ReleaseToBackingStore();
  109. + std::unique_ptr<v8::BackingStore> ReleaseToBackingStore(Environment* env);
  110. v8::Local<v8::ArrayBuffer> ToArrayBuffer(Environment* env);
  111. diff --git a/src/node_i18n.cc b/src/node_i18n.cc
  112. index d45325954d980724f80d49298bbe837197237a9b..ccea18080142bd9cba3765dbbec61c2a63406667 100644
  113. --- a/src/node_i18n.cc
  114. +++ b/src/node_i18n.cc
  115. @@ -105,7 +105,7 @@ namespace {
  116. template <typename T>
  117. MaybeLocal<Object> ToBufferEndian(Environment* env, MaybeStackBuffer<T>* buf) {
  118. - MaybeLocal<Object> ret = Buffer::New(env, buf);
  119. + MaybeLocal<Object> ret = Buffer::Copy(env, reinterpret_cast<char*>(buf->out()), buf->length() * sizeof(T));
  120. if (ret.IsEmpty())
  121. return ret;
  122. diff --git a/src/node_internals.h b/src/node_internals.h
  123. index 9a96e042fc5cda18beb13b89965a6267fba3e0a6..bd210db0b6ad12075e8ced8b321da38752b3d050 100644
  124. --- a/src/node_internals.h
  125. +++ b/src/node_internals.h
  126. @@ -112,7 +112,9 @@ v8::Maybe<bool> InitializePrimordials(v8::Local<v8::Context> context);
  127. class NodeArrayBufferAllocator : public ArrayBufferAllocator {
  128. public:
  129. - inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
  130. + NodeArrayBufferAllocator();
  131. + ~NodeArrayBufferAllocator() override;
  132. + inline uint32_t* zero_fill_field() { return zero_fill_field_; }
  133. void* Allocate(size_t size) override; // Defined in src/node.cc
  134. void* AllocateUninitialized(size_t size) override;
  135. @@ -131,7 +133,7 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
  136. }
  137. private:
  138. - uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
  139. + uint32_t* zero_fill_field_ = nullptr; // Boolean but exposed as uint32 to JS land.
  140. std::atomic<size_t> total_mem_usage_ {0};
  141. // Delegate to V8's allocator for compatibility with the V8 memory cage.
  142. diff --git a/src/node_serdes.cc b/src/node_serdes.cc
  143. index 6698a1df81cb4e0947c86fb30c2d77fca8e2d9d1..dad297652b347819805b09fbfd869f1d037e31c1 100644
  144. --- a/src/node_serdes.cc
  145. +++ b/src/node_serdes.cc
  146. @@ -29,6 +29,11 @@ using v8::ValueSerializer;
  147. namespace serdes {
  148. +v8::ArrayBuffer::Allocator* GetAllocator() {
  149. + static v8::ArrayBuffer::Allocator* allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
  150. + return allocator;
  151. +}
  152. +
  153. class SerializerContext : public BaseObject,
  154. public ValueSerializer::Delegate {
  155. public:
  156. @@ -37,10 +42,15 @@ class SerializerContext : public BaseObject,
  157. ~SerializerContext() override = default;
  158. + // v8::ValueSerializer::Delegate
  159. void ThrowDataCloneError(Local<String> message) override;
  160. Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override;
  161. Maybe<uint32_t> GetSharedArrayBufferId(
  162. Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override;
  163. + void* ReallocateBufferMemory(void* old_buffer,
  164. + size_t old_length,
  165. + size_t* new_length) override;
  166. + void FreeBufferMemory(void* buffer) override;
  167. static void SetTreatArrayBufferViewsAsHostObjects(
  168. const FunctionCallbackInfo<Value>& args);
  169. @@ -61,6 +71,7 @@ class SerializerContext : public BaseObject,
  170. private:
  171. ValueSerializer serializer_;
  172. + size_t last_length_ = 0;
  173. };
  174. class DeserializerContext : public BaseObject,
  175. @@ -144,6 +155,24 @@ Maybe<uint32_t> SerializerContext::GetSharedArrayBufferId(
  176. return id.ToLocalChecked()->Uint32Value(env()->context());
  177. }
  178. +void* SerializerContext::ReallocateBufferMemory(void* old_buffer,
  179. + size_t requested_size,
  180. + size_t* new_length) {
  181. + *new_length = std::max(static_cast<size_t>(4096), requested_size);
  182. + if (old_buffer) {
  183. + void* ret = GetAllocator()->Reallocate(old_buffer, last_length_, *new_length);
  184. + last_length_ = *new_length;
  185. + return ret;
  186. + } else {
  187. + last_length_ = *new_length;
  188. + return GetAllocator()->Allocate(*new_length);
  189. + }
  190. +}
  191. +
  192. +void SerializerContext::FreeBufferMemory(void* buffer) {
  193. + GetAllocator()->Free(buffer, last_length_);
  194. +}
  195. +
  196. Maybe<bool> SerializerContext::WriteHostObject(Isolate* isolate,
  197. Local<Object> input) {
  198. MaybeLocal<Value> ret;
  199. @@ -209,9 +238,14 @@ void SerializerContext::ReleaseBuffer(const FunctionCallbackInfo<Value>& args) {
  200. // Note: Both ValueSerializer and this Buffer::New() variant use malloc()
  201. // as the underlying allocator.
  202. std::pair<uint8_t*, size_t> ret = ctx->serializer_.Release();
  203. - auto buf = Buffer::New(ctx->env(),
  204. - reinterpret_cast<char*>(ret.first),
  205. - ret.second);
  206. + std::unique_ptr<v8::BackingStore> bs =
  207. + v8::ArrayBuffer::NewBackingStore(reinterpret_cast<char*>(ret.first), ret.second,
  208. + [](void* data, size_t length, void* deleter_data) {
  209. + if (data) GetAllocator()->Free(reinterpret_cast<char*>(data), length);
  210. + }, nullptr);
  211. + Local<ArrayBuffer> ab = v8::ArrayBuffer::New(ctx->env()->isolate(), std::move(bs));
  212. +
  213. + auto buf = Buffer::New(ctx->env(), ab, 0, ret.second);
  214. if (!buf.IsEmpty()) {
  215. args.GetReturnValue().Set(buf.ToLocalChecked());