1#![no_std]
91
92mod syscalls;
93
94const MFD_CLOEXEC: u8 = 0x1;
95
96#[used]
97pub static EMPTY_STRING: [u8; 8] = [0; 8];
98
99#[derive(Debug)]
101pub enum RunError {
102 FdCreationFailed(i32),
104 BytesNotWritten(usize, usize),
106 ExecError(i32),
108 ForkError(i32),
110 WaitError(i32),
112 InvalidElfFormat,
114 TooManyArgs,
116 TooManyEnvVars,
118 ArgTooLong,
120 EnvVarTooLong,
122}
123
124const MAX_ARGS: usize = 32;
125const MAX_ENV: usize = 64;
126const MAX_STRING_LEN: usize = 256;
127
128#[derive(Clone, Default)]
130pub struct RunOptions<'a> {
131 replace: bool,
132 args: Option<&'a [&'a str]>,
133 env: Option<&'a [&'a str]>,
134 argv0: Option<&'a str>,
135}
136
137impl<'a> RunOptions<'a> {
138 pub fn new() -> Self {
142 Self::default()
143 }
144
145 pub fn with_replace(mut self, replace: bool) -> Self {
148 self.replace = replace;
149 self
150 }
151
152 pub fn with_args(mut self, args: &'a [&'a str]) -> Self {
167 self.args = Some(args);
168 self
169 }
170
171 pub fn with_env(mut self, env: &'a [&'a str]) -> Self {
183 self.env = Some(env);
184 self
185 }
186
187 pub fn with_argv0(mut self, argv0: &'a str) -> Self {
204 self.argv0 = Some(argv0);
205 self
206 }
207}
208
209pub fn run<B: AsRef<[u8]>>(bytes: B) -> Result<i32, RunError> {
233 run_with_options(bytes, RunOptions::default())
234}
235
236pub fn run_with_options<B: AsRef<[u8]>>(
265 bytes: B,
266 options: RunOptions<'_>,
267) -> Result<i32, RunError> {
268 let fd = create_fd()?;
269 let bytes = bytes.as_ref();
270 write_bytes(fd, bytes)?;
271 execute(fd, options)
272}
273
274fn create_fd() -> Result<u16, RunError> {
275 let fd = unsafe { syscalls::memfd_create(EMPTY_STRING, MFD_CLOEXEC as u32) };
277 if fd == -1 {
278 return Err(RunError::FdCreationFailed(-1)); }
280 Ok(fd as _)
281}
282
283fn validate_elf_header(bytes: &[u8]) -> bool {
284 if bytes.len() < 16 {
286 return false;
287 }
288 bytes[0] == 0x7f && bytes[1] == b'E' && bytes[2] == b'L' && bytes[3] == b'F'
290}
291
292fn write_bytes(fd: u16, bytes: &[u8]) -> Result<(), RunError> {
293 if !validate_elf_header(bytes) {
294 unsafe { syscalls::close(fd as i32) };
295 return Err(RunError::InvalidElfFormat);
296 }
297 let written = unsafe { syscalls::write(fd as _, bytes.as_ptr().cast_mut(), bytes.len()) };
298 if written != bytes.len() as _ {
299 unsafe { syscalls::close(fd as i32) };
300 return Err(RunError::BytesNotWritten(written as usize, bytes.len()));
301 }
302 Ok(())
303}
304
305fn prepare_argv(
308 fd: u16,
309 options: &RunOptions<'_>,
310 storage: &mut [[u8; MAX_STRING_LEN]; MAX_ARGS],
311 ptrs: &mut [*const u8; MAX_ARGS + 1],
312) -> Result<usize, RunError> {
313 if let Some(custom_argv0) = options.argv0 {
315 let argv0_bytes = custom_argv0.as_bytes();
316 if argv0_bytes.len() >= MAX_STRING_LEN {
317 return Err(RunError::ArgTooLong);
318 }
319 storage[0][..argv0_bytes.len()].copy_from_slice(argv0_bytes);
320 storage[0][argv0_bytes.len()] = 0; } else {
322 let path = build_path(fd);
324 let null_pos = path.iter().position(|&b| b == 0).unwrap();
325 storage[0][..null_pos].copy_from_slice(&path[..null_pos]);
326 storage[0][null_pos] = 0; }
328
329 let mut arg_count = 1;
331 if let Some(user_args) = options.args {
332 if user_args.len() > MAX_ARGS - 1 {
333 return Err(RunError::TooManyArgs);
334 }
335
336 for &arg in user_args.iter() {
337 let arg_bytes = arg.as_bytes();
338 if arg_bytes.len() >= MAX_STRING_LEN {
339 return Err(RunError::ArgTooLong);
340 }
341
342 storage[arg_count][..arg_bytes.len()].copy_from_slice(arg_bytes);
343 storage[arg_count][arg_bytes.len()] = 0; arg_count += 1;
345 }
346 }
347
348 for i in 0..arg_count {
350 ptrs[i] = storage[i].as_ptr();
351 }
352 ptrs[arg_count] = core::ptr::null();
353
354 Ok(arg_count)
355}
356
357fn prepare_envp(
360 env: Option<&[&str]>,
361 storage: &mut [[u8; MAX_STRING_LEN]; MAX_ENV],
362 ptrs: &mut [*const u8; MAX_ENV + 1],
363) -> Result<usize, RunError> {
364 let mut env_count = 0;
365
366 if let Some(user_env) = env {
367 if user_env.len() > MAX_ENV {
368 return Err(RunError::TooManyEnvVars);
369 }
370
371 for (i, &env_var) in user_env.iter().enumerate() {
372 let env_bytes = env_var.as_bytes();
373 if env_bytes.len() >= MAX_STRING_LEN {
374 return Err(RunError::EnvVarTooLong);
375 }
376
377 storage[i][..env_bytes.len()].copy_from_slice(env_bytes);
378 storage[i][env_bytes.len()] = 0; ptrs[i] = storage[i].as_ptr();
380 env_count += 1;
381 }
382 }
383 ptrs[env_count] = core::ptr::null();
384 Ok(env_count)
385}
386
387fn execute_child(fd: u16, options: &RunOptions<'_>) -> Result<i32, RunError> {
389 let path = build_path(fd);
390
391 let mut argv_storage: [[u8; MAX_STRING_LEN]; MAX_ARGS] = [[0; MAX_STRING_LEN]; MAX_ARGS];
393 let mut argv: [*const u8; MAX_ARGS + 1] = [core::ptr::null(); MAX_ARGS + 1];
394
395 let mut envp_storage: [[u8; MAX_STRING_LEN]; MAX_ENV] = [[0; MAX_STRING_LEN]; MAX_ENV];
396 let mut envp: [*const u8; MAX_ENV + 1] = [core::ptr::null(); MAX_ENV + 1];
397
398 prepare_argv(fd, options, &mut argv_storage, &mut argv)?;
400 prepare_envp(options.env, &mut envp_storage, &mut envp)?;
401
402 let ret = unsafe { syscalls::execve(path, argv.as_ptr() as *mut u8, envp.as_ptr() as *mut u8) };
405
406 if ret == -1 {
407 return Err(RunError::ExecError(-1)); }
409 unreachable!("execve should not return on success");
410}
411
412fn execute(fd: u16, options: RunOptions<'_>) -> Result<i32, RunError> {
413 let pid = match options.replace {
414 true => 0, false => unsafe { syscalls::fork() },
416 };
417
418 match pid {
420 0 => execute_child(fd, &options),
421 -1 => Err(RunError::ForkError(-1)), _ => {
423 let mut status: i32 = 0;
424 let waited_pid = unsafe {
425 syscalls::wait4(
426 pid,
427 &mut status as *mut i32 as *mut u8,
428 0,
429 core::ptr::null_mut(),
430 )
431 };
432 if waited_pid == -1 {
433 return Err(RunError::WaitError(-1)); }
435 Ok((status >> 8) & 0xff)
437 }
438 }
439}
440
441const EXEC_PATH: [u8; 20] = *b"/proc/self/fd/\0\0\0\0\0\0";
442const EXEC_PATH_LEN: usize = EXEC_PATH.len();
443
444fn build_path(fd: u16) -> [u8; EXEC_PATH_LEN] {
445 let mut path = [0u8; EXEC_PATH_LEN];
446
447 unsafe { core::ptr::copy_nonoverlapping(EXEC_PATH.as_ptr(), path.as_mut_ptr(), EXEC_PATH_LEN) };
448 let mut idx = 14;
449 if fd >= 10000 {
450 path[idx] = b'0' + (fd / 10000) as u8;
451 idx += 1;
452 }
453 if fd >= 1000 {
454 path[idx] = b'0' + ((fd / 1000) % 10) as u8;
455 idx += 1;
456 }
457 if fd >= 100 {
458 path[idx] = b'0' + ((fd / 100) % 10) as u8;
459 idx += 1;
460 }
461 if fd >= 10 {
462 path[idx] = b'0' + ((fd / 10) % 10) as u8;
463 idx += 1;
464 }
465 path[idx] = b'0' + (fd % 10) as u8;
466 path
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472 extern crate std;
473 use std::format;
474
475 fn test_prepare_argv(fd: u16, options: &RunOptions<'_>) -> Result<usize, RunError> {
477 let mut storage: [[u8; MAX_STRING_LEN]; MAX_ARGS] = [[0; MAX_STRING_LEN]; MAX_ARGS];
478 let mut ptrs: [*const u8; MAX_ARGS + 1] = [core::ptr::null(); MAX_ARGS + 1];
479 prepare_argv(fd, options, &mut storage, &mut ptrs)
480 }
481
482 fn test_prepare_envp(env: Option<&[&str]>) -> Result<usize, RunError> {
483 let mut storage: [[u8; MAX_STRING_LEN]; MAX_ENV] = [[0; MAX_STRING_LEN]; MAX_ENV];
484 let mut ptrs: [*const u8; MAX_ENV + 1] = [core::ptr::null(); MAX_ENV + 1];
485 prepare_envp(env, &mut storage, &mut ptrs)
486 }
487
488 #[test]
490 fn test_run_options_default() {
491 let options = RunOptions::new();
492 assert!(!options.replace);
493 assert!(options.args.is_none());
494 assert!(options.env.is_none());
495 }
496
497 #[test]
498 fn test_run_options_with_replace() {
499 let options = RunOptions::new().with_replace(true);
500 assert!(options.replace);
501 }
502
503 #[test]
504 fn test_run_options_with_args() {
505 let args = ["test", "arg1", "arg2"];
506 let options = RunOptions::new().with_args(&args);
507 assert!(options.args.is_some());
508 assert_eq!(options.args.unwrap().len(), 3);
509 assert_eq!(options.args.unwrap()[0], "test");
510 assert_eq!(options.args.unwrap()[1], "arg1");
511 assert_eq!(options.args.unwrap()[2], "arg2");
512 }
513
514 #[test]
515 fn test_run_options_with_env() {
516 let env = ["PATH=/usr/bin", "HOME=/tmp"];
517 let options = RunOptions::new().with_env(&env);
518 assert!(options.env.is_some());
519 assert_eq!(options.env.unwrap().len(), 2);
520 assert_eq!(options.env.unwrap()[0], "PATH=/usr/bin");
521 assert_eq!(options.env.unwrap()[1], "HOME=/tmp");
522 }
523
524 #[test]
525 fn test_run_options_chaining() {
526 let args = ["test", "arg1"];
527 let env = ["VAR=value"];
528 let options = RunOptions::new()
529 .with_replace(true)
530 .with_args(&args)
531 .with_env(&env);
532
533 assert!(options.replace);
534 assert!(options.args.is_some());
535 assert!(options.env.is_some());
536 assert_eq!(options.args.unwrap().len(), 2);
537 assert_eq!(options.env.unwrap().len(), 1);
538 }
539
540 #[test]
541 fn test_run_options_with_argv0() {
542 let options = RunOptions::new().with_argv0("custom-program");
543 assert!(options.argv0.is_some());
544 assert_eq!(options.argv0.unwrap(), "custom-program");
545 }
546
547 #[test]
548 fn test_run_options_argv0_chaining() {
549 let args = ["test", "arg1"];
550 let env = ["VAR=value"];
551 let options = RunOptions::new()
552 .with_argv0("my-program")
553 .with_args(&args)
554 .with_env(&env);
555
556 assert!(options.argv0.is_some());
557 assert_eq!(options.argv0.unwrap(), "my-program");
558 assert!(options.args.is_some());
559 assert!(options.env.is_some());
560 }
561
562 #[test]
564 fn test_prepare_argv_path_only() {
565 let options = RunOptions::new();
566 let result = test_prepare_argv(123, &options);
567 assert!(result.is_ok());
568 assert_eq!(result.unwrap(), 1);
569 }
570
571 #[test]
572 fn test_prepare_argv_with_args() {
573 let args = ["arg1", "arg2"];
574 let options = RunOptions::new().with_args(&args);
575 let result = test_prepare_argv(123, &options);
576 assert!(result.is_ok());
577 assert_eq!(result.unwrap(), 3);
578 }
579
580 #[test]
581 fn test_prepare_argv_too_many_args() {
582 let mut args = std::vec::Vec::new();
583 for _i in 0..MAX_ARGS {
584 args.push("arg");
585 }
586 let options = RunOptions::new().with_args(&args);
587 let result = test_prepare_argv(123, &options);
588 assert!(matches!(result, Err(RunError::TooManyArgs)));
589 }
590
591 #[test]
592 fn test_prepare_argv_arg_too_long() {
593 let long_arg = "a".repeat(MAX_STRING_LEN);
594 let args = [long_arg.as_str()];
595 let options = RunOptions::new().with_args(&args);
596 let result = test_prepare_argv(123, &options);
597 assert!(matches!(result, Err(RunError::ArgTooLong)));
598 }
599
600 #[test]
601 fn test_prepare_argv_custom_argv0() {
602 let options = RunOptions::new().with_argv0("my-custom-program");
603 let result = test_prepare_argv(123, &options);
604 assert!(result.is_ok());
605 assert_eq!(result.unwrap(), 1);
606 }
607
608 #[test]
609 fn test_prepare_argv_custom_argv0_with_args() {
610 let args = ["arg1", "arg2"];
611 let options = RunOptions::new().with_argv0("custom-name").with_args(&args);
612 let result = test_prepare_argv(123, &options);
613 assert!(result.is_ok());
614 assert_eq!(result.unwrap(), 3);
615 }
616
617 #[test]
618 fn test_prepare_argv_custom_argv0_too_long() {
619 let long_argv0 = "a".repeat(MAX_STRING_LEN);
620 let options = RunOptions::new().with_argv0(&long_argv0);
621 let result = test_prepare_argv(123, &options);
622 assert!(matches!(result, Err(RunError::ArgTooLong)));
623 }
624
625 #[test]
627 fn test_prepare_envp_none() {
628 let result = test_prepare_envp(None);
629 assert!(result.is_ok());
630 assert_eq!(result.unwrap(), 0);
631 }
632
633 #[test]
634 fn test_prepare_envp_with_env() {
635 let env = ["PATH=/usr/bin", "HOME=/tmp"];
636 let result = test_prepare_envp(Some(&env));
637 assert!(result.is_ok());
638 assert_eq!(result.unwrap(), 2);
639 }
640
641 #[test]
642 fn test_prepare_envp_too_many_vars() {
643 let mut env = std::vec::Vec::new();
644 for _i in 0..MAX_ENV + 1 {
645 env.push("VAR=value");
646 }
647 let result = test_prepare_envp(Some(&env));
648 assert!(matches!(result, Err(RunError::TooManyEnvVars)));
649 }
650
651 #[test]
652 fn test_prepare_envp_var_too_long() {
653 let long_var = format!("VAR={}", "a".repeat(MAX_STRING_LEN));
654 let env = [long_var.as_str()];
655 let result = test_prepare_envp(Some(&env));
656 assert!(matches!(result, Err(RunError::EnvVarTooLong)));
657 }
658
659 #[test]
661 fn test_new_error_types() {
662 let errors = [
663 RunError::TooManyArgs,
664 RunError::TooManyEnvVars,
665 RunError::ArgTooLong,
666 RunError::EnvVarTooLong,
667 ];
668
669 for error in &errors {
670 let debug_str = format!("{error:?}");
671 assert!(!debug_str.is_empty());
672 }
673 }
674}