From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Wed, 28 Jun 2023 21:11:40 +0900
Subject: fix: harden blink::ScriptState::MaybeFrom

NOTE: since https://chromium-review.googlesource.com/c/chromium/src/+/6973697
the patch is only needed for 32-bit builds or builds where the V8 sandbox is disabled.

This is needed as side effect of https://chromium-review.googlesource.com/c/chromium/src/+/4609446
which now gets blink::ExecutionContext from blink::ScriptState
and there are isolate callbacks which get entered from Node.js
environment that has v8::Context not associated with blink::ScriptState.
Some examples are ModifyCodeGenerationFromStrings in node_bindings.cc,
blink::UseCounterCallback etc.

Without this patch when blink::ScriptState::MaybeFrom tries to extract
blink::ScriptState from the provided v8::Context and since Node.js has context
embedder data fields with index greater than blink (see node_context_data.h)
leading to the following CHECK failure.

```
script_state.h(169)] Security Check Failed: script_state
```

This patch adds a new tag in the context associated with ScriptState
to uniquely identify. It is based on what Node.js does to identify the
context created by it in `node_context_data.h`.

PS: We are not performing a check like

```
ScriptState* script_state =
    static_cast<ScriptState*>(context->GetAlignedPointerFromEmbedderData(
        kV8ContextPerContextDataIndex));
if (!script_state) {
  return nullptr;
}
```

since in 32-bit builds which does not have v8 sandbox enabled unlike 64-bit builds,
the embedder data slot will not lazy initialize indexes in the former. This means
accessing uninitialized lower indexes can return garbage values that cannot be null checked.
Refer to v8::EmbedderDataSlot::store_aligned_pointer for context.

diff --git a/gin/public/gin_embedders.h b/gin/public/gin_embedders.h
index cecf528475cb832ed1876381878eade582bc83d6..71308b2d963c2d083328aad6be356dc547ba5629 100644
--- a/gin/public/gin_embedders.h
+++ b/gin/public/gin_embedders.h
@@ -20,6 +20,8 @@ enum GinEmbedder : uint16_t {
   kEmbedderBlink,
   kEmbedderPDFium,
   kEmbedderFuchsia,
+  kEmbedderElectron,
+  kEmbedderBlinkTag,
 };
 
 enum EmbedderDataTag : uint16_t {
diff --git a/third_party/blink/renderer/platform/bindings/script_state.cc b/third_party/blink/renderer/platform/bindings/script_state.cc
index 8b6522c9299bef5ab766795b64a1ba30bc382a12..4615dc04a3814a096898a36c7bbeb30f960a8b4d 100644
--- a/third_party/blink/renderer/platform/bindings/script_state.cc
+++ b/third_party/blink/renderer/platform/bindings/script_state.cc
@@ -14,6 +14,12 @@ namespace blink {
 
 ScriptState::CreateCallback ScriptState::s_create_callback_ = nullptr;
 
+#if !defined(V8_ENABLE_SANDBOX)
+int const ScriptState::kScriptStateTag = 0x6e6f64;
+void* const ScriptState::kScriptStateTagPtr = const_cast<void*>(
+    static_cast<const void*>(&ScriptState::kScriptStateTag));
+#endif  // !defined(V8_ENABLE_SANDBOX)
+
 // static
 void ScriptState::SetCreateCallback(CreateCallback create_callback) {
   DCHECK(create_callback);
@@ -40,6 +46,10 @@ ScriptState::ScriptState(v8::Local<v8::Context> context,
   context_.SetWeak(this, &OnV8ContextCollectedCallback);
   context->SetAlignedPointerInEmbedderData(kV8ContextPerContextDataIndex, this,
                                            gin::kBlinkScriptState);
+#if !defined(V8_ENABLE_SANDBOX)
+  context->SetAlignedPointerInEmbedderData(
+      kV8ContextPerContextDataTagIndex, ScriptState::kScriptStateTagPtr, v8::kEmbedderDataTypeTagDefault);
+#endif  // !defined(V8_ENABLE_SANDBOX)
   RendererResourceCoordinator::Get()->OnScriptStateCreated(this,
                                                            execution_context);
 }
@@ -83,6 +93,10 @@ void ScriptState::DissociateContext() {
   // Cut the reference from V8 context to ScriptState.
   GetContext()->SetAlignedPointerInEmbedderData(
       kV8ContextPerContextDataIndex, nullptr, gin::kBlinkScriptState);
+#if !defined(V8_ENABLE_SANDBOX)
+  GetContext()->SetAlignedPointerInEmbedderData(
+      kV8ContextPerContextDataTagIndex, nullptr, v8::kEmbedderDataTypeTagDefault);
+#endif  // !defined(V8_ENABLE_SANDBOX)
   reference_from_v8_context_.Clear();
 
   // Cut the reference from ScriptState to V8 context.
diff --git a/third_party/blink/renderer/platform/bindings/script_state.h b/third_party/blink/renderer/platform/bindings/script_state.h
index 5ccdf26cead17031d510589b74288cbe79692779..54ede003ebe0a46e624c9d67f7272b8898bbc83e 100644
--- a/third_party/blink/renderer/platform/bindings/script_state.h
+++ b/third_party/blink/renderer/platform/bindings/script_state.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_STATE_H_
 
 #include "base/memory/raw_ptr.h"
+#include "build/build_config.h"
 #include "gin/public/context_holder.h"
 #include "gin/public/gin_embedders.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
@@ -188,6 +189,16 @@ class PLATFORM_EXPORT ScriptState : public GarbageCollected<ScriptState> {
         kV8ContextPerContextDataIndex) {
       return nullptr;
     }
+#if !defined(V8_ENABLE_SANDBOX)
+    if (context->GetNumberOfEmbedderDataFields() <=
+        kV8ContextPerContextDataTagIndex ||
+        context->GetAlignedPointerFromEmbedderData(
+            kV8ContextPerContextDataTagIndex,
+            v8::kEmbedderDataTypeTagDefault) !=
+        ScriptState::kScriptStateTagPtr) {
+      return nullptr;
+    }
+#endif   // !defined(V8_ENABLE_SANDBOX)
     ScriptState* script_state =
         static_cast<ScriptState*>(context->GetAlignedPointerFromEmbedderData(
             isolate, kV8ContextPerContextDataIndex, gin::kBlinkScriptState));
@@ -270,6 +281,14 @@ class PLATFORM_EXPORT ScriptState : public GarbageCollected<ScriptState> {
       static_cast<int>(gin::kPerContextDataStartIndex) +
       static_cast<int>(gin::kEmbedderBlink);
 
+#if !defined(V8_ENABLE_SANDBOX)
+  static void* const kScriptStateTagPtr;
+  static int const kScriptStateTag;
+  static constexpr int kV8ContextPerContextDataTagIndex =
+      static_cast<int>(gin::kPerContextDataStartIndex) +
+      static_cast<int>(gin::kEmbedderBlinkTag);
+#endif  // !defined(V8_ENABLE_SANDBOX)
+
   // For accessing information about the last script compilation via
   // internals.idl.
   String last_compiled_script_file_name_;
