From 38b97cc266f482d4fa6d4f9e869766ba2125b4ef Mon Sep 17 00:00:00 2001
From: legendecas <legendecas@gmail.com>
Date: Fri, 19 Aug 2022 21:42:39 +0800
Subject: [PATCH] src: restore context default
 IsCodeGenerationFromStringsAllowed value

Context's default IsCodeGenerationFromStringsAllowed value can be
changed by v8 flag `--disallow-code-generation-from-strings`. Restore
the value at runtime when delegating the code generation validation to
`node::ModifyCodeGenerationFromStrings`.

The context's settings are serialized in the snapshot. Reset the setting
values to its default values before the serialization so that it can be
correctly re-initialized after deserialization at runtime.
---
 src/api/environment.cc                        | 19 ++++++++++++++++---
 src/node_snapshotable.cc                      | 13 +++++++++++++
 ...l-disallow-code-generation-from-strings.js |  9 +++++++++
 test/parallel/test-eval.js                    |  7 +++++++
 4 files changed, 45 insertions(+), 3 deletions(-)
 create mode 100644 test/parallel/test-eval-disallow-code-generation-from-strings.js
 create mode 100644 test/parallel/test-eval.js

diff --git a/src/api/environment.cc b/src/api/environment.cc
index 2cecae617f07f7..53581405bac712 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -556,6 +556,19 @@ Maybe<bool> InitializeContextRuntime(Local<Context> context) {
   Isolate* isolate = context->GetIsolate();
   HandleScope handle_scope(isolate);
 
+  // When `IsCodeGenerationFromStringsAllowed` is true, V8 takes the fast path
+  // and ignores the ModifyCodeGenerationFromStrings callback. Set it to false
+  // to delegate the code generation validation to
+  // node::ModifyCodeGenerationFromStrings.
+  // The `IsCodeGenerationFromStringsAllowed` can be refreshed by V8 according
+  // to the runtime flags, propagate the value to the embedder data.
+  bool is_code_generation_from_strings_allowed =
+      context->IsCodeGenerationFromStringsAllowed();
+  context->AllowCodeGenerationFromStrings(false);
+  context->SetEmbedderData(
+      ContextEmbedderIndex::kAllowCodeGenerationFromStrings,
+      is_code_generation_from_strings_allowed ? True(isolate) : False(isolate));
+
   if (per_process::cli_options->disable_proto == "") {
     return Just(true);
   }
@@ -648,11 +661,11 @@ Maybe<bool> InitializeMainContextForSnapshot(Local<Context> context) {
   Isolate* isolate = context->GetIsolate();
   HandleScope handle_scope(isolate);
 
-  context->AllowCodeGenerationFromStrings(false);
-  context->SetEmbedderData(
-      ContextEmbedderIndex::kAllowCodeGenerationFromStrings, True(isolate));
+  // Initialize the default values.
   context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
                            True(isolate));
+  context->SetEmbedderData(
+      ContextEmbedderIndex::kAllowCodeGenerationFromStrings, True(isolate));
 
   if (InitializeBaseContextForSnapshot(context).IsNothing()) {
     return Nothing<bool>();
diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc
index 9aac5dd7f31d0c..98660f7d171209 100644
--- a/src/node_snapshotable.cc
+++ b/src/node_snapshotable.cc
@@ -958,6 +958,16 @@ const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() {
 )";
 }
 
+// Reset context settings that need to be initialized again after
+// deserialization.
+static void ResetContextSettingsBeforeSnapshot(Local<Context> context) {
+  // Reset the AllowCodeGenerationFromStrings flag to true (default value) so
+  // that it can be re-initialized with v8 flag
+  // --disallow-code-generation-from-strings and recognized in
+  // node::InitializeContextRuntime.
+  context->AllowCodeGenerationFromStrings(true);
+}
+
 Mutex SnapshotBuilder::snapshot_data_mutex_;
 
 const std::vector<intptr_t>& SnapshotBuilder::CollectExternalReferences() {
@@ -1053,6 +1063,7 @@ int SnapshotBuilder::Generate(SnapshotData* out,
     if (base_context.IsEmpty()) {
       return BOOTSTRAP_ERROR;
     }
+    ResetContextSettingsBeforeSnapshot(base_context);
 
     Local<Context> main_context = NewContext(isolate);
     if (main_context.IsEmpty()) {
@@ -1121,6 +1132,8 @@ int SnapshotBuilder::Generate(SnapshotData* out,
                            size_str.c_str());
       }
 #endif
+
+      ResetContextSettingsBeforeSnapshot(main_context);
     }
 
     // Global handles to the contexts can't be disposed before the
diff --git a/test/parallel/test-eval-disallow-code-generation-from-strings.js b/test/parallel/test-eval-disallow-code-generation-from-strings.js
new file mode 100644
index 00000000000000..c9c0c60b773ce8
--- /dev/null
+++ b/test/parallel/test-eval-disallow-code-generation-from-strings.js
@@ -0,0 +1,9 @@
+// Flags: --disallow-code-generation-from-strings
+'use strict';
+
+require('../common');
+const assert = require('assert');
+
+// Verify that v8 option --disallow-code-generation-from-strings is still
+// respected
+assert.throws(() => eval('"eval"'), EvalError);
diff --git a/test/parallel/test-eval.js b/test/parallel/test-eval.js
new file mode 100644
index 00000000000000..46a4b7c54689b1
--- /dev/null
+++ b/test/parallel/test-eval.js
@@ -0,0 +1,7 @@
+'use strict';
+
+require('../common');
+const assert = require('assert');
+
+// Verify that eval is allowed by default.
+assert.strictEqual(eval('"eval"'), 'eval');