[functions][not_needed] Copy scope so it isn't overridden

An edge case found an ASAN use-after-free error where a scope value was
getting overridden by the evaluated result. This copies the scope in the
case where it would have been overridden.

Bug: crbug.com/gn/12
Change-Id: I08322fc73e6e459fbe4c5f35e7f5154ded74746c
Reviewed-on: https://gn-review.googlesource.com/c/2920
Reviewed-by: Scott Graham <scottmg@google.com>
Commit-Queue: Julie Hockett <juliehockett@google.com>
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
index 7fc8b62..4c423cb 100644
--- a/tools/gn/functions.cc
+++ b/tools/gn/functions.cc
@@ -677,6 +677,7 @@
 
   Value* value = nullptr;  // Value to use, may point to result_value.
   Value result_value;      // Storage for the "evaluate" case.
+  Value scope_value;       // Storage for an evaluated scope.
   const IdentifierNode* identifier = (*args_cur)->AsIdentifier();
   if (identifier) {
     // Optimize the common case where the input scope is an identifier. This
@@ -699,7 +700,13 @@
   // Extract the source scope if different from current one.
   Scope* source = scope;
   if (value->type() == Value::SCOPE) {
-    source = value->scope_value();
+    // Copy the scope value if it will be overridden.
+    if (value == &result_value) {
+      scope_value = Value(nullptr, value->scope_value()->MakeClosure());
+      source = scope_value.scope_value();
+    } else {
+      source = value->scope_value();
+    }
     result_value = (*args_cur)->Execute(scope, err);
     if (err->has_error())
       return Value();
diff --git a/tools/gn/functions_unittest.cc b/tools/gn/functions_unittest.cc
index 589986c..8725c2e 100644
--- a/tools/gn/functions_unittest.cc
+++ b/tools/gn/functions_unittest.cc
@@ -202,3 +202,15 @@
   reading_from_different_call.parsed()->Execute(setup2.scope(), &err);
   ASSERT_FALSE(err.has_error());
 }
+
+TEST(Functions, NotNeeded) {
+  TestWithScope setup;
+
+  TestParseInput input("not_needed({ a = 1 }, \"*\")");
+  ASSERT_FALSE(input.has_error());
+
+  Err err;
+  input.parsed()->Execute(setup.scope(), &err);
+  ASSERT_FALSE(err.has_error())
+      << err.message() << err.location().Describe(true);
+}