wasmer_wasix/syscalls/wasix/
proc_exec4.rs1use virtual_mio::block_on;
2use wasmer::FromToNativeWasmType;
3
4use super::*;
5use crate::{VIRTUAL_ROOT_FD, WasiFs, syscalls::*};
6
7#[instrument(level = "trace", skip_all, fields(name = field::Empty, %args_len), ret)]
17pub fn proc_exec4<M: MemorySize>(
18 mut ctx: FunctionEnvMut<'_, WasiEnv>,
19 name: WasmPtr<u8, M>,
20 name_len: M::Offset,
21 args: WasmPtr<WasmPtr<u8, M>, M>,
22 args_len: M::Offset,
23 envs: WasmPtr<WasmPtr<u8, M>, M>,
24 envs_len: M::Offset,
25 search_path: Bool,
26 path: WasmPtr<u8, M>,
27 path_len: M::Offset,
28) -> Result<Errno, WasiError> {
29 WasiEnv::do_pending_operations(&mut ctx)?;
30
31 if let Some(exit_code) = unsafe { handle_rewind::<M, i32>(&mut ctx) } {
33 let exit_code = ExitCode::from_native(exit_code);
36 ctx.data().process.terminate(exit_code);
37 return Err(WasiError::Exit(exit_code));
38 }
39
40 let memory = unsafe { ctx.data().memory_view(&ctx) };
41 let mut name = name.read_utf8_string(&memory, name_len).map_err(|err| {
42 warn!("failed to execve as the name could not be read - {}", err);
43 WasiError::Exit(Errno::Inval.into())
44 })?;
45 Span::current().record("name", name.as_str());
46
47 let mut args = read_string_array(&memory, args, args_len).map_err(|err| {
48 warn!("failed to execve as the args could not be read - {}", err);
49 WasiError::Exit(err.into())
50 })?;
51 if args.is_empty() {
52 args.push(name.clone());
54 }
55
56 let envs = if !envs.is_null() {
57 let envs = read_string_array(&memory, envs, envs_len).map_err(|err| {
58 warn!("failed to execve as the envs could not be read - {}", err);
59 WasiError::Exit(err.into())
60 })?;
61 Some(parse_env_entries(envs).map_err(|err| WasiError::Exit(err.into()))?)
62 } else {
63 None
64 };
65
66 let path = if path.is_null() {
67 None
68 } else {
69 Some(path.read_utf8_string(&memory, path_len).map_err(|err| {
70 warn!("failed to execve as the path could not be read - {}", err);
71 WasiError::Exit(Errno::Inval.into())
72 })?)
73 };
74
75 proc_exec4_impl::<M>(ctx, &mut name, args, envs, search_path, path.as_deref())
76}
77
78pub(crate) fn proc_exec4_impl<M: MemorySize>(
79 mut ctx: FunctionEnvMut<'_, WasiEnv>,
80 name: &mut String,
81 args: Vec<String>,
82 envs: Option<Vec<(String, String)>>,
83 search_path: Bool,
84 path: Option<&str>,
85) -> Result<Errno, WasiError> {
86 let searches_path = search_path == Bool::True && !name.contains('/');
90 let invoked_as = (!searches_path).then(|| name.clone());
91
92 if search_path == Bool::True && !name.contains('/') {
94 let path = if let Some(path) = path {
95 path.split(':').collect::<Vec<_>>()
96 } else {
97 vec!["/usr/local/bin", "/bin", "/usr/bin"]
98 };
99 let (_, state, inodes) =
100 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
101 match find_executable_in_path(&state.fs, inodes, path.iter().map(AsRef::as_ref), name) {
102 FindExecutableResult::Found(p) => *name = p,
103 FindExecutableResult::AccessError => return Ok(Errno::Access),
104 FindExecutableResult::NotFound => return Ok(Errno::Noent),
105 }
106 } else if name.starts_with("./") {
107 *name = ctx.data().state.fs.relative_path_to_absolute(name.clone());
108 }
109
110 if search_path == Bool::False && !name.starts_with('/') {
111 *name = ctx.data().state.fs.relative_path_to_absolute(name.clone());
112 }
113
114 trace!(%name);
115
116 if name.split('/').any(|seg| seg.len() > 255) {
118 return Ok(Errno::Nametoolong);
119 }
120
121 if name.contains('/') {
122 let (_, state, inodes) =
123 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
124 match state
125 .fs
126 .get_inode_at_path(inodes, VIRTUAL_ROOT_FD, name, true)
127 {
128 Ok(_) => (),
129 Err(Errno::Notdir) => return Ok(Errno::Notdir),
130 Err(Errno::Noent) => return Ok(Errno::Noent),
131 Err(Errno::Access) => return Ok(Errno::Access),
132 Err(_) => (),
133 }
134 }
135
136 let preopen = ctx.data().state.preopen.clone();
138
139 let (_, cur_dir) = {
141 let (memory, state, inodes) =
142 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
143 match state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD) {
144 Ok(a) => a,
145 Err(err) => {
146 warn!("failed to create subprocess for fork - {}", err);
147 return Err(WasiError::Exit(err.into()));
148 }
149 }
150 };
151
152 let new_store = ctx.data().runtime.new_store();
153
154 if let Some(mut vfork) = ctx.data_mut().vfork.take() {
157 let mut child_env = Box::new(ctx.data().clone());
159
160 let child_pid = ctx.data().process.pid();
162
163 tracing::debug!(
164 %child_pid,
165 vfork_pid = %vfork.env.process.pid(),
166 "proc_exec in presence of vfork"
167 );
168
169 let mut vfork_env = vfork.env.clone();
171 vfork_env.swap_inner(ctx.data_mut());
172 std::mem::swap(vfork_env.as_mut(), ctx.data_mut());
173 let mut wasi_env = *vfork_env;
174 wasi_env.owned_handles.push(vfork.handle.clone());
175 _prepare_wasi(&mut wasi_env, Some(args), envs, None);
176
177 let stack_lower = wasi_env.layout.stack_lower;
179 let stack_upper = wasi_env.layout.stack_upper;
180
181 let mut err_exit_code: ExitCode = Errno::Success.into();
183
184 let spawn_result = {
185 let bin_factory = Box::new(ctx.data().bin_factory.clone());
186 let tasks = wasi_env.tasks().clone();
187
188 let mut config = Some(wasi_env);
189
190 match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut config) {
191 Ok(a) => Ok(()),
192 Err(err) => {
193 if !err.is_not_found() {
194 error!("builtin failed - {}", err);
195 }
196
197 let env = config.take().unwrap();
198
199 let name_inner = name.clone();
200 let invoked_as_inner = invoked_as.clone();
201 __asyncify_light(ctx.data(), None, async {
202 let ret = bin_factory.spawn(name_inner, invoked_as_inner, env).await;
203 match ret {
204 Ok(ret) => {
205 trace!(%child_pid, "spawned sub-process");
206 Ok(())
207 }
208 Err(err) => {
209 err_exit_code = conv_spawn_err_to_exit_code(&err);
210
211 debug!(%child_pid, "process failed with (err={})", err_exit_code);
212
213 Err(conv_spawn_err_to_errno(&err))
214 }
215 }
216 })
217 .unwrap()
218 }
219 }
220 };
221
222 match spawn_result {
223 Err(e) => {
224 child_env.swap_inner(ctx.data_mut());
226 std::mem::swap(child_env.as_mut(), ctx.data_mut());
227
228 ctx.data_mut().vfork = Some(vfork);
230 Ok(e)
231 }
232 Ok(()) => {
233 ctx.data_mut().swap_inner(&mut vfork.env);
235 std::mem::swap(ctx.data_mut(), &mut vfork.env);
236
237 let Some(asyncify_info) = vfork.asyncify else {
238 return Ok(Errno::Success);
243 };
244
245 let rewind_stack = asyncify_info.rewind_stack.freeze();
248 let store_data = asyncify_info.store_data;
249 unwind::<M, _>(ctx, move |mut ctx, _, _| {
250 match rewind::<M, _>(
252 ctx,
253 None,
254 rewind_stack,
255 store_data,
256 ForkResult {
257 pid: child_pid.raw() as Pid,
258 ret: Errno::Success,
259 },
260 ) {
261 Errno::Success => OnCalledAction::InvokeAgain,
262 err => {
263 warn!("fork failed - could not rewind the stack - errno={}", err);
264 OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
265 }
266 }
267 })?;
268 Ok(Errno::Success)
269 }
270 }
271 }
272 else {
276 let mut wasi_env = ctx.data().clone();
278 _prepare_wasi(&mut wasi_env, Some(args), envs, None);
279
280 let bin_factory = ctx.data().bin_factory.clone();
282 let tasks = wasi_env.tasks().clone();
283
284 let bin_factory = Box::new(ctx.data().bin_factory.clone());
286
287 let mut builder = Some(wasi_env);
288
289 let process = match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) {
290 Ok(a) => Ok(a),
291 Err(err) => {
292 if !err.is_not_found() {
293 error!("builtin failed - {}", err);
294 }
295
296 let env = builder.take().unwrap();
297
298 block_on(bin_factory.spawn(name.clone(), invoked_as.clone(), env))
300 }
301 };
302
303 match process {
304 Ok(mut process) => {
305 let env = ctx.data();
307
308 let thread = env.thread.clone();
309
310 let res = __asyncify_with_deep_sleep::<M, _, _>(ctx, async move {
312 process
313 .wait_finished()
314 .await
315 .unwrap_or_else(|_| Errno::Child.into())
316 .to_native()
317 })?;
318 match res {
319 AsyncifyAction::Finish(mut ctx, result) => {
320 let exit_code = ExitCode::from_native(result);
322 ctx.data().process.terminate(exit_code);
323 WasiEnv::process_signals_and_exit(&mut ctx)?;
324 Err(WasiError::Exit(Errno::Unknown.into()))
325 }
326 AsyncifyAction::Unwind => Ok(Errno::Success),
327 }
328 }
329 Err(err) => {
330 warn!(
331 "failed to execve as the process could not be spawned (fork)[0] - {}",
332 err
333 );
334 Ok(conv_spawn_err_to_errno(&err))
335 }
336 }
337 }
338}
339
340pub(crate) enum FindExecutableResult {
341 Found(String),
342 AccessError,
343 NotFound,
344}
345
346pub(crate) fn find_executable_in_path<'a>(
347 fs: &WasiFs,
348 inodes: &WasiInodes,
349 path: impl IntoIterator<Item = &'a str>,
350 file_name: &str,
351) -> FindExecutableResult {
352 let mut encountered_eaccess = false;
353 for p in path {
354 let full_path = format!("{}/{}", p.trim_end_matches('/'), file_name);
355 match fs.get_inode_at_path(inodes, VIRTUAL_ROOT_FD, &full_path, true) {
356 Ok(_) => return FindExecutableResult::Found(full_path),
357 Err(Errno::Access) => encountered_eaccess = true,
358 Err(_) => (),
359 }
360 }
361
362 if encountered_eaccess {
363 FindExecutableResult::AccessError
364 } else {
365 FindExecutableResult::NotFound
366 }
367}