Fix lifetime of starlark struct keys passed to C++. This works fine *most* of the time, since we mostly return structs stored on frozen heaps, thus guarunteeing they will live as long as the starlark session. But we make no guaruntee of this. Bug: 528225104 Change-Id: Ie76b00e2c10fdb4b166e1496fdae04eb6a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/24680 Commit-Queue: Matt Stark <msta@google.com> Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/starlark/crates/ffi/src/scope.rs b/src/gn/starlark/crates/ffi/src/scope.rs index 09d2682..eb9dab0 100644 --- a/src/gn/starlark/crates/ffi/src/scope.rs +++ b/src/gn/starlark/crates/ffi/src/scope.rs
@@ -5,11 +5,12 @@ use std::pin::Pin; use starlark::values::{Heap, Value as StarlarkValue}; +use types::intern_string; use crate::{bridge::Value, Immutable, OwnedSlice, Scope}; impl Scope { - pub(crate) fn new<'b>( + fn new<'b>( parent: &Self, keys: &[&str], ) -> (cxx::UniquePtr<Self>, OwnedSlice<Pin<&'b mut Value>>) { @@ -55,7 +56,11 @@ impl types::Scope for OwnedScope { fn copy_with<'a, 'v>(&self, kv: impl Iterator<Item = (&'a str, StarlarkValue<'v>)>) -> Self { let parent = self.0.as_ref().unwrap(); - let (keys, vals): (Vec<&str>, Vec<StarlarkValue<'v>>) = kv.unzip(); + // Scope stores a map from string_view to value. Since we don't know the + // lifetime of the string we were given, we must intern it in order to + // guarantee it can be safely dereferenced. + let (keys, vals): (Vec<&str>, Vec<StarlarkValue<'v>>) = + kv.map(|(s, v)| (intern_string(s), v)).unzip(); let (mut child_scope, mut placeholders) = Scope::new(parent, &keys); let child_pin = child_scope.as_mut().unwrap();
diff --git a/src/gn/starlark/crates/types/src/file.rs b/src/gn/starlark/crates/types/src/file.rs index 81b1dd6..32233b0 100644 --- a/src/gn/starlark/crates/types/src/file.rs +++ b/src/gn/starlark/crates/types/src/file.rs
@@ -36,6 +36,15 @@ starlark_simple_value!(File); +/// Interns a string for the duration of the program. +pub fn intern_string(s: &str) -> &'static str { + extern "C" { + fn intern_string(s: &str) -> &'static str; + } + // Safety: Just an ffi function + unsafe { intern_string(s) } +} + impl File { /// Creates a `File` from a string representing a path. /// The path is relative to the root_build_dir. @@ -46,11 +55,7 @@ /// Creates a `File` by interning a string representing a path. /// The path is relative to the root_build_dir. pub fn intern(s: &str) -> Self { - extern "C" { - fn intern_string(s: &str) -> &'static str; - } - // Safety: Just an ffi function - Self(unsafe { intern_string(s) }) + Self(intern_string(s)) } /// Returns the file path relative to the root_build_dir.
diff --git a/src/gn/starlark/crates/types/src/lib.rs b/src/gn/starlark/crates/types/src/lib.rs index 44613d7..ae22eaf 100644 --- a/src/gn/starlark/crates/types/src/lib.rs +++ b/src/gn/starlark/crates/types/src/lib.rs
@@ -21,7 +21,7 @@ pub use ctx_state::CtxState; pub(crate) use errors::Error; pub use eval_context::{EvalContext, EvaluatorContextExt}; -pub use file::File; +pub use file::{intern_string, File}; pub use label::Label; pub use label_ref::LabelRef; pub use output_type::OutputType;