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 if search_path == Bool::True && !name.contains('/') {
88 let path = if let Some(path) = path {
89 path.split(':').collect::<Vec<_>>()
90 } else {
91 vec!["/usr/local/bin", "/bin", "/usr/bin"]
92 };
93 let (_, state, inodes) =
94 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
95 match find_executable_in_path(&state.fs, inodes, path.iter().map(AsRef::as_ref), name) {
96 FindExecutableResult::Found(p) => *name = p,
97 FindExecutableResult::AccessError => return Ok(Errno::Access),
98 FindExecutableResult::NotFound => return Ok(Errno::Noent),
99 }
100 } else if name.starts_with("./") {
101 *name = ctx.data().state.fs.relative_path_to_absolute(name.clone());
102 }
103
104 if search_path == Bool::False && !name.starts_with('/') {
105 *name = ctx.data().state.fs.relative_path_to_absolute(name.clone());
106 }
107
108 trace!(%name);
109
110 if name.split('/').any(|seg| seg.len() > 255) {
112 return Ok(Errno::Nametoolong);
113 }
114
115 if name.contains('/') {
116 let (_, state, inodes) =
117 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
118 match state
119 .fs
120 .get_inode_at_path(inodes, VIRTUAL_ROOT_FD, name, true)
121 {
122 Ok(_) => (),
123 Err(Errno::Notdir) => return Ok(Errno::Notdir),
124 Err(Errno::Noent) => return Ok(Errno::Noent),
125 Err(Errno::Access) => return Ok(Errno::Access),
126 Err(_) => (),
127 }
128 }
129
130 let preopen = ctx.data().state.preopen.clone();
132
133 let (_, cur_dir) = {
135 let (memory, state, inodes) =
136 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
137 match state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD) {
138 Ok(a) => a,
139 Err(err) => {
140 warn!("failed to create subprocess for fork - {}", err);
141 return Err(WasiError::Exit(err.into()));
142 }
143 }
144 };
145
146 let new_store = ctx.data().runtime.new_store();
147
148 if let Some(mut vfork) = ctx.data_mut().vfork.take() {
151 let mut child_env = Box::new(ctx.data().clone());
153
154 let child_pid = ctx.data().process.pid();
156
157 tracing::debug!(
158 %child_pid,
159 vfork_pid = %vfork.env.process.pid(),
160 "proc_exec in presence of vfork"
161 );
162
163 let mut vfork_env = vfork.env.clone();
165 vfork_env.swap_inner(ctx.data_mut());
166 std::mem::swap(vfork_env.as_mut(), ctx.data_mut());
167 let mut wasi_env = *vfork_env;
168 wasi_env.owned_handles.push(vfork.handle.clone());
169 _prepare_wasi(&mut wasi_env, Some(args), envs, None);
170
171 let stack_lower = wasi_env.layout.stack_lower;
173 let stack_upper = wasi_env.layout.stack_upper;
174
175 let mut err_exit_code: ExitCode = Errno::Success.into();
177
178 let spawn_result = {
179 let bin_factory = Box::new(ctx.data().bin_factory.clone());
180 let tasks = wasi_env.tasks().clone();
181
182 let mut config = Some(wasi_env);
183
184 match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut config) {
185 Ok(a) => Ok(()),
186 Err(err) => {
187 if !err.is_not_found() {
188 error!("builtin failed - {}", err);
189 }
190
191 let env = config.take().unwrap();
192
193 let name_inner = name.clone();
194 __asyncify_light(ctx.data(), None, async {
195 let ret = bin_factory.spawn(name_inner, env).await;
196 match ret {
197 Ok(ret) => {
198 trace!(%child_pid, "spawned sub-process");
199 Ok(())
200 }
201 Err(err) => {
202 err_exit_code = conv_spawn_err_to_exit_code(&err);
203
204 debug!(%child_pid, "process failed with (err={})", err_exit_code);
205
206 Err(Errno::Noexec)
207 }
208 }
209 })
210 .unwrap()
211 }
212 }
213 };
214
215 match spawn_result {
216 Err(e) => {
217 child_env.swap_inner(ctx.data_mut());
219 std::mem::swap(child_env.as_mut(), ctx.data_mut());
220
221 ctx.data_mut().vfork = Some(vfork);
223 Ok(e)
224 }
225 Ok(()) => {
226 ctx.data_mut().swap_inner(&mut vfork.env);
228 std::mem::swap(ctx.data_mut(), &mut vfork.env);
229
230 let Some(asyncify_info) = vfork.asyncify else {
231 return Ok(Errno::Success);
236 };
237
238 let rewind_stack = asyncify_info.rewind_stack.freeze();
241 let store_data = asyncify_info.store_data;
242 unwind::<M, _>(ctx, move |mut ctx, _, _| {
243 match rewind::<M, _>(
245 ctx,
246 None,
247 rewind_stack,
248 store_data,
249 ForkResult {
250 pid: child_pid.raw() as Pid,
251 ret: Errno::Success,
252 },
253 ) {
254 Errno::Success => OnCalledAction::InvokeAgain,
255 err => {
256 warn!("fork failed - could not rewind the stack - errno={}", err);
257 OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
258 }
259 }
260 })?;
261 Ok(Errno::Success)
262 }
263 }
264 }
265 else {
269 let mut wasi_env = ctx.data().clone();
271 _prepare_wasi(&mut wasi_env, Some(args), envs, None);
272
273 let bin_factory = ctx.data().bin_factory.clone();
275 let tasks = wasi_env.tasks().clone();
276
277 let bin_factory = Box::new(ctx.data().bin_factory.clone());
279
280 let mut builder = Some(wasi_env);
281
282 let process = match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) {
283 Ok(a) => Ok(a),
284 Err(err) => {
285 if !err.is_not_found() {
286 error!("builtin failed - {}", err);
287 }
288
289 let env = builder.take().unwrap();
290
291 block_on(bin_factory.spawn(name.clone(), env))
293 }
294 };
295
296 match process {
297 Ok(mut process) => {
298 let env = ctx.data();
300
301 let thread = env.thread.clone();
302
303 let res = __asyncify_with_deep_sleep::<M, _, _>(ctx, async move {
305 process
306 .wait_finished()
307 .await
308 .unwrap_or_else(|_| Errno::Child.into())
309 .to_native()
310 })?;
311 match res {
312 AsyncifyAction::Finish(mut ctx, result) => {
313 let exit_code = ExitCode::from_native(result);
315 ctx.data().process.terminate(exit_code);
316 WasiEnv::process_signals_and_exit(&mut ctx)?;
317 Err(WasiError::Exit(Errno::Unknown.into()))
318 }
319 AsyncifyAction::Unwind => Ok(Errno::Success),
320 }
321 }
322 Err(err) => {
323 warn!(
324 "failed to execve as the process could not be spawned (fork)[0] - {}",
325 err
326 );
327 Ok(Errno::Noexec)
328 }
329 }
330 }
331}
332
333pub(crate) enum FindExecutableResult {
334 Found(String),
335 AccessError,
336 NotFound,
337}
338
339pub(crate) fn find_executable_in_path<'a>(
340 fs: &WasiFs,
341 inodes: &WasiInodes,
342 path: impl IntoIterator<Item = &'a str>,
343 file_name: &str,
344) -> FindExecutableResult {
345 let mut encountered_eaccess = false;
346 for p in path {
347 let full_path = format!("{}/{}", p.trim_end_matches('/'), file_name);
348 match fs.get_inode_at_path(inodes, VIRTUAL_ROOT_FD, &full_path, true) {
349 Ok(_) => return FindExecutableResult::Found(full_path),
350 Err(Errno::Access) => encountered_eaccess = true,
351 Err(_) => (),
352 }
353 }
354
355 if encountered_eaccess {
356 FindExecutableResult::AccessError
357 } else {
358 FindExecutableResult::NotFound
359 }
360}