fuchsia_component_escrow/
lib.rs1#[cfg(fuchsia_api_level_at_least = "HEAD")]
8pub use everything::*;
9
10#[cfg(fuchsia_api_level_less_than = "HEAD")]
11mod everything {
12 use anyhow as _;
13 use fidl as _;
14 use fidl_fuchsia_component_sandbox as _;
15 use fidl_fuchsia_io as _;
16 use fidl_fuchsia_process_lifecycle as _;
17 use fuchsia_async as _;
18 use fuchsia_component_runtime as _;
19 use fuchsia_runtime as _;
20 use fuchsia_sync as _;
21 use futures as _;
22}
23
24#[cfg(fuchsia_api_level_at_least = "HEAD")]
25mod everything {
26 use anyhow::{Error, format_err};
27 use fidl::endpoints::ServerEnd;
28 use fidl_fuchsia_component_sandbox as fsandbox;
29 use fidl_fuchsia_io as fio;
30 use fidl_fuchsia_process_lifecycle as flifecycle;
31 use fuchsia_async::Task;
32 use fuchsia_component_runtime::Dictionary;
33 use fuchsia_runtime::{self as fruntime, HandleInfo, HandleType};
34 use fuchsia_sync::Mutex;
35 use futures::StreamExt;
36 use std::collections::HashSet;
37 use std::process;
38 use std::sync::Arc;
39
40 #[derive(Default, Clone)]
45 pub struct EscrowOperation {
46 inner: Arc<Mutex<Inner>>,
47 }
48
49 #[derive(Default)]
50 struct Inner {
51 fsandbox_dictionary: Option<fsandbox::DictionaryRef>,
52 lifecycle_control_handle: Option<flifecycle::LifecycleControlHandle>,
53 lifecycle_handle: Option<ServerEnd<flifecycle::LifecycleMarker>>,
54 watch_for_stop_task: Option<Task<()>>,
55 dictionary: Option<Dictionary>,
56 }
57
58 impl EscrowOperation {
59 pub fn new() -> Self {
62 let lifecycle_handle =
63 fruntime::take_startup_handle(HandleInfo::new(HandleType::Lifecycle, 0))
64 .expect("No lifecycle channel received, unable to escrow handles");
65 let channel: fidl::Channel = lifecycle_handle.into();
66 let lifecycle_handle: ServerEnd<flifecycle::LifecycleMarker> = channel.into();
67 Self::new_with_lifecycle_handle(lifecycle_handle)
68 }
69
70 pub fn new_with_lifecycle_handle(
73 lifecycle_handle: ServerEnd<flifecycle::LifecycleMarker>,
74 ) -> Self {
75 Self {
76 inner: Arc::new(Mutex::new(Inner {
77 lifecycle_handle: Some(lifecycle_handle),
78 ..Default::default()
79 })),
80 }
81 }
82
83 pub fn new_with_control_handle(control_handle: flifecycle::LifecycleControlHandle) -> Self {
86 Self {
87 inner: Arc::new(Mutex::new(Inner {
88 lifecycle_control_handle: Some(control_handle),
89 ..Default::default()
90 })),
91 }
92 }
93
94 pub fn with_fsandbox_dictionary(&self, fsandbox_dictionary: fsandbox::DictionaryRef) {
99 self.inner.lock().fsandbox_dictionary = Some(fsandbox_dictionary);
100 }
101
102 pub fn with_dictionary(&self, dictionary: Dictionary) {
105 self.inner.lock().dictionary = Some(dictionary);
106 }
107
108 pub fn watch_for_stop(&self) -> Result<(), Error> {
111 let mut inner_guard = self.inner.lock();
112 let lifecycle_handle = inner_guard.lifecycle_handle.take().ok_or_else(|| {
113 format_err!(
114 "EscrowOperation::wait_for_stop called without setting lifecycle handle"
115 )
116 })?;
117 let (mut stream, control_handle) = lifecycle_handle.into_stream_and_control_handle();
118 inner_guard.lifecycle_control_handle = Some(control_handle);
119 inner_guard.watch_for_stop_task = Some(Task::spawn(async move {
120 let Some(Ok(request)) = stream.next().await else {
121 return;
122 };
123 match request {
124 flifecycle::LifecycleRequest::Stop { .. } => {
125 process::exit(1);
137 }
138 }
139 }));
140 Ok(())
141 }
142
143 pub fn run(
147 &self,
148 outgoing_directory: ServerEnd<fio::DirectoryMarker>,
149 ) -> Result<(), Error> {
150 let mut inner_guard = self.inner.lock();
151 let lifecycle_control_handle = match inner_guard.lifecycle_control_handle.take() {
152 Some(lifecycle_control_handle) => lifecycle_control_handle,
153 None => {
154 let lifecycle_handle = inner_guard
155 .lifecycle_handle
156 .take()
157 .ok_or_else(|| format_err!("EscrowOperation::run called without setting lifecycle handle or control handle"))?;
158 let (_stream, control) = lifecycle_handle.into_stream_and_control_handle();
159 control
160 }
161 };
162
163 lifecycle_control_handle
164 .send_on_escrow(flifecycle::LifecycleOnEscrowRequest {
165 outgoing_dir: Some(outgoing_directory),
166 escrowed_dictionary: inner_guard.fsandbox_dictionary.take(),
167 escrowed_dictionary_handle: inner_guard.dictionary.take().map(|d| d.handle),
168 recoverable_bytes: Some(calculate_recoverable_memory()?),
169 ..Default::default()
170 })
171 .map_err(|e| format_err!("Failed to escrow handles: {e:?}"))
172 }
173 }
174
175 fn calculate_recoverable_memory() -> Result<u64, Error> {
178 let process = fruntime::process_self();
187 let vmo_infos = process
188 .info_vmos_vec()
189 .map_err(|e| format_err!("Failed to inspect own VMOs: {e:?}"))?;
190 let vmos_infos_to_include = vmo_infos.into_iter().filter(|vmo_info| {
191 vmo_info.name.as_bstr().starts_with(b"scudo")
192 || vmo_info.name.as_bstr().starts_with(b"relro")
193 || vmo_info.name.as_bstr().starts_with(b"pthread_t")
194 || vmo_info.name.as_bstr().starts_with(b"pthread_create")
195 || vmo_info.name.as_bstr().starts_with(b"thrd_t:0x")
196 || vmo_info.name.as_bstr() == "initial-thread"
197 });
198 let savings_by_koid = vmos_infos_to_include
201 .map(|vmo_info| (vmo_info.koid, vmo_info.committed_private_bytes))
202 .collect::<HashSet<_>>();
203 let total_recoverable_bytes = savings_by_koid.into_iter().map(|(_, num)| num).sum();
204 Ok(total_recoverable_bytes)
205 }
206}