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 mut args: Vec<_> = args
57 .trim_end_matches(['\r', '\n'])
58 .lines()
59 .map(str::to_owned)
60 .collect();
61 if args.is_empty() {
62 args.push(name.clone());
64 }
65
66 let envs = if !envs.is_null() {
67 let envs = envs.read_utf8_string(&memory, envs_len).map_err(|err| {
68 warn!("failed to execve as the envs could not be read - {}", err);
69 WasiError::Exit(Errno::Inval.into())
70 })?;
71
72 let envs = envs
73 .split(&['\n', '\r'])
74 .map(|a| a.to_string())
75 .filter(|a| !a.is_empty());
76
77 let mut vec = vec![];
78 for env in envs {
79 let (key, value) = wasi_try_ok!(env.split_once('=').ok_or(Errno::Inval));
80
81 vec.push((key.to_string(), value.to_string()));
82 }
83
84 Some(vec)
85 } else {
86 None
87 };
88
89 if search_path == Bool::True && !name.contains('/') {
91 let path_str;
92
93 let path = if path.is_null() {
94 vec!["/usr/local/bin", "/bin", "/usr/bin"]
95 } else {
96 path_str = path.read_utf8_string(&memory, path_len).map_err(|err| {
97 warn!("failed to execve as the path could not be read - {}", err);
98 WasiError::Exit(Errno::Inval.into())
99 })?;
100 path_str.split(':').collect()
101 };
102 let (_, state, inodes) =
103 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
104 match find_executable_in_path(&state.fs, inodes, path.iter().map(AsRef::as_ref), &name) {
105 FindExecutableResult::Found(p) => name = p,
106 FindExecutableResult::AccessError => return Ok(Errno::Access),
107 FindExecutableResult::NotFound => return Ok(Errno::Noent),
108 }
109 } else if name.starts_with("./") {
110 name = ctx.data().state.fs.relative_path_to_absolute(name);
111 }
112
113 if search_path == Bool::False && !name.starts_with('/') {
114 name = ctx.data().state.fs.relative_path_to_absolute(name);
115 }
116
117 trace!(name);
118
119 if name.split('/').any(|seg| seg.len() > 255) {
121 return Ok(Errno::Nametoolong);
122 }
123
124 if name.contains('/') {
125 let (_, state, inodes) =
126 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
127 match state
128 .fs
129 .get_inode_at_path(inodes, VIRTUAL_ROOT_FD, &name, true)
130 {
131 Ok(_) => (),
132 Err(Errno::Notdir) => return Ok(Errno::Notdir),
133 Err(Errno::Noent) => return Ok(Errno::Noent),
134 Err(Errno::Access) => return Ok(Errno::Access),
135 Err(_) => (),
136 }
137 }
138
139 let preopen = ctx.data().state.preopen.clone();
141
142 let (_, cur_dir) = {
144 let (memory, state, inodes) =
145 unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
146 match state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD) {
147 Ok(a) => a,
148 Err(err) => {
149 warn!("failed to create subprocess for fork - {}", err);
150 return Err(WasiError::Exit(err.into()));
151 }
152 }
153 };
154
155 let new_store = ctx.data().runtime.new_store();
156
157 if let Some(mut vfork) = ctx.data_mut().vfork.take() {
160 let mut child_env = Box::new(ctx.data().clone());
162
163 let child_pid = ctx.data().process.pid();
165
166 tracing::debug!(
167 %child_pid,
168 vfork_pid = %vfork.env.process.pid(),
169 "proc_exec in presence of vfork"
170 );
171
172 let mut vfork_env = vfork.env.clone();
174 vfork_env.swap_inner(ctx.data_mut());
175 std::mem::swap(vfork_env.as_mut(), ctx.data_mut());
176 let mut wasi_env = *vfork_env;
177 wasi_env.owned_handles.push(vfork.handle.clone());
178 _prepare_wasi(&mut wasi_env, Some(args), envs, None);
179
180 let stack_lower = wasi_env.layout.stack_lower;
182 let stack_upper = wasi_env.layout.stack_upper;
183
184 let mut err_exit_code: ExitCode = Errno::Success.into();
186
187 let spawn_result = {
188 let bin_factory = Box::new(ctx.data().bin_factory.clone());
189 let tasks = wasi_env.tasks().clone();
190
191 let mut config = Some(wasi_env);
192
193 match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut config) {
194 Ok(a) => Ok(()),
195 Err(err) => {
196 if !err.is_not_found() {
197 error!("builtin failed - {}", err);
198 }
199
200 let env = config.take().unwrap();
201
202 let name_inner = name.clone();
203 __asyncify_light(ctx.data(), None, async {
204 let ret = bin_factory.spawn(name_inner, env).await;
205 match ret {
206 Ok(ret) => {
207 trace!(%child_pid, "spawned sub-process");
208 Ok(())
209 }
210 Err(err) => {
211 err_exit_code = conv_spawn_err_to_exit_code(&err);
212
213 debug!(%child_pid, "process failed with (err={})", err_exit_code);
214
215 Err(Errno::Noexec)
216 }
217 }
218 })
219 .unwrap()
220 }
221 }
222 };
223
224 match spawn_result {
225 Err(e) => {
226 child_env.swap_inner(ctx.data_mut());
228 std::mem::swap(child_env.as_mut(), ctx.data_mut());
229
230 ctx.data_mut().vfork = Some(vfork);
232 return Ok(e);
233 }
234 Ok(()) => {
235 ctx.data_mut().swap_inner(&mut vfork.env);
237 std::mem::swap(ctx.data_mut(), &mut vfork.env);
238
239 let Some(asyncify_info) = vfork.asyncify else {
240 return Ok(Errno::Success);
245 };
246
247 let rewind_stack = asyncify_info.rewind_stack.freeze();
250 let store_data = asyncify_info.store_data;
251 unwind::<M, _>(ctx, move |mut ctx, _, _| {
252 match rewind::<M, _>(
254 ctx,
255 None,
256 rewind_stack,
257 store_data,
258 ForkResult {
259 pid: child_pid.raw() as Pid,
260 ret: Errno::Success,
261 },
262 ) {
263 Errno::Success => OnCalledAction::InvokeAgain,
264 err => {
265 warn!("fork failed - could not rewind the stack - errno={}", err);
266 OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
267 }
268 }
269 })?;
270 Ok(Errno::Success)
271 }
272 }
273 }
274 else {
278 let mut wasi_env = ctx.data().clone();
280 _prepare_wasi(&mut wasi_env, Some(args), envs, None);
281
282 let bin_factory = ctx.data().bin_factory.clone();
284 let tasks = wasi_env.tasks().clone();
285
286 let bin_factory = Box::new(ctx.data().bin_factory.clone());
288
289 let mut builder = Some(wasi_env);
290
291 let process = match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) {
292 Ok(a) => Ok(a),
293 Err(err) => {
294 if !err.is_not_found() {
295 error!("builtin failed - {}", err);
296 }
297
298 let env = builder.take().unwrap();
299
300 block_on(bin_factory.spawn(name, env))
302 }
303 };
304
305 match process {
306 Ok(mut process) => {
307 let env = ctx.data();
309
310 let thread = env.thread.clone();
311
312 let res = __asyncify_with_deep_sleep::<M, _, _>(ctx, async move {
314 process
315 .wait_finished()
316 .await
317 .unwrap_or_else(|_| Errno::Child.into())
318 .to_native()
319 })?;
320 match res {
321 AsyncifyAction::Finish(mut ctx, result) => {
322 let exit_code = ExitCode::from_native(result);
324 ctx.data().process.terminate(exit_code);
325 WasiEnv::process_signals_and_exit(&mut ctx)?;
326 Err(WasiError::Exit(Errno::Unknown.into()))
327 }
328 AsyncifyAction::Unwind => Ok(Errno::Success),
329 }
330 }
331 Err(err) => {
332 warn!(
333 "failed to execve as the process could not be spawned (fork)[0] - {}",
334 err
335 );
336 Ok(Errno::Noexec)
337 }
338 }
339 }
340}
341
342pub(crate) enum FindExecutableResult {
343 Found(String),
344 AccessError,
345 NotFound,
346}
347
348pub(crate) fn find_executable_in_path<'a>(
349 fs: &WasiFs,
350 inodes: &WasiInodes,
351 path: impl IntoIterator<Item = &'a str>,
352 file_name: &str,
353) -> FindExecutableResult {
354 let mut encountered_eaccess = false;
355 for p in path {
356 let full_path = format!("{}/{}", p.trim_end_matches('/'), file_name);
357 match fs.get_inode_at_path(inodes, VIRTUAL_ROOT_FD, &full_path, true) {
358 Ok(_) => return FindExecutableResult::Found(full_path),
359 Err(Errno::Access) => encountered_eaccess = true,
360 Err(_) => (),
361 }
362 }
363
364 if encountered_eaccess {
365 FindExecutableResult::AccessError
366 } else {
367 FindExecutableResult::NotFound
368 }
369}