[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); +}