starnix_core/execution/
executor.rs1use crate::execution::loop_entry::enter_syscall_loop;
6use crate::ptrace::{PtraceCoreState, ptrace_attach_from_state};
7use crate::task::{CurrentTask, DelayedReleaser, ExitStatus, TaskBuilder, ZirconThread};
8use anyhow::Error;
9use starnix_logging::{log_error, log_warn};
10use starnix_sync::{ExecutorVmarManagerLock, LockDepMutex};
11use starnix_uapi::errors::Errno;
12use starnix_uapi::{errno, error};
13use std::os::unix::thread::JoinHandleExt;
14use std::sync::Arc;
15use std::sync::mpsc::sync_channel;
16use thread_create_vmars::ThreadCreateVmars;
17
18struct ExecutorVmarManager(LockDepMutex<ThreadCreateVmars, ExecutorVmarManagerLock>);
22
23pub fn execute_task_with_prerun_result<F, R, G>(
24 task_builder: TaskBuilder,
25 pre_run: F,
26 task_complete: G,
27 ptrace_state: Option<PtraceCoreState>,
28) -> Result<R, Errno>
29where
30 F: FnOnce(&mut CurrentTask) -> Result<R, Errno> + Send + Sync + 'static,
31 R: Send + Sync + 'static,
32 G: FnOnce(Result<ExitStatus, Error>) + Send + Sync + 'static,
33{
34 let (sender, receiver) = sync_channel::<Result<R, Errno>>(1);
35 execute_task(
36 task_builder,
37 move |current_task| match pre_run(current_task) {
38 Err(errno) => {
39 let _ = sender.send(Err(errno.clone()));
40 Err(errno)
41 }
42 Ok(value) => sender.send(Ok(value)).map_err(|error| {
43 log_error!("Unable to send `pre_run` result: {error:?}");
44 errno!(EINVAL)
45 }),
46 },
47 task_complete,
48 ptrace_state,
49 )?;
50 receiver.recv().map_err(|e| {
51 log_error!("Unable to retrieve result from `pre_run`: {e:?}");
52 errno!(EINVAL)
53 })?
54}
55
56pub fn execute_task<F, G>(
57 task_builder: TaskBuilder,
58 pre_run: F,
59 task_complete: G,
60 ptrace_state: Option<PtraceCoreState>,
61) -> Result<(), Errno>
62where
63 F: FnOnce(&mut CurrentTask) -> Result<(), Errno> + Send + Sync + 'static,
64 G: FnOnce(Result<ExitStatus, Error>) + Send + Sync + 'static,
65{
66 let process_handle = task_builder.task.thread_group().process.raw_handle();
69
70 let kernel = task_builder.task.kernel();
71 let create_vmars =
72 kernel.expando.get_or_init(|| ExecutorVmarManager(ThreadCreateVmars::new().into()));
73 let mut create_vmars = create_vmars.0.lock();
74
75 let old_handles = unsafe {
82 thrd_set_zx_create_handles(thrd_zx_create_handles {
83 process: process_handle,
84 machine_stack_vmar: create_vmars.machine_stack.probe()?.raw_handle(),
85 security_stack_vmar: create_vmars.security_stack.probe()?.raw_handle(),
86 thread_block_vmar: create_vmars.thread_block.probe()?.raw_handle(),
87 })
88 };
89 scopeguard::defer! {
90 unsafe {
95 thrd_set_zx_create_handles(old_handles);
96 };
97 };
98
99 if let Some(ptrace_state) = ptrace_state {
100 let _ = ptrace_attach_from_state(&task_builder.task, ptrace_state);
101 }
102
103 let ref_task = Arc::clone(&task_builder.task);
104 let running_state = ref_task.running_state().unwrap();
105
106 let (sender, receiver) = sync_channel::<TaskBuilder>(1);
109 let result = std::thread::Builder::new().name("user-thread".to_string()).spawn(move || {
110 let _rcu_registration = fuchsia_rcu::register_thread();
111
112 let mut current_task: CurrentTask = receiver
116 .recv()
117 .expect("caller should always send task builder before disconnecting")
118 .into();
119
120 std::mem::drop(receiver);
123
124 let pre_run_result = { pre_run(&mut current_task) };
125 if pre_run_result.is_err() {
126 if current_task.exit_status().is_none() {
129 log_error!("Pre run failed from {pre_run_result:?}. The task will not be run.");
130 }
131
132 std::mem::drop(task_complete);
135 } else {
136 let exit_status = enter_syscall_loop(&mut current_task);
137 current_task.write().set_exit_status(exit_status.clone());
138 task_complete(Ok(exit_status));
139 }
140
141 current_task.release(());
144
145 DelayedReleaser::finalize();
147 });
148 let join_handle = match result {
149 Ok(handle) => handle,
150 Err(e) => {
151 task_builder.release(());
152 match e.kind() {
153 std::io::ErrorKind::WouldBlock => return error!(EAGAIN),
154 other => panic!("unexpected error on thread spawn: {other}"),
155 }
156 }
157 };
158 std::mem::drop(create_vmars);
163
164 task_builder.task.write().set_spawned();
166
167 let pthread = join_handle.as_pthread_t();
173 #[allow(
174 clippy::undocumented_unsafe_blocks,
175 reason = "Force documented unsafe blocks in Starnix"
176 )]
177 let raw_thread_handle =
178 unsafe { zx::Unowned::<'_, zx::Thread>::from_raw_handle(thrd_get_zx_handle(pthread)) };
179 let thread = Arc::new(
180 raw_thread_handle
181 .duplicate_handle(zx::Rights::SAME_RIGHTS)
182 .expect("must have RIGHT_DUPLICATE on handle we created"),
183 );
184 running_state.thread.set(ZirconThread::new(thread)).expect("thread should only be set once");
185 if let Err(err) = ref_task.sync_scheduler_state_to_role() {
187 log_warn!(err:?; "Couldn't update freshly spawned thread's profile.");
188 }
189
190 ref_task.record_pid_koid_mapping();
192
193 sender
197 .send(task_builder)
198 .expect("receiver should not be disconnected because thread spawned successfully");
199
200 Ok(())
201}
202
203#[repr(C)]
204#[derive(Debug)]
205pub struct thrd_zx_create_handles {
206 pub process: zx::sys::zx_handle_t,
207 pub machine_stack_vmar: zx::sys::zx_handle_t,
208 pub security_stack_vmar: zx::sys::zx_handle_t,
209 pub thread_block_vmar: zx::sys::zx_handle_t,
210}
211unsafe extern "C" {
212 fn thrd_set_zx_create_handles(handles: thrd_zx_create_handles) -> thrd_zx_create_handles;
213
214 fn thrd_get_zx_handle(thread: u64) -> zx::sys::zx_handle_t;
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222 use crate::ptrace::StopState;
223 use crate::signals::SignalInfo;
224 use crate::testing::*;
225 use starnix_uapi::signals::{SIGCONT, SIGSTOP};
226
227 #[::fuchsia::test]
228 async fn test_block_if_stopped_stop_and_continue() {
229 spawn_kernel_and_run(async |task| {
230 assert!(!task.block_if_stopped());
232
233 task.thread_group().set_stopped(
235 StopState::GroupStopping,
236 Some(SignalInfo::kernel(SIGSTOP)),
237 false,
238 );
239
240 let thread = std::thread::spawn({
241 let task = task.weak_task();
242 move || {
243 let task = task.upgrade().expect("task must be alive");
244 while !task.read().is_blocked() {
246 std::thread::sleep(std::time::Duration::from_millis(10));
247 }
248
249 task.thread_group().set_stopped(
251 StopState::Waking,
252 Some(SignalInfo::kernel(SIGCONT)),
253 false,
254 );
255 }
256 });
257
258 assert!(task.block_if_stopped());
260
261 thread.join().expect("joined");
263
264 assert!(!task.block_if_stopped());
266 })
267 .await;
268 }
269
270 #[::fuchsia::test]
271 async fn test_block_if_stopped_stop_and_exit() {
272 spawn_kernel_and_run(async |task| {
273 assert!(!task.block_if_stopped());
275
276 task.thread_group().set_stopped(
278 StopState::GroupStopping,
279 Some(SignalInfo::kernel(SIGSTOP)),
280 false,
281 );
282
283 let thread = std::thread::spawn({
284 let task = task.weak_task();
285 move || {
286 let task = task.upgrade().expect("task must be alive");
287 while !task.read().is_blocked() {
289 std::thread::sleep(std::time::Duration::from_millis(10));
290 }
291
292 task.thread_group().kill(ExitStatus::Exit(1), None);
294 }
295 });
296
297 assert!(task.block_if_stopped());
299
300 thread.join().expect("joined");
302
303 assert!(!task.block_if_stopped());
305 })
306 .await;
307 }
308}