Skip to main content

strat9_kernel/syscall/
exec.rs

1//! `execve()` syscall implementation.
2//! Replaces the current process image with a new one.
3
4use crate::{
5    memory::{AddressSpace, UserSliceRead, VmaFlags, VmaPageSize, VmaType},
6    process::{
7        current_task_clone,
8        elf::{load_elf_image, LoadedElfInfo, USER_STACK_BASE, USER_STACK_PAGES, USER_STACK_TOP},
9        get_task_ids_in_tgid,
10    },
11    syscall::{error::SyscallError, SyscallFrame},
12    vfs,
13};
14use alloc::vec::Vec;
15
16const AT_NULL: u64 = 0;
17const AT_PHDR: u64 = 3;
18const AT_PHENT: u64 = 4;
19const AT_PHNUM: u64 = 5;
20const AT_PAGESZ: u64 = 6;
21const AT_BASE: u64 = 7;
22const AT_ENTRY: u64 = 9;
23const AT_RANDOM: u64 = 25;
24const AT_EXECFN: u64 = 31;
25
26/// Read an executable image, borrowing static initfs bytes when available.
27fn read_exec_image(path: &str) -> Result<Option<Vec<u8>>, SyscallError> {
28    if crate::vfs::get_initfs_file_bytes(path).is_some() {
29        return Ok(None);
30    }
31
32    let fd = vfs::open(path, vfs::OpenFlags::READ)?;
33
34    const MAX_EXEC_SIZE: usize = 64 * 1024 * 1024;
35    let mut elf_data = Vec::new();
36    let mut buf = [0u8; 4096];
37    loop {
38        match vfs::read(fd, &mut buf) {
39            Ok(n) => {
40                if n == 0 {
41                    break;
42                }
43                if elf_data.len() + n > MAX_EXEC_SIZE {
44                    let _ = vfs::close(fd);
45                    return Err(SyscallError::OutOfMemory);
46                }
47                elf_data.extend_from_slice(&buf[..n]);
48            }
49            Err(e) => {
50                let _ = vfs::close(fd);
51                return Err(e);
52            }
53        }
54    }
55    let _ = vfs::close(fd);
56
57    Ok(Some(elf_data))
58}
59
60/// SYS_PROC_EXECVE (301): replace current process image.
61/// On success, does not return. On failure, returns an appropriate error code.
62/// This is the main syscall handler for execve, which performs the entire execve sequence:
63/// 1. Validate and read the executable image from the given path.
64/// 2. Create a new address space and load the ELF segments.
65/// 3. Set up the user stack with arguments, environment variables, and auxiliary vector.
66/// 4. Perform cleanup of the current process state (close fds, reset signals,
67///   clear TLS and TID pointer, etc) according to POSIX exec semantics.
68/// 5. Switch to the new address space and transfer control to the new image's entry point.
69/// The `setup_user_stack` function is a helper that performs step 3, which is complex enough to warrant its own function.
70/// The implementation assumes a simple model where sibling threads are not runnable during execve, which allows it to replace the entire address space without complex synchronization. This is a common approach in many kernels, but it does mean that multithreaded execve is not supported until the kernel can safely handle it.
71/// It also includes robust error handling to ensure that any failure during the execve sequence results in an appropriate error code without leaving the process in an inconsistent state.
72/// Note: This implementation does not currently support some features like setuid binaries, but it lays the groundwork for a full execve implementation with proper ELF loading and stack setup.
73///
74pub fn sys_execve(
75    frame: &mut SyscallFrame,
76    path_ptr: u64,
77    argv_ptr: u64,
78    envp_ptr: u64,
79) -> Result<u64, SyscallError> {
80    let current = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
81
82    // Replacing a shared address space while sibling threads are still runnable
83    // is unsafe in the current model. Refuse multithreaded exec until the
84    // kernel can synchronize and reap sibling threads atomically.
85    if get_task_ids_in_tgid(current.tgid).len() > 1 {
86        return Err(SyscallError::NotSupported);
87    }
88
89    let mut path_buf = [0u8; 4096];
90    let path_slice = UserSliceRead::new(path_ptr, 4096).map_err(|_| SyscallError::Fault)?;
91
92    let mut len = 0;
93
94    loop {
95        if len >= 4096 {
96            return Err(SyscallError::ArgumentListTooLong);
97        } // Reused error code
98        let b = path_slice.read_u8(len).map_err(|_| SyscallError::Fault)?;
99        if b == 0 {
100            break;
101        }
102        path_buf[len] = b;
103        len += 1;
104    }
105    let path_str =
106        core::str::from_utf8(&path_buf[..len]).map_err(|_| SyscallError::InvalidArgument)?;
107
108    let exec_path = vfs::resolve_and_check_path_for_current_task(path_str, true, false, true)?;
109
110    let owned_elf_data = read_exec_image(&exec_path)?;
111    let elf_data = owned_elf_data
112        .as_deref()
113        .or_else(|| crate::vfs::get_initfs_file_bytes(&exec_path))
114        .ok_or(SyscallError::NotFound)?;
115
116    if elf_data.len() < 4 {
117        return Err(SyscallError::ExecFormatError);
118    }
119
120    let new_as = AddressSpace::new_user().map_err(|_| SyscallError::OutOfMemory)?;
121    let new_as_arc = alloc::sync::Arc::new(new_as);
122    new_as_arc.set_owner_pid(current.pid);
123
124    let load_info = match load_elf_image(elf_data, &new_as_arc) {
125        Ok(info) => info,
126        Err("PT_INTERP execute denied") => return Err(SyscallError::PermissionDenied),
127        Err(_) => return Err(SyscallError::ExecFormatError),
128    };
129
130    let stack_flags = VmaFlags {
131        readable: true,
132        writable: true,
133        executable: false,
134        user_accessible: true,
135    };
136    new_as_arc
137        .map_region(
138            USER_STACK_BASE,
139            USER_STACK_PAGES,
140            stack_flags,
141            VmaType::Stack,
142            VmaPageSize::Small,
143        )
144        .map_err(|_| SyscallError::OutOfMemory)?;
145
146    let sp = setup_user_stack(
147        &new_as_arc,
148        argv_ptr,
149        envp_ptr,
150        &load_info,
151        path_str.as_bytes(),
152    )?;
153
154    // TLS setup (Variant II) if the ELF has a PT_TLS segment.
155    let mut new_fs_base = 0u64;
156    if load_info.tls_memsz > 0 {
157        let tls_align = core::cmp::max(load_info.tls_align, 8).next_power_of_two();
158        let aligned_memsz = (load_info.tls_memsz + tls_align - 1) & !(tls_align - 1);
159        let total_size = aligned_memsz + 8;
160        let n_pages = ((total_size + 4095) / 4096) as usize;
161        let tls_flags = VmaFlags {
162            readable: true,
163            writable: true,
164            executable: false,
165            user_accessible: true,
166        };
167        let tls_base = new_as_arc
168            .find_free_vma_range(0x7FFF_E000_0000, n_pages, VmaPageSize::Small)
169            .ok_or(SyscallError::OutOfMemory)?;
170        new_as_arc
171            .map_region(
172                tls_base,
173                n_pages,
174                tls_flags,
175                VmaType::Anonymous,
176                VmaPageSize::Small,
177            )
178            .map_err(|_| SyscallError::OutOfMemory)?;
179        if load_info.tls_filesz > 0 && load_info.tls_vaddr != 0 {
180            let src_vaddr = load_info.tls_vaddr;
181            let mut off = 0u64;
182            let mut tmp = [0u8; 256];
183            while off < load_info.tls_filesz {
184                let chunk = core::cmp::min(256, (load_info.tls_filesz - off) as usize);
185                crate::process::elf::read_user_mapped_bytes_pub(
186                    &new_as_arc,
187                    src_vaddr + off,
188                    &mut tmp[..chunk],
189                )
190                .map_err(|_| SyscallError::Fault)?;
191                crate::process::elf::write_user_mapped_bytes_pub(
192                    &new_as_arc,
193                    tls_base + off,
194                    &tmp[..chunk],
195                )
196                .map_err(|_| SyscallError::Fault)?;
197                off += chunk as u64;
198            }
199        }
200        let tp = tls_base + aligned_memsz;
201        crate::process::elf::write_user_u64_pub(&new_as_arc, tp, tp)
202            .map_err(|_| SyscallError::Fault)?;
203        new_fs_base = tp;
204    }
205
206    // === EXECVE CLEANUP (POSIX semantics) ===
207    // Now that ELF is valid and loaded, perform cleanup before switching address space.
208
209    // 1. Close all file descriptors with CLOEXEC flag
210    unsafe {
211        let fd_table = &mut *current.process.fd_table.get();
212        fd_table.close_cloexec();
213    }
214
215    // 2. Reset all signal handlers to SIG_DFL
216    current.reset_signals();
217
218    // 2b. POSIX: exec disables the alternate signal stack for the new image.
219    unsafe {
220        *current.signal_stack.get() = None;
221    }
222
223    // 3. Clear thread-local storage address and TID pointer : POSIX exec semantics.
224    current
225        .clear_child_tid
226        .store(0, core::sync::atomic::Ordering::Relaxed);
227    current
228        .user_fs_base
229        .store(new_fs_base, core::sync::atomic::Ordering::Relaxed);
230
231    // 4. Reset memory layout: brk and mmap_hint belong to the old image.
232    current
233        .process
234        .brk
235        .store(0, core::sync::atomic::Ordering::Relaxed);
236    current
237        .process
238        .mmap_hint
239        .store(0x0000_0000_6000_0000, core::sync::atomic::Ordering::Relaxed);
240    // Set FS.base MSR for the new image TLS (or 0 if no PT_TLS).
241    unsafe {
242        let lo = new_fs_base as u32;
243        let hi = (new_fs_base >> 32) as u32;
244        core::arch::asm!(
245            "mov ecx, 0xC0000100", // MSR_FS_BASE
246            "wrmsr",
247            in("eax") lo,
248            in("edx") hi,
249            options(nostack, preserves_flags),
250        );
251    }
252
253    let old_as = current.process.replace_address_space(new_as_arc.clone());
254
255    unsafe {
256        current.process.address_space_arc().switch_to();
257    }
258
259    frame.iret_rip = load_info.runtime_entry;
260    frame.iret_rsp = sp;
261    frame.iret_rflags = 0x200; // IF=1, clean slate for the new image
262
263    frame.rdi = 0;
264    frame.rsi = 0;
265    frame.rdx = 0;
266    frame.rcx = 0;
267    frame.r8 = 0;
268    frame.r9 = 0;
269    frame.r10 = 0;
270    frame.r11 = 0;
271    frame.rbx = 0;
272    frame.rbp = 0;
273    frame.r12 = 0;
274    frame.r13 = 0;
275    frame.r14 = 0;
276    frame.r15 = 0;
277    frame.rax = 0;
278
279    // Safely drop the old address space now that the new CR3 is loaded
280    drop(old_as);
281
282    Ok(0)
283}
284
285/// Performs the setup user stack operation.
286fn setup_user_stack(
287    new_as: &AddressSpace,
288    argv_ptr: u64,
289    envp_ptr: u64,
290    elf_info: &LoadedElfInfo,
291    exec_path: &[u8],
292) -> Result<u64, SyscallError> {
293    let args = read_string_array(argv_ptr)?;
294    let envs = read_string_array(envp_ptr)?;
295
296    let mut sp = USER_STACK_TOP;
297    let mut str_ptrs: Vec<u64> = Vec::with_capacity(args.len()); // stores pointers to arguments
298    let mut env_ptrs: Vec<u64> = Vec::with_capacity(envs.len()); // stores pointers to env vars
299
300    // Push strings to stack (highest addresses)
301    // We push them in reverse order so they appear in memory roughly sequentially for cache locality?
302    // Actually standard is to put them at very top. Order doesn't strictly matter as long as pointers are correct.
303    // We'll push ENV strings first (highest), then ARG strings.
304
305    // Push ENV strings
306    for env in envs.iter().rev() {
307        let len = (env.len() + 1) as u64;
308        sp -= len;
309        write_bytes_to_as(new_as, sp, env)?;
310        write_bytes_to_as(new_as, sp + env.len() as u64, &[0])?;
311        env_ptrs.push(sp);
312    }
313    // env_ptrs: [ptr_to_highest_env, ptr_to_second_highest...] which corresponds to [env[last], env[last-1]...]
314    // Userspace expects envp[0] to point to first env string.
315    // So we need to reverse env_ptrs to match original order.
316    env_ptrs.reverse();
317
318    // Push ARG strings
319    for arg in args.iter().rev() {
320        let len = (arg.len() + 1) as u64;
321        sp -= len;
322        write_bytes_to_as(new_as, sp, arg)?;
323        write_bytes_to_as(new_as, sp + arg.len() as u64, &[0])?;
324        str_ptrs.push(sp);
325    }
326    str_ptrs.reverse();
327
328    // Push exec path (for AT_EXECFN).
329    let mut execfn_ptr = 0u64;
330    if !exec_path.is_empty() {
331        let len = (exec_path.len() + 1) as u64;
332        sp -= len;
333        write_bytes_to_as(new_as, sp, exec_path)?;
334        write_bytes_to_as(new_as, sp + exec_path.len() as u64, &[0])?;
335        execfn_ptr = sp;
336    }
337
338    // Push 16 bytes of random seed for AT_RANDOM (deterministic fallback source).
339    sp -= 16;
340    let rand_ptr = sp;
341    let seed = generate_aux_random_seed();
342    write_bytes_to_as(new_as, rand_ptr, &seed)?;
343
344    // Align SP to 16 bytes for System V ABI
345    sp &= !0xF;
346
347    // Phase 2: Push auxv, pointer arrays, then argc.
348    let size_ptr = 8u64;
349
350    // auxv entries end with AT_NULL.
351    let mut auxv: Vec<(u64, u64)> = Vec::with_capacity(10);
352    auxv.push((AT_PHDR, elf_info.phdr_vaddr));
353    auxv.push((AT_PHENT, elf_info.phent as u64));
354    auxv.push((AT_PHNUM, elf_info.phnum as u64));
355    auxv.push((AT_PAGESZ, 4096));
356    if let Some(base) = elf_info.interp_base {
357        auxv.push((AT_BASE, base));
358    }
359    auxv.push((AT_ENTRY, elf_info.program_entry));
360    auxv.push((AT_RANDOM, rand_ptr));
361    if execfn_ptr != 0 {
362        auxv.push((AT_EXECFN, execfn_ptr));
363    }
364
365    // Reserve padding above auxv so the final entry RSP still points at argc
366    // while satisfying the SysV x86_64 16-byte alignment requirement.
367    let stack_words = 1u64
368        + (str_ptrs.len() as u64 + 1)
369        + (env_ptrs.len() as u64 + 1)
370        + ((auxv.len() as u64 + 1) * 2);
371    let align_pad = (0u64.wrapping_sub(stack_words * size_ptr)) & 0xF;
372    sp -= align_pad;
373
374    // AT_NULL terminator.
375    sp -= size_ptr;
376    write_u64_to_as(new_as, sp, 0)?;
377    sp -= size_ptr;
378    write_u64_to_as(new_as, sp, AT_NULL)?;
379    for &(key, val) in auxv.iter().rev() {
380        sp -= size_ptr;
381        write_u64_to_as(new_as, sp, val)?;
382        sp -= size_ptr;
383        write_u64_to_as(new_as, sp, key)?;
384    }
385
386    // Push ENVP array
387    // [NULL]
388    // [envp[n]]
389    // ...
390    // [envp[0]]
391    sp -= size_ptr;
392    write_u64_to_as(new_as, sp, 0)?; // NULL terminator
393
394    for &ptr in env_ptrs.iter().rev() {
395        sp -= size_ptr;
396        write_u64_to_as(new_as, sp, ptr)?;
397    }
398    // Note: sp now points to envp[0]
399
400    // Push ARGV array
401    // [NULL]
402    // [argv[n]]
403    // ...
404    // [argv[0]]
405    sp -= size_ptr;
406    write_u64_to_as(new_as, sp, 0)?; // NULL terminator
407
408    for &ptr in str_ptrs.iter().rev() {
409        sp -= size_ptr;
410        write_u64_to_as(new_as, sp, ptr)?;
411    }
412    // Note: sp now points to argv[0]
413
414    // Push ARGC
415    sp -= size_ptr;
416    write_u64_to_as(new_as, sp, args.len() as u64)?;
417
418    debug_assert_eq!(sp & 0xF, 0);
419
420    Ok(sp)
421}
422
423/// Reads string array.
424fn read_string_array(ptr: u64) -> Result<Vec<Vec<u8>>, SyscallError> {
425    let mut res = Vec::new();
426    if ptr == 0 {
427        return Ok(res);
428    }
429
430    let mut arr_off = 0;
431    loop {
432        // Read string pointer from user memory (current AS)
433        let str_ptr = match UserSliceRead::new(ptr + arr_off, 8) {
434            Ok(slice) => match slice.read_u64(0) {
435                Ok(p) => p,
436                Err(_) => return Err(SyscallError::Fault),
437            },
438            Err(_) => return Err(SyscallError::Fault),
439        };
440
441        if str_ptr == 0 {
442            break;
443        }
444        if res.len() > 1024 {
445            return Err(SyscallError::ArgumentListTooLong);
446        }
447
448        let mut s = Vec::new();
449        let mut i = 0;
450        loop {
451            if i > 4096 {
452                return Err(SyscallError::ArgumentListTooLong);
453            }
454            let b = match UserSliceRead::new(str_ptr + i, 1) {
455                Ok(slice) => match slice.read_u8(0) {
456                    Ok(byte) => byte,
457                    Err(_) => return Err(SyscallError::Fault),
458                },
459                Err(_) => return Err(SyscallError::Fault),
460            };
461            if b == 0 {
462                break;
463            }
464            s.push(b);
465            i += 1;
466        }
467        res.push(s);
468        arr_off += 8;
469    }
470    Ok(res)
471}
472
473/// Writes bytes to as.
474fn write_bytes_to_as(as_ref: &AddressSpace, vaddr: u64, data: &[u8]) -> Result<(), SyscallError> {
475    use x86_64::VirtAddr;
476    let mut written = 0;
477    // We assume data is small enough or we loop? Using unsafe pointer arithmetic.
478    // The `AddressSpace` methods like `translate` are needed.
479
480    // Since `load_elf_image` in `elf.rs` used `translate`, we should verify visibility.
481    // `AddressSpace` is usually public. `translate` is on `Mapper` trait?
482    // `AddressSpace` in `strat9` likely implements `Mapper` or has it.
483    // `elf.rs` used `user_as.translate(...)`.
484
485    // I need to import Translate? `AddressSpace` usually has `translate`.
486
487    while written < data.len() {
488        let curr_vaddr = vaddr + written as u64;
489        let page_offset = (curr_vaddr & 0xFFF) as usize;
490        let chunk_size = core::cmp::min(data.len() - written, 4096 - page_offset);
491
492        // translate might fail if page not mapped.
493        // `USER_STACK_BASE`..`USER_STACK_TOP` is mapped.
494        let phys = as_ref
495            .translate(VirtAddr::new(curr_vaddr))
496            .ok_or(SyscallError::Fault)?;
497        let virt = crate::memory::phys_to_virt(phys.as_u64()) as *mut u8;
498
499        unsafe {
500            core::ptr::copy_nonoverlapping(data.as_ptr().add(written), virt, chunk_size);
501        }
502        written += chunk_size;
503    }
504    Ok(())
505}
506
507/// Writes u64 to as.
508fn write_u64_to_as(as_ref: &AddressSpace, vaddr: u64, val: u64) -> Result<(), SyscallError> {
509    let bytes = val.to_ne_bytes();
510    write_bytes_to_as(as_ref, vaddr, &bytes)
511}
512
513/// Performs the generate aux random seed operation.
514fn generate_aux_random_seed() -> [u8; 16] {
515    let mut seed = [0u8; 16];
516    crate::entropy::fill_random(&mut seed);
517    seed
518}