wasmer_wasix/syscalls/wasix/
proc_exec3.rs1use wasmer::FromToNativeWasmType;
2
3use super::*;
4use crate::{
5 VIRTUAL_ROOT_FD, WasiFs,
6 os::task::{OwnedTaskStatus, TaskStatus},
7 syscalls::*,
8};
9
10#[instrument(level = "trace", skip_all, fields(name = field::Empty, %args_len), ret)]
23pub fn proc_exec3<M: MemorySize>(
24 mut ctx: FunctionEnvMut<'_, WasiEnv>,
25 name: WasmPtr<u8, M>,
26 name_len: M::Offset,
27 args: WasmPtr<u8, M>,
28 args_len: M::Offset,
29 envs: WasmPtr<u8, M>,
30 envs_len: M::Offset,
31 search_path: Bool,
32 path: WasmPtr<u8, M>,
33 path_len: M::Offset,
34) -> Result<Errno, WasiError> {
35 WasiEnv::do_pending_operations(&mut ctx)?;
36
37 if let Some(exit_code) = unsafe { handle_rewind::<M, i32>(&mut ctx) } {
39 let exit_code = ExitCode::from_native(exit_code);
42 ctx.data().process.terminate(exit_code);
43 return Err(WasiError::Exit(exit_code));
44 }
45
46 let memory = unsafe { ctx.data().memory_view(&ctx) };
47 let mut name = name.read_utf8_string(&memory, name_len).map_err(|err| {
48 warn!("failed to execve as the name could not be read - {}", err);
49 WasiError::Exit(Errno::Inval.into())
50 })?;
51 Span::current().record("name", name.as_str());
52 let args = args.read_utf8_string(&memory, args_len).map_err(|err| {
53 warn!("failed to execve as the args could not be read - {}", err);
54 WasiError::Exit(Errno::Inval.into())
55 })?;
56 let args: Vec<_> = args
57 .split(&['\n', '\r'])
58 .map(|a| a.to_string())
59 .filter(|a| !a.is_empty())
60 .collect();
61
62 let envs = if !envs.is_null() {
63 let envs = envs.read_utf8_string(&memory, envs_len).map_err(|err| {
64 warn!("failed to execve as the envs could not be read - {}", err);
65 WasiError::Exit(Errno::Inval.into())
66 })?;
67
68 let envs = envs
69 .split(&['\n', '\r'])
70 .map(|a| a.to_string())
71 .filter(|a| !a.is_empty());
72
73 let mut vec = vec![];
74 for env in envs {
75 let (key, value) = wasi_try_ok!(env.split_once('=').ok_or(Errno::Inval));
76
77 vec.push((key.to_string(), value.to_string()));
78 }
79
80 Some(vec)
81 } else {
82 None
83 };
84
85 if search_path == Bool::True && !name.contains('/') {
87 let path_str;
88
89 let path = if path.is_null() {
90 vec!["/usr/local/bin", "/bin", "/usr/bin"]
91 } else {
92 path_str = path.read_utf8_string(&memory, path_len).map_err(|err| {
93 warn!("failed to execve as the path could not be read - {}", err);
94 WasiError::Exit(Errno::Inval.into())
95 })?;
96 path_str.split(':').collect()
97 };
98 let (_, state, inodes) =
99 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
100 match find_executable_in_path(&state.fs, inodes, path.iter().map(AsRef::as_ref), &name) {
101 FindExecutableResult::Found(p) => name = p,
102 FindExecutableResult::AccessError => return Ok(Errno::Access),
103 FindExecutableResult::NotFound => return Ok(Errno::Noexec),
104 }
105 } else if name.starts_with("./") {
106 name = ctx.data().state.fs.relative_path_to_absolute(name);
107 }
108
109 trace!(name);
110
111 let preopen = ctx.data().state.preopen.clone();
113
114 let (_, cur_dir) = {
116 let (memory, state, inodes) =
117 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
118 match state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD) {
119 Ok(a) => a,
120 Err(err) => {
121 warn!("failed to create subprocess for fork - {}", err);
122 return Err(WasiError::Exit(err.into()));
123 }
124 }
125 };
126
127 let new_store = ctx.data().runtime.new_store();
128
129 if let Some(mut vfork) = ctx.data_mut().vfork.take() {
132 let mut child_env = Box::new(ctx.data().clone());
134
135 let child_pid = ctx.data().process.pid();
137
138 tracing::debug!(
139 %child_pid,
140 vfork_pid = %vfork.env.process.pid(),
141 "proc_exec in presence of vfork"
142 );
143
144 let mut vfork_env = vfork.env.clone();
146 vfork_env.swap_inner(ctx.data_mut());
147 std::mem::swap(vfork_env.as_mut(), ctx.data_mut());
148 let mut wasi_env = *vfork_env;
149 wasi_env.owned_handles.push(vfork.handle.clone());
150 _prepare_wasi(&mut wasi_env, Some(args), envs, None);
151
152 let stack_lower = wasi_env.layout.stack_lower;
154 let stack_upper = wasi_env.layout.stack_upper;
155
156 let mut err_exit_code: ExitCode = Errno::Success.into();
158
159 let spawn_result = {
160 let bin_factory = Box::new(ctx.data().bin_factory.clone());
161 let tasks = wasi_env.tasks().clone();
162
163 let mut config = Some(wasi_env);
164
165 match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut config) {
166 Ok(a) => Ok(()),
167 Err(err) => {
168 if !err.is_not_found() {
169 error!("builtin failed - {}", err);
170 }
171
172 let env = config.take().unwrap();
173
174 let name_inner = name.clone();
175 __asyncify_light(ctx.data(), None, async {
176 let ret = bin_factory.spawn(name_inner, env).await;
177 match ret {
178 Ok(ret) => {
179 trace!(%child_pid, "spawned sub-process");
180 Ok(())
181 }
182 Err(err) => {
183 err_exit_code = conv_spawn_err_to_exit_code(&err);
184
185 debug!(%child_pid, "process failed with (err={})", err_exit_code);
186
187 Err(Errno::Noexec)
188 }
189 }
190 })
191 .unwrap()
192 }
193 }
194 };
195
196 match spawn_result {
197 Err(e) => {
198 child_env.swap_inner(ctx.data_mut());
200 std::mem::swap(child_env.as_mut(), ctx.data_mut());
201
202 ctx.data_mut().vfork = Some(vfork);
204 return Ok(e);
205 }
206 Ok(()) => {
207 ctx.data_mut().swap_inner(&mut vfork.env);
209 std::mem::swap(ctx.data_mut(), &mut vfork.env);
210
211 assert!(vfork.env.context_switching_environment.is_none());
212 assert!(ctx.data().context_switching_environment.is_some());
213
214 let Some(asyncify_info) = vfork.asyncify else {
215 return Ok(Errno::Success);
220 };
221
222 let rewind_stack = asyncify_info.rewind_stack.freeze();
225 let store_data = asyncify_info.store_data;
226 unwind::<M, _>(ctx, move |mut ctx, _, _| {
227 match rewind::<M, _>(
229 ctx,
230 None,
231 rewind_stack,
232 store_data,
233 ForkResult {
234 pid: child_pid.raw() as Pid,
235 ret: Errno::Success,
236 },
237 ) {
238 Errno::Success => OnCalledAction::InvokeAgain,
239 err => {
240 warn!("fork failed - could not rewind the stack - errno={}", err);
241 OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
242 }
243 }
244 })?;
245 Ok(Errno::Success)
246 }
247 }
248 }
249 else {
253 let mut wasi_env = ctx.data().clone();
255 _prepare_wasi(&mut wasi_env, Some(args), envs, None);
256
257 let bin_factory = ctx.data().bin_factory.clone();
259 let tasks = wasi_env.tasks().clone();
260
261 let bin_factory = Box::new(ctx.data().bin_factory.clone());
263
264 let mut builder = Some(wasi_env);
265
266 let process = match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) {
267 Ok(a) => Ok(a),
268 Err(err) => {
269 if !err.is_not_found() {
270 error!("builtin failed - {}", err);
271 }
272
273 let env = builder.take().unwrap();
274
275 block_on(bin_factory.spawn(name, env))
277 }
278 };
279
280 match process {
281 Ok(mut process) => {
282 let env = ctx.data();
284
285 let thread = env.thread.clone();
286
287 let res = __asyncify_with_deep_sleep::<M, _, _>(ctx, async move {
289 process
290 .wait_finished()
291 .await
292 .unwrap_or_else(|_| Errno::Child.into())
293 .to_native()
294 })?;
295 match res {
296 AsyncifyAction::Finish(mut ctx, result) => {
297 let exit_code = ExitCode::from_native(result);
299 ctx.data().process.terminate(exit_code);
300 WasiEnv::process_signals_and_exit(&mut ctx)?;
301 Err(WasiError::Exit(Errno::Unknown.into()))
302 }
303 AsyncifyAction::Unwind => Ok(Errno::Success),
304 }
305 }
306 Err(err) => {
307 warn!(
308 "failed to execve as the process could not be spawned (fork)[0] - {}",
309 err
310 );
311 Ok(Errno::Noexec)
312 }
313 }
314 }
315}
316
317pub(crate) enum FindExecutableResult {
318 Found(String),
319 AccessError,
320 NotFound,
321}
322
323pub(crate) fn find_executable_in_path<'a>(
324 fs: &WasiFs,
325 inodes: &WasiInodes,
326 path: impl IntoIterator<Item = &'a str>,
327 file_name: &str,
328) -> FindExecutableResult {
329 let mut encountered_eaccess = false;
330 for p in path {
331 let full_path = format!("{}/{}", p.trim_end_matches('/'), file_name);
332 match fs.get_inode_at_path(inodes, VIRTUAL_ROOT_FD, &full_path, true) {
333 Ok(_) => return FindExecutableResult::Found(full_path),
334 Err(Errno::Access) => encountered_eaccess = true,
335 Err(_) => (),
336 }
337 }
338
339 if encountered_eaccess {
340 FindExecutableResult::AccessError
341 } else {
342 FindExecutableResult::NotFound
343 }
344}