From f17468582078026f28e3e084cb79b5c2a9c32ee1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 5 Jul 2022 14:06:59 +0200 Subject: [PATCH 001/135] Initial Commit. --- .gitignore | 1 + Cargo.lock | 113 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 25 ++++++++++ src/exec.rs | 41 ++++++++++++++++ src/lib.rs | 19 ++++++++ src/x86_64.asm | 42 +++++++++++++++++ src/x86_64.ld | 46 ++++++++++++++++++ src/x86_64.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 412 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/exec.rs create mode 100644 src/lib.rs create mode 100644 src/x86_64.asm create mode 100644 src/x86_64.ld create mode 100644 src/x86_64.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..a909a63bac --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,113 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bootstrap" +version = "0.0.0" +dependencies = [ + "arrayvec", + "goblin", + "libc", + "linked_list_allocator", + "memoffset", + "redox_syscall", + "scroll", +] + +[[package]] +name = "goblin" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32401e89c6446dcd28185931a01b1093726d0356820ac744023e6850689bf926" +dependencies = [ + "plain", + "scroll", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "linked_list_allocator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=userspace_fexec#933adb23683e9d6529ff2f6ffe7f1211c952897c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scroll" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" + +[[package]] +name = "spinning_top" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" +dependencies = [ + "lock_api", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..94cdc739ee --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "bootstrap" +version = "0.0.0" +authors = ["4lDO2 <4lDO2@protonmail.com>"] +edition = "2021" +license = "MIT" + +[lib] +name = "bootstrap" +crate-type = ["staticlib"] + +[dependencies] +arrayvec = { version = "0.7", default-features = false } +goblin = { version = "0.4", default-features = false, features = ["elf32", "elf64"] } +libc = "0.2" +memoffset = "0.6" +scroll = { version = "0.10", default-features = false } +linked_list_allocator = "0.9" +redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "userspace_fexec" } + +[profile.release] +panic = "abort" + +[profile.dev] +panic = "abort" diff --git a/src/exec.rs b/src/exec.rs new file mode 100644 index 0000000000..9f2563f290 --- /dev/null +++ b/src/exec.rs @@ -0,0 +1,41 @@ +#[no_mangle] +pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> libc::c_int { + let envp = { + let len = 4096; + let buf = libc::calloc(len, 1); + if buf.is_null() { panic!("failed to allocate env buf"); } + let env = core::slice::from_raw_parts_mut(buf as *mut u8, len); + + let fd = syscall::open("sys:env", syscall::O_RDONLY | syscall::O_CLOEXEC).expect("bootstrap: failed to open env"); + let bytes_read = syscall::read(fd, env).expect("bootstrap: failed to read env"); + + if bytes_read >= len { + // TODO: Handle this, we can allocate as much as we want in theory. + panic!("env is too large"); + } + + for c in &mut *env { + if *c == b'\n' { + *c = b'\0'; + } + } + let env_count = env.split(|c| *c == b'\0').count(); + + let envp = libc::calloc(env_count, core::mem::size_of::<*const u8>()) as *mut *const libc::c_char; + if envp.is_null() { panic!("failed to allocate envp buf"); } + + for (idx, var) in env.split(|c| *c == b'\0').enumerate() { + envp.add(idx).write(var.as_ptr() as *const libc::c_char); + } + + envp + }; + + let name_ptr = b"initfs:bin/init\0".as_ptr() as *const libc::c_char; + let argv = [name_ptr, core::ptr::null()]; + + if libc::execve(name_ptr, argv.as_ptr(), envp) == -1 { + libc::perror(b"Failed to execute init\0".as_ptr() as *const libc::c_char); + } + unreachable!() +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..c47e6c82cb --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,19 @@ +#![no_std] +#![feature(alloc_error_handler, core_intrinsics, lang_items)] + +#[cfg(target_arch = "x86_64")] +pub mod x86_64; + +pub mod exec; + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + core::intrinsics::abort(); +} + +#[alloc_error_handler] +fn alloc_error_handler(_: core::alloc::Layout) -> ! { + core::intrinsics::abort(); +} +#[lang = "eh_personality"] +extern "C" fn rust_eh_personality() {} diff --git a/src/x86_64.asm b/src/x86_64.asm new file mode 100644 index 0000000000..e8e49136c8 --- /dev/null +++ b/src/x86_64.asm @@ -0,0 +1,42 @@ +BITS 64 + +section .text + +global ustart +extern start + +ustart: + ; Setup a stack. + mov rax, 0x21000384 ; SYS_CLASS_FILE+SYS_ARG_SLICE+SYS_MMAP + mov rdi, 0xFFFFFFFFFFFFFFFF ; dummy `fd`, indicates anonymous map + mov rsi, map ; pointer to Map struct + mov rdx, map_size ; size of Map struct + syscall + + ; Test for success (nonzero value). + cmp rax, 0 + jg .continue + ; (failure) + ud2 +.continue: + ; Subtract 16 since all instructions seem to hate non-canonical RSP values :) + lea rsp, [rax+size-16] + mov rbp, rsp + + ; Stack has the same alignment as `size`. + call start + ; `start` must never return. + ud2 + +section .rodata +map: + dq 0 ; offset (unused) + dq size + dq flags + dq address - size + +map_size equ $ - map + +size equ 65536 ; 64 KiB +address equ 0x0000800000000000 ; highest possible (normal) user address, why not +flags equ 0x0006000E ; PROT_READ+PROT_WRITE, MAP_PRIVATE+MAP_FIXED_NOREPLACE diff --git a/src/x86_64.ld b/src/x86_64.ld new file mode 100644 index 0000000000..c8c89bc868 --- /dev/null +++ b/src/x86_64.ld @@ -0,0 +1,46 @@ +ENTRY(ustart) +OUTPUT_FORMAT(elf64-x86-64) + +SECTIONS { + . = 0; + . += SIZEOF_HEADERS; + . = ALIGN(4096); + + .text : { + __text_start = .; + *(.text*) + . = ALIGN(4096); + __text_end = .; + } + .rodata : { + __rodata_start = .; + *(.rodata*) + . = ALIGN(4096); + __rodata_end = .; + } + .data : { + __data_start = .; + *(.data*) + . = ALIGN(4096); + __data_end = .; + + *(.tbss*) + . = ALIGN(4096); + *(.tdata*) + . = ALIGN(4096); + + __bss_start = .; + *(.bss*) + . = ALIGN(4096); + __bss_end = .; + } + __end = .; + + /DISCARD/ : { + *(.comment*) + *(.eh_frame*) + *(.gcc_except_table*) + *(.note*) + *(.rel.eh_frame*) + } +} diff --git a/src/x86_64.rs b/src/x86_64.rs new file mode 100644 index 0000000000..199b19ff68 --- /dev/null +++ b/src/x86_64.rs @@ -0,0 +1,125 @@ +use syscall::flag::MapFlags; + +mod offsets { + extern "C" { + // text (R-X) + static __text_start: u8; + static __text_end: u8; + // rodata (R--) + static __rodata_start: u8; + static __rodata_end: u8; + // data+bss (RW-) + static __data_start: u8; + static __bss_end: u8; + + static __end: u8; + } + pub fn text() -> (usize, usize) { + unsafe { (&__text_start as *const u8 as usize, &__text_end as *const u8 as usize) } + } + pub fn rodata() -> (usize, usize) { + unsafe { (&__rodata_start as *const u8 as usize, &__rodata_end as *const u8 as usize) } + } + pub fn data_and_bss() -> (usize, usize) { + unsafe { (&__data_start as *const u8 as usize, &__bss_end as *const u8 as usize) } + } + #[allow(dead_code)] + pub fn end() -> usize { + unsafe { &__end as *const u8 as usize } + } +} + +// relibc linkage stuff +#[no_mangle] +extern "C" fn _init() {} +#[no_mangle] +extern "C" fn _fini() {} + +extern "C" fn nop() {} + +#[no_mangle] +static __preinit_array_start: extern "C" fn() = nop; +#[no_mangle] +static __preinit_array_end: extern "C" fn() = nop; +#[no_mangle] +static __init_array_start: extern "C" fn() = nop; +#[no_mangle] +static __init_array_end: extern "C" fn() = nop; + +#[no_mangle] +static __fini_array_start: extern "C" fn() = nop; +#[no_mangle] +static __fini_array_end: extern "C" fn() = nop; + +#[no_mangle] +pub unsafe extern "sysv64" fn start() -> ! { + // Remap self, from the previous RWX + + let (text_start, text_end) = offsets::text(); + let (rodata_start, rodata_end) = offsets::rodata(); + let (data_start, data_end) = offsets::data_and_bss(); + + let _ = syscall::open("debug:", syscall::O_RDONLY); // stdin + let _ = syscall::open("debug:", syscall::O_WRONLY); // stdout + let _ = syscall::open("debug:", syscall::O_WRONLY); // stderr + + let _ = syscall::mprotect(text_start, text_end - text_start, MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE).expect("mprotect failed for .text"); + let _ = syscall::mprotect(rodata_start, rodata_end - rodata_start, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for .rodata"); + let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); + + /*const FD_ANONYMOUS: usize = !0; + + { + let heap_size = 1024 * 1024; + let heap_start = syscall::fmap(FD_ANONYMOUS, &syscall::Map { offset: 0, address: 0, size: heap_size, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE }).expect("failed to map heap"); + crate::ALLOCATOR.0.get().write(linked_list_allocator::Heap::new(heap_start, heap_size)); + } + + let init_fd = syscall::open("initfs:/bin/init", syscall::O_CLOEXEC | syscall::O_RDONLY).expect("failed to open init"); + + const STACK_SIZE: usize = 1024 * 1024; + let stack = syscall::fmap(FD_ANONYMOUS, &syscall::Map { offset: 0, address: 0, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE, size: STACK_SIZE }).unwrap(); + + let stack_top = 1_usize << 48; + memranges.push(syscall::ExecMemRange { address: stack_top - STACK_SIZE, size: STACK_SIZE, flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE, old_address: stack }); + + // Now, push things such as argv, env (i.e. kernel cmdline when executing init) and auxv. + // TODO: Figure out reason behind subtraction by 256 (inherited from kernel behavior). + let mut target_sp = stack_top - 256; + let actual_sp = (stack + STACK_SIZE) as *mut usize; + + let mut push = |value: usize| { + target_sp -= core::mem::size_of::(); + actual_sp = actual_sp.sub(core::mem::size_of::()); + actual_sp.write(value); + };*/ + extern "C" { + fn relibc_start(stack: usize); + } + use goblin::elf::header::header64::Header; + use memoffset::offset_of; + + let stack = [ + // argc + 0, + // argv null terminator + 0_usize, + // envp null terminator + 0_usize, + + // Make the TLS part of ld.so happy, even though we do not use TLS. + syscall::AT_PHDR, + // The kernel loads the entire ELF, so the program header offset can be trivially received. + // TODO: Use goblin for the ELF header (except rust accepts no null pointers...). + (offset_of!(Header, e_phoff) as *mut u64).read() as usize, + syscall::AT_PHENT, + (offset_of!(Header, e_phentsize) as *mut u16).read() as usize, + syscall::AT_PHNUM, + (offset_of!(Header, e_phnum) as *mut u16).read() as usize, + // auxv null terminator + syscall::AT_NULL, + 0, + ]; + relibc_start(stack.as_ptr() as usize); + panic!(); +} From 3dac3d1ed6ee1cff8f5594ee947364658256fb6b Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 5 Jul 2022 14:24:17 +0200 Subject: [PATCH 002/135] Fix env parsing. --- src/exec.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index 9f2563f290..64741de024 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -13,18 +13,20 @@ pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> // TODO: Handle this, we can allocate as much as we want in theory. panic!("env is too large"); } + let env = &mut env[..bytes_read]; for c in &mut *env { if *c == b'\n' { *c = b'\0'; } } - let env_count = env.split(|c| *c == b'\0').count(); + let iter = || env.split(|c| *c == b'\0').filter(|var| !var.is_empty()); + let env_count = iter().count(); - let envp = libc::calloc(env_count, core::mem::size_of::<*const u8>()) as *mut *const libc::c_char; + let envp = libc::calloc(env_count + 1, core::mem::size_of::<*const u8>()) as *mut *const libc::c_char; if envp.is_null() { panic!("failed to allocate envp buf"); } - for (idx, var) in env.split(|c| *c == b'\0').enumerate() { + for (idx, var) in iter().enumerate() { envp.add(idx).write(var.as_ptr() as *const libc::c_char); } From 6eaec937d72236865064e6254502d93c4bb64703 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 7 Jul 2022 19:30:36 +0200 Subject: [PATCH 003/135] Remove commented code. --- src/x86_64.rs | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/x86_64.rs b/src/x86_64.rs index 199b19ff68..fe137247fb 100644 --- a/src/x86_64.rs +++ b/src/x86_64.rs @@ -67,32 +67,6 @@ pub unsafe extern "sysv64" fn start() -> ! { let _ = syscall::mprotect(rodata_start, rodata_end - rodata_start, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for .rodata"); let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); - /*const FD_ANONYMOUS: usize = !0; - - { - let heap_size = 1024 * 1024; - let heap_start = syscall::fmap(FD_ANONYMOUS, &syscall::Map { offset: 0, address: 0, size: heap_size, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE }).expect("failed to map heap"); - crate::ALLOCATOR.0.get().write(linked_list_allocator::Heap::new(heap_start, heap_size)); - } - - let init_fd = syscall::open("initfs:/bin/init", syscall::O_CLOEXEC | syscall::O_RDONLY).expect("failed to open init"); - - const STACK_SIZE: usize = 1024 * 1024; - let stack = syscall::fmap(FD_ANONYMOUS, &syscall::Map { offset: 0, address: 0, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE, size: STACK_SIZE }).unwrap(); - - let stack_top = 1_usize << 48; - memranges.push(syscall::ExecMemRange { address: stack_top - STACK_SIZE, size: STACK_SIZE, flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE, old_address: stack }); - - // Now, push things such as argv, env (i.e. kernel cmdline when executing init) and auxv. - // TODO: Figure out reason behind subtraction by 256 (inherited from kernel behavior). - let mut target_sp = stack_top - 256; - let actual_sp = (stack + STACK_SIZE) as *mut usize; - - let mut push = |value: usize| { - target_sp -= core::mem::size_of::(); - actual_sp = actual_sp.sub(core::mem::size_of::()); - actual_sp.write(value); - };*/ extern "C" { fn relibc_start(stack: usize); } From da090b007d4958b82b38ecb7b8fe5f1f7279ab45 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 12 Jul 2022 14:11:45 +0200 Subject: [PATCH 004/135] Serve the initfs scheme here --- Cargo.lock | 9 ++ Cargo.toml | 1 + src/exec.rs | 60 ++++++++++- src/initfs.rs | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 48 ++++++++- src/x86_64.rs | 4 - 6 files changed, 394 insertions(+), 7 deletions(-) create mode 100644 src/initfs.rs diff --git a/Cargo.lock b/Cargo.lock index a909a63bac..83e5b5499e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,7 @@ dependencies = [ "libc", "linked_list_allocator", "memoffset", + "redox-initfs", "redox_syscall", "scroll", ] @@ -83,6 +84,14 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "redox-initfs" +version = "0.1.0" +source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#89b8fb8984cf96c418880b7dcd9ce3d6afc3f71c" +dependencies = [ + "plain", +] + [[package]] name = "redox_syscall" version = "0.2.13" diff --git a/Cargo.toml b/Cargo.toml index 94cdc739ee..f336f0de67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ libc = "0.2" memoffset = "0.6" scroll = { version = "0.10", default-features = false } linked_list_allocator = "0.9" +redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "userspace_fexec" } [profile.release] diff --git a/src/exec.rs b/src/exec.rs index 64741de024..daf587b710 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,5 +1,7 @@ #[no_mangle] pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> libc::c_int { + let (initfs_offset, initfs_length); + let envp = { let len = 4096; let buf = libc::calloc(len, 1); @@ -20,7 +22,27 @@ pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> *c = b'\0'; } } - let iter = || env.split(|c| *c == b'\0').filter(|var| !var.is_empty()); + let raw_iter = || env.split(|c| *c == b'\0').filter(|var| !var.is_empty()); + + let mut initfs_offset_opt = None; + let mut initfs_length_opt = None; + + for var in raw_iter() { + let equal_sign = var.iter().position(|c| *c == b'=').expect("malformed environment variable"); + let name = &var[..equal_sign]; + let value = &var[equal_sign + 1..]; + + match name { + b"INITFS_OFFSET" => initfs_offset_opt = core::str::from_utf8(value).ok().and_then(|s| usize::from_str_radix(s, 16).ok()), + b"INITFS_LENGTH" => initfs_length_opt = core::str::from_utf8(value).ok().and_then(|s| usize::from_str_radix(s, 16).ok()), + + _ => continue, + } + } + initfs_offset = initfs_offset_opt.expect("missing INITFS_OFFSET"); + initfs_length = initfs_length_opt.expect("missing INITFS_LENGTH"); + + let iter = || raw_iter().filter(|var| !var.starts_with(b"INITFS_")); let env_count = iter().count(); let envp = libc::calloc(env_count + 1, core::mem::size_of::<*const u8>()) as *mut *const libc::c_char; @@ -32,12 +54,48 @@ pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> envp }; + { + use syscall::flag::MapFlags; + // XXX: It may be a little unsafe to mprotect this after relibc has started, but since only + // the bootloader can influence the data we use, it should be fine security-wise. + let _ = syscall::mprotect(initfs_offset, initfs_length, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for initfs"); + + spawn_initfs(initfs_offset, initfs_length); + } let name_ptr = b"initfs:bin/init\0".as_ptr() as *const libc::c_char; let argv = [name_ptr, core::ptr::null()]; if libc::execve(name_ptr, argv.as_ptr(), envp) == -1 { libc::perror(b"Failed to execute init\0".as_ptr() as *const libc::c_char); + panic!(); } unreachable!() } + +unsafe fn spawn_initfs(initfs_start: usize, initfs_length: usize) { + let mut buf = [0; 2]; + syscall::pipe2(&mut buf, syscall::O_CLOEXEC).expect("failed to open sync pipe"); + let [read, write] = buf; + + match libc::fork() { + -1 => { + libc::perror(b"Failed to fork in order to start initfs daemon\0".as_ptr() as *const libc::c_char); + panic!(); + } + // Continue serving the scheme as the child. + 0 => { + let _ = syscall::close(read); + } + // Return in order to execute init, as the parent. + _ => { + let _ = syscall::close(write); + let _ = syscall::read(read, &mut [0]); + + let _ = syscall::chdir("initfs:"); + + return; + } + } + crate::initfs::run(core::slice::from_raw_parts(initfs_start as *const u8, initfs_length), write); +} diff --git a/src/initfs.rs b/src/initfs.rs new file mode 100644 index 0000000000..06b5380cc2 --- /dev/null +++ b/src/initfs.rs @@ -0,0 +1,279 @@ +use core::convert::TryFrom; +use core::str; + +use alloc::collections::BTreeMap; +use alloc::string::String; + +use redox_initfs::{InitFs, InodeStruct, Inode, InodeDir, InodeKind, types::Timespec}; + +use syscall::data::{Packet, Stat}; +use syscall::error::*; +use syscall::flag::*; +use syscall::scheme::{calc_seek_offset_usize, SchemeMut}; + +struct Handle { + inode: Inode, + seek: usize, + // TODO: Any better way to implement fpath? Or maybe work around it, e.g. by giving paths such + // as `initfs:__inodes__/`? + filename: String, +} +pub struct InitFsScheme { + handles: BTreeMap, + next_id: usize, + fs: InitFs<'static>, +} +impl InitFsScheme { + pub fn new(bytes: &'static [u8]) -> Self { + Self { + handles: BTreeMap::new(), + next_id: 0, + fs: InitFs::new(bytes).expect("failed to parse initfs"), + } + } + + fn get_inode(fs: &InitFs<'static>, inode: Inode) -> Result> { + fs.get_inode(inode).ok_or_else(|| Error::new(EIO)) + } + fn next_id(&mut self) -> usize { + assert_ne!(self.next_id, usize::MAX, "usize overflow in initfs scheme"); + self.next_id += 1; + self.next_id + } +} + + +struct Iter { + dir: InodeDir<'static>, + idx: u32, +} +impl Iterator for Iter { + type Item = Result>; + + fn next(&mut self) -> Option { + let entry = self.dir.get_entry(self.idx).map_err(|_| Error::new(EIO)); + self.idx += 1; + entry.transpose() + } + fn size_hint(&self) -> (usize, Option) { + match self.dir.entry_count().ok() { + Some(size) => { + let size = usize::try_from(size).expect("expected u32 to be convertible into usize"); + (size, Some(size)) + } + None => (0, None), + } + } +} + +fn inode_len(inode: InodeStruct<'static>) -> Result { + Ok(match inode.kind() { + InodeKind::File(file) => file.data().map_err(|_| Error::new(EIO))?.len(), + InodeKind::Dir(dir) => (Iter { dir, idx: 0 }) + .fold(0, |len, entry| len + entry.and_then(|entry| entry.name().map_err(|_| Error::new(EIO))).map_or(0, |name| name.len() + 1)), + InodeKind::Unknown => return Err(Error::new(EIO)), + }) +} + +impl SchemeMut for InitFsScheme { + fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result { + let mut components = path + // trim leading and trailing slash + .trim_matches('/') + // divide into components + .split('/') + // filter out double slashes (e.g. /usr//bin/...) + .filter(|c| !c.is_empty()); + + let mut current_inode = InitFs::ROOT_INODE; + + while let Some(component) = components.next() { + match component { + "." => continue, + ".." => { + let _ = components.next_back(); + continue + } + + _ => (), + } + + let current_inode_struct = Self::get_inode(&self.fs, current_inode)?; + + let dir = match current_inode_struct.kind() { + InodeKind::Dir(dir) => dir, + + // If we still have more components in the path, and the file tree for that + // particular branch is not all directories except the last, then that file cannot + // exist. + InodeKind::File(_) | InodeKind::Unknown => return Err(Error::new(ENOENT)), + }; + + let mut entries = Iter { + dir, + idx: 0, + }; + + current_inode = loop { + let entry_res = match entries.next() { + Some(e) => e, + None => return Err(Error::new(ENOENT)), + }; + let entry = entry_res?; + let name = entry.name().map_err(|_| Error::new(EIO))?; + if name == component.as_bytes() { + break entry.inode(); + } + }; + } + + let id = self.next_id(); + let old = self.handles.insert(id, Handle { + inode: current_inode, + seek: 0_usize, + filename: path.into(), + }); + assert!(old.is_none()); + + Ok(id) + } + + fn read(&mut self, id: usize, buffer: &mut [u8]) -> Result { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + match Self::get_inode(&self.fs, handle.inode)?.kind() { + InodeKind::Dir(dir) => { + let mut bytes_read = 0; + let mut bytes_skipped = 0; + + for entry_res in (Iter { dir, idx: 0 }) { + let entry = entry_res?; + let name = entry.name().map_err(|_| Error::new(EIO))?; + let entry_len = name.len() + 1; + + let to_skip = core::cmp::min(handle.seek - bytes_skipped, entry_len); + + let to_copy = entry_len.saturating_sub(to_skip).saturating_sub(1); + buffer[bytes_read..bytes_read + to_copy].copy_from_slice(&name[..to_copy]); + + if to_copy.saturating_sub(to_skip) == 1 { + buffer[bytes_read + to_copy] = b'\n'; + bytes_read += 1; + } + + bytes_read += to_copy; + bytes_skipped += to_skip; + } + + handle.seek = handle.seek.checked_add(bytes_read).ok_or(Error::new(EOVERFLOW))?; + + Ok(bytes_read) + } + InodeKind::File(file) => { + let data = file.data().map_err(|_| Error::new(EIO))?; + let src_buf = &data[core::cmp::min(handle.seek, data.len())..]; + + let to_copy = core::cmp::min(src_buf.len(), buffer.len()); + buffer[..to_copy].copy_from_slice(&src_buf[..to_copy]); + + handle.seek = handle.seek.checked_add(to_copy).ok_or(Error::new(EOVERFLOW))?; + + Ok(to_copy) + } + InodeKind::Unknown => return Err(Error::new(EIO)), + } + } + + fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + let new_offset = calc_seek_offset_usize(handle.seek, pos, whence, inode_len(Self::get_inode(&self.fs, handle.inode)?)?)?; + handle.seek = new_offset as usize; + Ok(new_offset) + } + + fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result { + let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + Ok(0) + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + // TODO: Copy scheme part in kernel + let scheme_path = b"initfs:"; + let scheme_bytes = core::cmp::min(scheme_path.len(), buf.len()); + buf[..scheme_bytes].copy_from_slice(&scheme_path[..scheme_bytes]); + + let source = handle.filename.as_bytes(); + let path_bytes = core::cmp::min(buf.len() - scheme_bytes, source.len()); + buf[scheme_bytes..scheme_bytes + path_bytes].copy_from_slice(&source[..path_bytes]); + + Ok(scheme_bytes + path_bytes) + } + + fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + let Timespec { sec, nsec } = self.fs.image_creation_time(); + + let inode = Self::get_inode(&self.fs, handle.inode)?; + + stat.st_mode = inode.mode() | match inode.kind() { InodeKind::Dir(_) => MODE_DIR, InodeKind::File(_) => MODE_FILE, _ => 0 }; + stat.st_uid = inode.uid(); + stat.st_gid = inode.gid(); + stat.st_size = u64::try_from(inode_len(inode)?).unwrap_or(u64::MAX); + + stat.st_ctime = sec.get(); + stat.st_ctime_nsec = nsec.get(); + stat.st_mtime = sec.get(); + stat.st_mtime_nsec = nsec.get(); + + Ok(0) + } + + fn fsync(&mut self, id: usize) -> Result { + if !self.handles.contains_key(&id) { + return Err(Error::new(EBADF)); + } + + Ok(0) + } + + fn close(&mut self, id: usize) -> Result { + let _ = self.handles.remove(&id).ok_or(Error::new(EBADF))?; + Ok(0) + } +} + +pub fn run(bytes: &'static [u8], sync_pipe: usize) { + let mut scheme = InitFsScheme::new(bytes); + + let socket = syscall::open(":initfs", O_RDWR | O_CLOEXEC | O_CREAT) + .expect("failed to open initfs scheme socket"); + + let _ = syscall::write(sync_pipe, &[0]); + + let mut packet = Packet::default(); + + 'packets: loop { + loop { + match syscall::read(socket, &mut packet) { + Ok(0) => break 'packets, + Ok(_) => break, + Err(error) if error == Error::new(EINTR) => continue, + Err(error) => panic!("failed to read from scheme socket: {}", error), + } + } + scheme.handle(&mut packet); + loop { + match syscall::write(socket, &packet) { + Ok(0) => break 'packets, + Ok(_) => break, + Err(error) if error == Error::new(EINTR) => continue, + Err(error) => panic!("failed to write to scheme socket: {}", error), + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index c47e6c82cb..0df48f6548 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,32 @@ #![no_std] -#![feature(alloc_error_handler, core_intrinsics, lang_items)] +#![feature(alloc_error_handler, core_intrinsics, lang_items, panic_info_message)] #[cfg(target_arch = "x86_64")] pub mod x86_64; pub mod exec; +pub mod initfs; + +extern crate alloc; #[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo) -> ! { +fn panic_handler(info: &core::panic::PanicInfo) -> ! { + use core::fmt::Write; + + struct Writer; + + impl Write for Writer { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + syscall::write(1, s.as_bytes()).map_err(|_| core::fmt::Error).map(|_| ()) + } + } + + let _ = syscall::write(1, b"panic: "); + if let Some(message) = info.message() { + writeln!(&mut Writer, "{}", message).unwrap(); + } else { + let _ = syscall::write(1, b"(explicit panic)\n"); + } core::intrinsics::abort(); } @@ -17,3 +36,28 @@ fn alloc_error_handler(_: core::alloc::Layout) -> ! { } #[lang = "eh_personality"] extern "C" fn rust_eh_personality() {} + +mod allocator { + struct RelibcAllocator; + + #[global_allocator] + static GLOBAL: RelibcAllocator = RelibcAllocator; + + use alloc::alloc::*; + + unsafe impl GlobalAlloc for RelibcAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let mut ptr = core::ptr::null_mut(); + let align = core::cmp::max(layout.align(), core::mem::align_of::<*mut libc::c_void>()); + + if libc::posix_memalign(&mut ptr, align, layout.size()) == 0 { + ptr.cast() + } else { + core::ptr::null_mut() + } + } + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + libc::free(ptr.cast()); + } + } +} diff --git a/src/x86_64.rs b/src/x86_64.rs index fe137247fb..2303239102 100644 --- a/src/x86_64.rs +++ b/src/x86_64.rs @@ -23,10 +23,6 @@ mod offsets { pub fn data_and_bss() -> (usize, usize) { unsafe { (&__data_start as *const u8 as usize, &__bss_end as *const u8 as usize) } } - #[allow(dead_code)] - pub fn end() -> usize { - unsafe { &__end as *const u8 as usize } - } } // relibc linkage stuff From 6772933553a733eff83b06fc4a94ef5912f17226 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 28 Jul 2022 14:21:16 +0200 Subject: [PATCH 005/135] Don't link directly with relibc, use redox-exec. --- Cargo.lock | 130 +++++++++++++++++++++++++++++++++++++++----------- Cargo.toml | 8 +--- src/exec.rs | 65 +++++++++++-------------- src/initfs.rs | 4 +- src/lib.rs | 51 +++++++++++++------- src/x86_64.rs | 52 +------------------- 6 files changed, 172 insertions(+), 138 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83e5b5499e..41f997f99d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - [[package]] name = "autocfg" version = "1.1.0" @@ -24,32 +18,29 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" name = "bootstrap" version = "0.0.0" dependencies = [ - "arrayvec", - "goblin", - "libc", "linked_list_allocator", - "memoffset", + "redox-exec", "redox-initfs", "redox_syscall", - "scroll", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "goblin" -version = "0.4.3" +version = "0.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32401e89c6446dcd28185931a01b1093726d0356820ac744023e6850689bf926" +checksum = "6a4013e9182f2345c6b7829b9ef6e670bce0dfca12c6f974457ed2160c2c7fe9" dependencies = [ + "log", "plain", "scroll", ] -[[package]] -name = "libc" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" - [[package]] name = "linked_list_allocator" version = "0.9.1" @@ -70,12 +61,12 @@ dependencies = [ ] [[package]] -name = "memoffset" -version = "0.6.5" +name = "log" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "autocfg", + "cfg-if", ] [[package]] @@ -84,6 +75,34 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox-exec" +version = "0.1.0" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git?branch=redox-exec#16d0f96d64d0780dca88c363763222f63d7d85c3" +dependencies = [ + "goblin", + "plain", + "redox_syscall", +] + [[package]] name = "redox-initfs" version = "0.1.0" @@ -94,12 +113,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=userspace_fexec#933adb23683e9d6529ff2f6ffe7f1211c952897c" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8ce969b6629faa6f60a71f84022b0217e5ca8bcaeb6d11654506a55ebf8fed" dependencies = [ "bitflags", ] +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -108,9 +137,39 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scroll" -version = "0.10.2" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" +checksum = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" +dependencies = [ + "rustc_version", + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "spinning_top" @@ -120,3 +179,20 @@ checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" dependencies = [ "lock_api", ] + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/Cargo.toml b/Cargo.toml index f336f0de67..a6cc5c7d07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,10 @@ name = "bootstrap" crate-type = ["staticlib"] [dependencies] -arrayvec = { version = "0.7", default-features = false } -goblin = { version = "0.4", default-features = false, features = ["elf32", "elf64"] } -libc = "0.2" -memoffset = "0.6" -scroll = { version = "0.10", default-features = false } linked_list_allocator = "0.9" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } -redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "userspace_fexec" } +redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", branch = "redox-exec" } +redox_syscall = "0.3" [profile.release] panic = "abort" diff --git a/src/exec.rs b/src/exec.rs index daf587b710..fee5702201 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,28 +1,26 @@ -#[no_mangle] -pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> libc::c_int { +use alloc::borrow::ToOwned; +use alloc::vec::Vec; + +use syscall::flag::{O_CLOEXEC, O_RDONLY}; + +use redox_exec::*; + +pub fn main() -> ! { let (initfs_offset, initfs_length); - let envp = { - let len = 4096; - let buf = libc::calloc(len, 1); - if buf.is_null() { panic!("failed to allocate env buf"); } - let env = core::slice::from_raw_parts_mut(buf as *mut u8, len); + let envs = { + let mut env = [0_u8; 4096]; - let fd = syscall::open("sys:env", syscall::O_RDONLY | syscall::O_CLOEXEC).expect("bootstrap: failed to open env"); - let bytes_read = syscall::read(fd, env).expect("bootstrap: failed to read env"); + let fd = FdGuard::new(syscall::open("sys:env", O_RDONLY).expect("bootstrap: failed to open env")); + let bytes_read = syscall::read(*fd, &mut env).expect("bootstrap: failed to read env"); - if bytes_read >= len { + if bytes_read >= env.len() { // TODO: Handle this, we can allocate as much as we want in theory. panic!("env is too large"); } let env = &mut env[..bytes_read]; - for c in &mut *env { - if *c == b'\n' { - *c = b'\0'; - } - } - let raw_iter = || env.split(|c| *c == b'\0').filter(|var| !var.is_empty()); + let raw_iter = || env.split(|c| *c == b'\n').filter(|var| !var.is_empty()); let mut initfs_offset_opt = None; let mut initfs_length_opt = None; @@ -45,16 +43,9 @@ pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> let iter = || raw_iter().filter(|var| !var.starts_with(b"INITFS_")); let env_count = iter().count(); - let envp = libc::calloc(env_count + 1, core::mem::size_of::<*const u8>()) as *mut *const libc::c_char; - if envp.is_null() { panic!("failed to allocate envp buf"); } - - for (idx, var) in iter().enumerate() { - envp.add(idx).write(var.as_ptr() as *const libc::c_char); - } - - envp + iter().map(|var| var.to_owned()).collect::>() }; - { + unsafe { use syscall::flag::MapFlags; // XXX: It may be a little unsafe to mprotect this after relibc has started, but since only // the bootloader can influence the data we use, it should be fine security-wise. @@ -62,14 +53,15 @@ pub unsafe extern "C" fn main(_: libc::c_int, _: *const *const libc::c_char) -> spawn_initfs(initfs_offset, initfs_length); } + let path = "initfs:bin/init"; + let total_args_envs_size = path.len() + 1 + envs.len() + envs.iter().map(|v| v.len()).sum::(); - let name_ptr = b"initfs:bin/init\0".as_ptr() as *const libc::c_char; - let argv = [name_ptr, core::ptr::null()]; + let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); + let open_via_dup = FdGuard::new(syscall::open("thisproc:current/open_via_dup", 0).expect("failed to open open_via_dup")); + let memory = FdGuard::new(syscall::open("memory:", 0).expect("failed to open memory")); + + fexec_impl(image_file, open_via_dup, &memory, path.as_bytes(), [path], envs.iter(), total_args_envs_size, None).expect("failed to execute init"); - if libc::execve(name_ptr, argv.as_ptr(), envp) == -1 { - libc::perror(b"Failed to execute init\0".as_ptr() as *const libc::c_char); - panic!(); - } unreachable!() } @@ -78,17 +70,16 @@ unsafe fn spawn_initfs(initfs_start: usize, initfs_length: usize) { syscall::pipe2(&mut buf, syscall::O_CLOEXEC).expect("failed to open sync pipe"); let [read, write] = buf; - match libc::fork() { - -1 => { - libc::perror(b"Failed to fork in order to start initfs daemon\0".as_ptr() as *const libc::c_char); - panic!(); + match redox_exec::fork_impl() { + Err(err) => { + panic!("Failed to fork in order to start initfs daemon: {}", err); } // Continue serving the scheme as the child. - 0 => { + Ok(0) => { let _ = syscall::close(read); } // Return in order to execute init, as the parent. - _ => { + Ok(_) => { let _ = syscall::close(write); let _ = syscall::read(read, &mut [0]); diff --git a/src/initfs.rs b/src/initfs.rs index 06b5380cc2..01a4b0ff13 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -247,7 +247,7 @@ impl SchemeMut for InitFsScheme { } } -pub fn run(bytes: &'static [u8], sync_pipe: usize) { +pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { let mut scheme = InitFsScheme::new(bytes); let socket = syscall::open(":initfs", O_RDWR | O_CLOEXEC | O_CREAT) @@ -276,4 +276,6 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) { } } } + syscall::exit(0); + unreachable!() } diff --git a/src/lib.rs b/src/lib.rs index 0df48f6548..aa37fb28da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,9 @@ pub mod initfs; extern crate alloc; +use syscall::data::Map; +use syscall::flag::MapFlags; + #[panic_handler] fn panic_handler(info: &core::panic::PanicInfo) -> ! { use core::fmt::Write; @@ -37,27 +40,43 @@ fn alloc_error_handler(_: core::alloc::Layout) -> ! { #[lang = "eh_personality"] extern "C" fn rust_eh_personality() {} -mod allocator { - struct RelibcAllocator; +const HEAP_OFF: usize = 0x4000_0000_0000; - #[global_allocator] - static GLOBAL: RelibcAllocator = RelibcAllocator; +struct Allocator; +#[global_allocator] +static ALLOCATOR: Allocator = Allocator; - use alloc::alloc::*; +static mut HEAP: Option = None; +static mut HEAP_TOP: usize = HEAP_OFF + SIZE; +const SIZE: usize = 1024 * 1024; +const HEAP_INCREASE_BY: usize = SIZE; - unsafe impl GlobalAlloc for RelibcAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let mut ptr = core::ptr::null_mut(); - let align = core::cmp::max(layout.align(), core::mem::align_of::<*mut libc::c_void>()); +unsafe impl alloc::alloc::GlobalAlloc for Allocator { + unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { + let heap = HEAP.get_or_insert_with(|| { + HEAP_TOP = HEAP_OFF + SIZE; + let _ = syscall::fmap(!0, &Map { offset: 0, size: SIZE, address: HEAP_OFF, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED_NOREPLACE }) + .expect("failed to map initial heap"); + linked_list_allocator::Heap::new(HEAP_OFF, SIZE) + }); - if libc::posix_memalign(&mut ptr, align, layout.size()) == 0 { - ptr.cast() - } else { - core::ptr::null_mut() + match heap.allocate_first_fit(layout) { + Ok(p) => p.as_ptr(), + Err(_) => { + if layout.size() > HEAP_INCREASE_BY || layout.align() > 4096 { + return core::ptr::null_mut(); + } + + let _ = syscall::fmap(!0, &Map { offset: 0, size: HEAP_INCREASE_BY, address: HEAP_TOP, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED_NOREPLACE }) + .expect("failed to extend heap"); + heap.extend(HEAP_INCREASE_BY); + HEAP_TOP += HEAP_INCREASE_BY; + + return self.alloc(layout); } } - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - libc::free(ptr.cast()); - } + } + unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { + HEAP.as_mut().unwrap().deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) } } diff --git a/src/x86_64.rs b/src/x86_64.rs index 2303239102..49d65f601a 100644 --- a/src/x86_64.rs +++ b/src/x86_64.rs @@ -25,28 +25,6 @@ mod offsets { } } -// relibc linkage stuff -#[no_mangle] -extern "C" fn _init() {} -#[no_mangle] -extern "C" fn _fini() {} - -extern "C" fn nop() {} - -#[no_mangle] -static __preinit_array_start: extern "C" fn() = nop; -#[no_mangle] -static __preinit_array_end: extern "C" fn() = nop; -#[no_mangle] -static __init_array_start: extern "C" fn() = nop; -#[no_mangle] -static __init_array_end: extern "C" fn() = nop; - -#[no_mangle] -static __fini_array_start: extern "C" fn() = nop; -#[no_mangle] -static __fini_array_end: extern "C" fn() = nop; - #[no_mangle] pub unsafe extern "sysv64" fn start() -> ! { // Remap self, from the previous RWX @@ -63,33 +41,5 @@ pub unsafe extern "sysv64" fn start() -> ! { let _ = syscall::mprotect(rodata_start, rodata_end - rodata_start, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for .rodata"); let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); - extern "C" { - fn relibc_start(stack: usize); - } - use goblin::elf::header::header64::Header; - use memoffset::offset_of; - - let stack = [ - // argc - 0, - // argv null terminator - 0_usize, - // envp null terminator - 0_usize, - - // Make the TLS part of ld.so happy, even though we do not use TLS. - syscall::AT_PHDR, - // The kernel loads the entire ELF, so the program header offset can be trivially received. - // TODO: Use goblin for the ELF header (except rust accepts no null pointers...). - (offset_of!(Header, e_phoff) as *mut u64).read() as usize, - syscall::AT_PHENT, - (offset_of!(Header, e_phentsize) as *mut u16).read() as usize, - syscall::AT_PHNUM, - (offset_of!(Header, e_phnum) as *mut u16).read() as usize, - // auxv null terminator - syscall::AT_NULL, - 0, - ]; - relibc_start(stack.as_ptr() as usize); - panic!(); + crate::exec::main(); } From ff56ac5499926120bd7c1c7d2c75456d148459f2 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 29 Jul 2022 08:58:57 -0600 Subject: [PATCH 006/135] Add x86 32-bit support --- src/i686.asm | 42 +++++++++++++++++++++++++++++++++ src/i686.ld | 46 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 ++++--- src/{x86_64.rs => start.rs} | 2 +- 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 src/i686.asm create mode 100644 src/i686.ld rename src/{x86_64.rs => start.rs} (97%) diff --git a/src/i686.asm b/src/i686.asm new file mode 100644 index 0000000000..e67198bd8f --- /dev/null +++ b/src/i686.asm @@ -0,0 +1,42 @@ +BITS 64 + +section .text + +global ustart +extern start + +ustart: + ; Setup a stack. + mov eax, 0x21000384 ; SYS_CLASS_FILE+SYS_ARG_SLICE+SYS_MMAP + mov edi, 0xFFFFFFFF ; dummy `fd`, indicates anonymous map + mov esi, map ; pointer to Map struct + mov edx, map_size ; size of Map struct + int 0x80 + + ; Test for success (nonzero value). + cmp eax, 0 + jg .continue + ; (failure) + ud2 +.continue: + ; Subtract 16 since all instructions seem to hate non-canonical ESP values :) + lea esp, [eax+size-16] + mov ebp, esp + + ; Stack has the same alignment as `size`. + call start + ; `start` must never return. + ud2 + +section .rodata +map: + dd 0 ; offset (unused) + dd size + dd flags + dd address - size + +map_size equ $ - map + +size equ 65536 ; 64 KiB +address equ 0x80000000 ; highest possible (normal) user address, why not +flags equ 0x0006000E ; PROT_READ+PROT_WRITE, MAP_PRIVATE+MAP_FIXED_NOREPLACE diff --git a/src/i686.ld b/src/i686.ld new file mode 100644 index 0000000000..90bd467067 --- /dev/null +++ b/src/i686.ld @@ -0,0 +1,46 @@ +ENTRY(ustart) +OUTPUT_FORMAT(elf32-i386) + +SECTIONS { + . = 0; + . += SIZEOF_HEADERS; + . = ALIGN(4096); + + .text : { + __text_start = .; + *(.text*) + . = ALIGN(4096); + __text_end = .; + } + .rodata : { + __rodata_start = .; + *(.rodata*) + . = ALIGN(4096); + __rodata_end = .; + } + .data : { + __data_start = .; + *(.data*) + . = ALIGN(4096); + __data_end = .; + + *(.tbss*) + . = ALIGN(4096); + *(.tdata*) + . = ALIGN(4096); + + __bss_start = .; + *(.bss*) + . = ALIGN(4096); + __bss_end = .; + } + __end = .; + + /DISCARD/ : { + *(.comment*) + *(.eh_frame*) + *(.gcc_except_table*) + *(.note*) + *(.rel.eh_frame*) + } +} diff --git a/src/lib.rs b/src/lib.rs index aa37fb28da..b4ca55ac9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,9 @@ #![no_std] #![feature(alloc_error_handler, core_intrinsics, lang_items, panic_info_message)] -#[cfg(target_arch = "x86_64")] -pub mod x86_64; - pub mod exec; pub mod initfs; +pub mod start; extern crate alloc; @@ -40,6 +38,10 @@ fn alloc_error_handler(_: core::alloc::Layout) -> ! { #[lang = "eh_personality"] extern "C" fn rust_eh_personality() {} +#[cfg(target_pointer_width = "32")] +const HEAP_OFF: usize = 0x4000_0000; + +#[cfg(target_pointer_width = "64")] const HEAP_OFF: usize = 0x4000_0000_0000; struct Allocator; diff --git a/src/x86_64.rs b/src/start.rs similarity index 97% rename from src/x86_64.rs rename to src/start.rs index 49d65f601a..12ea3b195f 100644 --- a/src/x86_64.rs +++ b/src/start.rs @@ -26,7 +26,7 @@ mod offsets { } #[no_mangle] -pub unsafe extern "sysv64" fn start() -> ! { +pub unsafe extern "C" fn start() -> ! { // Remap self, from the previous RWX let (text_start, text_end) = offsets::text(); From 015b9ebd4318db95878716c1b5eeda49d959c593 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 29 Jul 2022 09:54:28 -0600 Subject: [PATCH 007/135] Update redox-exec --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41f997f99d..8b581a3d90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git?branch=redox-exec#16d0f96d64d0780dca88c363763222f63d7d85c3" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#13e58007bcf11e9c0341d066168b90fb981f4cdf" dependencies = [ "goblin", "plain", diff --git a/Cargo.toml b/Cargo.toml index a6cc5c7d07..6295880eaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["staticlib"] [dependencies] linked_list_allocator = "0.9" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } -redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", branch = "redox-exec" } +redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } redox_syscall = "0.3" [profile.release] From f99c17ab4ab8cfe97e9482cb7268b99d88d607c8 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 29 Jul 2022 23:46:13 +0200 Subject: [PATCH 008/135] Fix reading directories in initfs. --- src/initfs.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/initfs.rs b/src/initfs.rs index 01a4b0ff13..473666dd0e 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -138,34 +138,38 @@ impl SchemeMut for InitFsScheme { Ok(id) } - fn read(&mut self, id: usize, buffer: &mut [u8]) -> Result { + fn read(&mut self, id: usize, mut buffer: &mut [u8]) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; match Self::get_inode(&self.fs, handle.inode)?.kind() { InodeKind::Dir(dir) => { let mut bytes_read = 0; - let mut bytes_skipped = 0; + let mut total_to_skip = handle.seek; for entry_res in (Iter { dir, idx: 0 }) { let entry = entry_res?; let name = entry.name().map_err(|_| Error::new(EIO))?; - let entry_len = name.len() + 1; - let to_skip = core::cmp::min(handle.seek - bytes_skipped, entry_len); + let to_skip = core::cmp::min(total_to_skip, name.len() + 1); + if to_skip == name.len() + 1 { continue; } - let to_copy = entry_len.saturating_sub(to_skip).saturating_sub(1); - buffer[bytes_read..bytes_read + to_copy].copy_from_slice(&name[..to_copy]); + let name = &name[to_skip..]; - if to_copy.saturating_sub(to_skip) == 1 { - buffer[bytes_read + to_copy] = b'\n'; + let to_copy = core::cmp::min(name.len(), buffer.len()); + buffer[..to_copy].copy_from_slice(&name[..to_copy]); + bytes_read += to_copy; + buffer = &mut buffer[to_copy..]; + + if !buffer.is_empty() { + buffer[0] = b'\n'; bytes_read += 1; + buffer = &mut buffer[1..]; } - bytes_read += to_copy; - bytes_skipped += to_skip; + total_to_skip -= to_skip; } - handle.seek = handle.seek.checked_add(bytes_read).ok_or(Error::new(EOVERFLOW))?; + handle.seek = handle.seek.saturating_add(bytes_read); Ok(bytes_read) } From 91a389cebfe094c2e6669a412d820e82bdabae29 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 12 Aug 2022 15:05:37 +0200 Subject: [PATCH 009/135] Adapt to cwd only being in relibc. --- Cargo.lock | 2 +- Cargo.toml | 3 ++- src/exec.rs | 11 +++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b581a3d90..fd097b64dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#13e58007bcf11e9c0341d066168b90fb981f4cdf" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=relibc_cwd#bc8a6d2cc4d2ed4cffdfc1e4797566605c5e54ea" dependencies = [ "goblin", "plain", diff --git a/Cargo.toml b/Cargo.toml index 6295880eaa..bae7802958 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ crate-type = ["staticlib"] [dependencies] linked_list_allocator = "0.9" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } -redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } +#redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } +redox-exec = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "relibc_cwd" } redox_syscall = "0.3" [profile.release] diff --git a/src/exec.rs b/src/exec.rs index fee5702201..f310d43dc4 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -53,14 +53,19 @@ pub fn main() -> ! { spawn_initfs(initfs_offset, initfs_length); } + const CWD: &[u8] = b"initfs:"; + let extrainfo = redox_exec::ExtraInfo { + cwd: Some(CWD), + }; + let path = "initfs:bin/init"; - let total_args_envs_size = path.len() + 1 + envs.len() + envs.iter().map(|v| v.len()).sum::(); + let total_args_envs_auxvpointee_size = path.len() + 1 + envs.len() + envs.iter().map(|v| v.len()).sum::() + CWD.len() + 1; let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); let open_via_dup = FdGuard::new(syscall::open("thisproc:current/open_via_dup", 0).expect("failed to open open_via_dup")); let memory = FdGuard::new(syscall::open("memory:", 0).expect("failed to open memory")); - fexec_impl(image_file, open_via_dup, &memory, path.as_bytes(), [path], envs.iter(), total_args_envs_size, None).expect("failed to execute init"); + fexec_impl(image_file, open_via_dup, &memory, path.as_bytes(), [path], envs.iter(), total_args_envs_auxvpointee_size, &extrainfo, None).expect("failed to execute init"); unreachable!() } @@ -83,8 +88,6 @@ unsafe fn spawn_initfs(initfs_start: usize, initfs_length: usize) { let _ = syscall::close(write); let _ = syscall::read(read, &mut [0]); - let _ = syscall::chdir("initfs:"); - return; } } From 35a8c6d9acfa4434efe04f962a5a80833f4cc38e Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 16 Aug 2022 11:02:08 +0200 Subject: [PATCH 010/135] Use upstream relibc again. --- Cargo.lock | 6 +++--- Cargo.toml | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd097b64dc..f8d3e22b90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=relibc_cwd#bc8a6d2cc4d2ed4cffdfc1e4797566605c5e54ea" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#1315171a70ba0e9861b73eadd2da316f364b9f6a" dependencies = [ "goblin", "plain", @@ -113,9 +113,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8ce969b6629faa6f60a71f84022b0217e5ca8bcaeb6d11654506a55ebf8fed" +checksum = "78340485586b4f341ac019ab057e275c8b54d5025c73289bb521d83f17c54cbb" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index bae7802958..fb0c29819f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,8 @@ crate-type = ["staticlib"] [dependencies] linked_list_allocator = "0.9" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } -#redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox-exec = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "relibc_cwd" } -redox_syscall = "0.3" +redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } +redox_syscall = "0.3.1" [profile.release] panic = "abort" From 04f9c840c7f082756a1218f0b37429fa02192fba Mon Sep 17 00:00:00 2001 From: 4lDO2 <4ldo2@protonmail.com> Date: Tue, 16 Aug 2022 10:49:42 +0000 Subject: [PATCH 011/135] Add LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..2c457a7229 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 4lDO2 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 639ba8061237597c9ebca2e022dc959d5d83f8f7 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 17 Aug 2022 11:19:23 -0600 Subject: [PATCH 012/135] Fix argument registers for i686 --- src/i686.asm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i686.asm b/src/i686.asm index e67198bd8f..0032c813cc 100644 --- a/src/i686.asm +++ b/src/i686.asm @@ -8,8 +8,8 @@ extern start ustart: ; Setup a stack. mov eax, 0x21000384 ; SYS_CLASS_FILE+SYS_ARG_SLICE+SYS_MMAP - mov edi, 0xFFFFFFFF ; dummy `fd`, indicates anonymous map - mov esi, map ; pointer to Map struct + mov ebx, 0xFFFFFFFF ; dummy `fd`, indicates anonymous map + mov ecx, map ; pointer to Map struct mov edx, map_size ; size of Map struct int 0x80 From fb93690ea4af9d84a83465915fec6682a27b6dd7 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 17 Aug 2022 14:02:51 -0600 Subject: [PATCH 013/135] Use BITS 32 for 32-bit assembly --- src/i686.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i686.asm b/src/i686.asm index 0032c813cc..57e9809324 100644 --- a/src/i686.asm +++ b/src/i686.asm @@ -1,4 +1,4 @@ -BITS 64 +BITS 32 section .text From 7e8b92045fe1b08cf374ac218ab7eb2f6c50f456 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 17 Aug 2022 14:21:47 -0600 Subject: [PATCH 014/135] Update relibc --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f8d3e22b90..adfc2127ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#1315171a70ba0e9861b73eadd2da316f364b9f6a" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#ebb0507f5912396112f8b884a5c5623aa1ca1312" dependencies = [ "goblin", "plain", From 62283649bad5f7c5171f47af5d7f84704482eec7 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 18 Aug 2022 15:25:50 -0600 Subject: [PATCH 015/135] Update redox-exec --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index adfc2127ab..1ea0b0abe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#ebb0507f5912396112f8b884a5c5623aa1ca1312" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#60a4b59194540f347977cb2c9b0168ed79ffb1fb" dependencies = [ "goblin", "plain", From 00fbd71519b4adba8dbc47cf502fa1ce8b968504 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 19 Aug 2022 10:05:24 -0600 Subject: [PATCH 016/135] Reduce complexity of arch-specific assembly --- src/lib.rs | 18 ++++++++++++++++- src/x86_64.asm | 42 --------------------------------------- src/x86_64.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 43 deletions(-) delete mode 100644 src/x86_64.asm create mode 100644 src/x86_64.rs diff --git a/src/lib.rs b/src/lib.rs index b4ca55ac9c..1186760eae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,21 @@ #![no_std] -#![feature(alloc_error_handler, core_intrinsics, lang_items, panic_info_message)] +#![feature( + asm_const, + asm_sym, + alloc_error_handler, + core_intrinsics, + lang_items, + naked_functions, + panic_info_message +)] + +#[cfg(target_arch = "x86")] +#[path = "i686.rs"] +pub mod arch; + +#[cfg(target_arch = "x86_64")] +#[path = "x86_64.rs"] +pub mod arch; pub mod exec; pub mod initfs; diff --git a/src/x86_64.asm b/src/x86_64.asm deleted file mode 100644 index e8e49136c8..0000000000 --- a/src/x86_64.asm +++ /dev/null @@ -1,42 +0,0 @@ -BITS 64 - -section .text - -global ustart -extern start - -ustart: - ; Setup a stack. - mov rax, 0x21000384 ; SYS_CLASS_FILE+SYS_ARG_SLICE+SYS_MMAP - mov rdi, 0xFFFFFFFFFFFFFFFF ; dummy `fd`, indicates anonymous map - mov rsi, map ; pointer to Map struct - mov rdx, map_size ; size of Map struct - syscall - - ; Test for success (nonzero value). - cmp rax, 0 - jg .continue - ; (failure) - ud2 -.continue: - ; Subtract 16 since all instructions seem to hate non-canonical RSP values :) - lea rsp, [rax+size-16] - mov rbp, rsp - - ; Stack has the same alignment as `size`. - call start - ; `start` must never return. - ud2 - -section .rodata -map: - dq 0 ; offset (unused) - dq size - dq flags - dq address - size - -map_size equ $ - map - -size equ 65536 ; 64 KiB -address equ 0x0000800000000000 ; highest possible (normal) user address, why not -flags equ 0x0006000E ; PROT_READ+PROT_WRITE, MAP_PRIVATE+MAP_FIXED_NOREPLACE diff --git a/src/x86_64.rs b/src/x86_64.rs new file mode 100644 index 0000000000..f90f651ed8 --- /dev/null +++ b/src/x86_64.rs @@ -0,0 +1,53 @@ +use core::mem; +use syscall::{ + data::Map, + flag::MapFlags, + number::SYS_FMAP, +}; + +const STACK_SIZE: usize = 64 * 1024; // 64 KiB +static MAP: Map = Map { + offset: 0, + size: STACK_SIZE, + flags: MapFlags::PROT_READ + .union(MapFlags::PROT_WRITE) + .union(MapFlags::MAP_PRIVATE) + .union(MapFlags::MAP_FIXED_NOREPLACE), + address: 0x0000_8000_0000_0000 - STACK_SIZE, // highest possible user address +}; + +#[naked] +#[no_mangle] +pub unsafe extern "C" fn ustart() { + core::arch::asm!( + " + # Setup a stack. + mov rax, {number} + mov rdi, {fd} + lea rsi, {map} # pointer to Map struct + mov rdx, {map_size} # size of Map struct + syscall + + # Test for success (nonzero value). + cmp rax, 0 + jg 1f + # (failure) + ud2 + 1: + # Subtract 16 since all instructions seem to hate non-canonical RSP values :) + lea rsp, [rax+{stack_size}-16] + mov rbp, rsp + + # Stack has the same alignment as `size`. + call start + # `start` must never return. + ud2 + ", + fd = const usize::MAX, // dummy fd indicates anonymous map + map = sym MAP, + map_size = const mem::size_of::(), + number = const SYS_FMAP, + stack_size = const STACK_SIZE, + options(noreturn), + ); +} \ No newline at end of file From af040956df73dd7459defd173bef200009cad8d4 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 19 Aug 2022 10:13:28 -0600 Subject: [PATCH 017/135] Simplify 32-bit x86 assembly --- src/i686.asm | 42 ----------------------------------------- src/i686.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 42 deletions(-) delete mode 100644 src/i686.asm create mode 100644 src/i686.rs diff --git a/src/i686.asm b/src/i686.asm deleted file mode 100644 index 57e9809324..0000000000 --- a/src/i686.asm +++ /dev/null @@ -1,42 +0,0 @@ -BITS 32 - -section .text - -global ustart -extern start - -ustart: - ; Setup a stack. - mov eax, 0x21000384 ; SYS_CLASS_FILE+SYS_ARG_SLICE+SYS_MMAP - mov ebx, 0xFFFFFFFF ; dummy `fd`, indicates anonymous map - mov ecx, map ; pointer to Map struct - mov edx, map_size ; size of Map struct - int 0x80 - - ; Test for success (nonzero value). - cmp eax, 0 - jg .continue - ; (failure) - ud2 -.continue: - ; Subtract 16 since all instructions seem to hate non-canonical ESP values :) - lea esp, [eax+size-16] - mov ebp, esp - - ; Stack has the same alignment as `size`. - call start - ; `start` must never return. - ud2 - -section .rodata -map: - dd 0 ; offset (unused) - dd size - dd flags - dd address - size - -map_size equ $ - map - -size equ 65536 ; 64 KiB -address equ 0x80000000 ; highest possible (normal) user address, why not -flags equ 0x0006000E ; PROT_READ+PROT_WRITE, MAP_PRIVATE+MAP_FIXED_NOREPLACE diff --git a/src/i686.rs b/src/i686.rs new file mode 100644 index 0000000000..fa6fcd27f4 --- /dev/null +++ b/src/i686.rs @@ -0,0 +1,53 @@ +use core::mem; +use syscall::{ + data::Map, + flag::MapFlags, + number::SYS_FMAP, +}; + +const STACK_SIZE: usize = 64 * 1024; // 64 KiB +static MAP: Map = Map { + offset: 0, + size: STACK_SIZE, + flags: MapFlags::PROT_READ + .union(MapFlags::PROT_WRITE) + .union(MapFlags::MAP_PRIVATE) + .union(MapFlags::MAP_FIXED_NOREPLACE), + address: 0x8000_0000 - STACK_SIZE, // highest possible user address +}; + +#[naked] +#[no_mangle] +pub unsafe extern "C" fn ustart() { + core::arch::asm!( + " + # Setup a stack. + mov eax, {number} + mov ebx, {fd} + lea ecx, {map} # pointer to Map struct + mov edx, {map_size} # size of Map struct + int 0x80 + + # Test for success (nonzero value). + cmp eax, 0 + jg 1f + # (failure) + ud2 + 1: + # Subtract 16 since all instructions seem to hate non-canonical ESP values :) + lea esp, [eax+{stack_size}-16] + mov ebp, esp + + # Stack has the same alignment as `size`. + call start + # `start` must never return. + ud2 + ", + fd = const usize::MAX, // dummy fd indicates anonymous map + map = sym MAP, + map_size = const mem::size_of::(), + number = const SYS_FMAP, + stack_size = const STACK_SIZE, + options(noreturn), + ); +} From 3d75c0462809aefb98d34c6cf8b3b4647e5b33ac Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 19 Aug 2022 11:53:20 -0600 Subject: [PATCH 018/135] Convert use of lea instruction --- src/i686.rs | 2 +- src/x86_64.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i686.rs b/src/i686.rs index fa6fcd27f4..1c73d45293 100644 --- a/src/i686.rs +++ b/src/i686.rs @@ -24,7 +24,7 @@ pub unsafe extern "C" fn ustart() { # Setup a stack. mov eax, {number} mov ebx, {fd} - lea ecx, {map} # pointer to Map struct + mov ecx, offset {map} # pointer to Map struct mov edx, {map_size} # size of Map struct int 0x80 diff --git a/src/x86_64.rs b/src/x86_64.rs index f90f651ed8..9ca5018afc 100644 --- a/src/x86_64.rs +++ b/src/x86_64.rs @@ -24,7 +24,7 @@ pub unsafe extern "C" fn ustart() { # Setup a stack. mov rax, {number} mov rdi, {fd} - lea rsi, {map} # pointer to Map struct + mov rsi, offset {map} # pointer to Map struct mov rdx, {map_size} # size of Map struct syscall From 28c12e0382b7f56503803ae89ea59e420c7c9030 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 19 Aug 2022 12:51:27 -0600 Subject: [PATCH 019/135] Add aarch64 code --- src/aarch64.ld | 46 ++++++++++++++++++++++++++++++++++++++++ src/aarch64.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 ++++ 3 files changed, 107 insertions(+) create mode 100644 src/aarch64.ld create mode 100644 src/aarch64.rs diff --git a/src/aarch64.ld b/src/aarch64.ld new file mode 100644 index 0000000000..a3c3b3b65c --- /dev/null +++ b/src/aarch64.ld @@ -0,0 +1,46 @@ +ENTRY(ustart) +OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") + +SECTIONS { + . = 0; + . += SIZEOF_HEADERS; + . = ALIGN(4096); + + .text : { + __text_start = .; + *(.text*) + . = ALIGN(4096); + __text_end = .; + } + .rodata : { + __rodata_start = .; + *(.rodata*) + . = ALIGN(4096); + __rodata_end = .; + } + .data : { + __data_start = .; + *(.data*) + . = ALIGN(4096); + __data_end = .; + + *(.tbss*) + . = ALIGN(4096); + *(.tdata*) + . = ALIGN(4096); + + __bss_start = .; + *(.bss*) + . = ALIGN(4096); + __bss_end = .; + } + __end = .; + + /DISCARD/ : { + *(.comment*) + *(.eh_frame*) + *(.gcc_except_table*) + *(.note*) + *(.rel.eh_frame*) + } +} diff --git a/src/aarch64.rs b/src/aarch64.rs new file mode 100644 index 0000000000..52257fb495 --- /dev/null +++ b/src/aarch64.rs @@ -0,0 +1,57 @@ +use core::mem; +use syscall::{ + data::Map, + flag::MapFlags, + number::SYS_FMAP, +}; + +const STACK_SIZE: usize = 64 * 1024; // 64 KiB +static MAP: Map = Map { + offset: 0, + size: STACK_SIZE, + flags: MapFlags::PROT_READ + .union(MapFlags::PROT_WRITE) + .union(MapFlags::MAP_PRIVATE) + .union(MapFlags::MAP_FIXED_NOREPLACE), + address: 0x0000_8000_0000_0000 - STACK_SIZE, // highest possible user address +}; + +#[naked] +#[no_mangle] +pub unsafe extern "C" fn ustart() { + core::arch::asm!( + " + // Setup a stack. + ldr x8, ={number} + ldr x0, ={fd} + ldr x1, ={map} // pointer to Map struct + ldr x2, ={map_size} // size of Map struct + svc 0 + + // Failure if return value is zero + cbz x0, 1f + + // Failure if return value is negative + tbnz x0, 63, 1f + + // Set up stack frame + mov sp, x0 + add sp, sp, #{stack_size} + mov fp, sp + + // Stack has the same alignment as `size`. + bl start + // `start` must never return. + + // failure, emit undefined instruction + 1: + udf #0 + ", + fd = const usize::MAX, // dummy fd indicates anonymous map + map = sym MAP, + map_size = const mem::size_of::(), + number = const SYS_FMAP, + stack_size = const STACK_SIZE, + options(noreturn), + ); +} diff --git a/src/lib.rs b/src/lib.rs index 1186760eae..b0601dd061 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,10 @@ panic_info_message )] +#[cfg(target_arch = "aarch64")] +#[path = "aarch64.rs"] +pub mod arch; + #[cfg(target_arch = "x86")] #[path = "i686.rs"] pub mod arch; From b194ed56396b6a99df8d5ca8b2856b96d7f875de Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 20 Aug 2022 20:28:49 -0600 Subject: [PATCH 020/135] Update redox-exec --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 1ea0b0abe2..8e39f16165 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#60a4b59194540f347977cb2c9b0168ed79ffb1fb" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#619245024a8697b5cc12b64c0e6dd7e1b778a5fd" dependencies = [ "goblin", "plain", From 32674f319399fe5be55fd437ef16f94c69a0a964 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 24 Aug 2022 15:51:49 -0600 Subject: [PATCH 021/135] Update dependencies --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e39f16165..d7648f19db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#619245024a8697b5cc12b64c0e6dd7e1b778a5fd" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#adbdf1d73daa6f807aee359a6a63c2cef44a5791" dependencies = [ "goblin", "plain", @@ -113,9 +113,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78340485586b4f341ac019ab057e275c8b54d5025c73289bb521d83f17c54cbb" +checksum = "da878fa5823593d85e75e0f702fdf2ae5de760b9b4e3ee4ef1b133b7d989360f" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index fb0c29819f..685b584fa4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ crate-type = ["staticlib"] linked_list_allocator = "0.9" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox_syscall = "0.3.1" +redox_syscall = "0.3.2" [profile.release] panic = "abort" From 1effea3ce82292515de7b06a59303d9ad01d1562 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 24 Aug 2022 18:42:27 -0600 Subject: [PATCH 022/135] Update redox-exec --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index d7648f19db..bd5dd9446a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,7 +96,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#adbdf1d73daa6f807aee359a6a63c2cef44a5791" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#38576a37abfd7ac5ceb6b52ceea97368c3ad91fd" dependencies = [ "goblin", "plain", From c6c46803d2935bb4c1c9b6b036652bd7de4e6c78 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 27 Jun 2023 16:37:59 +0200 Subject: [PATCH 023/135] Replace pipe2 with pipe scheme. --- src/exec.rs | 7 ++++--- src/initfs.rs | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index f310d43dc4..524a12308f 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -71,9 +71,10 @@ pub fn main() -> ! { } unsafe fn spawn_initfs(initfs_start: usize, initfs_length: usize) { - let mut buf = [0; 2]; - syscall::pipe2(&mut buf, syscall::O_CLOEXEC).expect("failed to open sync pipe"); - let [read, write] = buf; + let read = syscall::open("pipe:", syscall::O_CLOEXEC).expect("failed to open sync read pipe"); + + // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. + let write = syscall::dup(read, b"write").expect("failed to open sync write pipe"); match redox_exec::fork_impl() { Err(err) => { diff --git a/src/initfs.rs b/src/initfs.rs index 473666dd0e..5c6fd84f4c 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -258,6 +258,7 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { .expect("failed to open initfs scheme socket"); let _ = syscall::write(sync_pipe, &[0]); + let _ = syscall::close(sync_pipe); let mut packet = Packet::default(); From d7fd637ca31b2a35ae5288f4638f0f99fa4250e0 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 27 Jun 2023 16:39:25 +0200 Subject: [PATCH 024/135] Fix warnings. --- src/exec.rs | 3 +-- src/initfs.rs | 2 +- src/lib.rs | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index 524a12308f..c0587cc85d 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -41,7 +41,6 @@ pub fn main() -> ! { initfs_length = initfs_length_opt.expect("missing INITFS_LENGTH"); let iter = || raw_iter().filter(|var| !var.starts_with(b"INITFS_")); - let env_count = iter().count(); iter().map(|var| var.to_owned()).collect::>() }; @@ -71,7 +70,7 @@ pub fn main() -> ! { } unsafe fn spawn_initfs(initfs_start: usize, initfs_length: usize) { - let read = syscall::open("pipe:", syscall::O_CLOEXEC).expect("failed to open sync read pipe"); + let read = syscall::open("pipe:", O_CLOEXEC).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. let write = syscall::dup(read, b"write").expect("failed to open sync write pipe"); diff --git a/src/initfs.rs b/src/initfs.rs index 5c6fd84f4c..7303134594 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -281,6 +281,6 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { } } } - syscall::exit(0); + syscall::exit(0).expect("initfs: failed to exit"); unreachable!() } diff --git a/src/lib.rs b/src/lib.rs index b0601dd061..3a000f6847 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![feature( asm_const, - asm_sym, alloc_error_handler, core_intrinsics, lang_items, From c3e2f0eca200d8c70f6df65744f59a8c75211293 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 27 Jun 2023 15:29:43 +0200 Subject: [PATCH 025/135] Ensure stack does not remain as RWX! --- Cargo.lock | 26 +++++++------------------- Cargo.toml | 6 +++++- src/aarch64.rs | 4 +++- src/exec.rs | 13 +++++++------ src/i686.rs | 4 +++- src/start.rs | 4 ++++ src/x86_64.rs | 7 +++++-- 7 files changed, 34 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd5dd9446a..6cb2650de8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,12 +24,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "goblin" version = "0.0.21" @@ -52,9 +46,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -62,12 +56,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "plain" @@ -96,7 +87,6 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#38576a37abfd7ac5ceb6b52ceea97368c3ad91fd" dependencies = [ "goblin", "plain", @@ -113,9 +103,7 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da878fa5823593d85e75e0f702fdf2ae5de760b9b4e3ee4ef1b133b7d989360f" +version = "0.3.5" dependencies = [ "bitflags", ] @@ -173,9 +161,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "spinning_top" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" dependencies = [ "lock_api", ] diff --git a/Cargo.toml b/Cargo.toml index 685b584fa4..dd06e45b9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ crate-type = ["staticlib"] [dependencies] linked_list_allocator = "0.9" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } -redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } +#redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } +redox-exec = { path = "../../../../relibc/src/platform/redox/redox-exec" } redox_syscall = "0.3.2" [profile.release] @@ -20,3 +21,6 @@ panic = "abort" [profile.dev] panic = "abort" + +[patch.crates-io] +redox_syscall = { path = "../../kernel/source/syscall" } diff --git a/src/aarch64.rs b/src/aarch64.rs index 52257fb495..3726cdc41e 100644 --- a/src/aarch64.rs +++ b/src/aarch64.rs @@ -5,6 +5,8 @@ use syscall::{ number::SYS_FMAP, }; +pub const STACK_START: usize = 0x0000_8000_0000_0000 - STACK_SIZE; + const STACK_SIZE: usize = 64 * 1024; // 64 KiB static MAP: Map = Map { offset: 0, @@ -13,7 +15,7 @@ static MAP: Map = Map { .union(MapFlags::PROT_WRITE) .union(MapFlags::MAP_PRIVATE) .union(MapFlags::MAP_FIXED_NOREPLACE), - address: 0x0000_8000_0000_0000 - STACK_SIZE, // highest possible user address + address: STACK_START, // highest possible user address }; #[naked] diff --git a/src/exec.rs b/src/exec.rs index c0587cc85d..8008d9f243 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,6 +1,7 @@ use alloc::borrow::ToOwned; use alloc::vec::Vec; +use syscall::{Error, EINTR}; use syscall::flag::{O_CLOEXEC, O_RDONLY}; use redox_exec::*; @@ -45,11 +46,6 @@ pub fn main() -> ! { iter().map(|var| var.to_owned()).collect::>() }; unsafe { - use syscall::flag::MapFlags; - // XXX: It may be a little unsafe to mprotect this after relibc has started, but since only - // the bootloader can influence the data we use, it should be fine security-wise. - let _ = syscall::mprotect(initfs_offset, initfs_length, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for initfs"); - spawn_initfs(initfs_offset, initfs_length); } const CWD: &[u8] = b"initfs:"; @@ -86,7 +82,12 @@ unsafe fn spawn_initfs(initfs_start: usize, initfs_length: usize) { // Return in order to execute init, as the parent. Ok(_) => { let _ = syscall::close(write); - let _ = syscall::read(read, &mut [0]); + loop { + match syscall::read(read, &mut [0]) { + Err(Error { errno: EINTR }) => continue, + _ => break, + } + } return; } diff --git a/src/i686.rs b/src/i686.rs index 1c73d45293..f5b46c9b7d 100644 --- a/src/i686.rs +++ b/src/i686.rs @@ -6,6 +6,8 @@ use syscall::{ }; const STACK_SIZE: usize = 64 * 1024; // 64 KiB +pub const STACK_START: usize = 0x8000_0000 - STACK_SIZE; + static MAP: Map = Map { offset: 0, size: STACK_SIZE, @@ -13,7 +15,7 @@ static MAP: Map = Map { .union(MapFlags::PROT_WRITE) .union(MapFlags::MAP_PRIVATE) .union(MapFlags::MAP_FIXED_NOREPLACE), - address: 0x8000_0000 - STACK_SIZE, // highest possible user address + address: STACK_START, // highest possible user address }; #[naked] diff --git a/src/start.rs b/src/start.rs index 12ea3b195f..c05980865c 100644 --- a/src/start.rs +++ b/src/start.rs @@ -37,9 +37,13 @@ pub unsafe extern "C" fn start() -> ! { let _ = syscall::open("debug:", syscall::O_WRONLY); // stdout let _ = syscall::open("debug:", syscall::O_WRONLY); // stderr + // TODO: PROT_NONE + let _ = syscall::mprotect(0, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for NULL page"); + let _ = syscall::mprotect(text_start, text_end - text_start, MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE).expect("mprotect failed for .text"); let _ = syscall::mprotect(rodata_start, rodata_end - rodata_start, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for .rodata"); let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); + let _ = syscall::mprotect(data_end, crate::arch::STACK_START - data_end, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for rest of memory"); crate::exec::main(); } diff --git a/src/x86_64.rs b/src/x86_64.rs index 9ca5018afc..cb6cef71c0 100644 --- a/src/x86_64.rs +++ b/src/x86_64.rs @@ -6,6 +6,9 @@ use syscall::{ }; const STACK_SIZE: usize = 64 * 1024; // 64 KiB + +pub const STACK_START: usize = 0x0000_8000_0000_0000 - STACK_SIZE; + static MAP: Map = Map { offset: 0, size: STACK_SIZE, @@ -13,7 +16,7 @@ static MAP: Map = Map { .union(MapFlags::PROT_WRITE) .union(MapFlags::MAP_PRIVATE) .union(MapFlags::MAP_FIXED_NOREPLACE), - address: 0x0000_8000_0000_0000 - STACK_SIZE, // highest possible user address + address: STACK_START, // highest possible user address }; #[naked] @@ -50,4 +53,4 @@ pub unsafe extern "C" fn ustart() { stack_size = const STACK_SIZE, options(noreturn), ); -} \ No newline at end of file +} From 84951891a78f465fc9e451783524722df5e6e6b1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 7 Aug 2023 17:44:07 +0200 Subject: [PATCH 026/135] Fix dependencies. --- Cargo.lock | 6 ++++-- Cargo.toml | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cb2650de8..58371d2d66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,6 +87,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#3327bc820dc089d28b855e902ae058119d5a9c80" dependencies = [ "goblin", "plain", @@ -104,6 +105,7 @@ dependencies = [ [[package]] name = "redox_syscall" version = "0.3.5" +source = "git+https://gitlab.redox-os.org/redox-os/syscall.git#a525620c818a801bb7038e111b71033eff56a3ee" dependencies = [ "bitflags", ] @@ -119,9 +121,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scroll" diff --git a/Cargo.toml b/Cargo.toml index dd06e45b9f..73a7e7c80f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,7 @@ crate-type = ["staticlib"] [dependencies] linked_list_allocator = "0.9" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } -#redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox-exec = { path = "../../../../relibc/src/platform/redox/redox-exec" } +redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } redox_syscall = "0.3.2" [profile.release] @@ -23,4 +22,4 @@ panic = "abort" panic = "abort" [patch.crates-io] -redox_syscall = { path = "../../kernel/source/syscall" } +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } From 39baa5969302027e0a2ea012e20d6fb6273a978a Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 14 Oct 2023 17:08:10 +0200 Subject: [PATCH 027/135] Update dependencies. --- Cargo.lock | 15 ++++++++------- Cargo.toml | 7 ++----- src/lib.rs | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58371d2d66..d31e2933ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "linked_list_allocator" -version = "0.9.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" dependencies = [ "spinning_top", ] @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "plain" @@ -87,7 +87,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#3327bc820dc089d28b855e902ae058119d5a9c80" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#2c7ec0dc8eec7307cfcb5d4aa9ddf332b7132a83" dependencies = [ "goblin", "plain", @@ -104,8 +104,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" -source = "git+https://gitlab.redox-os.org/redox-os/syscall.git#a525620c818a801bb7038e111b71033eff56a3ee" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 73a7e7c80f..cfb3538b4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,16 +10,13 @@ name = "bootstrap" crate-type = ["staticlib"] [dependencies] -linked_list_allocator = "0.9" +linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox_syscall = "0.3.2" +redox_syscall = "0.4" [profile.release] panic = "abort" [profile.dev] panic = "abort" - -[patch.crates-io] -redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } diff --git a/src/lib.rs b/src/lib.rs index 3a000f6847..da57f084ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,7 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { HEAP_TOP = HEAP_OFF + SIZE; let _ = syscall::fmap(!0, &Map { offset: 0, size: SIZE, address: HEAP_OFF, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED_NOREPLACE }) .expect("failed to map initial heap"); - linked_list_allocator::Heap::new(HEAP_OFF, SIZE) + linked_list_allocator::Heap::new(HEAP_OFF as *mut u8, SIZE) }); match heap.allocate_first_fit(layout) { From f33e8b54af278d12ca2d89c371c1503ecb1b0044 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 18 Jan 2024 15:05:38 -0700 Subject: [PATCH 028/135] Update path for initfs --- Cargo.lock | 65 +++++++++++++++++------------------------------------ src/exec.rs | 4 ++-- 2 files changed, 22 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d31e2933ec..e92a7c6d7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "goblin" -version = "0.0.21" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a4013e9182f2345c6b7829b9ef6e670bce0dfca12c6f974457ed2160c2c7fe9" +checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134" dependencies = [ "log", "plain", @@ -46,9 +46,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -68,18 +68,18 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "0.4.30" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "0.6.13" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -87,7 +87,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#2c7ec0dc8eec7307cfcb5d4aa9ddf332b7132a83" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#780e6ff81c6973625f8e6132673479250fcc9d76" dependencies = [ "goblin", "plain", @@ -111,15 +111,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -128,40 +119,24 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scroll" -version = "0.9.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" dependencies = [ - "rustc_version", "scroll_derive", ] [[package]] name = "scroll_derive" -version = "0.9.5" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "spinning_top" version = "0.2.5" @@ -173,17 +148,17 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.44" +version = "2.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] -name = "unicode-xid" -version = "0.1.0" +name = "unicode-ident" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/src/exec.rs b/src/exec.rs index 8008d9f243..a2bd02c324 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -48,12 +48,12 @@ pub fn main() -> ! { unsafe { spawn_initfs(initfs_offset, initfs_length); } - const CWD: &[u8] = b"initfs:"; + const CWD: &[u8] = b"/scheme/initfs"; let extrainfo = redox_exec::ExtraInfo { cwd: Some(CWD), }; - let path = "initfs:bin/init"; + let path = "/scheme/initfs/bin/init"; let total_args_envs_auxvpointee_size = path.len() + 1 + envs.len() + envs.iter().map(|v| v.len()).sum::() + CWD.len() + 1; let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); From 2860c0bdfbd5844a4236fb3ac7a2142d3580cbe1 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:39:11 +0100 Subject: [PATCH 029/135] Remove usage of naked functions --- src/aarch64.rs | 66 +++++++++++++++++++++----------------------- src/i686.rs | 75 +++++++++++++++++++++++--------------------------- src/lib.rs | 1 - src/x86_64.rs | 75 +++++++++++++++++++++++--------------------------- 4 files changed, 100 insertions(+), 117 deletions(-) diff --git a/src/aarch64.rs b/src/aarch64.rs index 3726cdc41e..964c2c46e7 100644 --- a/src/aarch64.rs +++ b/src/aarch64.rs @@ -18,42 +18,40 @@ static MAP: Map = Map { address: STACK_START, // highest possible user address }; -#[naked] -#[no_mangle] -pub unsafe extern "C" fn ustart() { - core::arch::asm!( - " - // Setup a stack. - ldr x8, ={number} - ldr x0, ={fd} - ldr x1, ={map} // pointer to Map struct - ldr x2, ={map_size} // size of Map struct - svc 0 +core::arch::global_asm!( + " + .globl ustart + ustart: + // Setup a stack. + ldr x8, ={number} + ldr x0, ={fd} + ldr x1, ={map} // pointer to Map struct + ldr x2, ={map_size} // size of Map struct + svc 0 - // Failure if return value is zero - cbz x0, 1f + // Failure if return value is zero + cbz x0, 1f - // Failure if return value is negative - tbnz x0, 63, 1f + // Failure if return value is negative + tbnz x0, 63, 1f - // Set up stack frame - mov sp, x0 - add sp, sp, #{stack_size} - mov fp, sp + // Set up stack frame + mov sp, x0 + add sp, sp, #{stack_size} + mov fp, sp - // Stack has the same alignment as `size`. - bl start - // `start` must never return. + // Stack has the same alignment as `size`. + bl start + // `start` must never return. - // failure, emit undefined instruction - 1: - udf #0 - ", - fd = const usize::MAX, // dummy fd indicates anonymous map - map = sym MAP, - map_size = const mem::size_of::(), - number = const SYS_FMAP, - stack_size = const STACK_SIZE, - options(noreturn), - ); -} + // failure, emit undefined instruction + 1: + udf #0 + ", + fd = const usize::MAX, // dummy fd indicates anonymous map + map = sym MAP, + map_size = const mem::size_of::(), + number = const SYS_FMAP, + stack_size = const STACK_SIZE, + options(noreturn), +); diff --git a/src/i686.rs b/src/i686.rs index f5b46c9b7d..2a0beac59b 100644 --- a/src/i686.rs +++ b/src/i686.rs @@ -1,9 +1,5 @@ use core::mem; -use syscall::{ - data::Map, - flag::MapFlags, - number::SYS_FMAP, -}; +use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; const STACK_SIZE: usize = 64 * 1024; // 64 KiB pub const STACK_START: usize = 0x8000_0000 - STACK_SIZE; @@ -12,44 +8,41 @@ static MAP: Map = Map { offset: 0, size: STACK_SIZE, flags: MapFlags::PROT_READ - .union(MapFlags::PROT_WRITE) - .union(MapFlags::MAP_PRIVATE) - .union(MapFlags::MAP_FIXED_NOREPLACE), + .union(MapFlags::PROT_WRITE) + .union(MapFlags::MAP_PRIVATE) + .union(MapFlags::MAP_FIXED_NOREPLACE), address: STACK_START, // highest possible user address }; -#[naked] -#[no_mangle] -pub unsafe extern "C" fn ustart() { - core::arch::asm!( - " - # Setup a stack. - mov eax, {number} - mov ebx, {fd} - mov ecx, offset {map} # pointer to Map struct - mov edx, {map_size} # size of Map struct - int 0x80 +core::arch::global_asm!( +" + .globl ustart + ustart: + # Setup a stack. + mov eax, {number} + mov ebx, {fd} + mov ecx, offset {map} # pointer to Map struct + mov edx, {map_size} # size of Map struct + int 0x80 - # Test for success (nonzero value). - cmp eax, 0 - jg 1f - # (failure) - ud2 - 1: - # Subtract 16 since all instructions seem to hate non-canonical ESP values :) - lea esp, [eax+{stack_size}-16] - mov ebp, esp + # Test for success (nonzero value). + cmp eax, 0 + jg 1f + # (failure) + ud2 + 1: + # Subtract 16 since all instructions seem to hate non-canonical ESP values :) + lea esp, [eax+{stack_size}-16] + mov ebp, esp - # Stack has the same alignment as `size`. - call start - # `start` must never return. - ud2 - ", - fd = const usize::MAX, // dummy fd indicates anonymous map - map = sym MAP, - map_size = const mem::size_of::(), - number = const SYS_FMAP, - stack_size = const STACK_SIZE, - options(noreturn), - ); -} + # Stack has the same alignment as `size`. + call start + # `start` must never return. + ud2 + ", + fd = const usize::MAX, // dummy fd indicates anonymous map + map = sym MAP, + map_size = const mem::size_of::(), + number = const SYS_FMAP, + stack_size = const STACK_SIZE, +); diff --git a/src/lib.rs b/src/lib.rs index da57f084ba..ad6eca8d5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ alloc_error_handler, core_intrinsics, lang_items, - naked_functions, panic_info_message )] diff --git a/src/x86_64.rs b/src/x86_64.rs index cb6cef71c0..b1e7343e53 100644 --- a/src/x86_64.rs +++ b/src/x86_64.rs @@ -1,9 +1,5 @@ use core::mem; -use syscall::{ - data::Map, - flag::MapFlags, - number::SYS_FMAP, -}; +use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; const STACK_SIZE: usize = 64 * 1024; // 64 KiB @@ -13,44 +9,41 @@ static MAP: Map = Map { offset: 0, size: STACK_SIZE, flags: MapFlags::PROT_READ - .union(MapFlags::PROT_WRITE) - .union(MapFlags::MAP_PRIVATE) - .union(MapFlags::MAP_FIXED_NOREPLACE), + .union(MapFlags::PROT_WRITE) + .union(MapFlags::MAP_PRIVATE) + .union(MapFlags::MAP_FIXED_NOREPLACE), address: STACK_START, // highest possible user address }; -#[naked] -#[no_mangle] -pub unsafe extern "C" fn ustart() { - core::arch::asm!( - " - # Setup a stack. - mov rax, {number} - mov rdi, {fd} - mov rsi, offset {map} # pointer to Map struct - mov rdx, {map_size} # size of Map struct - syscall +core::arch::global_asm!( + " + .globl ustart + ustart: + # Setup a stack. + mov rax, {number} + mov rdi, {fd} + mov rsi, offset {map} # pointer to Map struct + mov rdx, {map_size} # size of Map struct + syscall - # Test for success (nonzero value). - cmp rax, 0 - jg 1f - # (failure) - ud2 - 1: - # Subtract 16 since all instructions seem to hate non-canonical RSP values :) - lea rsp, [rax+{stack_size}-16] - mov rbp, rsp + # Test for success (nonzero value). + cmp rax, 0 + jg 1f + # (failure) + ud2 + 1: + # Subtract 16 since all instructions seem to hate non-canonical RSP values :) + lea rsp, [rax+{stack_size}-16] + mov rbp, rsp - # Stack has the same alignment as `size`. - call start - # `start` must never return. - ud2 - ", - fd = const usize::MAX, // dummy fd indicates anonymous map - map = sym MAP, - map_size = const mem::size_of::(), - number = const SYS_FMAP, - stack_size = const STACK_SIZE, - options(noreturn), - ); -} + # Stack has the same alignment as `size`. + call start + # `start` must never return. + ud2 + ", + fd = const usize::MAX, // dummy fd indicates anonymous map + map = sym MAP, + map_size = const mem::size_of::(), + number = const SYS_FMAP, + stack_size = const STACK_SIZE, +); From 89e0a71d10bb529150d46a55964955a0739387ba Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:40:47 +0100 Subject: [PATCH 030/135] Remove unnecessary definition of eh_personality All crates are compiled with panic=abort and no extern "C-unwind" is used, so the personality function is referenced by rustc. --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ad6eca8d5f..5a850066c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ asm_const, alloc_error_handler, core_intrinsics, - lang_items, panic_info_message )] @@ -53,8 +52,6 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { fn alloc_error_handler(_: core::alloc::Layout) -> ! { core::intrinsics::abort(); } -#[lang = "eh_personality"] -extern "C" fn rust_eh_personality() {} #[cfg(target_pointer_width = "32")] const HEAP_OFF: usize = 0x4000_0000; From c15c6e1dd29ed7a293d8816e793e762ff7163b7b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:41:53 +0100 Subject: [PATCH 031/135] Use the Display impl of PanicInfo --- src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5a850066c0..d6bb9b1234 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ asm_const, alloc_error_handler, core_intrinsics, - panic_info_message )] #[cfg(target_arch = "aarch64")] @@ -39,12 +38,7 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { } } - let _ = syscall::write(1, b"panic: "); - if let Some(message) = info.message() { - writeln!(&mut Writer, "{}", message).unwrap(); - } else { - let _ = syscall::write(1, b"(explicit panic)\n"); - } + let _ = writeln!(&mut Writer, "{}", info); core::intrinsics::abort(); } From 57c1bf94b67c96195e61233acd2ecdec9487231f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:46:43 +0100 Subject: [PATCH 032/135] Use default alloc error handler in liballoc --- src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d6bb9b1234..4b70f73c1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![feature( asm_const, - alloc_error_handler, core_intrinsics, )] @@ -42,11 +41,6 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { core::intrinsics::abort(); } -#[alloc_error_handler] -fn alloc_error_handler(_: core::alloc::Layout) -> ! { - core::intrinsics::abort(); -} - #[cfg(target_pointer_width = "32")] const HEAP_OFF: usize = 0x4000_0000; From 0111bcfd4890d7039dc47c0686e8d5ff1403bf90 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:57:30 +0100 Subject: [PATCH 033/135] Remove unused __end global --- src/aarch64.ld | 1 - src/i686.ld | 1 - src/start.rs | 2 -- src/x86_64.ld | 1 - 4 files changed, 5 deletions(-) diff --git a/src/aarch64.ld b/src/aarch64.ld index a3c3b3b65c..0d2a0ae2f0 100644 --- a/src/aarch64.ld +++ b/src/aarch64.ld @@ -34,7 +34,6 @@ SECTIONS { . = ALIGN(4096); __bss_end = .; } - __end = .; /DISCARD/ : { *(.comment*) diff --git a/src/i686.ld b/src/i686.ld index 90bd467067..fbe030b5de 100644 --- a/src/i686.ld +++ b/src/i686.ld @@ -34,7 +34,6 @@ SECTIONS { . = ALIGN(4096); __bss_end = .; } - __end = .; /DISCARD/ : { *(.comment*) diff --git a/src/start.rs b/src/start.rs index c05980865c..920f285645 100644 --- a/src/start.rs +++ b/src/start.rs @@ -11,8 +11,6 @@ mod offsets { // data+bss (RW-) static __data_start: u8; static __bss_end: u8; - - static __end: u8; } pub fn text() -> (usize, usize) { unsafe { (&__text_start as *const u8 as usize, &__text_end as *const u8 as usize) } diff --git a/src/x86_64.ld b/src/x86_64.ld index c8c89bc868..efaf015f09 100644 --- a/src/x86_64.ld +++ b/src/x86_64.ld @@ -34,7 +34,6 @@ SECTIONS { . = ALIGN(4096); __bss_end = .; } - __end = .; /DISCARD/ : { *(.comment*) From 8e11205784fcbad5a015b87cdaa5ba81031ce74d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:59:44 +0100 Subject: [PATCH 034/135] Rename entrypoint to _start This matches the default. --- src/aarch64.ld | 2 +- src/aarch64.rs | 4 ++-- src/i686.ld | 2 +- src/i686.rs | 6 +++--- src/x86_64.ld | 2 +- src/x86_64.rs | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/aarch64.ld b/src/aarch64.ld index 0d2a0ae2f0..3abb18108d 100644 --- a/src/aarch64.ld +++ b/src/aarch64.ld @@ -1,4 +1,4 @@ -ENTRY(ustart) +ENTRY(_start) OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") SECTIONS { diff --git a/src/aarch64.rs b/src/aarch64.rs index 964c2c46e7..f94d41d37f 100644 --- a/src/aarch64.rs +++ b/src/aarch64.rs @@ -20,8 +20,8 @@ static MAP: Map = Map { core::arch::global_asm!( " - .globl ustart - ustart: + .globl _start + _start: // Setup a stack. ldr x8, ={number} ldr x0, ={fd} diff --git a/src/i686.ld b/src/i686.ld index fbe030b5de..e07ab63713 100644 --- a/src/i686.ld +++ b/src/i686.ld @@ -1,4 +1,4 @@ -ENTRY(ustart) +ENTRY(_start) OUTPUT_FORMAT(elf32-i386) SECTIONS { diff --git a/src/i686.rs b/src/i686.rs index 2a0beac59b..f07beb4397 100644 --- a/src/i686.rs +++ b/src/i686.rs @@ -15,9 +15,9 @@ static MAP: Map = Map { }; core::arch::global_asm!( -" - .globl ustart - ustart: + " + .globl _start + _start: # Setup a stack. mov eax, {number} mov ebx, {fd} diff --git a/src/x86_64.ld b/src/x86_64.ld index efaf015f09..232fbf72fc 100644 --- a/src/x86_64.ld +++ b/src/x86_64.ld @@ -1,4 +1,4 @@ -ENTRY(ustart) +ENTRY(_start) OUTPUT_FORMAT(elf64-x86-64) SECTIONS { diff --git a/src/x86_64.rs b/src/x86_64.rs index b1e7343e53..c4c051ca0a 100644 --- a/src/x86_64.rs +++ b/src/x86_64.rs @@ -17,8 +17,8 @@ static MAP: Map = Map { core::arch::global_asm!( " - .globl ustart - ustart: + .globl _start + _start: # Setup a stack. mov rax, {number} mov rdi, {fd} From ba7eea30438b6dd9274e9ab91ca6232800daa281 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 14:08:25 +0100 Subject: [PATCH 035/135] Compile with fat LTO This reduces the file size from 119KiB to 87KiB. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index cfb3538b4c..2ae5bb45a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ redox_syscall = "0.4" [profile.release] panic = "abort" +lto = "fat" [profile.dev] panic = "abort" From 626a5af3a496bd44d313deab00fe141d4fbe58ec Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 14:39:29 +0100 Subject: [PATCH 036/135] Use HashMap instead of BTreeMap This reduces the file size from 87KiB to 79KiB. --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/initfs.rs | 9 ++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e92a7c6d7a..885b28f1f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,7 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" name = "bootstrap" version = "0.0.0" dependencies = [ + "hashbrown", "linked_list_allocator", "redox-exec", "redox-initfs", @@ -35,6 +36,12 @@ dependencies = [ "scroll", ] +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "linked_list_allocator" version = "0.10.5" diff --git a/Cargo.toml b/Cargo.toml index 2ae5bb45a0..feb816d5e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ name = "bootstrap" crate-type = ["staticlib"] [dependencies] +hashbrown = { version = "0.14", default-features = false, features = ["inline-more"] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } diff --git a/src/initfs.rs b/src/initfs.rs index 7303134594..e4ed8d4951 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -1,9 +1,11 @@ use core::convert::TryFrom; +#[allow(deprecated)] +use core::hash::{BuildHasherDefault, SipHasher}; use core::str; -use alloc::collections::BTreeMap; use alloc::string::String; +use hashbrown::HashMap; use redox_initfs::{InitFs, InodeStruct, Inode, InodeDir, InodeKind, types::Timespec}; use syscall::data::{Packet, Stat}; @@ -19,14 +21,15 @@ struct Handle { filename: String, } pub struct InitFsScheme { - handles: BTreeMap, + #[allow(deprecated)] + handles: HashMap>, next_id: usize, fs: InitFs<'static>, } impl InitFsScheme { pub fn new(bytes: &'static [u8]) -> Self { Self { - handles: BTreeMap::new(), + handles: HashMap::default(), next_id: 0, fs: InitFs::new(bytes).expect("failed to parse initfs"), } From b50d6fad52b02fd7727fd09cfbed5c0f288e8f21 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 10 Mar 2024 19:24:30 +0100 Subject: [PATCH 037/135] Support embedding bootstrap blob into the initfs --- src/aarch64.ld | 3 ++- src/exec.rs | 19 ++++++++++++------- src/i686.ld | 3 ++- src/start.rs | 5 +++-- src/x86_64.ld | 3 ++- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/aarch64.ld b/src/aarch64.ld index 3abb18108d..cee830f08a 100644 --- a/src/aarch64.ld +++ b/src/aarch64.ld @@ -2,7 +2,8 @@ ENTRY(_start) OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") SECTIONS { - . = 0; + . = 4096 + 4096; /* Reserved for the null page and the initfs header prepended by redox-initfs-ar */ + __initfs_header = . - 4096; . += SIZEOF_HEADERS; . = ALIGN(4096); diff --git a/src/exec.rs b/src/exec.rs index a2bd02c324..d76a98105f 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -7,7 +7,7 @@ use syscall::flag::{O_CLOEXEC, O_RDONLY}; use redox_exec::*; pub fn main() -> ! { - let (initfs_offset, initfs_length); + let initfs_length; let envs = { let mut env = [0_u8; 4096]; @@ -23,7 +23,6 @@ pub fn main() -> ! { let raw_iter = || env.split(|c| *c == b'\n').filter(|var| !var.is_empty()); - let mut initfs_offset_opt = None; let mut initfs_length_opt = None; for var in raw_iter() { @@ -32,21 +31,27 @@ pub fn main() -> ! { let value = &var[equal_sign + 1..]; match name { - b"INITFS_OFFSET" => initfs_offset_opt = core::str::from_utf8(value).ok().and_then(|s| usize::from_str_radix(s, 16).ok()), b"INITFS_LENGTH" => initfs_length_opt = core::str::from_utf8(value).ok().and_then(|s| usize::from_str_radix(s, 16).ok()), _ => continue, } } - initfs_offset = initfs_offset_opt.expect("missing INITFS_OFFSET"); initfs_length = initfs_length_opt.expect("missing INITFS_LENGTH"); let iter = || raw_iter().filter(|var| !var.starts_with(b"INITFS_")); iter().map(|var| var.to_owned()).collect::>() }; + + extern { + // The linker script will define this as the location of the initfs header. + static __initfs_header: u8; + } + unsafe { - spawn_initfs(initfs_offset, initfs_length); + // Creating a reference to NULL is UB. Mask the UB for now using black_box. + // FIXME use a raw pointer and inline asm for reading instead for the initfs header. + spawn_initfs(core::ptr::addr_of!(__initfs_header), initfs_length); } const CWD: &[u8] = b"/scheme/initfs"; let extrainfo = redox_exec::ExtraInfo { @@ -65,7 +70,7 @@ pub fn main() -> ! { unreachable!() } -unsafe fn spawn_initfs(initfs_start: usize, initfs_length: usize) { +unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { let read = syscall::open("pipe:", O_CLOEXEC).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. @@ -92,5 +97,5 @@ unsafe fn spawn_initfs(initfs_start: usize, initfs_length: usize) { return; } } - crate::initfs::run(core::slice::from_raw_parts(initfs_start as *const u8, initfs_length), write); + crate::initfs::run(core::slice::from_raw_parts(initfs_start, initfs_length), write); } diff --git a/src/i686.ld b/src/i686.ld index e07ab63713..9bfb676b6b 100644 --- a/src/i686.ld +++ b/src/i686.ld @@ -2,7 +2,8 @@ ENTRY(_start) OUTPUT_FORMAT(elf32-i386) SECTIONS { - . = 0; + . = 4096 + 4096; /* Reserved for the null page and the initfs header prepended by redox-initfs-ar */ + __initfs_header = . - 4096; . += SIZEOF_HEADERS; . = ALIGN(4096); diff --git a/src/start.rs b/src/start.rs index 920f285645..12b271b037 100644 --- a/src/start.rs +++ b/src/start.rs @@ -35,13 +35,14 @@ pub unsafe extern "C" fn start() -> ! { let _ = syscall::open("debug:", syscall::O_WRONLY); // stdout let _ = syscall::open("debug:", syscall::O_WRONLY); // stderr - // TODO: PROT_NONE - let _ = syscall::mprotect(0, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for NULL page"); + let _ = syscall::mprotect(4096, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for initfs header page"); let _ = syscall::mprotect(text_start, text_end - text_start, MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE).expect("mprotect failed for .text"); let _ = syscall::mprotect(rodata_start, rodata_end - rodata_start, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for .rodata"); let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); let _ = syscall::mprotect(data_end, crate::arch::STACK_START - data_end, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for rest of memory"); + // FIXME make the initfs read-only + crate::exec::main(); } diff --git a/src/x86_64.ld b/src/x86_64.ld index 232fbf72fc..ecb2094222 100644 --- a/src/x86_64.ld +++ b/src/x86_64.ld @@ -2,7 +2,8 @@ ENTRY(_start) OUTPUT_FORMAT(elf64-x86-64) SECTIONS { - . = 0; + . = 4096 + 4096; /* Reserved for the null page and the initfs header prepended by redox-initfs-ar */ + __initfs_header = . - 4096; . += SIZEOF_HEADERS; . = ALIGN(4096); From 70700df944b932c253ad116881945fa0dfbb24eb Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:49:53 +0100 Subject: [PATCH 038/135] Update redox-initfs and use the initfs_size header field instead of INITFS_LENGTH --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/exec.rs | 21 +++------------------ 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 885b28f1f3..ad49099eb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,7 +104,7 @@ dependencies = [ [[package]] name = "redox-initfs" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#89b8fb8984cf96c418880b7dcd9ce3d6afc3f71c" +source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#7dd9b2e84d983a444518218f3ec74d3e9ac900b3" dependencies = [ "plain", ] diff --git a/Cargo.toml b/Cargo.toml index feb816d5e8..9c7c863cac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["staticlib"] [dependencies] hashbrown = { version = "0.14", default-features = false, features = ["inline-more"] } linked_list_allocator = "0.10" -redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false } +redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } redox_syscall = "0.4" diff --git a/src/exec.rs b/src/exec.rs index d76a98105f..37f7781a17 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -7,8 +7,6 @@ use syscall::flag::{O_CLOEXEC, O_RDONLY}; use redox_exec::*; pub fn main() -> ! { - let initfs_length; - let envs = { let mut env = [0_u8; 4096]; @@ -23,21 +21,6 @@ pub fn main() -> ! { let raw_iter = || env.split(|c| *c == b'\n').filter(|var| !var.is_empty()); - let mut initfs_length_opt = None; - - for var in raw_iter() { - let equal_sign = var.iter().position(|c| *c == b'=').expect("malformed environment variable"); - let name = &var[..equal_sign]; - let value = &var[equal_sign + 1..]; - - match name { - b"INITFS_LENGTH" => initfs_length_opt = core::str::from_utf8(value).ok().and_then(|s| usize::from_str_radix(s, 16).ok()), - - _ => continue, - } - } - initfs_length = initfs_length_opt.expect("missing INITFS_LENGTH"); - let iter = || raw_iter().filter(|var| !var.starts_with(b"INITFS_")); iter().map(|var| var.to_owned()).collect::>() @@ -48,10 +31,12 @@ pub fn main() -> ! { static __initfs_header: u8; } + let initfs_length = unsafe { (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size }; + unsafe { // Creating a reference to NULL is UB. Mask the UB for now using black_box. // FIXME use a raw pointer and inline asm for reading instead for the initfs header. - spawn_initfs(core::ptr::addr_of!(__initfs_header), initfs_length); + spawn_initfs(core::ptr::addr_of!(__initfs_header), initfs_length.get() as usize); } const CWD: &[u8] = b"/scheme/initfs"; let extrainfo = redox_exec::ExtraInfo { From 02fd768c9f510b834f7ee89f718df437111c634e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:03:04 +0100 Subject: [PATCH 039/135] Fix building for AArch64 --- src/aarch64.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/aarch64.rs b/src/aarch64.rs index f94d41d37f..0fef48b0c9 100644 --- a/src/aarch64.rs +++ b/src/aarch64.rs @@ -53,5 +53,4 @@ core::arch::global_asm!( map_size = const mem::size_of::(), number = const SYS_FMAP, stack_size = const STACK_SIZE, - options(noreturn), ); From cc7ee6ce49deca9dfca38c0d4a6b9e938c1823e4 Mon Sep 17 00:00:00 2001 From: Jacob Lorentzon <4ldo2@protonmail.com> Date: Sun, 17 Mar 2024 17:01:43 +0000 Subject: [PATCH 040/135] Signals --- Cargo.lock | 27 +++++++++++++-------------- Cargo.toml | 3 ++- src/exec.rs | 2 ++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad49099eb9..b78f4cdc52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bootstrap" @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "plain" @@ -75,18 +75,18 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -94,7 +94,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#780e6ff81c6973625f8e6132673479250fcc9d76" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#6ff5691d189945a51b997a6e729646592015cb68" dependencies = [ "goblin", "plain", @@ -111,9 +111,8 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +version = "0.5.0" +source = "git+https://gitlab.redox-os.org/redox-os/syscall.git#7163519226f89f47d77118af27495de231ad9092" dependencies = [ "bitflags", ] @@ -155,9 +154,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.40" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 9c7c863cac..922db2af88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,9 @@ crate-type = ["staticlib"] hashbrown = { version = "0.14", default-features = false, features = ["inline-more"] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } +#redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox_syscall = "0.4" +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } [profile.release] panic = "abort" diff --git a/src/exec.rs b/src/exec.rs index 37f7781a17..ae21e5267e 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -41,6 +41,8 @@ pub fn main() -> ! { const CWD: &[u8] = b"/scheme/initfs"; let extrainfo = redox_exec::ExtraInfo { cwd: Some(CWD), + sigprocmask: 0, + sigignmask: 0, }; let path = "/scheme/initfs/bin/init"; From d9bc645185b56ce8b5e4d25793c6fd460aca8b06 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 10 Jun 2024 19:59:01 +0200 Subject: [PATCH 041/135] Switch to v2 scheme format. --- Cargo.lock | 61 ++++++++++++++++++++++++++----------- Cargo.toml | 6 +++- src/initfs.rs | 84 ++++++++++++++++++++++++++++++++------------------- src/lib.rs | 2 ++ 4 files changed, 104 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b78f4cdc52..96e79a2f6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,15 +4,15 @@ version = 3 [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bootstrap" @@ -22,6 +22,7 @@ dependencies = [ "linked_list_allocator", "redox-exec", "redox-initfs", + "redox-scheme", "redox_syscall", ] @@ -38,9 +39,26 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] [[package]] name = "linked_list_allocator" @@ -53,9 +71,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -75,18 +93,18 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -94,7 +112,7 @@ dependencies = [ [[package]] name = "redox-exec" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#6ff5691d189945a51b997a6e729646592015cb68" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#ac08f016cd3b37fae8434f2da6168542d3171ec6" dependencies = [ "goblin", "plain", @@ -109,10 +127,19 @@ dependencies = [ "plain", ] +[[package]] +name = "redox-scheme" +version = "0.2.0" +source = "git+https://gitlab.redox-os.org/4lDO2/redox-scheme.git?branch=schemev2#54a79f82bf82f14256b33dace7c68673b0851898" +dependencies = [ + "libredox", + "redox_syscall", +] + [[package]] name = "redox_syscall" -version = "0.5.0" -source = "git+https://gitlab.redox-os.org/redox-os/syscall.git#7163519226f89f47d77118af27495de231ad9092" +version = "0.5.1" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=schemev2#f72cc4d7431841aaad039bce57af4e59c82f78c4" dependencies = [ "bitflags", ] @@ -154,9 +181,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.53" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 922db2af88..646c401077 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } #redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } +redox_syscall = "0.5.1" +redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2" } [profile.release] panic = "abort" @@ -23,3 +24,6 @@ lto = "fat" [profile.dev] panic = "abort" + +[patch.crates-io] +redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "schemev2" } diff --git a/src/initfs.rs b/src/initfs.rs index e4ed8d4951..f440ea0b65 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -8,10 +8,16 @@ use alloc::string::String; use hashbrown::HashMap; use redox_initfs::{InitFs, InodeStruct, Inode, InodeDir, InodeKind, types::Timespec}; -use syscall::data::{Packet, Stat}; +use redox_scheme::OpenResult; +use redox_scheme::RequestKind; +use redox_scheme::SchemeV2; + +use redox_scheme::SignalBehavior; +use redox_scheme::Socket; +use redox_scheme::V2; +use syscall::data::Stat; use syscall::error::*; use syscall::flag::*; -use syscall::scheme::{calc_seek_offset_usize, SchemeMut}; struct Handle { inode: Inode, @@ -78,8 +84,8 @@ fn inode_len(inode: InodeStruct<'static>) -> Result { }) } -impl SchemeMut for InitFsScheme { - fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result { +impl SchemeV2 for InitFsScheme { + fn open(&mut self, path: &str, _flags: usize) -> Result { let mut components = path // trim leading and trailing slash .trim_matches('/') @@ -138,7 +144,7 @@ impl SchemeMut for InitFsScheme { }); assert!(old.is_none()); - Ok(id) + Ok(OpenResult::ThisScheme { number: id }) } fn read(&mut self, id: usize, mut buffer: &mut [u8]) -> Result { @@ -191,12 +197,12 @@ impl SchemeMut for InitFsScheme { } } - fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result { + fn seek(&mut self, id: usize, pos: i64, whence: usize) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let new_offset = calc_seek_offset_usize(handle.seek, pos, whence, inode_len(Self::get_inode(&self.fs, handle.inode)?)?)?; + let new_offset = redox_scheme::calc_seek_offset_usize(handle.seek, pos as isize, whence, inode_len(Self::get_inode(&self.fs, handle.inode)?)?)?; handle.seek = new_offset as usize; - Ok(new_offset) + Ok(new_offset as u64) } fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result { @@ -240,50 +246,66 @@ impl SchemeMut for InitFsScheme { Ok(0) } - fn fsync(&mut self, id: usize) -> Result { + fn fsync(&mut self, id: usize) -> Result<()> { if !self.handles.contains_key(&id) { return Err(Error::new(EBADF)); } - Ok(0) + Ok(()) } - fn close(&mut self, id: usize) -> Result { + fn close(&mut self, id: usize) -> Result<()> { let _ = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - Ok(0) + Ok(()) } } pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { let mut scheme = InitFsScheme::new(bytes); - let socket = syscall::open(":initfs", O_RDWR | O_CLOEXEC | O_CREAT) + let socket = Socket::::create("initfs") .expect("failed to open initfs scheme socket"); let _ = syscall::write(sync_pipe, &[0]); let _ = syscall::close(sync_pipe); - let mut packet = Packet::default(); + loop { + let RequestKind::Call(req) = (match socket.next_request(SignalBehavior::Restart).expect("bootstrap: failed to read scheme request from kernel") { + Some(req) => req.kind(), + None => break, + }) else { + continue; + }; + let resp = req.handle_scheme_mut(&mut scheme); - 'packets: loop { - loop { - match syscall::read(socket, &mut packet) { - Ok(0) => break 'packets, - Ok(_) => break, - Err(error) if error == Error::new(EINTR) => continue, - Err(error) => panic!("failed to read from scheme socket: {}", error), - } - } - scheme.handle(&mut packet); - loop { - match syscall::write(socket, &packet) { - Ok(0) => break 'packets, - Ok(_) => break, - Err(error) if error == Error::new(EINTR) => continue, - Err(error) => panic!("failed to write to scheme socket: {}", error), - } + if !socket.write_response(resp, SignalBehavior::Restart).expect("bootstrap: failed to write scheme response to kernel") { + break; } } + syscall::exit(0).expect("initfs: failed to exit"); unreachable!() } + +// TODO: Restructure bootstrap so it calls into relibc, or a split-off derivative without the C +// parts, such as "redox-rt". + +#[no_mangle] +pub unsafe extern "C" fn redox_read_v1(fd: usize, ptr: *mut u8, len: usize) -> isize { + Error::mux(syscall::read(fd, core::slice::from_raw_parts_mut(ptr, len))) as isize +} + +#[no_mangle] +pub unsafe extern "C" fn redox_write_v1(fd: usize, ptr: *const u8, len: usize) -> isize { + Error::mux(syscall::write(fd, core::slice::from_raw_parts(ptr, len))) as isize +} + +#[no_mangle] +pub unsafe extern "C" fn redox_open_v1(ptr: *const u8, len: usize, flags: usize) -> isize { + Error::mux(syscall::open(core::str::from_raw_parts(ptr, len), flags)) as isize +} + +#[no_mangle] +pub extern "C" fn redox_close_v1(fd: usize) -> isize { + Error::mux(syscall::close(fd)) as isize +} diff --git a/src/lib.rs b/src/lib.rs index 4b70f73c1c..3c45afb000 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,9 @@ #![no_std] +#![allow(internal_features)] #![feature( asm_const, core_intrinsics, + str_from_raw_parts, )] #[cfg(target_arch = "aarch64")] From 74fd559f6ecb5bc2f94d642bdf3a4ac9c30c8567 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 11 Jun 2024 18:37:32 +0200 Subject: [PATCH 042/135] Update redox_scheme. --- Cargo.lock | 4 ++-- src/initfs.rs | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96e79a2f6a..3f67956fb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ dependencies = [ [[package]] name = "redox-scheme" version = "0.2.0" -source = "git+https://gitlab.redox-os.org/4lDO2/redox-scheme.git?branch=schemev2#54a79f82bf82f14256b33dace7c68673b0851898" +source = "git+https://gitlab.redox-os.org/4lDO2/redox-scheme.git?branch=schemev2#40c29c6f08e095e7b3739feae47a3c24f469c4d3" dependencies = [ "libredox", "redox_syscall", @@ -139,7 +139,7 @@ dependencies = [ [[package]] name = "redox_syscall" version = "0.5.1" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=schemev2#f72cc4d7431841aaad039bce57af4e59c82f78c4" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=schemev2#495a0adbd8333fd21fae2d97129b094607878ba7" dependencies = [ "bitflags", ] diff --git a/src/initfs.rs b/src/initfs.rs index f440ea0b65..5811ff5267 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -8,9 +8,10 @@ use alloc::string::String; use hashbrown::HashMap; use redox_initfs::{InitFs, InodeStruct, Inode, InodeDir, InodeKind, types::Timespec}; +use redox_scheme::CallerCtx; use redox_scheme::OpenResult; use redox_scheme::RequestKind; -use redox_scheme::SchemeV2; +use redox_scheme::SchemeMut; use redox_scheme::SignalBehavior; use redox_scheme::Socket; @@ -84,8 +85,8 @@ fn inode_len(inode: InodeStruct<'static>) -> Result { }) } -impl SchemeV2 for InitFsScheme { - fn open(&mut self, path: &str, _flags: usize) -> Result { +impl SchemeMut for InitFsScheme { + fn xopen(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result { let mut components = path // trim leading and trailing slash .trim_matches('/') @@ -197,12 +198,12 @@ impl SchemeV2 for InitFsScheme { } } - fn seek(&mut self, id: usize, pos: i64, whence: usize) -> Result { + fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let new_offset = redox_scheme::calc_seek_offset_usize(handle.seek, pos as isize, whence, inode_len(Self::get_inode(&self.fs, handle.inode)?)?)?; + let new_offset = redox_scheme::calc_seek_offset_usize(handle.seek, pos, whence, inode_len(Self::get_inode(&self.fs, handle.inode)?)?)?; handle.seek = new_offset as usize; - Ok(new_offset as u64) + Ok(new_offset) } fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result { @@ -246,17 +247,17 @@ impl SchemeV2 for InitFsScheme { Ok(0) } - fn fsync(&mut self, id: usize) -> Result<()> { + fn fsync(&mut self, id: usize) -> Result { if !self.handles.contains_key(&id) { return Err(Error::new(EBADF)); } - Ok(()) + Ok(0) } - fn close(&mut self, id: usize) -> Result<()> { + fn close(&mut self, id: usize) -> Result { let _ = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - Ok(()) + Ok(0) } } From 54f4a7623830cb247699a5e2672747b1f5fffc98 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 12 Jun 2024 17:44:47 +0200 Subject: [PATCH 043/135] Use positioned IO scheme interface. --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- src/initfs.rs | 25 +++++++++++-------------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f67956fb1..a59147682b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ dependencies = [ [[package]] name = "redox-scheme" version = "0.2.0" -source = "git+https://gitlab.redox-os.org/4lDO2/redox-scheme.git?branch=schemev2#40c29c6f08e095e7b3739feae47a3c24f469c4d3" +source = "git+https://gitlab.redox-os.org/4lDO2/redox-scheme.git?branch=schemev2plus#ca239589c873374a17715b637be1f9385345721c" dependencies = [ "libredox", "redox_syscall", @@ -139,7 +139,7 @@ dependencies = [ [[package]] name = "redox_syscall" version = "0.5.1" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=schemev2#495a0adbd8333fd21fae2d97129b094607878ba7" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=schemev2plus#35600d6c9c46a4d54ec17d80c9ff5d17ef79a052" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 646c401077..dbcd5af4d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", #redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } redox_syscall = "0.5.1" -redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2" } +redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2plus" } [profile.release] panic = "abort" @@ -26,4 +26,4 @@ lto = "fat" panic = "abort" [patch.crates-io] -redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "schemev2" } +redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "schemev2plus" } diff --git a/src/initfs.rs b/src/initfs.rs index 5811ff5267..81384df4c7 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -19,10 +19,10 @@ use redox_scheme::V2; use syscall::data::Stat; use syscall::error::*; use syscall::flag::*; +use syscall::schemev2::NewFdFlags; struct Handle { inode: Inode, - seek: usize, // TODO: Any better way to implement fpath? Or maybe work around it, e.g. by giving paths such // as `initfs:__inodes__/`? filename: String, @@ -140,21 +140,24 @@ impl SchemeMut for InitFsScheme { let id = self.next_id(); let old = self.handles.insert(id, Handle { inode: current_inode, - seek: 0_usize, filename: path.into(), }); assert!(old.is_none()); - Ok(OpenResult::ThisScheme { number: id }) + Ok(OpenResult::ThisScheme { number: id, flags: NewFdFlags::POSITIONED }) } - fn read(&mut self, id: usize, mut buffer: &mut [u8]) -> Result { + fn read(&mut self, id: usize, mut buffer: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result { + let Ok(offset) = usize::try_from(offset) else { + return Ok(0); + }; + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; match Self::get_inode(&self.fs, handle.inode)?.kind() { InodeKind::Dir(dir) => { let mut bytes_read = 0; - let mut total_to_skip = handle.seek; + let mut total_to_skip = offset; for entry_res in (Iter { dir, idx: 0 }) { let entry = entry_res?; @@ -179,31 +182,25 @@ impl SchemeMut for InitFsScheme { total_to_skip -= to_skip; } - handle.seek = handle.seek.saturating_add(bytes_read); - Ok(bytes_read) } InodeKind::File(file) => { let data = file.data().map_err(|_| Error::new(EIO))?; - let src_buf = &data[core::cmp::min(handle.seek, data.len())..]; + let src_buf = &data[core::cmp::min(offset, data.len())..]; let to_copy = core::cmp::min(src_buf.len(), buffer.len()); buffer[..to_copy].copy_from_slice(&src_buf[..to_copy]); - handle.seek = handle.seek.checked_add(to_copy).ok_or(Error::new(EOVERFLOW))?; - Ok(to_copy) } InodeKind::Unknown => return Err(Error::new(EIO)), } } - fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result { + fn fsize(&mut self, id: usize) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let new_offset = redox_scheme::calc_seek_offset_usize(handle.seek, pos, whence, inode_len(Self::get_inode(&self.fs, handle.inode)?)?)?; - handle.seek = new_offset as usize; - Ok(new_offset) + Ok(inode_len(Self::get_inode(&self.fs, handle.inode)?)? as u64) } fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result { From 77f7903b6687a10501473f48fecd635431c75a81 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 14 Jun 2024 14:13:10 +0200 Subject: [PATCH 044/135] Update dependencies. --- Cargo.lock | 7 ++++--- Cargo.toml | 8 ++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a59147682b..23c1fee11c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ dependencies = [ [[package]] name = "redox-scheme" version = "0.2.0" -source = "git+https://gitlab.redox-os.org/4lDO2/redox-scheme.git?branch=schemev2plus#ca239589c873374a17715b637be1f9385345721c" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#62aed6c712cca31bb18078adacd27b400f5ccf93" dependencies = [ "libredox", "redox_syscall", @@ -138,8 +138,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=schemev2plus#35600d6c9c46a4d54ec17d80c9ff5d17ef79a052" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index dbcd5af4d1..df541b7a2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,9 @@ crate-type = ["staticlib"] hashbrown = { version = "0.14", default-features = false, features = ["inline-more"] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } -#redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox_syscall = "0.5.1" -redox-scheme = { git = "https://gitlab.redox-os.org/4lDO2/redox-scheme.git", branch = "schemev2plus" } +redox_syscall = "0.5.2" +redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } [profile.release] panic = "abort" @@ -24,6 +23,3 @@ lto = "fat" [profile.dev] panic = "abort" - -[patch.crates-io] -redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "schemev2plus" } From 88338f36f2a894ae2f0e8c243e3d20bd0e1f47be Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 11 Jul 2024 21:02:40 +0200 Subject: [PATCH 045/135] Use the new scheme format --- src/exec.rs | 13 +++++++++---- src/start.rs | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index ae21e5267e..ff942f46d8 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -10,7 +10,9 @@ pub fn main() -> ! { let envs = { let mut env = [0_u8; 4096]; - let fd = FdGuard::new(syscall::open("sys:env", O_RDONLY).expect("bootstrap: failed to open env")); + let fd = FdGuard::new( + syscall::open("/scheme/sys/env", O_RDONLY).expect("bootstrap: failed to open env"), + ); let bytes_read = syscall::read(*fd, &mut env).expect("bootstrap: failed to read env"); if bytes_read >= env.len() { @@ -49,8 +51,11 @@ pub fn main() -> ! { let total_args_envs_auxvpointee_size = path.len() + 1 + envs.len() + envs.iter().map(|v| v.len()).sum::() + CWD.len() + 1; let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); - let open_via_dup = FdGuard::new(syscall::open("thisproc:current/open_via_dup", 0).expect("failed to open open_via_dup")); - let memory = FdGuard::new(syscall::open("memory:", 0).expect("failed to open memory")); + let open_via_dup = FdGuard::new( + syscall::open("/scheme/thisproc/current/open_via_dup", 0) + .expect("failed to open open_via_dup"), + ); + let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); fexec_impl(image_file, open_via_dup, &memory, path.as_bytes(), [path], envs.iter(), total_args_envs_auxvpointee_size, &extrainfo, None).expect("failed to execute init"); @@ -58,7 +63,7 @@ pub fn main() -> ! { } unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { - let read = syscall::open("pipe:", O_CLOEXEC).expect("failed to open sync read pipe"); + let read = syscall::open("/scheme/pipe", O_CLOEXEC).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. let write = syscall::dup(read, b"write").expect("failed to open sync write pipe"); diff --git a/src/start.rs b/src/start.rs index 12b271b037..67589a179c 100644 --- a/src/start.rs +++ b/src/start.rs @@ -31,9 +31,9 @@ pub unsafe extern "C" fn start() -> ! { let (rodata_start, rodata_end) = offsets::rodata(); let (data_start, data_end) = offsets::data_and_bss(); - let _ = syscall::open("debug:", syscall::O_RDONLY); // stdin - let _ = syscall::open("debug:", syscall::O_WRONLY); // stdout - let _ = syscall::open("debug:", syscall::O_WRONLY); // stderr + let _ = syscall::open("/scheme/debug", syscall::O_RDONLY); // stdin + let _ = syscall::open("/scheme/debug", syscall::O_WRONLY); // stdout + let _ = syscall::open("/scheme/debug", syscall::O_WRONLY); // stderr let _ = syscall::mprotect(4096, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for initfs header page"); From c695af7674358f8eaacb7e4dc8f85a1a9b766237 Mon Sep 17 00:00:00 2001 From: Jacob Lorentzon <4ldo2@protonmail.com> Date: Mon, 15 Jul 2024 15:43:34 +0000 Subject: [PATCH 046/135] Switch to redox-rt. --- Cargo.lock | 53 +++++++++++++++++++++++++++++----------------------- Cargo.toml | 4 ++-- src/exec.rs | 6 +++--- src/start.rs | 2 ++ 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23c1fee11c..fde8798a56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bootstrap" @@ -20,12 +20,17 @@ version = "0.0.0" dependencies = [ "hashbrown", "linked_list_allocator", - "redox-exec", "redox-initfs", + "redox-rt", "redox-scheme", "redox_syscall", ] +[[package]] +name = "generic-rt" +version = "0.1.0" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git?rev=e97f26c7#e97f26c7632b1033ca062199db5633f3236821ed" + [[package]] name = "goblin" version = "0.7.1" @@ -81,9 +86,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "plain" @@ -93,9 +98,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -109,16 +114,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "redox-exec" -version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#ac08f016cd3b37fae8434f2da6168542d3171ec6" -dependencies = [ - "goblin", - "plain", - "redox_syscall", -] - [[package]] name = "redox-initfs" version = "0.1.0" @@ -127,10 +122,22 @@ dependencies = [ "plain", ] +[[package]] +name = "redox-rt" +version = "0.1.0" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git?rev=e97f26c7#e97f26c7632b1033ca062199db5633f3236821ed" +dependencies = [ + "bitflags", + "generic-rt", + "goblin", + "plain", + "redox_syscall", +] + [[package]] name = "redox-scheme" -version = "0.2.0" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#62aed6c712cca31bb18078adacd27b400f5ccf93" +version = "0.2.1" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#79d9d54f572f53386981fb9b6ef054fd9e45110c" dependencies = [ "libredox", "redox_syscall", @@ -138,9 +145,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags", ] @@ -182,9 +189,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index df541b7a2c..87c558457e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,9 @@ crate-type = ["staticlib"] hashbrown = { version = "0.14", default-features = false, features = ["inline-more"] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } -redox-exec = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox_syscall = "0.5.2" +redox_syscall = "0.5.3" redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } +redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", rev = "e97f26c7" } [profile.release] panic = "abort" diff --git a/src/exec.rs b/src/exec.rs index ff942f46d8..52f69c333a 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -4,7 +4,7 @@ use alloc::vec::Vec; use syscall::{Error, EINTR}; use syscall::flag::{O_CLOEXEC, O_RDONLY}; -use redox_exec::*; +use redox_rt::proc::*; pub fn main() -> ! { let envs = { @@ -41,7 +41,7 @@ pub fn main() -> ! { spawn_initfs(core::ptr::addr_of!(__initfs_header), initfs_length.get() as usize); } const CWD: &[u8] = b"/scheme/initfs"; - let extrainfo = redox_exec::ExtraInfo { + let extrainfo = ExtraInfo { cwd: Some(CWD), sigprocmask: 0, sigignmask: 0, @@ -68,7 +68,7 @@ unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. let write = syscall::dup(read, b"write").expect("failed to open sync write pipe"); - match redox_exec::fork_impl() { + match fork_impl() { Err(err) => { panic!("Failed to fork in order to start initfs daemon: {}", err); } diff --git a/src/start.rs b/src/start.rs index 67589a179c..f8b2ec4c87 100644 --- a/src/start.rs +++ b/src/start.rs @@ -42,6 +42,8 @@ pub unsafe extern "C" fn start() -> ! { let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); let _ = syscall::mprotect(data_end, crate::arch::STACK_START - data_end, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for rest of memory"); + redox_rt::initialize_freestanding(); + // FIXME make the initfs read-only crate::exec::main(); From 0fe6bbcee21e90d1b5c0a8264acb234c6fbcf7fe Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 11 Sep 2024 21:57:39 +0200 Subject: [PATCH 047/135] Implement getdents --- Cargo.lock | 24 ++++++++++---------- Cargo.toml | 2 +- src/initfs.rs | 62 +++++++++++++++++++++++++-------------------------- 3 files changed, 43 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fde8798a56..a92562a7a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,9 +50,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libredox" @@ -107,9 +107,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -117,7 +117,7 @@ dependencies = [ [[package]] name = "redox-initfs" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#7dd9b2e84d983a444518218f3ec74d3e9ac900b3" +source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#65cf406b7d4c5befc38b79696cdeb317bf06ed62" dependencies = [ "plain", ] @@ -137,7 +137,7 @@ dependencies = [ [[package]] name = "redox-scheme" version = "0.2.1" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#79d9d54f572f53386981fb9b6ef054fd9e45110c" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#67c75441660e11280a97396714c0df3e677119bc" dependencies = [ "libredox", "redox_syscall", @@ -145,9 +145,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags", ] @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -200,6 +200,6 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/Cargo.toml b/Cargo.toml index 87c558457e..1a533d48b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ crate-type = ["staticlib"] hashbrown = { version = "0.14", default-features = false, features = ["inline-more"] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } -redox_syscall = "0.5.3" +redox_syscall = "0.5.4" redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", rev = "e97f26c7" } diff --git a/src/initfs.rs b/src/initfs.rs index 81384df4c7..48389949dd 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -17,6 +17,9 @@ use redox_scheme::SignalBehavior; use redox_scheme::Socket; use redox_scheme::V2; use syscall::data::Stat; +use syscall::dirent::DirEntry; +use syscall::dirent::DirentBuf; +use syscall::dirent::DirentKind; use syscall::error::*; use syscall::flag::*; use syscall::schemev2::NewFdFlags; @@ -147,7 +150,7 @@ impl SchemeMut for InitFsScheme { Ok(OpenResult::ThisScheme { number: id, flags: NewFdFlags::POSITIONED }) } - fn read(&mut self, id: usize, mut buffer: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result { + fn read(&mut self, id: usize, buffer: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result { let Ok(offset) = usize::try_from(offset) else { return Ok(0); }; @@ -155,35 +158,6 @@ impl SchemeMut for InitFsScheme { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; match Self::get_inode(&self.fs, handle.inode)?.kind() { - InodeKind::Dir(dir) => { - let mut bytes_read = 0; - let mut total_to_skip = offset; - - for entry_res in (Iter { dir, idx: 0 }) { - let entry = entry_res?; - let name = entry.name().map_err(|_| Error::new(EIO))?; - - let to_skip = core::cmp::min(total_to_skip, name.len() + 1); - if to_skip == name.len() + 1 { continue; } - - let name = &name[to_skip..]; - - let to_copy = core::cmp::min(name.len(), buffer.len()); - buffer[..to_copy].copy_from_slice(&name[..to_copy]); - bytes_read += to_copy; - buffer = &mut buffer[to_copy..]; - - if !buffer.is_empty() { - buffer[0] = b'\n'; - bytes_read += 1; - buffer = &mut buffer[1..]; - } - - total_to_skip -= to_skip; - } - - Ok(bytes_read) - } InodeKind::File(file) => { let data = file.data().map_err(|_| Error::new(EIO))?; let src_buf = &data[core::cmp::min(offset, data.len())..]; @@ -193,9 +167,33 @@ impl SchemeMut for InitFsScheme { Ok(to_copy) } - InodeKind::Unknown => return Err(Error::new(EIO)), + InodeKind::Dir(_) => Err(Error::new(EISDIR)), + InodeKind::Unknown => Err(Error::new(EIO)), } } + fn getdents<'buf>(&mut self, id: usize, mut buf: DirentBuf<&'buf mut [u8]>, opaque_offset: u64) -> Result> { + let Ok(offset) = u32::try_from(opaque_offset) else { + return Ok(buf); + }; + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let InodeKind::Dir(dir) = Self::get_inode(&self.fs, handle.inode)?.kind() else { + return Err(Error::new(ENOTDIR)); + }; + let iter = Iter { dir, idx: offset }; + for (index, entry) in iter.enumerate() { + let entry = entry?; + buf.entry(DirEntry { + // TODO: Add getter + //inode: entry.inode(), + inode: 0, + + name: entry.name().ok().and_then(|utf8| core::str::from_utf8(utf8).ok()).ok_or(Error::new(EIO))?, + next_opaque_id: index as u64 + 1, + kind: DirentKind::Unspecified, + })?; + } + Ok(buf) + } fn fsize(&mut self, id: usize) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; @@ -213,7 +211,7 @@ impl SchemeMut for InitFsScheme { let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; // TODO: Copy scheme part in kernel - let scheme_path = b"initfs:"; + let scheme_path = b"/scheme/initfs"; let scheme_bytes = core::cmp::min(scheme_path.len(), buf.len()); buf[..scheme_bytes].copy_from_slice(&scheme_path[..scheme_bytes]); From e0c153a5edb757000d3f81dfac7dbfd7612ac55c Mon Sep 17 00:00:00 2001 From: Kamil Koczurek Date: Thu, 19 Sep 2024 12:19:40 +0200 Subject: [PATCH 048/135] Switch to newest redox-rt --- Cargo.lock | 35 +++++++++------- Cargo.toml | 7 +++- src/exec.rs | 42 +++++++++++++++---- src/initfs.rs | 111 +++++++++++++++++++++++++++++++++++++++----------- src/lib.rs | 44 +++++++++++++++----- src/start.rs | 52 +++++++++++++++++++---- 6 files changed, 225 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a92562a7a2..46a3b52d71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,7 @@ dependencies = [ "hashbrown", "linked_list_allocator", "redox-initfs", + "redox-path", "redox-rt", "redox-scheme", "redox_syscall", @@ -29,7 +30,7 @@ dependencies = [ [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git?rev=e97f26c7#e97f26c7632b1033ca062199db5633f3236821ed" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#1e97bae11b1a51a1589bb6ea44cf8e507d2d1275" [[package]] name = "goblin" @@ -50,9 +51,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libredox" @@ -107,25 +108,31 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "redox-initfs" -version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#65cf406b7d4c5befc38b79696cdeb317bf06ed62" +version = "0.2.0" +source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#d0802237d7894881df7ddd338dfc64220d0646ff" dependencies = [ "plain", ] +[[package]] +name = "redox-path" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" + [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git?rev=e97f26c7#e97f26c7632b1033ca062199db5633f3236821ed" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#1e97bae11b1a51a1589bb6ea44cf8e507d2d1275" dependencies = [ "bitflags", "generic-rt", @@ -136,8 +143,8 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.2.1" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#67c75441660e11280a97396714c0df3e677119bc" +version = "0.2.2" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#9a2e8a31894e397594ea4e3f033c4a91709bbbdc" dependencies = [ "libredox", "redox_syscall", @@ -189,9 +196,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -200,6 +207,6 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml index 1a533d48b5..da9af52a15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,15 @@ name = "bootstrap" crate-type = ["staticlib"] [dependencies] -hashbrown = { version = "0.14", default-features = false, features = ["inline-more"] } +hashbrown = { version = "0.14", default-features = false, features = [ + "inline-more", +] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = "0.5.4" redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } -redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", rev = "e97f26c7" } +redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } +redox-path = "0.3.1" [profile.release] panic = "abort" diff --git a/src/exec.rs b/src/exec.rs index 52f69c333a..ae03aa2269 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,8 +1,8 @@ use alloc::borrow::ToOwned; use alloc::vec::Vec; -use syscall::{Error, EINTR}; use syscall::flag::{O_CLOEXEC, O_RDONLY}; +use syscall::{Error, EINTR}; use redox_rt::proc::*; @@ -28,27 +28,41 @@ pub fn main() -> ! { iter().map(|var| var.to_owned()).collect::>() }; - extern { + extern "C" { // The linker script will define this as the location of the initfs header. static __initfs_header: u8; } - let initfs_length = unsafe { (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size }; + let initfs_length = unsafe { + (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size + }; unsafe { // Creating a reference to NULL is UB. Mask the UB for now using black_box. // FIXME use a raw pointer and inline asm for reading instead for the initfs header. - spawn_initfs(core::ptr::addr_of!(__initfs_header), initfs_length.get() as usize); + spawn_initfs( + core::ptr::addr_of!(__initfs_header), + initfs_length.get() as usize, + ); } const CWD: &[u8] = b"/scheme/initfs"; + const DEFAULT_SCHEME: &[u8] = b"initfs"; let extrainfo = ExtraInfo { cwd: Some(CWD), + default_scheme: Some(DEFAULT_SCHEME), sigprocmask: 0, sigignmask: 0, }; let path = "/scheme/initfs/bin/init"; - let total_args_envs_auxvpointee_size = path.len() + 1 + envs.len() + envs.iter().map(|v| v.len()).sum::() + CWD.len() + 1; + let total_args_envs_auxvpointee_size = path.len() + + 1 + + envs.len() + + envs.iter().map(|v| v.len()).sum::() + + CWD.len() + + 1 + + DEFAULT_SCHEME.len() + + 1; let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); let open_via_dup = FdGuard::new( @@ -57,7 +71,18 @@ pub fn main() -> ! { ); let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); - fexec_impl(image_file, open_via_dup, &memory, path.as_bytes(), [path], envs.iter(), total_args_envs_auxvpointee_size, &extrainfo, None).expect("failed to execute init"); + fexec_impl( + image_file, + open_via_dup, + &memory, + path.as_bytes(), + [path], + envs.iter(), + total_args_envs_auxvpointee_size, + &extrainfo, + None, + ) + .expect("failed to execute init"); unreachable!() } @@ -89,5 +114,8 @@ unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { return; } } - crate::initfs::run(core::slice::from_raw_parts(initfs_start, initfs_length), write); + crate::initfs::run( + core::slice::from_raw_parts(initfs_start, initfs_length), + write, + ); } diff --git a/src/initfs.rs b/src/initfs.rs index 48389949dd..6b96ea1f60 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -6,8 +6,9 @@ use core::str; use alloc::string::String; use hashbrown::HashMap; -use redox_initfs::{InitFs, InodeStruct, Inode, InodeDir, InodeKind, types::Timespec}; +use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct}; +use redox_path::canonicalize_to_standard; use redox_scheme::CallerCtx; use redox_scheme::OpenResult; use redox_scheme::RequestKind; @@ -55,7 +56,6 @@ impl InitFsScheme { } } - struct Iter { dir: InodeDir<'static>, idx: u32, @@ -71,7 +71,8 @@ impl Iterator for Iter { fn size_hint(&self) -> (usize, Option) { match self.dir.entry_count().ok() { Some(size) => { - let size = usize::try_from(size).expect("expected u32 to be convertible into usize"); + let size = + usize::try_from(size).expect("expected u32 to be convertible into usize"); (size, Some(size)) } None => (0, None), @@ -82,14 +83,18 @@ impl Iterator for Iter { fn inode_len(inode: InodeStruct<'static>) -> Result { Ok(match inode.kind() { InodeKind::File(file) => file.data().map_err(|_| Error::new(EIO))?.len(), - InodeKind::Dir(dir) => (Iter { dir, idx: 0 }) - .fold(0, |len, entry| len + entry.and_then(|entry| entry.name().map_err(|_| Error::new(EIO))).map_or(0, |name| name.len() + 1)), + InodeKind::Dir(dir) => (Iter { dir, idx: 0 }).fold(0, |len, entry| { + len + entry + .and_then(|entry| entry.name().map_err(|_| Error::new(EIO))) + .map_or(0, |name| name.len() + 1) + }), + InodeKind::Link(link) => link.data().map_err(|_| Error::new(EIO))?.len(), InodeKind::Unknown => return Err(Error::new(EIO)), }) } impl SchemeMut for InitFsScheme { - fn xopen(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result { + fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { let mut components = path // trim leading and trailing slash .trim_matches('/') @@ -105,7 +110,7 @@ impl SchemeMut for InitFsScheme { "." => continue, ".." => { let _ = components.next_back(); - continue + continue; } _ => (), @@ -116,16 +121,18 @@ impl SchemeMut for InitFsScheme { let dir = match current_inode_struct.kind() { InodeKind::Dir(dir) => dir, + // TODO: Support symlinks in other position than xopen target + InodeKind::Link(link) => { + return Err(Error::new(EOPNOTSUPP)); + } + // If we still have more components in the path, and the file tree for that // particular branch is not all directories except the last, then that file cannot // exist. InodeKind::File(_) | InodeKind::Unknown => return Err(Error::new(ENOENT)), }; - let mut entries = Iter { - dir, - idx: 0, - }; + let mut entries = Iter { dir, idx: 0 }; current_inode = loop { let entry_res = match entries.next() { @@ -140,17 +147,40 @@ impl SchemeMut for InitFsScheme { }; } + // xopen target is link -- return EXDEV so that the file is opened as a link. + // TODO: Maybe follow initfs-local symlinks here? Would be faster + let is_link = matches!( + Self::get_inode(&self.fs, current_inode)?.kind(), + InodeKind::Link(_) + ); + let o_symlink = flags & O_SYMLINK != 0; + if is_link && !o_symlink { + return Err(Error::new(EXDEV)); + } + let id = self.next_id(); - let old = self.handles.insert(id, Handle { - inode: current_inode, - filename: path.into(), - }); + let old = self.handles.insert( + id, + Handle { + inode: current_inode, + filename: path.into(), + }, + ); assert!(old.is_none()); - Ok(OpenResult::ThisScheme { number: id, flags: NewFdFlags::POSITIONED }) + Ok(OpenResult::ThisScheme { + number: id, + flags: NewFdFlags::POSITIONED, + }) } - fn read(&mut self, id: usize, buffer: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result { + fn read( + &mut self, + id: usize, + buffer: &mut [u8], + offset: u64, + _fcntl_flags: u32, + ) -> Result { let Ok(offset) = usize::try_from(offset) else { return Ok(0); }; @@ -168,10 +198,29 @@ impl SchemeMut for InitFsScheme { Ok(to_copy) } InodeKind::Dir(_) => Err(Error::new(EISDIR)), + InodeKind::Link(link) => { + let link_data = link.data().map_err(|_| Error::new(EIO))?; + let path = core::str::from_utf8(link_data).map_err(|_| Error::new(ENOENT))?; + let cannonical = + canonicalize_to_standard(Some("/"), path).ok_or_else(|| Error::new(ENOENT))?; + let data = cannonical.as_bytes(); + + let src_buf = &data[core::cmp::min(offset, data.len())..]; + + let to_copy = core::cmp::min(src_buf.len(), buffer.len()); + buffer[..to_copy].copy_from_slice(&src_buf[..to_copy]); + + Ok(to_copy) + } InodeKind::Unknown => Err(Error::new(EIO)), } } - fn getdents<'buf>(&mut self, id: usize, mut buf: DirentBuf<&'buf mut [u8]>, opaque_offset: u64) -> Result> { + fn getdents<'buf>( + &mut self, + id: usize, + mut buf: DirentBuf<&'buf mut [u8]>, + opaque_offset: u64, + ) -> Result> { let Ok(offset) = u32::try_from(opaque_offset) else { return Ok(buf); }; @@ -187,7 +236,11 @@ impl SchemeMut for InitFsScheme { //inode: entry.inode(), inode: 0, - name: entry.name().ok().and_then(|utf8| core::str::from_utf8(utf8).ok()).ok_or(Error::new(EIO))?, + name: entry + .name() + .ok() + .and_then(|utf8| core::str::from_utf8(utf8).ok()) + .ok_or(Error::new(EIO))?, next_opaque_id: index as u64 + 1, kind: DirentKind::Unspecified, })?; @@ -229,7 +282,12 @@ impl SchemeMut for InitFsScheme { let inode = Self::get_inode(&self.fs, handle.inode)?; - stat.st_mode = inode.mode() | match inode.kind() { InodeKind::Dir(_) => MODE_DIR, InodeKind::File(_) => MODE_FILE, _ => 0 }; + stat.st_mode = inode.mode() + | match inode.kind() { + InodeKind::Dir(_) => MODE_DIR, + InodeKind::File(_) => MODE_FILE, + _ => 0, + }; stat.st_uid = inode.uid(); stat.st_gid = inode.gid(); stat.st_size = u64::try_from(inode_len(inode)?).unwrap_or(u64::MAX); @@ -259,14 +317,16 @@ impl SchemeMut for InitFsScheme { pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { let mut scheme = InitFsScheme::new(bytes); - let socket = Socket::::create("initfs") - .expect("failed to open initfs scheme socket"); + let socket = Socket::::create("initfs").expect("failed to open initfs scheme socket"); let _ = syscall::write(sync_pipe, &[0]); let _ = syscall::close(sync_pipe); loop { - let RequestKind::Call(req) = (match socket.next_request(SignalBehavior::Restart).expect("bootstrap: failed to read scheme request from kernel") { + let RequestKind::Call(req) = (match socket + .next_request(SignalBehavior::Restart) + .expect("bootstrap: failed to read scheme request from kernel") + { Some(req) => req.kind(), None => break, }) else { @@ -274,7 +334,10 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { }; let resp = req.handle_scheme_mut(&mut scheme); - if !socket.write_response(resp, SignalBehavior::Restart).expect("bootstrap: failed to write scheme response to kernel") { + if !socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel") + { break; } } diff --git a/src/lib.rs b/src/lib.rs index 3c45afb000..66d3c7d8f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,6 @@ #![no_std] #![allow(internal_features)] -#![feature( - asm_const, - core_intrinsics, - str_from_raw_parts, -)] +#![feature(asm_const, core_intrinsics, str_from_raw_parts, iter_intersperse)] #[cfg(target_arch = "aarch64")] #[path = "aarch64.rs"] @@ -35,7 +31,9 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { impl Write for Writer { fn write_str(&mut self, s: &str) -> core::fmt::Result { - syscall::write(1, s.as_bytes()).map_err(|_| core::fmt::Error).map(|_| ()) + syscall::write(1, s.as_bytes()) + .map_err(|_| core::fmt::Error) + .map(|_| ()) } } @@ -62,8 +60,19 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { let heap = HEAP.get_or_insert_with(|| { HEAP_TOP = HEAP_OFF + SIZE; - let _ = syscall::fmap(!0, &Map { offset: 0, size: SIZE, address: HEAP_OFF, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED_NOREPLACE }) - .expect("failed to map initial heap"); + let _ = syscall::fmap( + !0, + &Map { + offset: 0, + size: SIZE, + address: HEAP_OFF, + flags: MapFlags::PROT_WRITE + | MapFlags::PROT_READ + | MapFlags::MAP_PRIVATE + | MapFlags::MAP_FIXED_NOREPLACE, + }, + ) + .expect("failed to map initial heap"); linked_list_allocator::Heap::new(HEAP_OFF as *mut u8, SIZE) }); @@ -74,8 +83,19 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { return core::ptr::null_mut(); } - let _ = syscall::fmap(!0, &Map { offset: 0, size: HEAP_INCREASE_BY, address: HEAP_TOP, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED_NOREPLACE }) - .expect("failed to extend heap"); + let _ = syscall::fmap( + !0, + &Map { + offset: 0, + size: HEAP_INCREASE_BY, + address: HEAP_TOP, + flags: MapFlags::PROT_WRITE + | MapFlags::PROT_READ + | MapFlags::MAP_PRIVATE + | MapFlags::MAP_FIXED_NOREPLACE, + }, + ) + .expect("failed to extend heap"); heap.extend(HEAP_INCREASE_BY); HEAP_TOP += HEAP_INCREASE_BY; @@ -84,6 +104,8 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { } } unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { - HEAP.as_mut().unwrap().deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) + HEAP.as_mut() + .unwrap() + .deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) } } diff --git a/src/start.rs b/src/start.rs index f8b2ec4c87..ebaeb17505 100644 --- a/src/start.rs +++ b/src/start.rs @@ -13,13 +13,28 @@ mod offsets { static __bss_end: u8; } pub fn text() -> (usize, usize) { - unsafe { (&__text_start as *const u8 as usize, &__text_end as *const u8 as usize) } + unsafe { + ( + &__text_start as *const u8 as usize, + &__text_end as *const u8 as usize, + ) + } } pub fn rodata() -> (usize, usize) { - unsafe { (&__rodata_start as *const u8 as usize, &__rodata_end as *const u8 as usize) } + unsafe { + ( + &__rodata_start as *const u8 as usize, + &__rodata_end as *const u8 as usize, + ) + } } pub fn data_and_bss() -> (usize, usize) { - unsafe { (&__data_start as *const u8 as usize, &__bss_end as *const u8 as usize) } + unsafe { + ( + &__data_start as *const u8 as usize, + &__bss_end as *const u8 as usize, + ) + } } } @@ -35,12 +50,33 @@ pub unsafe extern "C" fn start() -> ! { let _ = syscall::open("/scheme/debug", syscall::O_WRONLY); // stdout let _ = syscall::open("/scheme/debug", syscall::O_WRONLY); // stderr - let _ = syscall::mprotect(4096, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for initfs header page"); + let _ = syscall::mprotect(4096, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE) + .expect("mprotect failed for initfs header page"); - let _ = syscall::mprotect(text_start, text_end - text_start, MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE).expect("mprotect failed for .text"); - let _ = syscall::mprotect(rodata_start, rodata_end - rodata_start, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for .rodata"); - let _ = syscall::mprotect(data_start, data_end - data_start, MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE).expect("mprotect failed for .data/.bss"); - let _ = syscall::mprotect(data_end, crate::arch::STACK_START - data_end, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE).expect("mprotect failed for rest of memory"); + let _ = syscall::mprotect( + text_start, + text_end - text_start, + MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .text"); + let _ = syscall::mprotect( + rodata_start, + rodata_end - rodata_start, + MapFlags::PROT_READ | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .rodata"); + let _ = syscall::mprotect( + data_start, + data_end - data_start, + MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .data/.bss"); + let _ = syscall::mprotect( + data_end, + crate::arch::STACK_START - data_end, + MapFlags::PROT_READ | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for rest of memory"); redox_rt::initialize_freestanding(); From 508f566fea48a0c3f04de1780433d7aa9c584252 Mon Sep 17 00:00:00 2001 From: Andrey Turkin Date: Wed, 26 Jun 2024 08:09:35 +0300 Subject: [PATCH 049/135] RISC-V target --- Cargo.lock | 36 ++++++++++++++++++------------------ src/aarch64.rs | 3 ++- src/exec.rs | 1 + src/i686.rs | 3 ++- src/initfs.rs | 4 ++-- src/lib.rs | 10 +++++----- src/riscv64.ld | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/riscv64.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/x86_64.rs | 4 ++-- 9 files changed, 123 insertions(+), 29 deletions(-) create mode 100644 src/riscv64.ld create mode 100644 src/riscv64.rs diff --git a/Cargo.lock b/Cargo.lock index 46a3b52d71..3cda46101e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" @@ -30,7 +30,7 @@ dependencies = [ [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#1e97bae11b1a51a1589bb6ea44cf8e507d2d1275" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#0a29bfae68121b2639a75b50902d5dd7f0531848" [[package]] name = "goblin" @@ -51,9 +51,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libredox" @@ -99,18 +99,18 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -132,7 +132,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#1e97bae11b1a51a1589bb6ea44cf8e507d2d1275" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#0a29bfae68121b2639a75b50902d5dd7f0531848" dependencies = [ "bitflags", "generic-rt", @@ -143,8 +143,8 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.2.2" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#9a2e8a31894e397594ea4e3f033c4a91709bbbdc" +version = "0.2.3" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#dc6a047842353ed49c1788396d9135de2c3d7382" dependencies = [ "libredox", "redox_syscall", @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -207,6 +207,6 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/src/aarch64.rs b/src/aarch64.rs index 0fef48b0c9..56aecc3ee5 100644 --- a/src/aarch64.rs +++ b/src/aarch64.rs @@ -5,7 +5,8 @@ use syscall::{ number::SYS_FMAP, }; -pub const STACK_START: usize = 0x0000_8000_0000_0000 - STACK_SIZE; +pub const USERMODE_END: usize = 0x0000_8000_0000_0000; +pub const STACK_START: usize = USERMODE_END - STACK_SIZE; const STACK_SIZE: usize = 64 * 1024; // 64 KiB static MAP: Map = Map { diff --git a/src/exec.rs b/src/exec.rs index ae03aa2269..0083c906b3 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -52,6 +52,7 @@ pub fn main() -> ! { default_scheme: Some(DEFAULT_SCHEME), sigprocmask: 0, sigignmask: 0, + umask: redox_rt::sys::get_umask(), }; let path = "/scheme/initfs/bin/init"; diff --git a/src/i686.rs b/src/i686.rs index f07beb4397..a3805d10e2 100644 --- a/src/i686.rs +++ b/src/i686.rs @@ -2,7 +2,8 @@ use core::mem; use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; const STACK_SIZE: usize = 64 * 1024; // 64 KiB -pub const STACK_START: usize = 0x8000_0000 - STACK_SIZE; +pub const USERMODE_END: usize = 0x8000_0000; +pub const STACK_START: usize = USERMODE_END - STACK_SIZE; static MAP: Map = Map { offset: 0, diff --git a/src/initfs.rs b/src/initfs.rs index 6b96ea1f60..b1e97cdfbc 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -94,7 +94,7 @@ fn inode_len(inode: InodeStruct<'static>) -> Result { } impl SchemeMut for InitFsScheme { - fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn xopen(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { let mut components = path // trim leading and trailing slash .trim_matches('/') @@ -122,7 +122,7 @@ impl SchemeMut for InitFsScheme { InodeKind::Dir(dir) => dir, // TODO: Support symlinks in other position than xopen target - InodeKind::Link(link) => { + InodeKind::Link(_) => { return Err(Error::new(EOPNOTSUPP)); } diff --git a/src/lib.rs b/src/lib.rs index 66d3c7d8f3..7fc400fe98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,10 @@ pub mod arch; #[path = "x86_64.rs"] pub mod arch; +#[cfg(target_arch = "riscv64")] +#[path = "riscv64.rs"] +pub mod arch; + pub mod exec; pub mod initfs; pub mod start; @@ -41,11 +45,7 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! { core::intrinsics::abort(); } -#[cfg(target_pointer_width = "32")] -const HEAP_OFF: usize = 0x4000_0000; - -#[cfg(target_pointer_width = "64")] -const HEAP_OFF: usize = 0x4000_0000_0000; +const HEAP_OFF: usize = arch::USERMODE_END / 2; struct Allocator; #[global_allocator] diff --git a/src/riscv64.ld b/src/riscv64.ld new file mode 100644 index 0000000000..98b5239bbd --- /dev/null +++ b/src/riscv64.ld @@ -0,0 +1,44 @@ +ENTRY(_start) +OUTPUT_FORMAT(elf64-littleriscv) + +SECTIONS { + . = 4096 + 4096; /* Reserved for the null page and the initfs header prepended by redox-initfs-ar */ + __initfs_header = . - 4096; + . += SIZEOF_HEADERS; + . = ALIGN(4096); + + .text : { + __text_start = .; + *(.text*) + . = ALIGN(4096); + __text_end = .; + } + .rodata : { + __rodata_start = .; + *(.rodata*) + . = ALIGN(4096); + __rodata_end = .; + } + .data : { + __data_start = .; + *(.data*) + *(.sdata*) + *(.got*) + . = ALIGN(4096); + __data_end = .; + + __bss_start = .; + *(.bss*) + *(.sbss*) + . = ALIGN(4096); + __bss_end = .; + } + + /DISCARD/ : { + *(.comment*) + *(.eh_frame*) + *(.gcc_except_table*) + *(.note*) + *(.rel.eh_frame*) + } +} diff --git a/src/riscv64.rs b/src/riscv64.rs new file mode 100644 index 0000000000..6a36039a4f --- /dev/null +++ b/src/riscv64.rs @@ -0,0 +1,47 @@ +use core::mem; +use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; + +const STACK_SIZE: usize = 64 * 1024; // 64 KiB +pub const USERMODE_END: usize = 1 << 47; // Assuming Sv48; it should work with Sv57 also +pub const STACK_START: usize = USERMODE_END - STACK_SIZE; + +static MAP: Map = Map { + offset: 0, + size: STACK_SIZE, + flags: MapFlags::PROT_READ + .union(MapFlags::PROT_WRITE) + .union(MapFlags::MAP_PRIVATE) + .union(MapFlags::MAP_FIXED_NOREPLACE), + address: STACK_START, // highest possible user address +}; + +core::arch::global_asm!( + " + .globl _start +_start: + # Setup a stack. + li a7, {number} + li a0, {fd} + la a1, {map} # pointer to Map struct + li a2, {map_size} # size of Map struct + ecall + + # Test for success (nonzero value). + bne a0, x0, 2f + # (failure) + unimp +2: + li sp, {stack_size} + add sp, sp, a0 + mv fp, x0 + + jal start + # `start` must never return. + unimp + ", + fd = const usize::MAX, // dummy fd indicates anonymous map + map = sym MAP, + map_size = const mem::size_of::(), + number = const SYS_FMAP, + stack_size = const STACK_SIZE, +); diff --git a/src/x86_64.rs b/src/x86_64.rs index c4c051ca0a..f7110a64e9 100644 --- a/src/x86_64.rs +++ b/src/x86_64.rs @@ -2,8 +2,8 @@ use core::mem; use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; const STACK_SIZE: usize = 64 * 1024; // 64 KiB - -pub const STACK_START: usize = 0x0000_8000_0000_0000 - STACK_SIZE; +pub const USERMODE_END: usize = 0x0000_8000_0000_0000; +pub const STACK_START: usize = USERMODE_END - STACK_SIZE; static MAP: Map = Map { offset: 0, From 9ffd72a59030028441e0974ada42105940a161b3 Mon Sep 17 00:00:00 2001 From: Ribbon Date: Thu, 9 Jan 2025 12:21:02 +0000 Subject: [PATCH 050/135] Add README.md --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000..2396cc28a1 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# bootstrap + +First code that the kernel executes, responsible for spawning init. + +## How To Contribute + +To learn how to contribute to this system component you need to read the following document: + +- [CONTRIBUTING.md](https://gitlab.redox-os.org/redox-os/redox/-/blob/master/CONTRIBUTING.md) + +## Development + +To learn how to do development with this system component inside the Redox build system you need to read the [Build System](https://doc.redox-os.org/book/build-system-reference.html) and [Coding and Building](https://doc.redox-os.org/book/coding-and-building.html) pages. + +### How To Build + +To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page. + +This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux. From 416b6e486f902cf697ed4412f94f58517dcdcf0c Mon Sep 17 00:00:00 2001 From: Ron Williams Date: Wed, 26 Mar 2025 14:53:14 -0700 Subject: [PATCH 051/135] Use redox-scheme 0.2.3 and cargo update --- Cargo.lock | 40 ++++++++++++++++++++-------------------- Cargo.toml | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3cda46101e..80c58ca452 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "autocfg" @@ -10,9 +10,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bootstrap" @@ -30,7 +30,7 @@ dependencies = [ [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#0a29bfae68121b2639a75b50902d5dd7f0531848" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#ea393613292c03e0fd00c5a23346f35b3909b579" [[package]] name = "goblin" @@ -51,9 +51,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libredox" @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "plain" @@ -99,18 +99,18 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -132,7 +132,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#0a29bfae68121b2639a75b50902d5dd7f0531848" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#ea393613292c03e0fd00c5a23346f35b3909b579" dependencies = [ "bitflags", "generic-rt", @@ -144,7 +144,7 @@ dependencies = [ [[package]] name = "redox-scheme" version = "0.2.3" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#dc6a047842353ed49c1788396d9135de2c3d7382" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git?tag=v0.2.3#dc6a047842353ed49c1788396d9135de2c3d7382" dependencies = [ "libredox", "redox_syscall", @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ "bitflags", ] @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -207,6 +207,6 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml index da9af52a15..f00f646349 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ hashbrown = { version = "0.14", default-features = false, features = [ linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = "0.5.4" -redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } +redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git", tag = "v0.2.3" } redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } redox-path = "0.3.1" From 9bcc8848208f4583c4b3cef4e43dd0bbcfad5ad6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 12 Apr 2025 14:08:55 +0200 Subject: [PATCH 052/135] Update redox-rt to fix arm64 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80c58ca452..6f88f5e6c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#ea393613292c03e0fd00c5a23346f35b3909b579" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#5ea71a02c7f9e436ed0dcf53bc7a5bdb311279cf" [[package]] name = "goblin" @@ -132,7 +132,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#ea393613292c03e0fd00c5a23346f35b3909b579" +source = "git+https://gitlab.redox-os.org/redox-os/relibc#5ea71a02c7f9e436ed0dcf53bc7a5bdb311279cf" dependencies = [ "bitflags", "generic-rt", From 72978e963ef51f7e48c4f53768fec509f50193d8 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 13:45:23 +0100 Subject: [PATCH 053/135] Patch redox-rt, syscall. --- Cargo.lock | 21 ++++++++++----------- Cargo.toml | 10 +++++++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f88f5e6c0..02ce92c312 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#5ea71a02c7f9e436ed0dcf53bc7a5bdb311279cf" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#e93311da1f12c942118cbde2b38643f574348606" [[package]] name = "goblin" @@ -51,9 +51,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libredox" @@ -99,9 +99,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -132,7 +132,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#5ea71a02c7f9e436ed0dcf53bc7a5bdb311279cf" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#e93311da1f12c942118cbde2b38643f574348606" dependencies = [ "bitflags", "generic-rt", @@ -143,8 +143,8 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.2.3" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git?tag=v0.2.3#dc6a047842353ed49c1788396d9135de2c3d7382" +version = "0.6.0" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#aee599d75a51cda40c23ca91689b1c92cc4a8557" dependencies = [ "libredox", "redox_syscall", @@ -152,9 +152,8 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +version = "0.5.11" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=nuke_proc#8a80d1f58bc92d8c0eb73a939420fb30fe1e85d0" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index f00f646349..8c7370749d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,10 @@ hashbrown = { version = "0.14", default-features = false, features = [ ] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } -redox_syscall = "0.5.4" -redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git", tag = "v0.2.3" } -redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } +redox_syscall = { version = "0.5.4", default-features = false } +redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } +#redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } +redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } redox-path = "0.3.1" [profile.release] @@ -26,3 +27,6 @@ lto = "fat" [profile.dev] panic = "abort" + +[patch.crates-io] +redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "nuke_proc" } From eecd046709263a312141948d88f81ece2abd1a4c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 13:45:39 +0100 Subject: [PATCH 054/135] TODO: start writing proc manager --- src/exec.rs | 29 +++++++++++--------- src/initfs.rs | 1 - src/lib.rs | 1 + src/procmngr.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 src/procmngr.rs diff --git a/src/exec.rs b/src/exec.rs index 0083c906b3..b9ba4d51e2 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -37,14 +37,20 @@ pub fn main() -> ! { (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size }; - unsafe { + spawn("initfs daemon", move |write_fd| unsafe { // Creating a reference to NULL is UB. Mask the UB for now using black_box. // FIXME use a raw pointer and inline asm for reading instead for the initfs header. - spawn_initfs( - core::ptr::addr_of!(__initfs_header), - initfs_length.get() as usize, + let initfs_start = core::ptr::addr_of!(__initfs_header); + let initfs_length = initfs_length.get() as usize; + + crate::initfs::run( + core::slice::from_raw_parts(initfs_start, initfs_length), + write_fd, ); - } + }); + + spawn("process manager", move |write_fd| crate::procmngr::run(write_fd)); + const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; let extrainfo = ExtraInfo { @@ -67,11 +73,13 @@ pub fn main() -> ! { let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); let open_via_dup = FdGuard::new( - syscall::open("/scheme/thisproc/current/open_via_dup", 0) + syscall::open("/scheme/thisproc/current", 0) .expect("failed to open open_via_dup"), ); let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); + redox_rt::proc::make_init(); + fexec_impl( image_file, open_via_dup, @@ -88,7 +96,7 @@ pub fn main() -> ! { unreachable!() } -unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { +pub(crate) fn spawn(name: &str, inner: impl FnOnce(usize)) { let read = syscall::open("/scheme/pipe", O_CLOEXEC).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. @@ -96,7 +104,7 @@ unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { match fork_impl() { Err(err) => { - panic!("Failed to fork in order to start initfs daemon: {}", err); + panic!("Failed to fork in order to start {name}: {err}"); } // Continue serving the scheme as the child. Ok(0) => { @@ -115,8 +123,5 @@ unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { return; } } - crate::initfs::run( - core::slice::from_raw_parts(initfs_start, initfs_length), - write, - ); + inner(write); } diff --git a/src/initfs.rs b/src/initfs.rs index b1e97cdfbc..ee7ccb10d1 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -342,7 +342,6 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { } } - syscall::exit(0).expect("initfs: failed to exit"); unreachable!() } diff --git a/src/lib.rs b/src/lib.rs index 7fc400fe98..71e88a352c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ pub mod arch; pub mod exec; pub mod initfs; +pub mod procmngr; pub mod start; extern crate alloc; diff --git a/src/procmngr.rs b/src/procmngr.rs new file mode 100644 index 0000000000..b70defe239 --- /dev/null +++ b/src/procmngr.rs @@ -0,0 +1,72 @@ +use core::cell::RefCell; + +use alloc::rc::Rc; +use hashbrown::hash_map::DefaultHashBuilder; +use hashbrown::{HashMap, HashSet}; +use redox_rt::proc::FdGuard; +use redox_scheme::{CallerCtx, OpenResult, RequestKind, SchemeMut, SignalBehavior, Socket, V2}; +use syscall::{Result, O_CREAT}; + +pub fn run(write_fd: usize) { + let socket = Socket::::create("proc").expect("failed to open proc scheme socket"); + let mut scheme = ProcScheme::new(); + + let _ = syscall::write(1, b"process manager started\n").unwrap(); + let _ = syscall::write(write_fd, &[0]); + let _ = syscall::close(write_fd); + + loop { + let RequestKind::Call(req) = (match socket + .next_request(SignalBehavior::Restart) + .expect("bootstrap: failed to read scheme request from kernel") + { + Some(req) => req.kind(), + None => break, + }) else { + continue; + }; + let resp = req.handle_scheme_mut(&mut scheme); + + if !socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel") + { + break; + } + } + + unreachable!() +} + +struct Process { + threads: Vec>>, + ppid: ProcessId, + pgid: ProcessId, + sid: ProcessId, +} +struct Thread { + fd: FdGuard, + // sig_ctrl: MmapGuard<...> +} +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +struct ProcessId(usize); + +struct ProcScheme { + processes: HashMap, + process_groups: HashSet, + sessions: HashSet, +} +impl ProcScheme { + pub fn new() -> ProcScheme { + ProcScheme { + processes: HashMap::new(), + process_groups: HashSet::new(), + sessions: HashSet::new(), + } + } +} +impl SchemeMut for ProcScheme { + fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + + } +} From 9f0ddc83f4d604e871cbc9928ac1034c8513df3c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 16:19:12 +0100 Subject: [PATCH 055/135] WIP: write skeleton for fork, new-thread --- Cargo.toml | 2 + src/aarch64.rs | 12 ++-- src/exec.rs | 11 ++-- src/initfs.rs | 15 ++--- src/procmngr.rs | 163 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 167 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8c7370749d..b7b2b3366f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["staticlib"] [dependencies] hashbrown = { version = "0.14", default-features = false, features = [ "inline-more", + "ahash", ] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } @@ -20,6 +21,7 @@ redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } #redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } redox-path = "0.3.1" +slab = { version = "0.4.9", default-features = false } [profile.release] panic = "abort" diff --git a/src/aarch64.rs b/src/aarch64.rs index 56aecc3ee5..6e1a732175 100644 --- a/src/aarch64.rs +++ b/src/aarch64.rs @@ -1,9 +1,5 @@ use core::mem; -use syscall::{ - data::Map, - flag::MapFlags, - number::SYS_FMAP, -}; +use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; pub const USERMODE_END: usize = 0x0000_8000_0000_0000; pub const STACK_START: usize = USERMODE_END - STACK_SIZE; @@ -13,9 +9,9 @@ static MAP: Map = Map { offset: 0, size: STACK_SIZE, flags: MapFlags::PROT_READ - .union(MapFlags::PROT_WRITE) - .union(MapFlags::MAP_PRIVATE) - .union(MapFlags::MAP_FIXED_NOREPLACE), + .union(MapFlags::PROT_WRITE) + .union(MapFlags::MAP_PRIVATE) + .union(MapFlags::MAP_FIXED_NOREPLACE), address: STACK_START, // highest possible user address }; diff --git a/src/exec.rs b/src/exec.rs index b9ba4d51e2..9d6f491a0e 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -49,7 +49,9 @@ pub fn main() -> ! { ); }); - spawn("process manager", move |write_fd| crate::procmngr::run(write_fd)); + spawn("process manager", move |write_fd| { + crate::procmngr::run(write_fd) + }); const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; @@ -73,12 +75,13 @@ pub fn main() -> ! { let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); let open_via_dup = FdGuard::new( - syscall::open("/scheme/thisproc/current", 0) - .expect("failed to open open_via_dup"), + syscall::open("/scheme/thisproc/current", 0).expect("failed to open open_via_dup"), ); let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); - redox_rt::proc::make_init(); + unsafe { + redox_rt::proc::make_init(); + } fexec_impl( image_file, diff --git a/src/initfs.rs b/src/initfs.rs index ee7ccb10d1..7568b15e9f 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -9,14 +9,9 @@ use hashbrown::HashMap; use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct}; use redox_path::canonicalize_to_standard; -use redox_scheme::CallerCtx; -use redox_scheme::OpenResult; -use redox_scheme::RequestKind; -use redox_scheme::SchemeMut; +use redox_scheme::{CallerCtx, OpenResult, RequestKind, Scheme}; -use redox_scheme::SignalBehavior; -use redox_scheme::Socket; -use redox_scheme::V2; +use redox_scheme::{SignalBehavior, Socket}; use syscall::data::Stat; use syscall::dirent::DirEntry; use syscall::dirent::DirentBuf; @@ -93,7 +88,7 @@ fn inode_len(inode: InodeStruct<'static>) -> Result { }) } -impl SchemeMut for InitFsScheme { +impl Scheme for InitFsScheme { fn xopen(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { let mut components = path // trim leading and trailing slash @@ -317,7 +312,7 @@ impl SchemeMut for InitFsScheme { pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { let mut scheme = InitFsScheme::new(bytes); - let socket = Socket::::create("initfs").expect("failed to open initfs scheme socket"); + let socket = Socket::create("initfs").expect("failed to open initfs scheme socket"); let _ = syscall::write(sync_pipe, &[0]); let _ = syscall::close(sync_pipe); @@ -332,7 +327,7 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { }) else { continue; }; - let resp = req.handle_scheme_mut(&mut scheme); + let resp = req.handle_scheme(&mut scheme); if !socket .write_response(resp, SignalBehavior::Restart) diff --git a/src/procmngr.rs b/src/procmngr.rs index b70defe239..01b67dc3cc 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -1,14 +1,21 @@ use core::cell::RefCell; use alloc::rc::Rc; +use alloc::vec::Vec; +use alloc::vec; + use hashbrown::hash_map::DefaultHashBuilder; use hashbrown::{HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_scheme::{CallerCtx, OpenResult, RequestKind, SchemeMut, SignalBehavior, Socket, V2}; -use syscall::{Result, O_CREAT}; +use redox_scheme::{ + CallerCtx, OpenResult, RequestKind, Response, Scheme, SendFdRequest, SignalBehavior, Socket, +}; +use slab::Slab; +use syscall::schemev2::NewFdFlags; +use syscall::{Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CREAT}; pub fn run(write_fd: usize) { - let socket = Socket::::create("proc").expect("failed to open proc scheme socket"); + let socket = Socket::create("proc").expect("failed to open proc scheme socket"); let mut scheme = ProcScheme::new(); let _ = syscall::write(1, b"process manager started\n").unwrap(); @@ -16,16 +23,17 @@ pub fn run(write_fd: usize) { let _ = syscall::close(write_fd); loop { - let RequestKind::Call(req) = (match socket + let Some(req) = socket .next_request(SignalBehavior::Restart) .expect("bootstrap: failed to read scheme request from kernel") - { - Some(req) => req.kind(), - None => break, - }) else { + else { continue; }; - let resp = req.handle_scheme_mut(&mut scheme); + let resp = match req.kind() { + RequestKind::Call(req) => req.handle_scheme(&mut scheme), + RequestKind::SendFd(req) => scheme.on_sendfd(&socket, &req), + _ => continue, + }; if !socket .write_response(resp, SignalBehavior::Restart) @@ -43,6 +51,13 @@ struct Process { ppid: ProcessId, pgid: ProcessId, sid: ProcessId, + + ruid: u32, + euid: u32, + rgid: u32, + egid: u32, + rns: u32, + ens: u32, } struct Thread { fd: FdGuard, @@ -51,22 +66,142 @@ struct Thread { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] struct ProcessId(usize); +const INIT_PID: ProcessId = ProcessId(1); + struct ProcScheme { processes: HashMap, process_groups: HashSet, sessions: HashSet, + handles: Slab, + + init_claimed: bool, + next_id: ProcessId, } + +enum Handle { + Init, + Proc(ProcessId), +} + impl ProcScheme { pub fn new() -> ProcScheme { ProcScheme { processes: HashMap::new(), process_groups: HashSet::new(), sessions: HashSet::new(), + handles: Slab::new(), + init_claimed: false, + next_id: ProcessId(2), + } + } + fn new_id(&mut self) -> ProcessId { + let id = self.next_id; + self.next_id.0 += 1; + id + } + fn on_sendfd(&mut self, socket: &Socket, req: &SendFdRequest) -> Response { + match self.handles[req.id()] { + ref mut st @ Handle::Init => { + let mut fd_out = usize::MAX; + if let Err(e) = req.obtain_fd(socket, FobtainFdFlags::empty(), Err(&mut fd_out)) { + return Response::for_sendfd(&req, Err(e)); + }; + let thread = Rc::new(RefCell::new(Thread { + fd: FdGuard::new(fd_out), + })); + self.processes.insert( + INIT_PID, + Process { + threads: vec![thread], + ppid: INIT_PID, + sid: INIT_PID, + pgid: INIT_PID, + ruid: 0, + euid: 0, + rgid: 0, + egid: 0, + rns: 1, + ens: 1, + }, + ); + self.process_groups.insert(INIT_PID); + self.sessions.insert(INIT_PID); + + *st = Handle::Proc(INIT_PID); + Response::for_sendfd(&req, Ok(0)) + } + _ => Response::for_sendfd(&req, Err(Error::new(EBADF))), + } + } + fn fork(&mut self, parent_pid: ProcessId) -> Result { + let child_pid = self.new_id(); + + let Process { + pgid, + sid, + euid, + ruid, + egid, + rgid, + ens, + rns, + .. + } = *self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; + + self.processes.insert( + child_pid, + Process { + threads: Vec::new(), + ppid: parent_pid, + pgid, + sid, + ruid, + rgid, + euid, + egid, + rns, + ens, + }, + ); + Ok(child_pid) + } + fn new_thread(&mut self, pid: ProcessId) -> Result { + let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + proc.threads + .push(Rc::new(RefCell::new(Thread { fd: todo!() }))); + } +} +impl Scheme for ProcScheme { + fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + if path == "init" { + if core::mem::replace(&mut self.init_claimed, true) { + return Err(Error::new(EEXIST)); + } + return Ok(OpenResult::ThisScheme { + number: self + .handles + .insert(Handle::Init), + flags: NewFdFlags::empty(), + }); + } + Err(Error::new(ENOENT)) + } + fn xdup(&mut self, old_id: usize, buf: &[u8], ctx: &CallerCtx) -> Result { + match self.handles[old_id] { + Handle::Proc(pid) => match buf { + b"fork" => { + let child_pid = self.fork(pid)?; + Ok(OpenResult::ThisScheme { + number: self.handles.insert(Handle::Proc(child_pid)), + flags: NewFdFlags::empty(), + }) + }, + b"new-thread" => Ok(OpenResult::OtherScheme { + fd: self.new_thread(pid)?.take(), + }), + _ => return Err(Error::new(EINVAL)), + }, + Handle::Init => Err(Error::new(EBADF)), } } } -impl SchemeMut for ProcScheme { - fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { - - } -} From b4a79920745b67219231e1042d3362a88cc50473 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 21:29:06 +0100 Subject: [PATCH 056/135] Support reading metadata from process. --- src/exec.rs | 22 +++++++++++--------- src/procmngr.rs | 54 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index 9d6f491a0e..77d00c068a 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -49,9 +49,17 @@ pub fn main() -> ! { ); }); - spawn("process manager", move |write_fd| { - crate::procmngr::run(write_fd) + let auth = FdGuard::new(syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC).expect("failed to get proc authority")); + + spawn("process manager", |write_fd| { + crate::procmngr::run(write_fd, &auth) }); + let this_thr_fd = FdGuard::new( + syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup"), + ); + let (init_proc_fd, init_thr_fd) = unsafe { + redox_rt::proc::make_init(this_thr_fd) + }; const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; @@ -74,18 +82,12 @@ pub fn main() -> ! { + 1; let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); - let open_via_dup = FdGuard::new( - syscall::open("/scheme/thisproc/current", 0).expect("failed to open open_via_dup"), - ); let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); - unsafe { - redox_rt::proc::make_init(); - } - fexec_impl( image_file, - open_via_dup, + init_proc_fd, + init_thr_fd, &memory, path.as_bytes(), [path], diff --git a/src/procmngr.rs b/src/procmngr.rs index 01b67dc3cc..af04a1bf88 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -7,16 +7,17 @@ use alloc::vec; use hashbrown::hash_map::DefaultHashBuilder; use hashbrown::{HashMap, HashSet}; use redox_rt::proc::FdGuard; +use redox_rt::protocol::ProcMeta; use redox_scheme::{ CallerCtx, OpenResult, RequestKind, Response, Scheme, SendFdRequest, SignalBehavior, Socket, }; use slab::Slab; use syscall::schemev2::NewFdFlags; -use syscall::{Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CREAT}; +use syscall::{Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CLOEXEC, O_CREAT}; -pub fn run(write_fd: usize) { +pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::create("proc").expect("failed to open proc scheme socket"); - let mut scheme = ProcScheme::new(); + let mut scheme = ProcScheme::new(auth); let _ = syscall::write(1, b"process manager started\n").unwrap(); let _ = syscall::write(write_fd, &[0]); @@ -68,7 +69,7 @@ struct ProcessId(usize); const INIT_PID: ProcessId = ProcessId(1); -struct ProcScheme { +struct ProcScheme<'a> { processes: HashMap, process_groups: HashSet, sessions: HashSet, @@ -76,6 +77,8 @@ struct ProcScheme { init_claimed: bool, next_id: ProcessId, + + auth: &'a FdGuard, } enum Handle { @@ -83,8 +86,8 @@ enum Handle { Proc(ProcessId), } -impl ProcScheme { - pub fn new() -> ProcScheme { +impl<'a> ProcScheme<'a> { + pub fn new(auth: &'a FdGuard) -> ProcScheme { ProcScheme { processes: HashMap::new(), process_groups: HashSet::new(), @@ -92,6 +95,7 @@ impl ProcScheme { handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), + auth, } } fn new_id(&mut self) -> ProcessId { @@ -148,10 +152,14 @@ impl ProcScheme { .. } = *self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; + let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); + self.processes.insert( child_pid, Process { - threads: Vec::new(), + threads: vec! [Rc::new(RefCell::new(Thread { + fd: new_ctxt_fd, + }))], ppid: parent_pid, pgid, sid, @@ -167,11 +175,13 @@ impl ProcScheme { } fn new_thread(&mut self, pid: ProcessId) -> Result { let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let fd = todo!(); proc.threads - .push(Rc::new(RefCell::new(Thread { fd: todo!() }))); + .push(Rc::new(RefCell::new(Thread { fd }))); + Ok(fd) } } -impl Scheme for ProcScheme { +impl Scheme for ProcScheme<'_> { fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { if path == "init" { if core::mem::replace(&mut self.init_claimed, true) { @@ -186,6 +196,25 @@ impl Scheme for ProcScheme { } Err(Error::new(ENOENT)) } + fn read(&mut self, id: usize, buf: &mut [u8], _offset: u64, _fcntl_flags: u32) -> Result { + match self.handles[id] { + Handle::Proc(pid) => { + let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let metadata = ProcMeta { + pid: pid.into(), + pgid: process.pgid.into(), + ppid: process.ppid.into(), + euid: process.euid, + egid: process.egid, + ruid: process.ruid, + rgid: process.rgid, + ens: process.ens, + rns: process.rns, + }; + } + Handle::Init => return Err(Error::new(EBADF)), + } + } fn xdup(&mut self, old_id: usize, buf: &[u8], ctx: &CallerCtx) -> Result { match self.handles[old_id] { Handle::Proc(pid) => match buf { @@ -199,6 +228,13 @@ impl Scheme for ProcScheme { b"new-thread" => Ok(OpenResult::OtherScheme { fd: self.new_thread(pid)?.take(), }), + w if w.starts_with("thread-") => { + let idx = core::str::from_utf8(&w["thread-".len()..]).ok().and_then(|s| s.parse::().ok()).ok_or(Error::new(EINVAL))?; + let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let thread = process.threads.get(idx).ok_or(Error::new(ENOENT))?.borrow(); + + return Ok(OpenResult::OtherScheme { fd: syscall::dup(*thread.fd, &[])? }); + } _ => return Err(Error::new(EINVAL)), }, Handle::Init => Err(Error::new(EBADF)), From 3dbd60608682ba20af2bae8600ddc60dd9408cf7 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 13:55:49 +0100 Subject: [PATCH 057/135] Reach init with proc manager. --- Cargo.lock | 1 + Cargo.toml | 1 + src/exec.rs | 50 +++++++++++++++++++++++++++---------------------- src/procmngr.rs | 49 ++++++++++++++++++++++++++++++------------------ src/start.rs | 2 -- 5 files changed, 61 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02ce92c312..fa5a500e3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ version = "0.0.0" dependencies = [ "hashbrown", "linked_list_allocator", + "plain", "redox-initfs", "redox-path", "redox-rt", diff --git a/Cargo.toml b/Cargo.toml index b7b2b3366f..3413303d20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ hashbrown = { version = "0.14", default-features = false, features = [ "ahash", ] } linked_list_allocator = "0.10" +plain = "0.2" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = { version = "0.5.4", default-features = false } redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } diff --git a/src/exec.rs b/src/exec.rs index 77d00c068a..3d247cbc9d 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -7,6 +7,14 @@ use syscall::{Error, EINTR}; use redox_rt::proc::*; pub fn main() -> ! { + let auth = FdGuard::new( + syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC) + .expect("failed to get proc authority"), + ); + let this_thr_fd = + FdGuard::new(syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup")); + let this_thr_fd = unsafe { redox_rt::initialize_freestanding(this_thr_fd) }; + let envs = { let mut env = [0_u8; 4096]; @@ -37,29 +45,27 @@ pub fn main() -> ! { (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size }; - spawn("initfs daemon", move |write_fd| unsafe { - // Creating a reference to NULL is UB. Mask the UB for now using black_box. - // FIXME use a raw pointer and inline asm for reading instead for the initfs header. - let initfs_start = core::ptr::addr_of!(__initfs_header); - let initfs_length = initfs_length.get() as usize; + spawn( + "initfs daemon", + &auth, + &this_thr_fd, + move |write_fd| unsafe { + // Creating a reference to NULL is UB. Mask the UB for now using black_box. + // FIXME use a raw pointer and inline asm for reading instead for the initfs header. + let initfs_start = core::ptr::addr_of!(__initfs_header); + let initfs_length = initfs_length.get() as usize; - crate::initfs::run( - core::slice::from_raw_parts(initfs_start, initfs_length), - write_fd, - ); - }); + crate::initfs::run( + core::slice::from_raw_parts(initfs_start, initfs_length), + write_fd, + ); + }, + ); - let auth = FdGuard::new(syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC).expect("failed to get proc authority")); - - spawn("process manager", |write_fd| { + spawn("process manager", &auth, &this_thr_fd, |write_fd| { crate::procmngr::run(write_fd, &auth) }); - let this_thr_fd = FdGuard::new( - syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup"), - ); - let (init_proc_fd, init_thr_fd) = unsafe { - redox_rt::proc::make_init(this_thr_fd) - }; + let init_proc_fd = unsafe { redox_rt::proc::make_init() }; const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; @@ -86,8 +92,8 @@ pub fn main() -> ! { fexec_impl( image_file, + this_thr_fd, init_proc_fd, - init_thr_fd, &memory, path.as_bytes(), [path], @@ -101,13 +107,13 @@ pub fn main() -> ! { unreachable!() } -pub(crate) fn spawn(name: &str, inner: impl FnOnce(usize)) { +pub(crate) fn spawn(name: &str, auth: &FdGuard, this_thr_fd: &FdGuard, inner: impl FnOnce(usize)) { let read = syscall::open("/scheme/pipe", O_CLOEXEC).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. let write = syscall::dup(read, b"write").expect("failed to open sync write pipe"); - match fork_impl() { + match fork_impl(&ForkArgs::Init { this_thr_fd, auth }) { Err(err) => { panic!("Failed to fork in order to start {name}: {err}"); } diff --git a/src/procmngr.rs b/src/procmngr.rs index af04a1bf88..3052a6206a 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -1,8 +1,9 @@ use core::cell::RefCell; +use core::mem::size_of; use alloc::rc::Rc; -use alloc::vec::Vec; use alloc::vec; +use alloc::vec::Vec; use hashbrown::hash_map::DefaultHashBuilder; use hashbrown::{HashMap, HashSet}; @@ -13,7 +14,9 @@ use redox_scheme::{ }; use slab::Slab; use syscall::schemev2::NewFdFlags; -use syscall::{Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CLOEXEC, O_CREAT}; +use syscall::{ + Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CLOEXEC, O_CREAT, +}; pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::create("proc").expect("failed to open proc scheme socket"); @@ -157,9 +160,7 @@ impl<'a> ProcScheme<'a> { self.processes.insert( child_pid, Process { - threads: vec! [Rc::new(RefCell::new(Thread { - fd: new_ctxt_fd, - }))], + threads: vec![Rc::new(RefCell::new(Thread { fd: new_ctxt_fd }))], ppid: parent_pid, pgid, sid, @@ -176,8 +177,7 @@ impl<'a> ProcScheme<'a> { fn new_thread(&mut self, pid: ProcessId) -> Result { let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; let fd = todo!(); - proc.threads - .push(Rc::new(RefCell::new(Thread { fd }))); + proc.threads.push(Rc::new(RefCell::new(Thread { fd }))); Ok(fd) } } @@ -188,22 +188,26 @@ impl Scheme for ProcScheme<'_> { return Err(Error::new(EEXIST)); } return Ok(OpenResult::ThisScheme { - number: self - .handles - .insert(Handle::Init), + number: self.handles.insert(Handle::Init), flags: NewFdFlags::empty(), }); } Err(Error::new(ENOENT)) } - fn read(&mut self, id: usize, buf: &mut [u8], _offset: u64, _fcntl_flags: u32) -> Result { + fn read( + &mut self, + id: usize, + buf: &mut [u8], + _offset: u64, + _fcntl_flags: u32, + ) -> Result { match self.handles[id] { Handle::Proc(pid) => { let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; let metadata = ProcMeta { - pid: pid.into(), - pgid: process.pgid.into(), - ppid: process.ppid.into(), + pid: pid.0 as u32, + pgid: process.pgid.0 as u32, + ppid: process.ppid.0 as u32, euid: process.euid, egid: process.egid, ruid: process.ruid, @@ -211,6 +215,10 @@ impl Scheme for ProcScheme<'_> { ens: process.ens, rns: process.rns, }; + *buf.get_mut(..size_of::()) + .and_then(|b| plain::from_mut_bytes(b).ok()) + .ok_or(Error::new(EINVAL))? = metadata; + Ok(size_of::()) } Handle::Init => return Err(Error::new(EBADF)), } @@ -224,16 +232,21 @@ impl Scheme for ProcScheme<'_> { number: self.handles.insert(Handle::Proc(child_pid)), flags: NewFdFlags::empty(), }) - }, + } b"new-thread" => Ok(OpenResult::OtherScheme { fd: self.new_thread(pid)?.take(), }), - w if w.starts_with("thread-") => { - let idx = core::str::from_utf8(&w["thread-".len()..]).ok().and_then(|s| s.parse::().ok()).ok_or(Error::new(EINVAL))?; + w if w.starts_with(b"thread-") => { + let idx = core::str::from_utf8(&w["thread-".len()..]) + .ok() + .and_then(|s| s.parse::().ok()) + .ok_or(Error::new(EINVAL))?; let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; let thread = process.threads.get(idx).ok_or(Error::new(ENOENT))?.borrow(); - return Ok(OpenResult::OtherScheme { fd: syscall::dup(*thread.fd, &[])? }); + return Ok(OpenResult::OtherScheme { + fd: syscall::dup(*thread.fd, &[])?, + }); } _ => return Err(Error::new(EINVAL)), }, diff --git a/src/start.rs b/src/start.rs index ebaeb17505..f85ec084ab 100644 --- a/src/start.rs +++ b/src/start.rs @@ -78,8 +78,6 @@ pub unsafe extern "C" fn start() -> ! { ) .expect("mprotect failed for rest of memory"); - redox_rt::initialize_freestanding(); - // FIXME make the initfs read-only crate::exec::main(); From 737d87cf9d570b9defa873dd7462f28ec052e34f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 16:32:48 +0100 Subject: [PATCH 058/135] Reach init fork. --- src/exec.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/exec.rs b/src/exec.rs index 3d247cbc9d..e092d6ec5b 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -75,6 +75,8 @@ pub fn main() -> ! { sigprocmask: 0, sigignmask: 0, umask: redox_rt::sys::get_umask(), + thr_fd: **this_thr_fd, + proc_fd: **init_proc_fd, }; let path = "/scheme/initfs/bin/init"; From 063430f7acaa27f38c020967c3fd099ee119ea03 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 16:35:31 +0100 Subject: [PATCH 059/135] Sync kernel ctxt attrs in fork. --- src/procmngr.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/procmngr.rs b/src/procmngr.rs index 3052a6206a..cdfbad5b6a 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -15,7 +15,8 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CLOEXEC, O_CREAT, + Error, FobtainFdFlags, ProcSchemeAttrs, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, + O_CLOEXEC, O_CREAT, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -156,6 +157,19 @@ impl<'a> ProcScheme<'a> { } = *self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); + let attr_fd = FdGuard::new(syscall::dup( + *new_ctxt_fd, + alloc::format!("attrs-{}", **self.auth).as_bytes(), + )?); + let _ = syscall::write( + *attr_fd, + &ProcSchemeAttrs { + pid: child_pid.0 as u32, + euid, + egid, + ens, + }, + )?; self.processes.insert( child_pid, From a839c8f2596b6b3baa2f5a04737003812f548184 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 19:55:08 +0100 Subject: [PATCH 060/135] Update redox-scheme. --- Cargo.lock | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index fa5a500e3e..a0c534e2fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -26,8 +38,15 @@ dependencies = [ "redox-rt", "redox-scheme", "redox_syscall", + "slab", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "generic-rt" version = "0.1.0" @@ -49,6 +68,9 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] name = "libc" @@ -92,6 +114,12 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "plain" version = "0.2.3" @@ -185,6 +213,15 @@ dependencies = [ "syn", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "spinning_top" version = "0.2.5" @@ -210,3 +247,29 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] From 84b4d1f274c4f076c162a9f32b317891d3572428 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 21:19:25 +0100 Subject: [PATCH 061/135] Implement basic state machines, add setrens. --- src/initfs.rs | 29 ++++---- src/procmngr.rs | 177 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 176 insertions(+), 30 deletions(-) diff --git a/src/initfs.rs b/src/initfs.rs index 7568b15e9f..b9f9d03b9a 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -9,7 +9,7 @@ use hashbrown::HashMap; use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct}; use redox_path::canonicalize_to_standard; -use redox_scheme::{CallerCtx, OpenResult, RequestKind, Scheme}; +use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult, RequestKind}; use redox_scheme::{SignalBehavior, Socket}; use syscall::data::Stat; @@ -88,8 +88,8 @@ fn inode_len(inode: InodeStruct<'static>) -> Result { }) } -impl Scheme for InitFsScheme { - fn xopen(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { +impl SchemeSync for InitFsScheme { + fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { let mut components = path // trim leading and trailing slash .trim_matches('/') @@ -175,6 +175,7 @@ impl Scheme for InitFsScheme { buffer: &mut [u8], offset: u64, _fcntl_flags: u32, + _ctx: &CallerCtx, ) -> Result { let Ok(offset) = usize::try_from(offset) else { return Ok(0); @@ -243,19 +244,19 @@ impl Scheme for InitFsScheme { Ok(buf) } - fn fsize(&mut self, id: usize) -> Result { + fn fsize(&mut self, id: usize, _ctx: &CallerCtx) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; Ok(inode_len(Self::get_inode(&self.fs, handle.inode)?)? as u64) } - fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result { + fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result { let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; Ok(0) } - fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { + fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; // TODO: Copy scheme part in kernel @@ -270,7 +271,7 @@ impl Scheme for InitFsScheme { Ok(scheme_bytes + path_bytes) } - fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result { + fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> { let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; let Timespec { sec, nsec } = self.fs.image_creation_time(); @@ -292,20 +293,20 @@ impl Scheme for InitFsScheme { stat.st_mtime = sec.get(); stat.st_mtime_nsec = nsec.get(); - Ok(0) + Ok(()) } - fn fsync(&mut self, id: usize) -> Result { + fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> { if !self.handles.contains_key(&id) { return Err(Error::new(EBADF)); } - Ok(0) + Ok(()) } - fn close(&mut self, id: usize) -> Result { + fn close(&mut self, id: usize) -> Result<()> { let _ = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - Ok(0) + Ok(()) } } @@ -327,7 +328,9 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { }) else { continue; }; - let resp = req.handle_scheme(&mut scheme); + let resp = req + .handle_sync(&mut scheme) + .expect("failed to handle request"); if !socket .write_response(resp, SignalBehavior::Restart) diff --git a/src/procmngr.rs b/src/procmngr.rs index cdfbad5b6a..904af7c011 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -1,23 +1,31 @@ use core::cell::RefCell; +use core::hash::BuildHasherDefault; use core::mem::size_of; +use core::task::Poll; +use core::task::Poll::*; +use alloc::collections::VecDeque; use alloc::rc::Rc; use alloc::vec; use alloc::vec::Vec; -use hashbrown::hash_map::DefaultHashBuilder; +use hashbrown::hash_map::{DefaultHashBuilder, Entry, OccupiedEntry}; use hashbrown::{HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::ProcMeta; +use redox_rt::protocol::{ProcCall, ProcMeta}; +use redox_scheme::scheme::Op; use redox_scheme::{ - CallerCtx, OpenResult, RequestKind, Response, Scheme, SendFdRequest, SignalBehavior, Socket, + CallerCtx, Id, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket, }; use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, FobtainFdFlags, ProcSchemeAttrs, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, - O_CLOEXEC, O_CREAT, + Error, FobtainFdFlags, ProcSchemeAttrs, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, ENOSYS, + EOPNOTSUPP, EPERM, O_CLOEXEC, O_CREAT, }; +enum PendingState { + Waiting, +} pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::create("proc").expect("failed to open proc scheme socket"); @@ -27,7 +35,24 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let _ = syscall::write(write_fd, &[0]); let _ = syscall::close(write_fd); + let mut states = HashMap::::new(); + let mut awoken = VecDeque::::new(); + loop { + for awoken in awoken.drain(..) { + let Entry::Occupied(entry) = states.entry(awoken) else { + continue; + }; + match scheme.work_on(entry) { + Ready(resp) => { + socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel"); + } + Pending => continue, + } + } + let Some(req) = socket .next_request(SignalBehavior::Restart) .expect("bootstrap: failed to read scheme request from kernel") @@ -35,7 +60,34 @@ pub fn run(write_fd: usize, auth: &FdGuard) { continue; }; let resp = match req.kind() { - RequestKind::Call(req) => req.handle_scheme(&mut scheme), + RequestKind::Call(req) => { + let res = req.with(|req, caller, op| match op { + Op::Open { path, flags } => { + Ready(Response::open_dup_like(&req, scheme.on_open(path, flags))) + } + Op::Dup { old_fd, buf } => { + Ready(Response::open_dup_like(&req, scheme.on_dup(old_fd, buf))) + } + Op::Read { fd, buf, .. } => Ready(Response::new(&req, scheme.on_read(fd, buf))), + Op::Call { + fd, + payload, + metadata, + } => scheme + .on_call( + fd, + payload, + metadata, + states.entry(req.request().request_id()), + ) + .map(|r| Response::new(&req, r)), + _ => Ready(Response::new(&req, Err(Error::new(ENOSYS)))), + }); + match res { + Ok(Ready(r)) | Err(r) => r, + Ok(Pending) => continue, + } + } RequestKind::SendFd(req) => scheme.on_sendfd(&socket, &req), _ => continue, }; @@ -194,9 +246,7 @@ impl<'a> ProcScheme<'a> { proc.threads.push(Rc::new(RefCell::new(Thread { fd }))); Ok(fd) } -} -impl Scheme for ProcScheme<'_> { - fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn on_open(&mut self, path: &str, flags: usize) -> Result { if path == "init" { if core::mem::replace(&mut self.init_claimed, true) { return Err(Error::new(EEXIST)); @@ -208,13 +258,7 @@ impl Scheme for ProcScheme<'_> { } Err(Error::new(ENOENT)) } - fn read( - &mut self, - id: usize, - buf: &mut [u8], - _offset: u64, - _fcntl_flags: u32, - ) -> Result { + fn on_read(&mut self, id: usize, buf: &mut [u8]) -> Result { match self.handles[id] { Handle::Proc(pid) => { let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; @@ -237,7 +281,7 @@ impl Scheme for ProcScheme<'_> { Handle::Init => return Err(Error::new(EBADF)), } } - fn xdup(&mut self, old_id: usize, buf: &[u8], ctx: &CallerCtx) -> Result { + fn on_dup(&mut self, old_id: usize, buf: &[u8]) -> Result { match self.handles[old_id] { Handle::Proc(pid) => match buf { b"fork" => { @@ -267,4 +311,103 @@ impl Scheme for ProcScheme<'_> { Handle::Init => Err(Error::new(EBADF)), } } + pub fn on_call( + &mut self, + id: usize, + payload: &mut [u8], + metadata: &[u64], + state: Entry, + ) -> Poll> { + match self.handles[id] { + Handle::Init => Ready(Err(Error::new(EBADF))), + Handle::Proc(pid) => { + let verb = + ProcCall::try_from_raw(metadata[0] as usize).ok_or(Error::new(EINVAL))?; + fn cvt_u32(u: u32) -> Option { + if u == u32::MAX { + None + } else { + Some(u) + } + } + match verb { + ProcCall::Setrens => Ready( + self.on_setrens( + pid, + cvt_u32(metadata[1] as u32), + cvt_u32(metadata[2] as u32), + ) + .map(|()| 0), + ), + ProcCall::Waitpid => Ready(Err(Error::new(EOPNOTSUPP))), + } + } + } + } + pub fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { + let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let setrns = if rns.is_none() { + // Ignore RNS if -1 is passed + false + } else if rns == Some(0) { + // Allow entering capability mode + true + } else if process.rns == 0 { + // Do not allow leaving capability mode + return Err(Error::new(EPERM)); + } else if process.euid == 0 { + // Allow setting RNS if root + true + } else if rns == Some(process.ens) { + // Allow setting RNS if used for ENS + true + } else if rns == Some(process.rns) { + // Allow setting RNS if used for RNS + true + } else { + // Not permitted otherwise + return Err(Error::new(EPERM)); + }; + + let setens = if ens.is_none() { + // Ignore ENS if -1 is passed + false + } else if ens == Some(0) { + // Allow entering capability mode + true + } else if process.ens == 0 { + // Do not allow leaving capability mode + return Err(Error::new(EPERM)); + } else if process.euid == 0 { + // Allow setting ENS if root + true + } else if ens == Some(process.ens) { + // Allow setting ENS if used for ENS + true + } else if ens == Some(process.rns) { + // Allow setting ENS if used for RNS + true + } else { + // Not permitted otherwise + return Err(Error::new(EPERM)); + }; + + if setrns { + process.rns = rns.unwrap(); + } + + if setens { + process.ens = ens.unwrap(); + } + Ok(()) + } + pub fn work_on( + &mut self, + mut entry: OccupiedEntry, + ) -> Poll { + match entry.get_mut() { + // TODO + PendingState::Waiting => Poll::Pending, + } + } } From 2293828906cffff757dc29e27455b77e818b3604 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 19 Jan 2025 14:56:54 +0100 Subject: [PATCH 062/135] WIP: waitpid handler. --- src/procmngr.rs | 153 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 11 deletions(-) diff --git a/src/procmngr.rs b/src/procmngr.rs index 904af7c011..bebb4b2e85 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -12,7 +12,7 @@ use alloc::vec::Vec; use hashbrown::hash_map::{DefaultHashBuilder, Entry, OccupiedEntry}; use hashbrown::{HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcMeta}; +use redox_rt::protocol::{ProcCall, ProcMeta, WaitFlags}; use redox_scheme::scheme::Op; use redox_scheme::{ CallerCtx, Id, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket, @@ -20,12 +20,9 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, FobtainFdFlags, ProcSchemeAttrs, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, ENOSYS, - EOPNOTSUPP, EPERM, O_CLOEXEC, O_CREAT, + Error, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, + ENOSYS, EOPNOTSUPP, EPERM, ESRCH, O_CLOEXEC, O_CREAT, }; -enum PendingState { - Waiting, -} pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::create("proc").expect("failed to open proc scheme socket"); @@ -102,7 +99,15 @@ pub fn run(write_fd: usize, auth: &FdGuard) { unreachable!() } +enum PendingState { + AwaitingStatusChange { + waiter: ProcessId, + target: WaitpidTarget, + }, + AwaitingThreadsTermination(ProcessId), +} +#[derive(Debug)] struct Process { threads: Vec>>, ppid: ProcessId, @@ -115,7 +120,20 @@ struct Process { egid: u32, rns: u32, ens: u32, + + status: ProcessStatus, + + waitpid: Vec, + children_waitpid: Vec, } +#[derive(Debug, Clone, Copy)] +enum ProcessStatus { + PossiblyRunnable, + Stopped(usize), + Exiting { status: i32 }, + Exited { status: i32 }, +} +#[derive(Debug)] struct Thread { fd: FdGuard, // sig_ctrl: MmapGuard<...> @@ -127,27 +145,35 @@ const INIT_PID: ProcessId = ProcessId(1); struct ProcScheme<'a> { processes: HashMap, - process_groups: HashSet, sessions: HashSet, handles: Slab, init_claimed: bool, next_id: ProcessId, + waitpgid: HashMap, DefaultHashBuilder>, auth: &'a FdGuard, } +#[derive(Debug)] enum Handle { Init, Proc(ProcessId), } +enum WaitpidTarget { + SingleProc(ProcessId), + ProcGroup(ProcessId), + AnyChild, + AnyGroupMember, +} + impl<'a> ProcScheme<'a> { pub fn new(auth: &'a FdGuard) -> ProcScheme { ProcScheme { processes: HashMap::new(), - process_groups: HashSet::new(), sessions: HashSet::new(), + waitpgid: HashMap::new(), handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), @@ -182,9 +208,12 @@ impl<'a> ProcScheme<'a> { egid: 0, rns: 1, ens: 1, + + status: ProcessStatus::PossiblyRunnable, + waitpid: Vec::new(), + children_waitpid: Vec::new(), }, ); - self.process_groups.insert(INIT_PID); self.sessions.insert(INIT_PID); *st = Handle::Proc(INIT_PID); @@ -236,6 +265,10 @@ impl<'a> ProcScheme<'a> { egid, rns, ens, + + status: ProcessStatus::PossiblyRunnable, + waitpid: Vec::new(), + children_waitpid: Vec::new(), }, ); Ok(child_pid) @@ -339,11 +372,98 @@ impl<'a> ProcScheme<'a> { ) .map(|()| 0), ), - ProcCall::Waitpid => Ready(Err(Error::new(EOPNOTSUPP))), + ProcCall::Exit => self.on_exit_start(pid, metadata[1] as i32, state), + ProcCall::Waitpid | ProcCall::Waitpgid => { + let req_pid = ProcessId(metadata[1] as usize); + let target = match (verb, metadata[1] == 0) { + (ProcCall::Waitpid, true) => WaitpidTarget::AnyChild, + (ProcCall::Waitpid, false) => WaitpidTarget::SingleProc(req_pid), + (ProcCall::Waitpgid, true) => WaitpidTarget::AnyGroupMember, + (ProcCall::Waitpgid, false) => WaitpidTarget::ProcGroup(req_pid), + _ => unreachable!(), + }; + self.on_waitpid( + pid, + target, + plain::from_mut_bytes(payload).map_err(|_| Error::new(EINVAL))?, + WaitFlags::from_bits(metadata[2] as usize).ok_or(Error::new(EINVAL))?, + state, + ) + } } } } } + pub fn on_exit_start( + &mut self, + pid: ProcessId, + status: i32, + mut state: Entry, + ) -> Poll> { + let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + match process.status { + ProcessStatus::Stopped(_) | ProcessStatus::PossiblyRunnable => (), + //ProcessStatus::Exiting => return Pending, + ProcessStatus::Exiting { .. } => return Ready(Err(Error::new(EAGAIN))), + ProcessStatus::Exited { .. } => return Ready(Err(Error::new(ESRCH))), + } + process.status = ProcessStatus::Exiting { status }; + if process.threads.is_empty() { + Self::on_exit_complete(); + Ready(Ok(0)) + } else { + let _ = syscall::write(1, b"\nEXIT PENDING\n"); + self.debug(); + // TODO: check? + state.insert(PendingState::AwaitingThreadsTermination(pid)); + Pending + } + } + fn on_exit_complete() { + // TODO: send waitpid status + } + pub fn on_waitpid( + &mut self, + this_pid: ProcessId, + target: WaitpidTarget, + stat_loc: &mut i32, + flags: WaitFlags, + mut state: Entry, + ) -> Poll> { + let _ = syscall::write(1, b"\nWAITPID\n"); + self.debug(); + Pending + } + fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { + struct Iter<'a> { + cur: Option, + procs: &'a HashMap, + } + impl Iterator for Iter<'_> { + type Item = ProcessId; + + fn next(&mut self) -> Option { + let proc = self.procs.get(&self.cur?)?; + self.cur = Some(proc.ppid); + Some(proc.ppid) + } + } + Iter { + cur: Some(pid), + procs: &self.processes, + } + } + fn check_waitpid_queues( + &mut self, + waiter: ProcessId, + target: WaitpidTarget, + mask: WaitFlags, + ) -> Option<(ProcessId, i32)> { + /*match target { + //WaitpidTarget::SingleProc(target_pid) => , + }*/ + todo!() + } pub fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; let setrns = if rns.is_none() { @@ -407,7 +527,18 @@ impl<'a> ProcScheme<'a> { ) -> Poll { match entry.get_mut() { // TODO - PendingState::Waiting => Poll::Pending, + PendingState::AwaitingThreadsTermination(_) => Pending, + PendingState::AwaitingStatusChange { waiter, target } => Pending, } } + fn debug(&self) { + let _ = syscall::write( + 1, + alloc::format!("PROCESSES\n\n{:#?}\n\n", self.processes).as_bytes(), + ); + let _ = syscall::write( + 1, + alloc::format!("HANDLES\n\n{:#?}\n\n", self.handles).as_bytes(), + ); + } } From 5fba53023e03a8886ab513d3a85f184e42940a38 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 21 Feb 2025 11:33:10 +0100 Subject: [PATCH 063/135] Add event queue. --- src/procmngr.rs | 193 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 55 deletions(-) diff --git a/src/procmngr.rs b/src/procmngr.rs index bebb4b2e85..84bc29090e 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -11,22 +11,35 @@ use alloc::vec::Vec; use hashbrown::hash_map::{DefaultHashBuilder, Entry, OccupiedEntry}; use hashbrown::{HashMap, HashSet}; + use redox_rt::proc::FdGuard; use redox_rt::protocol::{ProcCall, ProcMeta, WaitFlags}; use redox_scheme::scheme::Op; use redox_scheme::{ - CallerCtx, Id, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket, + CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, + Socket, }; use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, - ENOSYS, EOPNOTSUPP, EPERM, ESRCH, O_CLOEXEC, O_CREAT, + Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, EBADF, EBADFD, + EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, + O_CREAT, }; pub fn run(write_fd: usize, auth: &FdGuard) { - let socket = Socket::create("proc").expect("failed to open proc scheme socket"); - let mut scheme = ProcScheme::new(auth); + let socket = Socket::nonblock("proc").expect("failed to open proc scheme socket"); + + // TODO? + let socket_ident = socket.inner().raw(); + + let queue = RawEventQueue::new().expect("failed to create event queue"); + + queue + .subscribe(socket.inner().raw(), socket_ident, EventFlags::EVENT_READ) + .expect("failed to listen to scheme socket events"); + + let mut scheme = ProcScheme::new(auth, &queue); let _ = syscall::write(1, b"process manager started\n").unwrap(); let _ = syscall::write(write_fd, &[0]); @@ -35,70 +48,102 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut states = HashMap::::new(); let mut awoken = VecDeque::::new(); - loop { + 'outer: loop { for awoken in awoken.drain(..) { let Entry::Occupied(entry) = states.entry(awoken) else { continue; }; match scheme.work_on(entry) { - Ready(resp) => { - socket - .write_response(resp, SignalBehavior::Restart) - .expect("bootstrap: failed to write scheme response to kernel"); - } + Ready(resp) => loop { + match socket.write_response(resp, SignalBehavior::Interrupt) { + Ok(false) => break 'outer, + Ok(_) => break, + Err(err) if err.errno == EINTR => continue, + Err(err) => { + panic!("bootstrap: failed to write scheme response to kernel: {err}") + } + } + }, Pending => continue, } } + // TODO: multiple events? + let event = queue.next_event().expect("failed to get next event"); - let Some(req) = socket - .next_request(SignalBehavior::Restart) - .expect("bootstrap: failed to read scheme request from kernel") - else { - continue; - }; - let resp = match req.kind() { - RequestKind::Call(req) => { - let res = req.with(|req, caller, op| match op { - Op::Open { path, flags } => { - Ready(Response::open_dup_like(&req, scheme.on_open(path, flags))) + if event.data == socket_ident { + let req = loop { + match socket.next_request(SignalBehavior::Interrupt) { + Ok(None) => break 'outer, + Ok(Some(req)) => break req, + Err(e) if e.errno == EINTR => continue, + // spurious event + Err(e) if e.errno == EWOULDBLOCK || e.errno == EAGAIN => continue 'outer, + Err(other) => { + panic!("bootstrap: failed to read scheme request from kernel: {other}") } - Op::Dup { old_fd, buf } => { - Ready(Response::open_dup_like(&req, scheme.on_dup(old_fd, buf))) + } + }; + let Some(resp) = handle_scheme(req, &socket, &mut scheme, &mut states) else { + continue 'outer; + }; + loop { + match socket.write_response(resp, SignalBehavior::Interrupt) { + Ok(false) => break 'outer, + Ok(_) => break, + Err(err) if err.errno == EINTR => continue, + Err(err) => { + panic!("bootstrap: failed to write scheme response to kernel: {err}") } - Op::Read { fd, buf, .. } => Ready(Response::new(&req, scheme.on_read(fd, buf))), - Op::Call { - fd, - payload, - metadata, - } => scheme - .on_call( - fd, - payload, - metadata, - states.entry(req.request().request_id()), - ) - .map(|r| Response::new(&req, r)), - _ => Ready(Response::new(&req, Err(Error::new(ENOSYS)))), - }); - match res { - Ok(Ready(r)) | Err(r) => r, - Ok(Pending) => continue, } } - RequestKind::SendFd(req) => scheme.on_sendfd(&socket, &req), - _ => continue, - }; - - if !socket - .write_response(resp, SignalBehavior::Restart) - .expect("bootstrap: failed to write scheme response to kernel") - { - break; + } else { + let _ = syscall::write(1, b"\nTODO: EVENT\n"); } } unreachable!() } +fn handle_scheme<'a>( + req: Request, + socket: &'a Socket, + scheme: &mut ProcScheme<'a>, + states: &mut HashMap, +) -> Option { + match req.kind() { + RequestKind::Call(req) => { + let res = req.with(|req, caller, op| match op { + Op::Open { path, flags } => { + Ready(Response::open_dup_like(&req, scheme.on_open(path, flags))) + } + Op::Dup { old_fd, buf } => { + Ready(Response::open_dup_like(&req, scheme.on_dup(old_fd, buf))) + } + Op::Read { fd, buf, .. } => Ready(Response::new(&req, scheme.on_read(fd, buf))), + Op::Call { + fd, + payload, + metadata, + } => scheme + .on_call( + fd, + payload, + metadata, + states.entry(req.request().request_id()), + ) + .map(|r| Response::new(&req, r)), + _ => Ready(Response::new(&req, Err(Error::new(ENOSYS)))), + }); + match res { + Ok(Ready(r)) | Err(r) => Some(r), + // waker has already been registered, so the logic for the caller is to not send a + // response and continue with remaining events + Ok(Pending) => None, + } + } + RequestKind::SendFd(req) => Some(scheme.on_sendfd(socket, &req)), + _ => None, + } +} enum PendingState { AwaitingStatusChange { waiter: ProcessId, @@ -152,6 +197,7 @@ struct ProcScheme<'a> { next_id: ProcessId, waitpgid: HashMap, DefaultHashBuilder>, + queue: &'a RawEventQueue, auth: &'a FdGuard, } @@ -167,9 +213,40 @@ enum WaitpidTarget { AnyChild, AnyGroupMember, } +// TODO: Add 'syscall' backend for redox-event so it can act both as library-ABI frontend and +// backend +struct RawEventQueue(FdGuard); +impl RawEventQueue { + pub fn new() -> Result { + syscall::open("/scheme/event", O_CREAT) + .map(FdGuard::new) + .map(Self) + } + pub fn subscribe(&self, fd: usize, ident: usize, flags: EventFlags) -> Result<()> { + let _ = syscall::write( + *self.0, + &Event { + id: fd, + data: ident, + flags, + }, + )?; + Ok(()) + } + pub fn next_event(&self) -> Result { + let mut event = Event::default(); + let read = syscall::read(*self.0, &mut event)?; + assert_eq!( + read, + size_of::(), + "event queue EOF currently undefined" + ); + Ok(event) + } +} impl<'a> ProcScheme<'a> { - pub fn new(auth: &'a FdGuard) -> ProcScheme { + pub fn new(auth: &'a FdGuard, queue: &'a RawEventQueue) -> ProcScheme<'a> { ProcScheme { processes: HashMap::new(), sessions: HashSet::new(), @@ -177,6 +254,7 @@ impl<'a> ProcScheme<'a> { handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), + queue, auth, } } @@ -192,9 +270,14 @@ impl<'a> ProcScheme<'a> { if let Err(e) = req.obtain_fd(socket, FobtainFdFlags::empty(), Err(&mut fd_out)) { return Response::for_sendfd(&req, Err(e)); }; - let thread = Rc::new(RefCell::new(Thread { - fd: FdGuard::new(fd_out), - })); + let fd = FdGuard::new(fd_out); + + // TODO: Use global thread id etc. rather than reusing fd for identifier? + self.queue + .subscribe(*fd, fd_out, EventFlags::EVENT_READ) + .expect("TODO"); + + let thread = Rc::new(RefCell::new(Thread { fd })); self.processes.insert( INIT_PID, Process { From 910c714b46d2b3d3510311a9eb5d498ac76e3f0a Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 21 Feb 2025 11:34:00 +0100 Subject: [PATCH 064/135] Rename procmngr => procmgr. --- src/exec.rs | 2 +- src/lib.rs | 2 +- src/{procmngr.rs => procmgr.rs} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{procmngr.rs => procmgr.rs} (100%) diff --git a/src/exec.rs b/src/exec.rs index e092d6ec5b..825e4003bf 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -63,7 +63,7 @@ pub fn main() -> ! { ); spawn("process manager", &auth, &this_thr_fd, |write_fd| { - crate::procmngr::run(write_fd, &auth) + crate::procmgr::run(write_fd, &auth) }); let init_proc_fd = unsafe { redox_rt::proc::make_init() }; diff --git a/src/lib.rs b/src/lib.rs index 71e88a352c..515955489b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ pub mod arch; pub mod exec; pub mod initfs; -pub mod procmngr; +pub mod procmgr; pub mod start; extern crate alloc; diff --git a/src/procmngr.rs b/src/procmgr.rs similarity index 100% rename from src/procmngr.rs rename to src/procmgr.rs From 547b50e4b03df7d925c4887acbd9e0675b48eddb Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 23 Feb 2025 16:42:16 +0100 Subject: [PATCH 065/135] Successfully await thread death in exit impl. --- src/initfs.rs | 40 ++++++++++++++++++++-------------------- src/procmgr.rs | 31 ++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/initfs.rs b/src/initfs.rs index b9f9d03b9a..7bfa8a7ed3 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -303,11 +303,6 @@ impl SchemeSync for InitFsScheme { Ok(()) } - - fn close(&mut self, id: usize) -> Result<()> { - let _ = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - Ok(()) - } } pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { @@ -319,24 +314,29 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { let _ = syscall::close(sync_pipe); loop { - let RequestKind::Call(req) = (match socket + let Some(req) = socket .next_request(SignalBehavior::Restart) .expect("bootstrap: failed to read scheme request from kernel") - { - Some(req) => req.kind(), - None => break, - }) else { - continue; - }; - let resp = req - .handle_sync(&mut scheme) - .expect("failed to handle request"); - - if !socket - .write_response(resp, SignalBehavior::Restart) - .expect("bootstrap: failed to write scheme response to kernel") - { + else { break; + }; + match req.kind() { + RequestKind::Call(req) => { + let resp = req + .handle_sync(&mut scheme) + .expect("failed to handle request"); + + if !socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel") + { + break; + } + } + RequestKind::OnClose { id } => { + scheme.handles.remove(&id); + } + _ => (), } } diff --git a/src/procmgr.rs b/src/procmgr.rs index 84bc29090e..b29b8ec0a7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -131,7 +131,10 @@ fn handle_scheme<'a>( states.entry(req.request().request_id()), ) .map(|r| Response::new(&req, r)), - _ => Ready(Response::new(&req, Err(Error::new(ENOSYS)))), + _ => { + let _ = syscall::write(1, alloc::format!("\nUNKNOWN: {op:?}\n").as_bytes()); + Ready(Response::new(&req, Err(Error::new(ENOSYS)))) + } }); match res { Ok(Ready(r)) | Err(r) => Some(r), @@ -181,6 +184,7 @@ enum ProcessStatus { #[derive(Debug)] struct Thread { fd: FdGuard, + status_hndl: FdGuard, // sig_ctrl: MmapGuard<...> } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -276,8 +280,9 @@ impl<'a> ProcScheme<'a> { self.queue .subscribe(*fd, fd_out, EventFlags::EVENT_READ) .expect("TODO"); + let status_hndl = FdGuard::new(syscall::dup(*fd, b"status").expect("TODO")); - let thread = Rc::new(RefCell::new(Thread { fd })); + let thread = Rc::new(RefCell::new(Thread { fd, status_hndl })); self.processes.insert( INIT_PID, Process { @@ -334,11 +339,19 @@ impl<'a> ProcScheme<'a> { ens, }, )?; + let status_fd = FdGuard::new(syscall::dup(*new_ctxt_fd, b"status")?); + + self.queue + .subscribe(*new_ctxt_fd, *new_ctxt_fd, EventFlags::EVENT_READ) + .expect("TODO"); self.processes.insert( child_pid, Process { - threads: vec![Rc::new(RefCell::new(Thread { fd: new_ctxt_fd }))], + threads: vec![Rc::new(RefCell::new(Thread { + fd: new_ctxt_fd, + status_hndl: status_fd, + }))], ppid: parent_pid, pgid, sid, @@ -359,7 +372,9 @@ impl<'a> ProcScheme<'a> { fn new_thread(&mut self, pid: ProcessId) -> Result { let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; let fd = todo!(); - proc.threads.push(Rc::new(RefCell::new(Thread { fd }))); + let status_hndl = todo!(); + proc.threads + .push(Rc::new(RefCell::new(Thread { fd, status_hndl }))); Ok(fd) } fn on_open(&mut self, path: &str, flags: usize) -> Result { @@ -468,7 +483,7 @@ impl<'a> ProcScheme<'a> { self.on_waitpid( pid, target, - plain::from_mut_bytes(payload).map_err(|_| Error::new(EINVAL))?, + plain::from_mut_bytes(payload).unwrap(), //.map_err(|_| Error::new(EINVAL))?, WaitFlags::from_bits(metadata[2] as usize).ok_or(Error::new(EINVAL))?, state, ) @@ -495,6 +510,12 @@ impl<'a> ProcScheme<'a> { Self::on_exit_complete(); Ready(Ok(0)) } else { + // terminate all threads + for thread in &process.threads { + let mut thread = thread.borrow_mut(); + syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()).expect("TODO"); + } + let _ = syscall::write(1, b"\nEXIT PENDING\n"); self.debug(); // TODO: check? From edb8d80d4331f3658b0da56e0b2c695933c383b4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 26 Feb 2025 11:22:51 +0100 Subject: [PATCH 066/135] Add lookup event id => (thread, pid). --- src/procmgr.rs | 61 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b29b8ec0a7..86f0b566e5 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -5,7 +5,7 @@ use core::task::Poll; use core::task::Poll::*; use alloc::collections::VecDeque; -use alloc::rc::Rc; +use alloc::rc::{Rc, Weak}; use alloc::vec; use alloc::vec::Vec; @@ -96,8 +96,25 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } } + } else if let Some(thread) = scheme.thread_lookup.get(&event.data) { + let Some(thread) = thread.upgrade() else { + let _ = syscall::write( + 1, + alloc::format!("\nDEAD THREAD EVENT FROM {}\n", event.data).as_bytes(), + ); + continue; + }; + let _ = syscall::write( + 1, + alloc::format!( + "\nTHREAD EVENT FROM {}, {}, \n", + event.data, + thread.borrow().pid.0 + ) + .as_bytes(), + ); } else { - let _ = syscall::write(1, b"\nTODO: EVENT\n"); + let _ = syscall::write(1, b"\nTODO: UNKNOWN EVENT\n"); } } @@ -185,6 +202,7 @@ enum ProcessStatus { struct Thread { fd: FdGuard, status_hndl: FdGuard, + pid: ProcessId, // sig_ctrl: MmapGuard<...> } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -197,6 +215,8 @@ struct ProcScheme<'a> { sessions: HashSet, handles: Slab, + thread_lookup: HashMap>>, + init_claimed: bool, next_id: ProcessId, @@ -255,6 +275,7 @@ impl<'a> ProcScheme<'a> { processes: HashMap::new(), sessions: HashSet::new(), waitpgid: HashMap::new(), + thread_lookup: HashMap::new(), handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), @@ -282,7 +303,12 @@ impl<'a> ProcScheme<'a> { .expect("TODO"); let status_hndl = FdGuard::new(syscall::dup(*fd, b"status").expect("TODO")); - let thread = Rc::new(RefCell::new(Thread { fd, status_hndl })); + let thread = Rc::new(RefCell::new(Thread { + fd, + status_hndl, + pid: INIT_PID, + })); + let thread_weak = Rc::downgrade(&thread); self.processes.insert( INIT_PID, Process { @@ -304,6 +330,8 @@ impl<'a> ProcScheme<'a> { ); self.sessions.insert(INIT_PID); + self.thread_lookup.insert(fd_out, thread_weak); + *st = Handle::Proc(INIT_PID); Response::for_sendfd(&req, Ok(0)) } @@ -345,13 +373,18 @@ impl<'a> ProcScheme<'a> { .subscribe(*new_ctxt_fd, *new_ctxt_fd, EventFlags::EVENT_READ) .expect("TODO"); + let thread_ident = *new_ctxt_fd; + let thread = Rc::new(RefCell::new(Thread { + fd: new_ctxt_fd, + status_hndl: status_fd, + pid: child_pid, + })); + let thread_weak = Rc::downgrade(&thread); + self.processes.insert( child_pid, Process { - threads: vec![Rc::new(RefCell::new(Thread { - fd: new_ctxt_fd, - status_hndl: status_fd, - }))], + threads: vec![thread], ppid: parent_pid, pgid, sid, @@ -367,14 +400,22 @@ impl<'a> ProcScheme<'a> { children_waitpid: Vec::new(), }, ); + self.thread_lookup.insert(thread_ident, thread_weak); Ok(child_pid) } fn new_thread(&mut self, pid: ProcessId) -> Result { let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; - let fd = todo!(); + let fd: FdGuard = todo!(); let status_hndl = todo!(); - proc.threads - .push(Rc::new(RefCell::new(Thread { fd, status_hndl }))); + let ident = *fd; + let thread = Rc::new(RefCell::new(Thread { + fd, + status_hndl, + pid, + })); + let thread_weak = Rc::downgrade(&thread); + proc.threads.push(thread); + self.thread_lookup.insert(ident, thread_weak); Ok(fd) } fn on_open(&mut self, path: &str, flags: usize) -> Result { From 82d59302c84df509f298fd6b9b2c2c1231d70261 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 27 Feb 2025 00:55:05 +0100 Subject: [PATCH 067/135] Impl basic state machine for proc exit. --- src/procmgr.rs | 62 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 86f0b566e5..11894725f1 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -22,9 +22,9 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, EBADF, EBADFD, - EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, - O_CREAT, + ContextStatus, Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, + EBADF, EBADFD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, + O_CLOEXEC, O_CREAT, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -97,22 +97,36 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } } else if let Some(thread) = scheme.thread_lookup.get(&event.data) { - let Some(thread) = thread.upgrade() else { + let Some(thread_rc) = thread.upgrade() else { let _ = syscall::write( 1, alloc::format!("\nDEAD THREAD EVENT FROM {}\n", event.data).as_bytes(), ); continue; }; + let thread = thread_rc.borrow(); + let Some(proc) = scheme.processes.get_mut(&thread.pid) else { + // TODO? + continue; + }; let _ = syscall::write( 1, - alloc::format!( - "\nTHREAD EVENT FROM {}, {}, \n", - event.data, - thread.borrow().pid.0 - ) - .as_bytes(), + alloc::format!("\nTHREAD EVENT FROM {}, {}, \n", event.data, thread.pid.0) + .as_bytes(), ); + let mut buf = 0_usize.to_ne_bytes(); + let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); + let status = usize::from_ne_bytes(buf); + + let _ = syscall::write(1, alloc::format!("\nSTATUS {status}\n",).as_bytes()); + + if status != ContextStatus::Dead as usize { + // spurious event + continue; + } + scheme.thread_lookup.remove(&event.data); + proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); + awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { let _ = syscall::write(1, b"\nTODO: UNKNOWN EVENT\n"); } @@ -188,6 +202,7 @@ struct Process { status: ProcessStatus, + awaiting_threads_term: Vec, waitpid: Vec, children_waitpid: Vec, } @@ -326,6 +341,7 @@ impl<'a> ProcScheme<'a> { status: ProcessStatus::PossiblyRunnable, waitpid: Vec::new(), children_waitpid: Vec::new(), + awaiting_threads_term: Vec::new(), }, ); self.sessions.insert(INIT_PID); @@ -398,6 +414,7 @@ impl<'a> ProcScheme<'a> { status: ProcessStatus::PossiblyRunnable, waitpid: Vec::new(), children_waitpid: Vec::new(), + awaiting_threads_term: Vec::new(), }, ); self.thread_lookup.insert(thread_ident, thread_weak); @@ -524,7 +541,7 @@ impl<'a> ProcScheme<'a> { self.on_waitpid( pid, target, - plain::from_mut_bytes(payload).unwrap(), //.map_err(|_| Error::new(EINVAL))?, + plain::from_mut_bytes(payload).map_err(|_| Error::new(EINVAL))?, WaitFlags::from_bits(metadata[2] as usize).ok_or(Error::new(EINVAL))?, state, ) @@ -551,15 +568,17 @@ impl<'a> ProcScheme<'a> { Self::on_exit_complete(); Ready(Ok(0)) } else { - // terminate all threads + // terminate all threads (possibly including the caller, resulting in EINTR and a + // to-be-ignored cancellation request to this scheme). for thread in &process.threads { let mut thread = thread.borrow_mut(); syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()).expect("TODO"); } let _ = syscall::write(1, b"\nEXIT PENDING\n"); - self.debug(); + //self.debug(); // TODO: check? + process.awaiting_threads_term.push(*state.key()); state.insert(PendingState::AwaitingThreadsTermination(pid)); Pending } @@ -670,10 +689,21 @@ impl<'a> ProcScheme<'a> { &mut self, mut entry: OccupiedEntry, ) -> Poll { - match entry.get_mut() { + let req_id = *entry.key(); + match *entry.get_mut() { // TODO - PendingState::AwaitingThreadsTermination(_) => Pending, - PendingState::AwaitingStatusChange { waiter, target } => Pending, + PendingState::AwaitingThreadsTermination(pid) => { + let Some(proc) = self.processes.get_mut(&pid) else { + return Pending; // TODO + }; + if proc.threads.is_empty() { + let _ = syscall::write(1, b"\nWORKING ON AWAIT TERM\n"); + Ready(Response::new(req_id, Ok(0))) + } else { + Pending + } + } + PendingState::AwaitingStatusChange { waiter, ref target } => Pending, } } fn debug(&self) { From cbe00a9be86f3808a5ec1272d0f016de23ef141d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 1 Mar 2025 15:48:49 +0100 Subject: [PATCH 068/135] Impl waitpid further, still does not compile. --- src/procmgr.rs | 306 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 265 insertions(+), 41 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 11894725f1..3ada198067 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1,9 +1,11 @@ use core::cell::RefCell; +use core::cmp::Ordering; use core::hash::BuildHasherDefault; use core::mem::size_of; use core::task::Poll; use core::task::Poll::*; +use alloc::collections::btree_map::BTreeMap; use alloc::collections::VecDeque; use alloc::rc::{Rc, Weak}; use alloc::vec; @@ -23,8 +25,8 @@ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ ContextStatus, Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, - EBADF, EBADFD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, - O_CLOEXEC, O_CREAT, + EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, + EWOULDBLOCK, O_CLOEXEC, O_CREAT, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -47,13 +49,15 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut states = HashMap::::new(); let mut awoken = VecDeque::::new(); + let mut new_awoken = VecDeque::new(); 'outer: loop { + awoken.append(&mut new_awoken); for awoken in awoken.drain(..) { let Entry::Occupied(entry) = states.entry(awoken) else { continue; }; - match scheme.work_on(entry) { + match scheme.work_on(entry, &mut new_awoken) { Ready(resp) => loop { match socket.write_response(resp, SignalBehavior::Interrupt) { Ok(false) => break 'outer, @@ -83,7 +87,8 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } }; - let Some(resp) = handle_scheme(req, &socket, &mut scheme, &mut states) else { + let Some(resp) = handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) + else { continue 'outer; }; loop { @@ -139,6 +144,7 @@ fn handle_scheme<'a>( socket: &'a Socket, scheme: &mut ProcScheme<'a>, states: &mut HashMap, + awoken: &mut VecDeque, ) -> Option { match req.kind() { RequestKind::Call(req) => { @@ -160,6 +166,7 @@ fn handle_scheme<'a>( payload, metadata, states.entry(req.request().request_id()), + awoken, ) .map(|r| Response::new(&req, r)), _ => { @@ -182,6 +189,7 @@ enum PendingState { AwaitingStatusChange { waiter: ProcessId, target: WaitpidTarget, + flags: WaitFlags, }, AwaitingThreadsTermination(ProcessId), } @@ -203,15 +211,75 @@ struct Process { status: ProcessStatus, awaiting_threads_term: Vec, - waitpid: Vec, - children_waitpid: Vec, + + waitpid: BTreeMap, + waitpid_waiting: VecDeque, } +#[derive(Copy, Clone, Debug)] +pub struct WaitpidKey { + pub pid: Option, + pub pgid: Option, +} + +// TODO: Is this valid? (transitive?) +impl Ord for WaitpidKey { + fn cmp(&self, other: &WaitpidKey) -> Ordering { + // If both have pid set, compare that + if let Some(s_pid) = self.pid { + if let Some(o_pid) = other.pid { + return s_pid.cmp(&o_pid); + } + } + + // If both have pgid set, compare that + if let Some(s_pgid) = self.pgid { + if let Some(o_pgid) = other.pgid { + return s_pgid.cmp(&o_pgid); + } + } + + // If either has pid set, it is greater + if self.pid.is_some() { + return Ordering::Greater; + } + + if other.pid.is_some() { + return Ordering::Less; + } + + // If either has pgid set, it is greater + if self.pgid.is_some() { + return Ordering::Greater; + } + + if other.pgid.is_some() { + return Ordering::Less; + } + + // If all pid and pgid are None, they are equal + Ordering::Equal + } +} + +impl PartialOrd for WaitpidKey { + fn partial_cmp(&self, other: &WaitpidKey) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for WaitpidKey { + fn eq(&self, other: &WaitpidKey) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for WaitpidKey {} #[derive(Debug, Clone, Copy)] enum ProcessStatus { PossiblyRunnable, Stopped(usize), - Exiting { status: i32 }, - Exited { status: i32 }, + Exiting { signal: Option, status: u8 }, + Exited { signal: Option, status: u8 }, } #[derive(Debug)] struct Thread { @@ -220,7 +288,7 @@ struct Thread { pid: ProcessId, // sig_ctrl: MmapGuard<...> } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] struct ProcessId(usize); const INIT_PID: ProcessId = ProcessId(1); @@ -235,10 +303,15 @@ struct ProcScheme<'a> { init_claimed: bool, next_id: ProcessId, - waitpgid: HashMap, DefaultHashBuilder>, queue: &'a RawEventQueue, auth: &'a FdGuard, } +#[derive(Clone, Copy, Debug)] +enum WaitpidStatus { + Continued, + Stopped { signal: u8 }, + Terminated { signal: Option, status: u8 }, +} #[derive(Debug)] enum Handle { @@ -289,7 +362,6 @@ impl<'a> ProcScheme<'a> { ProcScheme { processes: HashMap::new(), sessions: HashSet::new(), - waitpgid: HashMap::new(), thread_lookup: HashMap::new(), handles: Slab::new(), init_claimed: false, @@ -339,9 +411,9 @@ impl<'a> ProcScheme<'a> { ens: 1, status: ProcessStatus::PossiblyRunnable, - waitpid: Vec::new(), - children_waitpid: Vec::new(), awaiting_threads_term: Vec::new(), + waitpid: BTreeMap::new(), + waitpid_waiting: VecDeque::new(), }, ); self.sessions.insert(INIT_PID); @@ -412,9 +484,10 @@ impl<'a> ProcScheme<'a> { ens, status: ProcessStatus::PossiblyRunnable, - waitpid: Vec::new(), - children_waitpid: Vec::new(), awaiting_threads_term: Vec::new(), + + waitpid: BTreeMap::new(), + waitpid_waiting: VecDeque::new(), }, ); self.thread_lookup.insert(thread_ident, thread_weak); @@ -506,6 +579,7 @@ impl<'a> ProcScheme<'a> { payload: &mut [u8], metadata: &[u64], state: Entry, + awoken: &mut VecDeque, ) -> Poll> { match self.handles[id] { Handle::Init => Ready(Err(Error::new(EBADF))), @@ -528,7 +602,7 @@ impl<'a> ProcScheme<'a> { ) .map(|()| 0), ), - ProcCall::Exit => self.on_exit_start(pid, metadata[1] as i32, state), + ProcCall::Exit => self.on_exit_start(pid, metadata[1] as i32, state, awoken), ProcCall::Waitpid | ProcCall::Waitpgid => { let req_pid = ProcessId(metadata[1] as usize); let target = match (verb, metadata[1] == 0) { @@ -538,13 +612,13 @@ impl<'a> ProcScheme<'a> { (ProcCall::Waitpgid, false) => WaitpidTarget::ProcGroup(req_pid), _ => unreachable!(), }; - self.on_waitpid( - pid, + let state = state.insert(PendingState::AwaitingStatusChange { + waiter: req_pid, target, - plain::from_mut_bytes(payload).map_err(|_| Error::new(EINVAL))?, - WaitFlags::from_bits(metadata[2] as usize).ok_or(Error::new(EINVAL))?, - state, - ) + flags: WaitFlags::from_bits(metadata[2] as usize) + .ok_or(Error::new(EINVAL))?, + }); + self.work_on(state, &mut awoken, Some(payload)) } } } @@ -555,6 +629,7 @@ impl<'a> ProcScheme<'a> { pid: ProcessId, status: i32, mut state: Entry, + awoken: &mut VecDeque, ) -> Poll> { let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; match process.status { @@ -563,40 +638,155 @@ impl<'a> ProcScheme<'a> { ProcessStatus::Exiting { .. } => return Ready(Err(Error::new(EAGAIN))), ProcessStatus::Exited { .. } => return Ready(Err(Error::new(ESRCH))), } - process.status = ProcessStatus::Exiting { status }; - if process.threads.is_empty() { - Self::on_exit_complete(); - Ready(Ok(0)) - } else { + // TODO: status/signal + process.status = ProcessStatus::Exiting { + status: status as u8, + signal: None, + }; + if !process.threads.is_empty() { // terminate all threads (possibly including the caller, resulting in EINTR and a // to-be-ignored cancellation request to this scheme). for thread in &process.threads { let mut thread = thread.borrow_mut(); - syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()).expect("TODO"); + syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes())?; } let _ = syscall::write(1, b"\nEXIT PENDING\n"); //self.debug(); // TODO: check? process.awaiting_threads_term.push(*state.key()); - state.insert(PendingState::AwaitingThreadsTermination(pid)); - Pending } - } - fn on_exit_complete() { - // TODO: send waitpid status + self.work_on( + state.insert(PendingState::AwaitingThreadsTermination(pid)), + awoken, + None, + ) } pub fn on_waitpid( &mut self, this_pid: ProcessId, target: WaitpidTarget, - stat_loc: &mut i32, flags: WaitFlags, - mut state: Entry, - ) -> Poll> { + ) -> Poll> { + let req_id = *state.key(); + + if matches!( + target, + WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember + ) { + // Check for existence of child. + // TODO: inefficient, keep refcount? + if !self.processes.values().any(|p| p.ppid == this_pid) { + return Ready(Err(Error::new(ECHILD))); + } + } + + let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; let _ = syscall::write(1, b"\nWAITPID\n"); - self.debug(); - Pending + + let recv_nonblock = |waitpid: &mut BTreeMap, + key: &WaitpidKey| + -> Option<(WaitpidKey, WaitpidStatus)> { + if let Some((key, value)) = waitpid.get(key).map(|(k, v)| (*k, *v)) { + waitpid.remove(&key); + Some((key, value)) + } else { + None + } + }; + let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| { + match status { + WaitpidStatus::Continued => { + // TODO: Handle None, i.e. restart everything until a match is found + if flags.contains(WaitFlags::WCONTINUED) { + Option::<(ProcessId, i32)>::Some((w_pid, todo!())) + } else { + None + } + } + WaitpidStatus::Stopped { signal } => { + if flags.contains(WaitFlags::WUNTRACED) { + Some((w_pid, todo!())) + } else { + None + } + } + WaitpidStatus::Terminated { signal, status } => Some((w_pid, todo!())), + } + }; + + match target { + // TODO: not the same + WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember => { + if let Some((wid, (w_pid, status))) = + proc.waitpid.first_key_value().map(|(k, v)| (*k, *v)) + { + let _ = proc.waitpid.remove(&wid); + Ready(grim_reaper(w_pid, status)) + } else if flags.contains(WaitFlags::WNOHANG) { + Ready(Ok((0, 0))) + } else { + proc.waitpid_waiting.push_back(req_id); + Pending + } + } + WaitpidTarget::SingleProc(pid) => { + if this_pid == pid { + return Err(Error::new(EINVAL)); + } + let [proc, target_proc] = self + .processes + .get_many_mut((&this_pid, &pid)) + .ok_or(Error::new(ESRCH))?; + if target_proc.ppid != this_pid { + return Ready(Err(Error::new(ECHILD))); + } + let key = WaitpidKey { + pid: Some(pid), + pgid: None, + }; + if let ProcessStatus::Exited { status, signal } = target_proc.status { + let _ = recv_nonblock(&mut proc.waitpid, &key); + Ready(grim_reaper( + pid, + WaitpidStatus::Terminated { signal, status }, + )) + } else { + let res = recv_nonblock(&mut proc.waitpid, &key); + if let Some((w_pid, status)) = res { + Ready(grim_reaper(w_pid, status)) + } else if flags.contains(WaitFlags::WNOHANG) { + Ready(Ok((0, 0))) + } else { + proc.waitpid_waiting.push_back(req_id); + Pending + } + } + } + WaitpidTarget::ProcGroup(pgid) => { + let this_pgid = proc.pgid; + if !self.processes.values().any(|p| p.pgid == this_pgid) { + return Ready(Err(Error::new(ECHILD))); + } + + // reborrow proc + let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; + + let key = WaitpidKey { + pid: None, + pgid: Some(pgid), + }; + if let Some((w_pid, status)) = proc.waitpid.get(&key) { + let _ = proc.waitpid.remove(&key); + grim_reaper(w_pid, status) + } else if flags.contains(WaitFlags::WNOHANG) { + Ready(Ok((0, 0))) + } else { + proc.waitpid_waiting.push_back(req_id); + Pending + } + } + } } fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { struct Iter<'a> { @@ -688,22 +878,56 @@ impl<'a> ProcScheme<'a> { pub fn work_on( &mut self, mut entry: OccupiedEntry, + awoken: &mut VecDeque, + buffer: Option<&mut [u8]>, ) -> Poll { let req_id = *entry.key(); match *entry.get_mut() { // TODO - PendingState::AwaitingThreadsTermination(pid) => { - let Some(proc) = self.processes.get_mut(&pid) else { + PendingState::AwaitingThreadsTermination(current_pid) => { + let Some(proc) = self.processes.get_mut(¤t_pid) else { return Pending; // TODO }; if proc.threads.is_empty() { let _ = syscall::write(1, b"\nWORKING ON AWAIT TERM\n"); + let (signal, status) = match proc.status { + ProcessStatus::Exiting { signal, status } => (signal, status), + ProcessStatus::Exited { .. } => return Ready(Response::new(req_id, Ok(0))), + _ => return Ready(Response::new(req_id, Err(Error::new(ESRCH)))), // TODO? + }; + proc.status = ProcessStatus::Exited { signal, status }; + let ppid = proc.ppid; + if let Some(parent) = self.processes.get_mut(&ppid) { + // TODO: transfer children to parent, and all of self.waitpid + parent.waitpid.insert( + WaitpidKey { + pid: Some(current_pid), + pgid: Some(pgid), + }, + (current_pid, WaitpidStatus::Terminated { signal, status }), + ); + // TODO: inefficient + awoken.extend(parent.waitpid_waiting.drain(..)); + } Ready(Response::new(req_id, Ok(0))) } else { Pending } } - PendingState::AwaitingStatusChange { waiter, ref target } => Pending, + PendingState::AwaitingStatusChange { + waiter, + target, + flags, + } => self + .on_waitpid(waiter, target, flags) + .map_ok(|(pid, status)| { + if let Some(buf) = buffer.and_then(|buf| plain::from_mut_bytes::(buf).ok()) + { + *buf = status; + } + pid + }) + .map(|res| Response::new(req_id, res)), } } fn debug(&self) { From 016c60c11fffc883d0b11725916b41d28298d841 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 2 Mar 2025 12:17:20 +0100 Subject: [PATCH 069/135] Fix most errors. --- src/procmgr.rs | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 3ada198067..12d67854f8 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -319,6 +319,7 @@ enum Handle { Proc(ProcessId), } +#[derive(Clone, Copy, Debug)] enum WaitpidTarget { SingleProc(ProcessId), ProcGroup(ProcessId), @@ -684,34 +685,35 @@ impl<'a> ProcScheme<'a> { let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; let _ = syscall::write(1, b"\nWAITPID\n"); - let recv_nonblock = |waitpid: &mut BTreeMap, + let recv_nonblock = |waitpid: &mut BTreeMap, key: &WaitpidKey| - -> Option<(WaitpidKey, WaitpidStatus)> { - if let Some((key, value)) = waitpid.get(key).map(|(k, v)| (*k, *v)) { - waitpid.remove(&key); - Some((key, value)) + -> Option<(ProcessId, WaitpidStatus)> { + if let Some((pid, sts)) = waitpid.get(key).map(|(k, v)| (*k, *v)) { + waitpid.remove(key); + Some((pid, sts)) } else { None } }; let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| { + let ret: i32 = todo!(); match status { WaitpidStatus::Continued => { // TODO: Handle None, i.e. restart everything until a match is found if flags.contains(WaitFlags::WCONTINUED) { - Option::<(ProcessId, i32)>::Some((w_pid, todo!())) + Ready((w_pid.0, ret)) } else { - None + Pending } } WaitpidStatus::Stopped { signal } => { if flags.contains(WaitFlags::WUNTRACED) { - Some((w_pid, todo!())) + Ready((w_pid.0, ret)) } else { - None + Pending } } - WaitpidStatus::Terminated { signal, status } => Some((w_pid, todo!())), + WaitpidStatus::Terminated { signal, status } => Ready((w_pid.0, ret)), } }; @@ -722,7 +724,7 @@ impl<'a> ProcScheme<'a> { proc.waitpid.first_key_value().map(|(k, v)| (*k, *v)) { let _ = proc.waitpid.remove(&wid); - Ready(grim_reaper(w_pid, status)) + grim_reaper(w_pid, status).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -732,11 +734,11 @@ impl<'a> ProcScheme<'a> { } WaitpidTarget::SingleProc(pid) => { if this_pid == pid { - return Err(Error::new(EINVAL)); + return Ready(Err(Error::new(EINVAL))); } let [proc, target_proc] = self .processes - .get_many_mut((&this_pid, &pid)) + .get_many_mut([&this_pid, &pid]) .ok_or(Error::new(ESRCH))?; if target_proc.ppid != this_pid { return Ready(Err(Error::new(ECHILD))); @@ -747,14 +749,11 @@ impl<'a> ProcScheme<'a> { }; if let ProcessStatus::Exited { status, signal } = target_proc.status { let _ = recv_nonblock(&mut proc.waitpid, &key); - Ready(grim_reaper( - pid, - WaitpidStatus::Terminated { signal, status }, - )) + grim_reaper(pid, WaitpidStatus::Terminated { signal, status }).map(Ok) } else { let res = recv_nonblock(&mut proc.waitpid, &key); if let Some((w_pid, status)) = res { - Ready(grim_reaper(w_pid, status)) + grim_reaper(w_pid, status).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -776,9 +775,9 @@ impl<'a> ProcScheme<'a> { pid: None, pgid: Some(pgid), }; - if let Some((w_pid, status)) = proc.waitpid.get(&key) { + if let Some(&(w_pid, status)) = proc.waitpid.get(&key) { let _ = proc.waitpid.remove(&key); - grim_reaper(w_pid, status) + grim_reaper(w_pid, status).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -896,7 +895,7 @@ impl<'a> ProcScheme<'a> { _ => return Ready(Response::new(req_id, Err(Error::new(ESRCH)))), // TODO? }; proc.status = ProcessStatus::Exited { signal, status }; - let ppid = proc.ppid; + let (ppid, pgid) = (proc.ppid, proc.pgid); if let Some(parent) = self.processes.get_mut(&ppid) { // TODO: transfer children to parent, and all of self.waitpid parent.waitpid.insert( From 7e8b8d7183b0e6e42e0ae7a5e37d6d74841bc9d3 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 2 Mar 2025 15:04:26 +0100 Subject: [PATCH 070/135] Make it compile. --- Cargo.toml | 4 +- src/procmgr.rs | 217 ++++++++++++++++++++++++++++--------------------- 2 files changed, 128 insertions(+), 93 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3413303d20..2f6308d655 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,9 @@ name = "bootstrap" crate-type = ["staticlib"] [dependencies] -hashbrown = { version = "0.14", default-features = false, features = [ +hashbrown = { version = "0.15", default-features = false, features = [ "inline-more", - "ahash", + "default-hasher", ] } linked_list_allocator = "0.10" plain = "0.2" diff --git a/src/procmgr.rs b/src/procmgr.rs index 12d67854f8..f596f39345 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -11,21 +11,21 @@ use alloc::rc::{Rc, Weak}; use alloc::vec; use alloc::vec::Vec; -use hashbrown::hash_map::{DefaultHashBuilder, Entry, OccupiedEntry}; -use hashbrown::{HashMap, HashSet}; +use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; +use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; use redox_rt::protocol::{ProcCall, ProcMeta, WaitFlags}; -use redox_scheme::scheme::Op; +use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, - Socket, + Socket, Tag, }; use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ ContextStatus, Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, - EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, + EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, }; @@ -54,10 +54,10 @@ pub fn run(write_fd: usize, auth: &FdGuard) { 'outer: loop { awoken.append(&mut new_awoken); for awoken in awoken.drain(..) { - let Entry::Occupied(entry) = states.entry(awoken) else { + let Entry::Occupied(state) = states.entry(awoken) else { continue; }; - match scheme.work_on(entry, &mut new_awoken) { + match scheme.work_on(state, &mut new_awoken) { Ready(resp) => loop { match socket.write_response(resp, SignalBehavior::Interrupt) { Ok(false) => break 'outer, @@ -87,7 +87,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } }; - let Some(resp) = handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) + let Ready(resp) = handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) else { continue 'outer; }; @@ -145,44 +145,44 @@ fn handle_scheme<'a>( scheme: &mut ProcScheme<'a>, states: &mut HashMap, awoken: &mut VecDeque, -) -> Option { +) -> Poll { match req.kind() { RequestKind::Call(req) => { - let res = req.with(|req, caller, op| match op { - Op::Open { path, flags } => { - Ready(Response::open_dup_like(&req, scheme.on_open(path, flags))) - } - Op::Dup { old_fd, buf } => { - Ready(Response::open_dup_like(&req, scheme.on_dup(old_fd, buf))) - } - Op::Read { fd, buf, .. } => Ready(Response::new(&req, scheme.on_read(fd, buf))), - Op::Call { - fd, - payload, - metadata, - } => scheme - .on_call( - fd, - payload, - metadata, - states.entry(req.request().request_id()), - awoken, - ) - .map(|r| Response::new(&req, r)), + let req_id = req.request_id(); + let op = match req.op() { + Ok(op) => op, + Err(req) => return Response::ready_err(ENOSYS, req), + }; + match op { + Op::Open(op) => Ready(Response::open_dup_like( + scheme.on_open(op.path(), op.flags), + op, + )), + Op::Dup(op) => Ready(Response::open_dup_like(scheme.on_dup(op.fd, op.buf()), op)), + Op::Read(mut op) => Ready(Response::new(scheme.on_read(op.fd, op.buf()), op)), + Op::Call(op) => scheme.on_call( + { + // TODO: cleanup + states.remove(&req_id); + if let Entry::Vacant(entry) = states.entry(req_id) { + entry + } else { + unreachable!() + } + }, + op, + awoken, + ), _ => { let _ = syscall::write(1, alloc::format!("\nUNKNOWN: {op:?}\n").as_bytes()); - Ready(Response::new(&req, Err(Error::new(ENOSYS)))) + Ready(Response::new(Err(Error::new(ENOSYS)), op)) } - }); - match res { - Ok(Ready(r)) | Err(r) => Some(r), - // waker has already been registered, so the logic for the caller is to not send a - // response and continue with remaining events - Ok(Pending) => None, } } - RequestKind::SendFd(req) => Some(scheme.on_sendfd(socket, &req)), - _ => None, + RequestKind::SendFd(req) => Ready(scheme.on_sendfd(socket, req)), + + // ignore + _ => Pending, } } enum PendingState { @@ -190,8 +190,19 @@ enum PendingState { waiter: ProcessId, target: WaitpidTarget, flags: WaitFlags, + op: OpCall, }, - AwaitingThreadsTermination(ProcessId), + AwaitingThreadsTermination(ProcessId, Tag), + Placeholder, +} +impl IntoTag for PendingState { + fn into_tag(self) -> Tag { + match self { + Self::AwaitingThreadsTermination(_, tag) => tag, + Self::AwaitingStatusChange { op, .. } => op.into_tag(), + Self::Placeholder => unreachable!(), + } + } } #[derive(Debug)] @@ -376,12 +387,12 @@ impl<'a> ProcScheme<'a> { self.next_id.0 += 1; id } - fn on_sendfd(&mut self, socket: &Socket, req: &SendFdRequest) -> Response { + fn on_sendfd(&mut self, socket: &Socket, req: SendFdRequest) -> Response { match self.handles[req.id()] { ref mut st @ Handle::Init => { let mut fd_out = usize::MAX; if let Err(e) = req.obtain_fd(socket, FobtainFdFlags::empty(), Err(&mut fd_out)) { - return Response::for_sendfd(&req, Err(e)); + return Response::new(Err(e), req); }; let fd = FdGuard::new(fd_out); @@ -422,9 +433,9 @@ impl<'a> ProcScheme<'a> { self.thread_lookup.insert(fd_out, thread_weak); *st = Handle::Proc(INIT_PID); - Response::for_sendfd(&req, Ok(0)) + Response::ok(0, req) } - _ => Response::for_sendfd(&req, Err(Error::new(EBADF))), + _ => Response::err(EBADF, req), } } fn fork(&mut self, parent_pid: ProcessId) -> Result { @@ -576,17 +587,18 @@ impl<'a> ProcScheme<'a> { } pub fn on_call( &mut self, - id: usize, - payload: &mut [u8], - metadata: &[u64], - state: Entry, + state: VacantEntry, + mut op: OpCall, awoken: &mut VecDeque, - ) -> Poll> { + ) -> Poll { + let id = op.fd; + let (payload, metadata) = op.payload_and_metadata(); match self.handles[id] { - Handle::Init => Ready(Err(Error::new(EBADF))), + Handle::Init => Response::ready_err(EBADF, op), Handle::Proc(pid) => { - let verb = - ProcCall::try_from_raw(metadata[0] as usize).ok_or(Error::new(EINVAL))?; + let Some(verb) = ProcCall::try_from_raw(metadata[0] as usize) else { + return Response::ready_err(EINVAL, op); + }; fn cvt_u32(u: u32) -> Option { if u == u32::MAX { None @@ -595,15 +607,18 @@ impl<'a> ProcScheme<'a> { } } match verb { - ProcCall::Setrens => Ready( + ProcCall::Setrens => Ready(Response::new( self.on_setrens( pid, cvt_u32(metadata[1] as u32), cvt_u32(metadata[2] as u32), ) .map(|()| 0), - ), - ProcCall::Exit => self.on_exit_start(pid, metadata[1] as i32, state, awoken), + op, + )), + ProcCall::Exit => { + self.on_exit_start(pid, metadata[1] as i32, state, awoken, op.into_tag()) + } ProcCall::Waitpid | ProcCall::Waitpgid => { let req_pid = ProcessId(metadata[1] as usize); let target = match (verb, metadata[1] == 0) { @@ -613,13 +628,17 @@ impl<'a> ProcScheme<'a> { (ProcCall::Waitpgid, false) => WaitpidTarget::ProcGroup(req_pid), _ => unreachable!(), }; - let state = state.insert(PendingState::AwaitingStatusChange { + let flags = match WaitFlags::from_bits(metadata[2] as usize) { + Some(fl) => fl, + None => return Response::ready_err(EINVAL, op), + }; + let state = state.insert_entry(PendingState::AwaitingStatusChange { waiter: req_pid, target, - flags: WaitFlags::from_bits(metadata[2] as usize) - .ok_or(Error::new(EINVAL))?, + flags, + op, }); - self.work_on(state, &mut awoken, Some(payload)) + self.work_on(state, awoken) } } } @@ -629,15 +648,18 @@ impl<'a> ProcScheme<'a> { &mut self, pid: ProcessId, status: i32, - mut state: Entry, + mut state: VacantEntry, awoken: &mut VecDeque, - ) -> Poll> { - let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + tag: Tag, + ) -> Poll { + let Some(process) = self.processes.get_mut(&pid) else { + return Response::ready_err(EBADFD, tag); + }; match process.status { ProcessStatus::Stopped(_) | ProcessStatus::PossiblyRunnable => (), //ProcessStatus::Exiting => return Pending, - ProcessStatus::Exiting { .. } => return Ready(Err(Error::new(EAGAIN))), - ProcessStatus::Exited { .. } => return Ready(Err(Error::new(ESRCH))), + ProcessStatus::Exiting { .. } => return Response::ready_err(EAGAIN, tag), + ProcessStatus::Exited { .. } => return Response::ready_err(ESRCH, tag), } // TODO: status/signal process.status = ProcessStatus::Exiting { @@ -649,7 +671,9 @@ impl<'a> ProcScheme<'a> { // to-be-ignored cancellation request to this scheme). for thread in &process.threads { let mut thread = thread.borrow_mut(); - syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes())?; + if let Err(err) = syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()) { + return Response::ready_err(err.errno, tag); + } } let _ = syscall::write(1, b"\nEXIT PENDING\n"); @@ -658,9 +682,8 @@ impl<'a> ProcScheme<'a> { process.awaiting_threads_term.push(*state.key()); } self.work_on( - state.insert(PendingState::AwaitingThreadsTermination(pid)), + state.insert_entry(PendingState::AwaitingThreadsTermination(pid, tag)), awoken, - None, ) } pub fn on_waitpid( @@ -668,9 +691,8 @@ impl<'a> ProcScheme<'a> { this_pid: ProcessId, target: WaitpidTarget, flags: WaitFlags, + req_id: Id, ) -> Poll> { - let req_id = *state.key(); - if matches!( target, WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember @@ -736,10 +758,11 @@ impl<'a> ProcScheme<'a> { if this_pid == pid { return Ready(Err(Error::new(EINVAL))); } - let [proc, target_proc] = self - .processes - .get_many_mut([&this_pid, &pid]) - .ok_or(Error::new(ESRCH))?; + let [Some(proc), Some(target_proc)] = + self.processes.get_many_mut([&this_pid, &pid]) + else { + return Ready(Err(Error::new(ESRCH))); + }; if target_proc.ppid != this_pid { return Ready(Err(Error::new(ECHILD))); } @@ -876,23 +899,24 @@ impl<'a> ProcScheme<'a> { } pub fn work_on( &mut self, - mut entry: OccupiedEntry, + mut state: OccupiedEntry, awoken: &mut VecDeque, - buffer: Option<&mut [u8]>, ) -> Poll { - let req_id = *entry.key(); - match *entry.get_mut() { + let req_id = *state.key(); + let mut state = state.get_mut(); + match core::mem::replace(state, PendingState::Placeholder) { + PendingState::Placeholder => unreachable!(), // TODO - PendingState::AwaitingThreadsTermination(current_pid) => { + PendingState::AwaitingThreadsTermination(current_pid, tag) => { let Some(proc) = self.processes.get_mut(¤t_pid) else { - return Pending; // TODO + return Response::ready_err(ESRCH, tag); }; if proc.threads.is_empty() { let _ = syscall::write(1, b"\nWORKING ON AWAIT TERM\n"); let (signal, status) = match proc.status { ProcessStatus::Exiting { signal, status } => (signal, status), - ProcessStatus::Exited { .. } => return Ready(Response::new(req_id, Ok(0))), - _ => return Ready(Response::new(req_id, Err(Error::new(ESRCH)))), // TODO? + ProcessStatus::Exited { .. } => return Response::ready_ok(0, tag), + _ => return Response::ready_err(ESRCH, tag), // TODO? }; proc.status = ProcessStatus::Exited { signal, status }; let (ppid, pgid) = (proc.ppid, proc.pgid); @@ -908,8 +932,10 @@ impl<'a> ProcScheme<'a> { // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } - Ready(Response::new(req_id, Ok(0))) + Ready(Response::new(Ok(0), tag)) } else { + proc.awaiting_threads_term.push(req_id); + *state = PendingState::AwaitingThreadsTermination(current_pid, tag); Pending } } @@ -917,16 +943,25 @@ impl<'a> ProcScheme<'a> { waiter, target, flags, - } => self - .on_waitpid(waiter, target, flags) - .map_ok(|(pid, status)| { - if let Some(buf) = buffer.and_then(|buf| plain::from_mut_bytes::(buf).ok()) - { - *buf = status; + mut op, + } => match self.on_waitpid(waiter, target, flags, req_id) { + Ready(Ok((pid, status))) => { + if let Ok(status_out) = plain::from_mut_bytes::(op.payload()) { + *status_out = status; } - pid - }) - .map(|res| Response::new(req_id, res)), + Response::ready_ok(pid, op) + } + Ready(Err(e)) => Response::ready_err(e.errno, op), + Pending => { + *state = PendingState::AwaitingStatusChange { + waiter, + target, + flags, + op, + }; + Pending + } + }, } } fn debug(&self) { From ddc47a7e43576d2c73c24a5cdb372b80a35b9d90 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 2 Mar 2025 15:20:00 +0100 Subject: [PATCH 071/135] Fix incorrect waitpid request pid. --- src/procmgr.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index f596f39345..0c5e768e74 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -595,7 +595,7 @@ impl<'a> ProcScheme<'a> { let (payload, metadata) = op.payload_and_metadata(); match self.handles[id] { Handle::Init => Response::ready_err(EBADF, op), - Handle::Proc(pid) => { + Handle::Proc(fd_pid) => { let Some(verb) = ProcCall::try_from_raw(metadata[0] as usize) else { return Response::ready_err(EINVAL, op); }; @@ -609,7 +609,7 @@ impl<'a> ProcScheme<'a> { match verb { ProcCall::Setrens => Ready(Response::new( self.on_setrens( - pid, + fd_pid, cvt_u32(metadata[1] as u32), cvt_u32(metadata[2] as u32), ) @@ -617,7 +617,7 @@ impl<'a> ProcScheme<'a> { op, )), ProcCall::Exit => { - self.on_exit_start(pid, metadata[1] as i32, state, awoken, op.into_tag()) + self.on_exit_start(fd_pid, metadata[1] as i32, state, awoken, op.into_tag()) } ProcCall::Waitpid | ProcCall::Waitpgid => { let req_pid = ProcessId(metadata[1] as usize); @@ -630,10 +630,12 @@ impl<'a> ProcScheme<'a> { }; let flags = match WaitFlags::from_bits(metadata[2] as usize) { Some(fl) => fl, - None => return Response::ready_err(EINVAL, op), + None => { + return Response::ready_err(EINVAL, op); + } }; let state = state.insert_entry(PendingState::AwaitingStatusChange { - waiter: req_pid, + waiter: fd_pid, target, flags, op, From bdb665d897c9d5851ae5e3bd34b4e8adad7c196f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 15:21:39 +0100 Subject: [PATCH 072/135] Ensure new_awoken are handled before event queue wait. --- src/initfs.rs | 4 +-- src/procmgr.rs | 94 +++++++++++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/src/initfs.rs b/src/initfs.rs index 7bfa8a7ed3..a594140c9c 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -322,9 +322,7 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { }; match req.kind() { RequestKind::Call(req) => { - let resp = req - .handle_sync(&mut scheme) - .expect("failed to handle request"); + let resp = req.handle_sync(&mut scheme); if !socket .write_response(resp, SignalBehavior::Restart) diff --git a/src/procmgr.rs b/src/procmgr.rs index 0c5e768e74..047694ae07 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -52,23 +52,30 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut new_awoken = VecDeque::new(); 'outer: loop { - awoken.append(&mut new_awoken); - for awoken in awoken.drain(..) { - let Entry::Occupied(state) = states.entry(awoken) else { - continue; - }; - match scheme.work_on(state, &mut new_awoken) { - Ready(resp) => loop { - match socket.write_response(resp, SignalBehavior::Interrupt) { - Ok(false) => break 'outer, - Ok(_) => break, - Err(err) if err.errno == EINTR => continue, - Err(err) => { - panic!("bootstrap: failed to write scheme response to kernel: {err}") + let _ = syscall::write(1, alloc::format!("\n{awoken:#?}\n").as_bytes()); + while !awoken.is_empty() || !new_awoken.is_empty() { + awoken.append(&mut new_awoken); + for awoken in awoken.drain(..) { + //let _ = syscall::write(1, alloc::format!("\nALL STATES {states:#?}, AWOKEN {awoken:#?}\n").as_bytes()); + let Entry::Occupied(state) = states.entry(awoken) else { + continue; + }; + //let _ = syscall::write(1, alloc::format!("\nSTATE {state:#?}\n").as_bytes()); + match scheme.work_on(state, &mut new_awoken) { + Ready(resp) => { + loop { + match socket.write_response(resp, SignalBehavior::Interrupt) { + Ok(false) => break 'outer, + Ok(_) => break, + Err(err) if err.errno == EINTR => continue, + Err(err) => { + panic!("bootstrap: failed to write scheme response to kernel: {err}") + } + } } } - }, - Pending => continue, + Pending => continue, + } } } // TODO: multiple events? @@ -131,6 +138,10 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } scheme.thread_lookup.remove(&event.data); proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); + let _ = syscall::write( + 1, + alloc::format!("\nAWAITING {}\n", proc.awaiting_threads_term.len()).as_bytes(), + ); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { let _ = syscall::write(1, b"\nTODO: UNKNOWN EVENT\n"); @@ -185,6 +196,7 @@ fn handle_scheme<'a>( _ => Pending, } } +#[derive(Debug)] enum PendingState { AwaitingStatusChange { waiter: ProcessId, @@ -901,13 +913,14 @@ impl<'a> ProcScheme<'a> { } pub fn work_on( &mut self, - mut state: OccupiedEntry, + mut state_entry: OccupiedEntry, awoken: &mut VecDeque, ) -> Poll { - let req_id = *state.key(); - let mut state = state.get_mut(); - match core::mem::replace(state, PendingState::Placeholder) { - PendingState::Placeholder => unreachable!(), + let req_id = *state_entry.key(); + let mut state = state_entry.get_mut(); + let this_state = core::mem::replace(state, PendingState::Placeholder); + match this_state { + PendingState::Placeholder => return Pending, // unreachable!(), // TODO PendingState::AwaitingThreadsTermination(current_pid, tag) => { let Some(proc) = self.processes.get_mut(¤t_pid) else { @@ -920,6 +933,9 @@ impl<'a> ProcScheme<'a> { ProcessStatus::Exited { .. } => return Response::ready_ok(0, tag), _ => return Response::ready_err(ESRCH, tag), // TODO? }; + // TODO: Properly remove state + state_entry.remove(); + proc.status = ProcessStatus::Exited { signal, status }; let (ppid, pgid) = (proc.ppid, proc.pgid); if let Some(parent) = self.processes.get_mut(&ppid) { @@ -931,11 +947,13 @@ impl<'a> ProcScheme<'a> { }, (current_pid, WaitpidStatus::Terminated { signal, status }), ); + //let _ = syscall::write(1, alloc::format!("\nAWAKING WAITPID {:?}\n", parent.waitpid_waiting).as_bytes()); // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } Ready(Response::new(Ok(0), tag)) } else { + let _ = syscall::write(1, b"\nWAITING AGAIN\n"); proc.awaiting_threads_term.push(req_id); *state = PendingState::AwaitingThreadsTermination(current_pid, tag); Pending @@ -946,24 +964,28 @@ impl<'a> ProcScheme<'a> { target, flags, mut op, - } => match self.on_waitpid(waiter, target, flags, req_id) { - Ready(Ok((pid, status))) => { - if let Ok(status_out) = plain::from_mut_bytes::(op.payload()) { - *status_out = status; + } => { + let _ = syscall::write(1, b"\nWORKING ON AWAIT STS CHANGE\n"); + + match self.on_waitpid(waiter, target, flags, req_id) { + Ready(Ok((pid, status))) => { + if let Ok(status_out) = plain::from_mut_bytes::(op.payload()) { + *status_out = status; + } + Response::ready_ok(pid, op) + } + Ready(Err(e)) => Response::ready_err(e.errno, op), + Pending => { + *state = PendingState::AwaitingStatusChange { + waiter, + target, + flags, + op, + }; + Pending } - Response::ready_ok(pid, op) } - Ready(Err(e)) => Response::ready_err(e.errno, op), - Pending => { - *state = PendingState::AwaitingStatusChange { - waiter, - target, - flags, - op, - }; - Pending - } - }, + } } } fn debug(&self) { From fdbc00b0fc3af1f484e1596b87c2c931298eb564 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 15:43:22 +0100 Subject: [PATCH 073/135] Pass status in waitpid. --- src/procmgr.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 047694ae07..bd75e7885d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -2,6 +2,7 @@ use core::cell::RefCell; use core::cmp::Ordering; use core::hash::BuildHasherDefault; use core::mem::size_of; +use core::num::NonZeroU8; use core::task::Poll; use core::task::Poll::*; @@ -301,8 +302,14 @@ impl Eq for WaitpidKey {} enum ProcessStatus { PossiblyRunnable, Stopped(usize), - Exiting { signal: Option, status: u8 }, - Exited { signal: Option, status: u8 }, + Exiting { + signal: Option, + status: u8, + }, + Exited { + signal: Option, + status: u8, + }, } #[derive(Debug)] struct Thread { @@ -332,8 +339,13 @@ struct ProcScheme<'a> { #[derive(Clone, Copy, Debug)] enum WaitpidStatus { Continued, - Stopped { signal: u8 }, - Terminated { signal: Option, status: u8 }, + Stopped { + signal: NonZeroU8, + }, + Terminated { + signal: Option, + status: u8, + }, } #[derive(Debug)] @@ -732,24 +744,25 @@ impl<'a> ProcScheme<'a> { } }; let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| { - let ret: i32 = todo!(); match status { WaitpidStatus::Continued => { // TODO: Handle None, i.e. restart everything until a match is found if flags.contains(WaitFlags::WCONTINUED) { - Ready((w_pid.0, ret)) + Ready((w_pid.0, 0xffff)) } else { Pending } } WaitpidStatus::Stopped { signal } => { if flags.contains(WaitFlags::WUNTRACED) { - Ready((w_pid.0, ret)) + Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) } else { Pending } } - WaitpidStatus::Terminated { signal, status } => Ready((w_pid.0, ret)), + WaitpidStatus::Terminated { signal, status } => { + Ready((w_pid.0, signal.map_or(0, NonZeroU8::get).into())) + } } }; From e5a97a68c98c2837715413fcc7baee2d1fe48f24 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 16:05:09 +0100 Subject: [PATCH 074/135] Use log crate. --- Cargo.lock | 1 + Cargo.toml | 1 + src/exec.rs | 18 +++++++++++++++++ src/procmgr.rs | 52 +++++++++++++++++--------------------------------- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0c534e2fc..9848284378 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,7 @@ version = "0.0.0" dependencies = [ "hashbrown", "linked_list_allocator", + "log", "plain", "redox-initfs", "redox-path", diff --git a/Cargo.toml b/Cargo.toml index 2f6308d655..4b2536203d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ hashbrown = { version = "0.15", default-features = false, features = [ "default-hasher", ] } linked_list_allocator = "0.10" +log = { version = "0.4", default-features = false } plain = "0.2" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = { version = "0.5.4", default-features = false } diff --git a/src/exec.rs b/src/exec.rs index 825e4003bf..a82b006ff3 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -6,6 +6,21 @@ use syscall::{Error, EINTR}; use redox_rt::proc::*; +struct Logger; + +impl log::Log for Logger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.level() <= log::max_level() + } + fn log(&self, record: &log::Record) { + let module = record.module_path().unwrap_or(""); + let level = record.level(); + let msg = record.args(); + let _ = syscall::write(1, alloc::format!("[{module} {level}] {msg}\n").as_bytes()); + } + fn flush(&self) {} +} + pub fn main() -> ! { let auth = FdGuard::new( syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC) @@ -15,6 +30,9 @@ pub fn main() -> ! { FdGuard::new(syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup")); let this_thr_fd = unsafe { redox_rt::initialize_freestanding(this_thr_fd) }; + log::set_max_level(log::LevelFilter::Trace); + let _ = log::set_logger(&Logger); + let envs = { let mut env = [0_u8; 4096]; diff --git a/src/procmgr.rs b/src/procmgr.rs index bd75e7885d..25639d7e8f 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -44,7 +44,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut scheme = ProcScheme::new(auth, &queue); - let _ = syscall::write(1, b"process manager started\n").unwrap(); + log::info!("process manager started"); let _ = syscall::write(write_fd, &[0]); let _ = syscall::close(write_fd); @@ -53,15 +53,14 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut new_awoken = VecDeque::new(); 'outer: loop { - let _ = syscall::write(1, alloc::format!("\n{awoken:#?}\n").as_bytes()); + log::trace!("{awoken:#?}"); while !awoken.is_empty() || !new_awoken.is_empty() { awoken.append(&mut new_awoken); for awoken in awoken.drain(..) { - //let _ = syscall::write(1, alloc::format!("\nALL STATES {states:#?}, AWOKEN {awoken:#?}\n").as_bytes()); + //log::trace!("ALL STATES {states:#?}, AWOKEN {awoken:#?}"); let Entry::Occupied(state) = states.entry(awoken) else { continue; }; - //let _ = syscall::write(1, alloc::format!("\nSTATE {state:#?}\n").as_bytes()); match scheme.work_on(state, &mut new_awoken) { Ready(resp) => { loop { @@ -111,10 +110,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } else if let Some(thread) = scheme.thread_lookup.get(&event.data) { let Some(thread_rc) = thread.upgrade() else { - let _ = syscall::write( - 1, - alloc::format!("\nDEAD THREAD EVENT FROM {}\n", event.data).as_bytes(), - ); + log::trace!("DEAD THREAD EVENT FROM {}", event.data,); continue; }; let thread = thread_rc.borrow(); @@ -122,16 +118,12 @@ pub fn run(write_fd: usize, auth: &FdGuard) { // TODO? continue; }; - let _ = syscall::write( - 1, - alloc::format!("\nTHREAD EVENT FROM {}, {}, \n", event.data, thread.pid.0) - .as_bytes(), - ); + log::trace!("THREAD EVENT FROM {}, {}", event.data, thread.pid.0); let mut buf = 0_usize.to_ne_bytes(); let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); let status = usize::from_ne_bytes(buf); - let _ = syscall::write(1, alloc::format!("\nSTATUS {status}\n",).as_bytes()); + log::trace!("STATUS {status}"); if status != ContextStatus::Dead as usize { // spurious event @@ -139,13 +131,10 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } scheme.thread_lookup.remove(&event.data); proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); - let _ = syscall::write( - 1, - alloc::format!("\nAWAITING {}\n", proc.awaiting_threads_term.len()).as_bytes(), - ); + log::trace!("AWAITING {}", proc.awaiting_threads_term.len(),); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { - let _ = syscall::write(1, b"\nTODO: UNKNOWN EVENT\n"); + log::debug!("TODO: UNKNOWN EVENT"); } } @@ -186,7 +175,7 @@ fn handle_scheme<'a>( awoken, ), _ => { - let _ = syscall::write(1, alloc::format!("\nUNKNOWN: {op:?}\n").as_bytes()); + log::trace!("UNKNOWN: {op:?}"); Ready(Response::new(Err(Error::new(ENOSYS)), op)) } } @@ -583,6 +572,7 @@ impl<'a> ProcScheme<'a> { match self.handles[old_id] { Handle::Proc(pid) => match buf { b"fork" => { + log::trace!("Forking {pid:?}"); let child_pid = self.fork(pid)?; Ok(OpenResult::ThisScheme { number: self.handles.insert(Handle::Proc(child_pid)), @@ -702,7 +692,7 @@ impl<'a> ProcScheme<'a> { } } - let _ = syscall::write(1, b"\nEXIT PENDING\n"); + log::trace!("EXIT PENDING"); //self.debug(); // TODO: check? process.awaiting_threads_term.push(*state.key()); @@ -731,7 +721,7 @@ impl<'a> ProcScheme<'a> { } let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; - let _ = syscall::write(1, b"\nWAITPID\n"); + log::trace!("WAITPID"); let recv_nonblock = |waitpid: &mut BTreeMap, key: &WaitpidKey| @@ -940,7 +930,7 @@ impl<'a> ProcScheme<'a> { return Response::ready_err(ESRCH, tag); }; if proc.threads.is_empty() { - let _ = syscall::write(1, b"\nWORKING ON AWAIT TERM\n"); + log::trace!("WORKING ON AWAIT TERM"); let (signal, status) = match proc.status { ProcessStatus::Exiting { signal, status } => (signal, status), ProcessStatus::Exited { .. } => return Response::ready_ok(0, tag), @@ -960,13 +950,13 @@ impl<'a> ProcScheme<'a> { }, (current_pid, WaitpidStatus::Terminated { signal, status }), ); - //let _ = syscall::write(1, alloc::format!("\nAWAKING WAITPID {:?}\n", parent.waitpid_waiting).as_bytes()); + //log::trace!("AWAKING WAITPID {:?}", parent.waitpid_waiting); // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } Ready(Response::new(Ok(0), tag)) } else { - let _ = syscall::write(1, b"\nWAITING AGAIN\n"); + log::trace!("WAITING AGAIN"); proc.awaiting_threads_term.push(req_id); *state = PendingState::AwaitingThreadsTermination(current_pid, tag); Pending @@ -978,7 +968,7 @@ impl<'a> ProcScheme<'a> { flags, mut op, } => { - let _ = syscall::write(1, b"\nWORKING ON AWAIT STS CHANGE\n"); + log::trace!("WORKING ON AWAIT STS CHANGE"); match self.on_waitpid(waiter, target, flags, req_id) { Ready(Ok((pid, status))) => { @@ -1002,13 +992,7 @@ impl<'a> ProcScheme<'a> { } } fn debug(&self) { - let _ = syscall::write( - 1, - alloc::format!("PROCESSES\n\n{:#?}\n\n", self.processes).as_bytes(), - ); - let _ = syscall::write( - 1, - alloc::format!("HANDLES\n\n{:#?}\n\n", self.handles).as_bytes(), - ); + log::trace!("PROCESSES\n{:#?}", self.processes,); + log::trace!("HANDLES\n{:#?}", self.handles,); } } From 80ed3c47d1e71dc5e9e29a302a7ba86fbb189452 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 16:13:47 +0100 Subject: [PATCH 075/135] Use inner loop to handle multiple reqs per event. --- src/exec.rs | 8 ++++++-- src/procmgr.rs | 51 +++++++++++++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index a82b006ff3..bbb012eb2f 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -13,10 +13,14 @@ impl log::Log for Logger { metadata.level() <= log::max_level() } fn log(&self, record: &log::Record) { - let module = record.module_path().unwrap_or(""); + let file = record.file().unwrap_or(""); + let line = record.line().unwrap_or(0); let level = record.level(); let msg = record.args(); - let _ = syscall::write(1, alloc::format!("[{module} {level}] {msg}\n").as_bytes()); + let _ = syscall::write( + 1, + alloc::format!("[{file}:{line} {level}] {msg}\n").as_bytes(), + ); } fn flush(&self) {} } diff --git a/src/procmgr.rs b/src/procmgr.rs index 25639d7e8f..e4073d4a38 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -53,7 +53,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut new_awoken = VecDeque::new(); 'outer: loop { - log::trace!("{awoken:#?}"); + log::trace!("AWOKEN {awoken:#?}"); while !awoken.is_empty() || !new_awoken.is_empty() { awoken.append(&mut new_awoken); for awoken in awoken.drain(..) { @@ -82,29 +82,33 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let event = queue.next_event().expect("failed to get next event"); if event.data == socket_ident { - let req = loop { - match socket.next_request(SignalBehavior::Interrupt) { - Ok(None) => break 'outer, - Ok(Some(req)) => break req, - Err(e) if e.errno == EINTR => continue, - // spurious event - Err(e) if e.errno == EWOULDBLOCK || e.errno == EAGAIN => continue 'outer, - Err(other) => { - panic!("bootstrap: failed to read scheme request from kernel: {other}") + 'reqs: loop { + let req = loop { + match socket.next_request(SignalBehavior::Interrupt) { + Ok(None) => break 'outer, + Ok(Some(req)) => break req, + Err(e) if e.errno == EINTR => continue, + // spurious event + Err(e) if e.errno == EWOULDBLOCK || e.errno == EAGAIN => break 'reqs, + Err(other) => { + panic!("bootstrap: failed to read scheme request from kernel: {other}") + } } - } - }; - let Ready(resp) = handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) - else { - continue 'outer; - }; - loop { - match socket.write_response(resp, SignalBehavior::Interrupt) { - Ok(false) => break 'outer, - Ok(_) => break, - Err(err) if err.errno == EINTR => continue, - Err(err) => { - panic!("bootstrap: failed to write scheme response to kernel: {err}") + }; + log::trace!("REQ{req:#?}"); + let Ready(resp) = + handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) + else { + continue 'reqs; + }; + loop { + match socket.write_response(resp, SignalBehavior::Interrupt) { + Ok(false) => break 'outer, + Ok(_) => break, + Err(err) if err.errno == EINTR => continue, + Err(err) => { + panic!("bootstrap: failed to write scheme response to kernel: {err}") + } } } } @@ -569,6 +573,7 @@ impl<'a> ProcScheme<'a> { } } fn on_dup(&mut self, old_id: usize, buf: &[u8]) -> Result { + log::trace!("Dup request"); match self.handles[old_id] { Handle::Proc(pid) => match buf { b"fork" => { From 0a1e1b2f9c1ee2298c287a268c99885f4d4f4059 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 23:15:43 +0100 Subject: [PATCH 076/135] Implement new-thread. --- src/procmgr.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index e4073d4a38..082ee97ab7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -523,19 +523,40 @@ impl<'a> ProcScheme<'a> { Ok(child_pid) } fn new_thread(&mut self, pid: ProcessId) -> Result { + // TODO: deduplicate code with fork let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; - let fd: FdGuard = todo!(); - let status_hndl = todo!(); - let ident = *fd; + let ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); + + let attr_fd = FdGuard::new(syscall::dup( + *ctxt_fd, + alloc::format!("attrs-{}", **self.auth).as_bytes(), + )?); + let _ = syscall::write( + *attr_fd, + &ProcSchemeAttrs { + pid: pid.0 as u32, + euid: proc.euid, + egid: proc.egid, + ens: proc.ens, + }, + )?; + + let status_hndl = FdGuard::new(syscall::dup(*ctxt_fd, b"status")?); + + self.queue + .subscribe(*ctxt_fd, *status_hndl, EventFlags::EVENT_READ) + .expect("TODO"); + + let ident = *ctxt_fd; let thread = Rc::new(RefCell::new(Thread { - fd, + fd: FdGuard::new(syscall::dup(*ctxt_fd, &[])?), status_hndl, pid, })); let thread_weak = Rc::downgrade(&thread); proc.threads.push(thread); self.thread_lookup.insert(ident, thread_weak); - Ok(fd) + Ok(ctxt_fd) } fn on_open(&mut self, path: &str, flags: usize) -> Result { if path == "init" { From 443c49f6c5a40cdd0a6def14b356122cd0ec3777 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 00:53:13 +0100 Subject: [PATCH 077/135] Implement most of getpgid and setpgid. --- src/procmgr.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 082ee97ab7..152855f1bf 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -2,7 +2,7 @@ use core::cell::RefCell; use core::cmp::Ordering; use core::hash::BuildHasherDefault; use core::mem::size_of; -use core::num::NonZeroU8; +use core::num::{NonZeroU8, NonZeroUsize}; use core::task::Poll; use core::task::Poll::*; @@ -682,10 +682,83 @@ impl<'a> ProcScheme<'a> { }); self.work_on(state, awoken) } + ProcCall::SetResugid => { + log::error!("SETRESGUID STUB"); + Ready(Response::ok(0, op)) + } + ProcCall::Setpgid => { + let target_pid = NonZeroUsize::new(metadata[1] as usize) + .map_or(fd_pid, |n| ProcessId(n.get())); + + let new_pgid = NonZeroUsize::new(metadata[2] as usize) + .map_or(target_pid, |n| ProcessId(n.get())); + if new_pgid.0 == usize::wrapping_neg(1) { + Ready(Response::new( + self.on_getpgid(fd_pid, target_pid).map(|ProcessId(p)| p), + op, + )) + } else { + Ready(Response::new( + self.on_setpgid(fd_pid, target_pid, new_pgid).map(|()| 0), + op, + )) + } + } + ProcCall::Getsid => { + log::error!("GETSID STUB"); + Ready(Response::ok(0, op)) + } + ProcCall::Setsid => { + log::error!("SETSID STUB"); + Ready(Response::ok(0, op)) + } + ProcCall::SetResugid => Ready(Response::new( + self.on_setresugid(fd_pid, payload).map(|()| 0), + op, + )), } } } } + pub fn on_getpgid( + &mut self, + caller_pid: ProcessId, + target_pid: ProcessId, + ) -> Result { + let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + let target_proc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; + + // Although not required, POSIX allows the impl to forbid getting the pgid of processes + // outside of the caller's session. + if caller_proc.sid != target_proc.sid && caller_proc.euid != 0 { + return Err(Error::new(EPERM)); + } + + Ok(target_proc.pgid) + } + pub fn on_setpgid( + &mut self, + caller_pid: ProcessId, + target_pid: ProcessId, + new_pgid: ProcessId, + ) -> Result<()> { + let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + + let proc = self + .processes + .get_mut(&target_pid) + .ok_or(Error::new(ESRCH))?; + + // Session leaders cannot have their pgid changed. + if proc.sid == target_pid { + return Err(Error::new(EPERM)); + } + + // TODO: other security checks + + proc.pgid = new_pgid; + Ok(()) + } pub fn on_exit_start( &mut self, pid: ProcessId, @@ -853,6 +926,19 @@ impl<'a> ProcScheme<'a> { } } } + pub fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { + log::info!("ON_SETRESUGID {pid:?} {raw_buf:?}"); + let ids = { + let raw_ids: [u32; 6] = plain::slice_from_bytes::(raw_buf) + .unwrap() + .try_into() + .map_err(|_| Error::new(EINVAL))?; + raw_ids.map(|i| if i == u32::MAX { None } else { Some(i) }) + }; + let proc = self.processes.get_mut(&pid).ok_or(Error::new(ESRCH))?; + log::warn!("TODO: on_setresugid({pid:?}): {ids:?}"); + Ok(()) + } fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { struct Iter<'a> { cur: Option, From fd3a9f9a947366d265cd381b3f43fa763641e0da Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 01:13:38 +0100 Subject: [PATCH 078/135] Add stub for kill and sigq. --- src/exec.rs | 2 +- src/procmgr.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/exec.rs b/src/exec.rs index bbb012eb2f..12e720e140 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -34,7 +34,7 @@ pub fn main() -> ! { FdGuard::new(syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup")); let this_thr_fd = unsafe { redox_rt::initialize_freestanding(this_thr_fd) }; - log::set_max_level(log::LevelFilter::Trace); + log::set_max_level(log::LevelFilter::Debug); let _ = log::set_logger(&Logger); let envs = { diff --git a/src/procmgr.rs b/src/procmgr.rs index 152855f1bf..84142fa34d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -716,6 +716,14 @@ impl<'a> ProcScheme<'a> { self.on_setresugid(fd_pid, payload).map(|()| 0), op, )), + ProcCall::Kill => { + log::error!("KILL STUB"); + Ready(Response::ok(0, op)) + } + ProcCall::Sigq => { + log::error!("SIGQ STUB"); + Ready(Response::ok(0, op)) + } } } } From 19cb1c05129c740150b90d9b44ae668180bc4b23 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 13:09:40 +0200 Subject: [PATCH 079/135] WIP: start implementing kill. --- src/lib.rs | 2 +- src/procmgr.rs | 399 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 379 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 515955489b..4b76efcf38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] #![allow(internal_features)] -#![feature(asm_const, core_intrinsics, str_from_raw_parts, iter_intersperse)] +#![feature(core_intrinsics, let_chains, iter_intersperse, str_from_raw_parts)] #[cfg(target_arch = "aarch64")] #[path = "aarch64.rs"] diff --git a/src/procmgr.rs b/src/procmgr.rs index 84142fa34d..a5f8d83cf1 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1,8 +1,10 @@ use core::cell::RefCell; -use core::cmp::Ordering; +use core::cmp; use core::hash::BuildHasherDefault; use core::mem::size_of; use core::num::{NonZeroU8, NonZeroUsize}; +use core::ptr::NonNull; +use core::sync::atomic::Ordering; use core::task::Poll; use core::task::Poll::*; @@ -16,7 +18,7 @@ use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcMeta, WaitFlags}; +use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, WaitFlags}; use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, @@ -25,9 +27,10 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - ContextStatus, Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, - EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, - EWOULDBLOCK, O_CLOEXEC, O_CREAT, + sig_bit, ContextStatus, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, + Result, RtSigInfo, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, + EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, + PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -211,6 +214,39 @@ impl IntoTag for PendingState { } } +#[derive(Debug)] +pub struct Page { + ptr: NonNull, +} +impl Page { + pub fn map(fd: &FdGuard) -> Result { + Ok(Self { + ptr: NonNull::new(unsafe { + syscall::fmap( + **fd, + &syscall::Map { + offset: 0, + size: PAGE_SIZE, + flags: MapFlags::PROT_READ, + address: 0, + }, + )? as *mut T + }) + .unwrap(), + }) + } + pub fn get(&self) -> &T { + unsafe { self.ptr.as_ref() } + } +} +impl Drop for Page { + fn drop(&mut self) { + unsafe { + let _ = syscall::funmap(self.ptr.as_ptr() as usize, PAGE_SIZE); + } + } +} + #[derive(Debug)] struct Process { threads: Vec>>, @@ -231,6 +267,8 @@ struct Process { waitpid: BTreeMap, waitpid_waiting: VecDeque, + + pctl: Option>, } #[derive(Copy, Clone, Debug)] pub struct WaitpidKey { @@ -240,7 +278,7 @@ pub struct WaitpidKey { // TODO: Is this valid? (transitive?) impl Ord for WaitpidKey { - fn cmp(&self, other: &WaitpidKey) -> Ordering { + fn cmp(&self, other: &WaitpidKey) -> cmp::Ordering { // If both have pid set, compare that if let Some(s_pid) = self.pid { if let Some(o_pid) = other.pid { @@ -257,36 +295,36 @@ impl Ord for WaitpidKey { // If either has pid set, it is greater if self.pid.is_some() { - return Ordering::Greater; + return cmp::Ordering::Greater; } if other.pid.is_some() { - return Ordering::Less; + return cmp::Ordering::Less; } // If either has pgid set, it is greater if self.pgid.is_some() { - return Ordering::Greater; + return cmp::Ordering::Greater; } if other.pgid.is_some() { - return Ordering::Less; + return cmp::Ordering::Less; } // If all pid and pgid are None, they are equal - Ordering::Equal + cmp::Ordering::Equal } } impl PartialOrd for WaitpidKey { - fn partial_cmp(&self, other: &WaitpidKey) -> Option { + fn partial_cmp(&self, other: &WaitpidKey) -> Option { Some(self.cmp(other)) } } impl PartialEq for WaitpidKey { fn eq(&self, other: &WaitpidKey) -> bool { - self.cmp(other) == Ordering::Equal + self.cmp(other) == cmp::Ordering::Equal } } @@ -309,7 +347,7 @@ struct Thread { fd: FdGuard, status_hndl: FdGuard, pid: ProcessId, - // sig_ctrl: MmapGuard<...> + sig_ctrl: Option>, } #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] struct ProcessId(usize); @@ -423,6 +461,7 @@ impl<'a> ProcScheme<'a> { fd, status_hndl, pid: INIT_PID, + sig_ctrl: None, })); let thread_weak = Rc::downgrade(&thread); self.processes.insert( @@ -443,6 +482,8 @@ impl<'a> ProcScheme<'a> { awaiting_threads_term: Vec::new(), waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), + + pctl: None, }, ); self.sessions.insert(INIT_PID); @@ -495,6 +536,7 @@ impl<'a> ProcScheme<'a> { fd: new_ctxt_fd, status_hndl: status_fd, pid: child_pid, + sig_ctrl: None, // TODO })); let thread_weak = Rc::downgrade(&thread); @@ -517,6 +559,8 @@ impl<'a> ProcScheme<'a> { waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), + + pctl: None, // TODO }, ); self.thread_lookup.insert(thread_ident, thread_weak); @@ -552,6 +596,7 @@ impl<'a> ProcScheme<'a> { fd: FdGuard::new(syscall::dup(*ctxt_fd, &[])?), status_hndl, pid, + sig_ctrl: None, })); let thread_weak = Rc::downgrade(&thread); proc.threads.push(thread); @@ -716,13 +761,33 @@ impl<'a> ProcScheme<'a> { self.on_setresugid(fd_pid, payload).map(|()| 0), op, )), - ProcCall::Kill => { - log::error!("KILL STUB"); - Ready(Response::ok(0, op)) - } - ProcCall::Sigq => { - log::error!("SIGQ STUB"); - Ready(Response::ok(0, op)) + ProcCall::Kill | ProcCall::Sigq => { + let (payload, metadata) = op.payload_and_metadata(); + let target = ProcKillTarget::from_raw(metadata[1] as usize); + let Some(signal) = u8::try_from(metadata[2]).ok().filter(|s| *s <= 64) + else { + return Response::ready_err(EINVAL, op); + }; + let mut killed_self = false; + + let mode = match verb { + ProcCall::Kill => KillMode::Idempotent, + ProcCall::Sigq => KillMode::Queued({ + let mut buf = RtSigInfo::default(); + if payload.len() != buf.len() { + return Response::ready_err(EINVAL, op); + } + buf.copy_from_slice(payload); + buf + }), + _ => unreachable!(), + }; + + let is_sigchld_to_parent = false; + Ready(Response::new( + self.on_kill(fd_pid, target, signal, mode).map(|()| 0), + op, + )) } } } @@ -1115,4 +1180,296 @@ impl<'a> ProcScheme<'a> { log::trace!("PROCESSES\n{:#?}", self.processes,); log::trace!("HANDLES\n{:#?}", self.handles,); } + pub fn on_kill( + &mut self, + caller_pid: ProcessId, + target: ProcKillTarget, + signal: u8, + mode: KillMode, + ) -> Result<()> { + let mut num_succeeded = 0; + + match target { + ProcKillTarget::SingleProc(proc) => (), + ProcKillTarget::ProcGroup(grp) => { + for (pid, proc) in self.processes.iter().filter(|(_, p)| p.pgid == grp) { + self.on_send_sig(caller_pid); + } + } + ProcKillTarget::All => { + for (pid, proc) in self.processes.iter() { + self.on_send_sig(caller_pid); + } + } + } + + Ok(()) + } + pub fn on_send_sig( + &mut self, + caller_pid: ProcessId, + target: KillTarget, + signal: u8, + killed_self: &mut bool, + mode: KillMode, + is_sigchld_to_parent: bool, + ) -> Result<()> { + debug_assert!(sig <= 64); + + let sig = usize::from(signal); + let sig_group = (sig - 1) / 32; + let sig_idx = sig - 1; + + /*let (context_lock, process_lock) = match target { + KillTarget::Thread(ref c) => (Arc::clone(&c), Arc::clone(&c.read().process)), + KillTarget::Process(ref p) => ( + p.read() + .threads + .iter() + .filter_map(|t| t.upgrade()) + .next() + .ok_or(Error::new(ESRCH))?, + Arc::clone(p), + ), + };*/ + let proc_info = process_lock.read().info; + + enum SendResult { + Succeeded, + SucceededSigchld { orig_signal: usize }, + SucceededSigcont { ppid: ProcessId, pgid: ProcessId }, + FullQ, + Invalid, + } + + let result = (|| { + let is_self = context::is_current(&context_lock); + + // If sig = 0, test that process exists and can be signalled, but don't send any + // signal. + if sig == 0 { + return SendResult::Succeeded; + } + + let mut process_guard = process_lock.write(); + + if sig == SIGCONT + && let ProcessStatus::Stopped(_sig) = process_guard.status + { + // Convert stopped processes to blocked if sending SIGCONT, regardless of whether + // SIGCONT is blocked or ignored. It can however be controlled whether the process + // will additionally ignore, defer, or handle that signal. + process_guard.status = ProcessStatus::PossiblyRunnable; + drop(process_guard); + + let mut context_guard = context_lock.write(); + if let Some((_, pctl, _)) = context_guard.sigcontrol() { + if !pctl.signal_will_ign(SIGCONT, false) { + pctl.pending.fetch_or(sig_bit(SIGCONT), Ordering::Relaxed); + } + drop(context_guard); + + // TODO: which threads should become Runnable? + for thread in process_lock + .read() + .threads + .iter() + .filter_map(|t| t.upgrade()) + { + let mut thread = thread.write(); + if let Some((tctl, _, _)) = thread.sigcontrol() { + tctl.word[0].fetch_and( + !(sig_bit(SIGSTOP) + | sig_bit(SIGTTIN) + | sig_bit(SIGTTOU) + | sig_bit(SIGTSTP)), + Ordering::Relaxed, + ); + } + thread.unblock(); + } + } + // POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs. + return SendResult::SucceededSigcont {}; + } + drop(process_guard); + let mut context_guard = context_lock.write(); + if sig == SIGSTOP + || (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP) + && context_guard + .sigcontrol() + .map_or(false, |(_, proc, _)| proc.signal_will_stop(sig))) + { + todo!("tell kernel to stop process"); + /* + context_guard.status = context::Status::Blocked; + drop(context_guard); + process_lock.write().status = ProcessStatus::Stopped(sig); + */ + + // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise. + let mut already = false; + for thread in process_lock + .read() + .threads + .iter() + .filter_map(|t| t.upgrade()) + { + let mut thread = thread.write(); + if let Some((tctl, pctl, _)) = thread.sigcontrol() { + if !already { + pctl.pending.fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); + already = true; + } + tctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); + } + } + + return SendResult::SucceededSigchld { orig_signal: sig }; + } + if sig == SIGKILL { + todo!("tell kernel to kill context"); + /* + context_guard.being_sigkilled = true; + context_guard.unblock(); + drop(context_guard); + */ + *killed_self |= is_self; + + // exit() will signal the parent, rather than immediately in kill() + return SendResult::Succeeded; + } + if let Some((tctl, pctl, sigst)) = context_guard.sigcontrol() + && !pctl.signal_will_ign(sig, is_sigchld_to_parent) + { + match target { + KillTarget::Thread => { + tctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); + + let _was_new = + tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Release); + if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 + { + context_guard.unblock(); + *killed_self |= is_self; + } + } + KillTarget::Proc(proc) => { + match mode { + KillMode::Queued(arg) => { + if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { + return SendResult::Invalid; + } + let rtidx = sig_idx - 32; + //log::info!("QUEUEING {arg:?} RTIDX {rtidx}"); + if rtidx >= sigst.rtqs.len() { + sigst.rtqs.resize_with(rtidx + 1, VecDeque::new); + } + let rtq = sigst.rtqs.get_mut(rtidx).unwrap(); + + // TODO: configurable limit? + if rtq.len() > 32 { + return SendResult::FullQ; + } + + rtq.push_back(arg); + } + KillMode::Idempotent => { + if pctl.pending.load(Ordering::Acquire) & sig_bit(sig) != 0 { + // If already pending, do not send this signal. While possible that + // another thread is concurrently clearing pending, and that other + // spuriously awoken threads would benefit from actually receiving + // this signal, there is no requirement by POSIX for such signals + // not to be mergeable. So unless the signal handler is observed to + // happen-before this syscall, it can be ignored. The pending bits + // would certainly have been cleared, thus contradicting this + // already reached statement. + return SendResult::Succeeded; + } + + if sig_group != 0 { + return SendResult::Invalid; + } + pctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); + } + } + + pctl.pending.fetch_or(sig_bit(sig), Ordering::Release); + drop(context_guard); + + for thread in proc.read().threads.iter().filter_map(|t| t.upgrade()) { + let mut thread = thread.write(); + let Some((tctl, _, _)) = thread.sigcontrol() else { + continue; + }; + if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) + != 0 + { + thread.unblock(); + *killed_self |= is_self; + break; + } + } + } + } + SendResult::Succeeded + } else { + // Discard signals if sighandler is unset. This includes both special contexts such + // as bootstrap, and child processes or threads that have not yet been started. + // This is semantically equivalent to having all signals except SIGSTOP and SIGKILL + // blocked/ignored (SIGCONT can be ignored and masked, but will always continue + // stopped processes first). + SendResult::Succeeded + } + })(); + + match result { + SendResult::Succeeded => (), + SendResult::FullQ => return Err(Error::new(EAGAIN)), + SendResult::Invalid => return Err(Error::new(EINVAL)), + SendResult::SucceededSigchld { + ppid, + pgid, + orig_signal, + } => {} + SendResult::SucceededSigcont { ppid, pgid } => { + // POSIX XSI allows but does not require SIGCONT to send signals to the parent. + //send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?; + } + } + + Ok(()) + } } +#[derive(Clone, Copy)] +pub enum KillMode { + Idempotent, + Queued(RtSigInfo), +} +pub enum KillTarget { + Proc(ProcessId), + Thread(Rc>), +} +/* +pub fn sigdequeue(out: &mut [u8], sig_idx: u32) -> Result<()> { + let Some((_tctl, pctl, st)) = current.sigcontrol() else { + return Err(Error::new(ESRCH)); + }; + if sig_idx >= 32 { + return Err(Error::new(EINVAL)); + } + let q = st + .rtqs + .get_mut(sig_idx as usize) + .ok_or(Error::new(EAGAIN))?; + let Some(front) = q.pop_front() else { + return Err(Error::new(EAGAIN)); + }; + if q.is_empty() { + pctl.pending + .fetch_and(!(1 << (32 + sig_idx as usize)), Ordering::Relaxed); + } + out.copy_exactly(&front)?; + Ok(()) +} +*/ From a7df1a1734437df290f9e4bd90d6a15ea59e4650 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 13:55:27 +0200 Subject: [PATCH 080/135] progress. --- src/procmgr.rs | 83 +++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index a5f8d83cf1..d868f4b93c 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1189,24 +1189,47 @@ impl<'a> ProcScheme<'a> { ) -> Result<()> { let mut num_succeeded = 0; - match target { - ProcKillTarget::SingleProc(proc) => (), - ProcKillTarget::ProcGroup(grp) => { - for (pid, proc) in self.processes.iter().filter(|(_, p)| p.pgid == grp) { - self.on_send_sig(caller_pid); - } + let mut killed_self = false; // TODO + let is_sigchld_to_parent = false; // TODO + + let match_grp = match target { + ProcKillTarget::SingleProc(pid) => { + return self.on_send_sig( + caller_pid, + KillTarget::Proc(ProcessId(pid)), + signal, + &mut killed_self, + mode, + is_sigchld_to_parent, + ) } - ProcKillTarget::All => { - for (pid, proc) in self.processes.iter() { - self.on_send_sig(caller_pid); - } + ProcKillTarget::All => None, + ProcKillTarget::ProcGroup(grp) => Some(ProcessId(grp)), + }; + + for (pid, proc) in self.processes.iter() { + if match_grp.map_or(false, |g| proc.pgid != g) { + continue; + } + let res = self.on_send_sig( + caller_pid, + KillTarget::Proc(*pid), + signal, + &mut killed_self, + mode, + is_sigchld_to_parent, + ); + match res { + Ok(()) => (), + Err(err) if num_succeeded > 0 => break, + Err(err) => return Err(err), } } Ok(()) } pub fn on_send_sig( - &mut self, + &self, caller_pid: ProcessId, target: KillTarget, signal: u8, @@ -1214,36 +1237,36 @@ impl<'a> ProcScheme<'a> { mode: KillMode, is_sigchld_to_parent: bool, ) -> Result<()> { - debug_assert!(sig <= 64); - let sig = usize::from(signal); + debug_assert!(sig <= 64); let sig_group = (sig - 1) / 32; let sig_idx = sig - 1; - /*let (context_lock, process_lock) = match target { - KillTarget::Thread(ref c) => (Arc::clone(&c), Arc::clone(&c.read().process)), - KillTarget::Process(ref p) => ( - p.read() - .threads - .iter() - .filter_map(|t| t.upgrade()) - .next() - .ok_or(Error::new(ESRCH))?, - Arc::clone(p), - ), - };*/ - let proc_info = process_lock.read().info; + let target_pid = match target { + KillTarget::Proc(pid) => pid, + KillTarget::Thread(thread) => thread.borrow().pid, + }; + let target_proc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; enum SendResult { Succeeded, - SucceededSigchld { orig_signal: usize }, - SucceededSigcont { ppid: ProcessId, pgid: ProcessId }, + SucceededSigchld { + orig_signal: usize, + ppid: ProcessId, + pgid: ProcessId, + }, + SucceededSigcont { + ppid: ProcessId, + pgid: ProcessId, + }, FullQ, Invalid, } let result = (|| { - let is_self = context::is_current(&context_lock); + // FIXME + let is_self = false; + //let is_self = context::is_current(&context_lock); // If sig = 0, test that process exists and can be signalled, but don't send any // signal. @@ -1251,8 +1274,6 @@ impl<'a> ProcScheme<'a> { return SendResult::Succeeded; } - let mut process_guard = process_lock.write(); - if sig == SIGCONT && let ProcessStatus::Stopped(_sig) = process_guard.status { From fbbb27b0e58d616c885f46f2cb417ceec04f1f1d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 10:13:35 +0200 Subject: [PATCH 081/135] Tmp disable kill and wrap Process in Rc. --- src/procmgr.rs | 105 ++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index d868f4b93c..b3f4d68600 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -121,10 +121,11 @@ pub fn run(write_fd: usize, auth: &FdGuard) { continue; }; let thread = thread_rc.borrow(); - let Some(proc) = scheme.processes.get_mut(&thread.pid) else { + let Some(proc_rc) = scheme.processes.get(&thread.pid) else { // TODO? continue; }; + let mut proc = proc_rc.borrow_mut(); log::trace!("THREAD EVENT FROM {}, {}", event.data, thread.pid.0); let mut buf = 0_usize.to_ne_bytes(); let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); @@ -355,7 +356,7 @@ struct ProcessId(usize); const INIT_PID: ProcessId = ProcessId(1); struct ProcScheme<'a> { - processes: HashMap, + processes: HashMap>, DefaultHashBuilder>, sessions: HashSet, handles: Slab, @@ -466,7 +467,7 @@ impl<'a> ProcScheme<'a> { let thread_weak = Rc::downgrade(&thread); self.processes.insert( INIT_PID, - Process { + Rc::new(RefCell::new(Process { threads: vec![thread], ppid: INIT_PID, sid: INIT_PID, @@ -484,7 +485,7 @@ impl<'a> ProcScheme<'a> { waitpid_waiting: VecDeque::new(), pctl: None, - }, + })), ); self.sessions.insert(INIT_PID); @@ -499,6 +500,8 @@ impl<'a> ProcScheme<'a> { fn fork(&mut self, parent_pid: ProcessId) -> Result { let child_pid = self.new_id(); + let proc_guard = self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; + let Process { pgid, sid, @@ -509,7 +512,7 @@ impl<'a> ProcScheme<'a> { ens, rns, .. - } = *self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; + } = *proc_guard.borrow(); let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); let attr_fd = FdGuard::new(syscall::dup( @@ -542,7 +545,7 @@ impl<'a> ProcScheme<'a> { self.processes.insert( child_pid, - Process { + Rc::new(RefCell::new(Process { threads: vec![thread], ppid: parent_pid, pgid, @@ -561,14 +564,16 @@ impl<'a> ProcScheme<'a> { waitpid_waiting: VecDeque::new(), pctl: None, // TODO - }, + })), ); self.thread_lookup.insert(thread_ident, thread_weak); Ok(child_pid) } fn new_thread(&mut self, pid: ProcessId) -> Result { // TODO: deduplicate code with fork - let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let proc_rc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let mut proc = proc_rc.borrow_mut(); + let ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); let attr_fd = FdGuard::new(syscall::dup( @@ -618,7 +623,8 @@ impl<'a> ProcScheme<'a> { fn on_read(&mut self, id: usize, buf: &mut [u8]) -> Result { match self.handles[id] { Handle::Proc(pid) => { - let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let process = proc_rc.borrow(); let metadata = ProcMeta { pid: pid.0 as u32, pgid: process.pgid.0 as u32, @@ -658,7 +664,7 @@ impl<'a> ProcScheme<'a> { .ok() .and_then(|s| s.parse::().ok()) .ok_or(Error::new(EINVAL))?; - let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?.borrow(); let thread = process.threads.get(idx).ok_or(Error::new(ENOENT))?.borrow(); return Ok(OpenResult::OtherScheme { @@ -798,8 +804,16 @@ impl<'a> ProcScheme<'a> { caller_pid: ProcessId, target_pid: ProcessId, ) -> Result { - let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; - let target_proc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; + let caller_proc = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); + let target_proc = self + .processes + .get(&target_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); // Although not required, POSIX allows the impl to forbid getting the pgid of processes // outside of the caller's session. @@ -817,10 +831,8 @@ impl<'a> ProcScheme<'a> { ) -> Result<()> { let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; - let proc = self - .processes - .get_mut(&target_pid) - .ok_or(Error::new(ESRCH))?; + let proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; + let mut proc = proc_rc.borrow_mut(); // Session leaders cannot have their pgid changed. if proc.sid == target_pid { @@ -840,9 +852,12 @@ impl<'a> ProcScheme<'a> { awoken: &mut VecDeque, tag: Tag, ) -> Poll { - let Some(process) = self.processes.get_mut(&pid) else { + let Some(proc_rc) = self.processes.get(&pid) else { return Response::ready_err(EBADFD, tag); }; + let mut process_guard = proc_rc.borrow_mut(); + let process = &mut *process_guard; + match process.status { ProcessStatus::Stopped(_) | ProcessStatus::PossiblyRunnable => (), //ProcessStatus::Exiting => return Pending, @@ -869,6 +884,7 @@ impl<'a> ProcScheme<'a> { // TODO: check? process.awaiting_threads_term.push(*state.key()); } + drop(process_guard); self.work_on( state.insert_entry(PendingState::AwaitingThreadsTermination(pid, tag)), awoken, @@ -887,12 +903,15 @@ impl<'a> ProcScheme<'a> { ) { // Check for existence of child. // TODO: inefficient, keep refcount? - if !self.processes.values().any(|p| p.ppid == this_pid) { + if !self.processes.values().any(|p| p.borrow().ppid == this_pid) { return Ready(Err(Error::new(ECHILD))); } } - let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; + let proc_rc = self.processes.get(&this_pid).ok_or(Error::new(ESRCH))?; + let mut proc_guard = proc_rc.borrow_mut(); + let proc = &mut *proc_guard; + log::trace!("WAITPID"); let recv_nonblock = |waitpid: &mut BTreeMap, @@ -947,11 +966,9 @@ impl<'a> ProcScheme<'a> { if this_pid == pid { return Ready(Err(Error::new(EINVAL))); } - let [Some(proc), Some(target_proc)] = - self.processes.get_many_mut([&this_pid, &pid]) - else { - return Ready(Err(Error::new(ESRCH))); - }; + let target_proc_rc = self.processes.get(&pid).ok_or(Error::new(ESRCH))?; + let mut target_proc = target_proc_rc.borrow_mut(); + if target_proc.ppid != this_pid { return Ready(Err(Error::new(ECHILD))); } @@ -976,13 +993,14 @@ impl<'a> ProcScheme<'a> { } WaitpidTarget::ProcGroup(pgid) => { let this_pgid = proc.pgid; - if !self.processes.values().any(|p| p.pgid == this_pgid) { + if !self + .processes + .values() + .any(|p| p.borrow().pgid == this_pgid) + { return Ready(Err(Error::new(ECHILD))); } - // reborrow proc - let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; - let key = WaitpidKey { pid: None, pgid: Some(pgid), @@ -1015,15 +1033,16 @@ impl<'a> ProcScheme<'a> { fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { struct Iter<'a> { cur: Option, - procs: &'a HashMap, + procs: &'a HashMap>, DefaultHashBuilder>, } impl Iterator for Iter<'_> { type Item = ProcessId; fn next(&mut self) -> Option { let proc = self.procs.get(&self.cur?)?; - self.cur = Some(proc.ppid); - Some(proc.ppid) + let ppid = proc.borrow().ppid; + self.cur = Some(ppid); + Some(ppid) } } Iter { @@ -1043,7 +1062,9 @@ impl<'a> ProcScheme<'a> { todo!() } pub fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { - let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let mut process = proc_rc.borrow_mut(); + let setrns = if rns.is_none() { // Ignore RNS if -1 is passed false @@ -1111,9 +1132,12 @@ impl<'a> ProcScheme<'a> { PendingState::Placeholder => return Pending, // unreachable!(), // TODO PendingState::AwaitingThreadsTermination(current_pid, tag) => { - let Some(proc) = self.processes.get_mut(¤t_pid) else { + let Some(proc_rc) = self.processes.get(¤t_pid) else { return Response::ready_err(ESRCH, tag); }; + let mut proc_guard = proc_rc.borrow_mut(); + let proc = &mut *proc_guard; + if proc.threads.is_empty() { log::trace!("WORKING ON AWAIT TERM"); let (signal, status) = match proc.status { @@ -1126,7 +1150,8 @@ impl<'a> ProcScheme<'a> { proc.status = ProcessStatus::Exited { signal, status }; let (ppid, pgid) = (proc.ppid, proc.pgid); - if let Some(parent) = self.processes.get_mut(&ppid) { + if let Some(parent_rc) = self.processes.get(&ppid) { + let mut parent = parent_rc.borrow_mut(); // TODO: transfer children to parent, and all of self.waitpid parent.waitpid.insert( WaitpidKey { @@ -1207,8 +1232,8 @@ impl<'a> ProcScheme<'a> { ProcKillTarget::ProcGroup(grp) => Some(ProcessId(grp)), }; - for (pid, proc) in self.processes.iter() { - if match_grp.map_or(false, |g| proc.pgid != g) { + for (pid, proc_rc) in self.processes.iter() { + if match_grp.map_or(false, |g| proc_rc.borrow().pgid != g) { continue; } let res = self.on_send_sig( @@ -1263,6 +1288,7 @@ impl<'a> ProcScheme<'a> { Invalid, } + /* let result = (|| { // FIXME let is_self = false; @@ -1275,13 +1301,12 @@ impl<'a> ProcScheme<'a> { } if sig == SIGCONT - && let ProcessStatus::Stopped(_sig) = process_guard.status + && let ProcessStatus::Stopped(_sig) = target_proc.status { // Convert stopped processes to blocked if sending SIGCONT, regardless of whether // SIGCONT is blocked or ignored. It can however be controlled whether the process // will additionally ignore, defer, or handle that signal. - process_guard.status = ProcessStatus::PossiblyRunnable; - drop(process_guard); + target_proc.status = ProcessStatus::PossiblyRunnable; let mut context_guard = context_lock.write(); if let Some((_, pctl, _)) = context_guard.sigcontrol() { @@ -1457,7 +1482,7 @@ impl<'a> ProcScheme<'a> { // POSIX XSI allows but does not require SIGCONT to send signals to the parent. //send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?; } - } + }*/ Ok(()) } From a0fc7febfc051b5b2bae4d19e2feb824223f3050 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 10:32:15 +0200 Subject: [PATCH 082/135] Make send_signal code compile. --- src/procmgr.rs | 164 +++++++++++++++++++++++++++---------------------- 1 file changed, 90 insertions(+), 74 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b3f4d68600..e4a0f48c73 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -3,6 +3,7 @@ use core::cmp; use core::hash::BuildHasherDefault; use core::mem::size_of; use core::num::{NonZeroU8, NonZeroUsize}; +use core::ops::Deref; use core::ptr::NonNull; use core::sync::atomic::Ordering; use core::task::Poll; @@ -28,9 +29,9 @@ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, - Result, RtSigInfo, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, - EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, - PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + Result, RtSigInfo, SenderInfo, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, + EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, + O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -236,7 +237,11 @@ impl Page { .unwrap(), }) } - pub fn get(&self) -> &T { +} +impl Deref for Page { + type Target = T; + + fn deref(&self) -> &T { unsafe { self.ptr.as_ref() } } } @@ -269,7 +274,8 @@ struct Process { waitpid: BTreeMap, waitpid_waiting: VecDeque, - pctl: Option>, + sig_pctl: Option>, + rtqs: Vec>, } #[derive(Copy, Clone, Debug)] pub struct WaitpidKey { @@ -484,7 +490,8 @@ impl<'a> ProcScheme<'a> { waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), - pctl: None, + sig_pctl: None, + rtqs: Vec::new(), })), ); self.sessions.insert(INIT_PID); @@ -563,7 +570,8 @@ impl<'a> ProcScheme<'a> { waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), - pctl: None, // TODO + sig_pctl: None, // TODO + rtqs: Vec::new(), })), ); self.thread_lookup.insert(thread_ident, thread_weak); @@ -1269,9 +1277,14 @@ impl<'a> ProcScheme<'a> { let target_pid = match target { KillTarget::Proc(pid) => pid, - KillTarget::Thread(thread) => thread.borrow().pid, + KillTarget::Thread(ref thread) => thread.borrow().pid, + }; + let target_proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; + + let sender = SenderInfo { + pid: caller_pid.0 as u32, + ruid: 0, // TODO }; - let target_proc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; enum SendResult { Succeeded, @@ -1288,7 +1301,6 @@ impl<'a> ProcScheme<'a> { Invalid, } - /* let result = (|| { // FIXME let is_self = false; @@ -1299,6 +1311,12 @@ impl<'a> ProcScheme<'a> { if sig == 0 { return SendResult::Succeeded; } + let mut target_proc = target_proc_rc.borrow_mut(); + let target_proc = &mut *target_proc; + + let Some(ref sig_pctl) = target_proc.sig_pctl else { + return SendResult::Invalid; + }; if sig == SIGCONT && let ProcessStatus::Stopped(_sig) = target_proc.status @@ -1308,43 +1326,39 @@ impl<'a> ProcScheme<'a> { // will additionally ignore, defer, or handle that signal. target_proc.status = ProcessStatus::PossiblyRunnable; - let mut context_guard = context_lock.write(); - if let Some((_, pctl, _)) = context_guard.sigcontrol() { - if !pctl.signal_will_ign(SIGCONT, false) { - pctl.pending.fetch_or(sig_bit(SIGCONT), Ordering::Relaxed); - } - drop(context_guard); + if !sig_pctl.signal_will_ign(SIGCONT, false) { + sig_pctl + .pending + .fetch_or(sig_bit(SIGCONT), Ordering::Relaxed); + } - // TODO: which threads should become Runnable? - for thread in process_lock - .read() - .threads - .iter() - .filter_map(|t| t.upgrade()) - { - let mut thread = thread.write(); - if let Some((tctl, _, _)) = thread.sigcontrol() { - tctl.word[0].fetch_and( - !(sig_bit(SIGSTOP) - | sig_bit(SIGTTIN) - | sig_bit(SIGTTOU) - | sig_bit(SIGTSTP)), - Ordering::Relaxed, - ); - } - thread.unblock(); + // TODO: which threads should become Runnable? + for thread_rc in target_proc.threads.iter() { + let mut thread = thread_rc.borrow_mut(); + if let Some(ref tctl) = thread.sig_ctrl { + tctl.word[0].fetch_and( + !(sig_bit(SIGSTOP) + | sig_bit(SIGTTIN) + | sig_bit(SIGTTOU) + | sig_bit(SIGTSTP)), + Ordering::Relaxed, + ); } + // TODO + //thread.unblock(); } // POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs. - return SendResult::SucceededSigcont {}; + return SendResult::SucceededSigcont { + ppid: target_proc.ppid, + pgid: target_proc.pgid, + }; } - drop(process_guard); - let mut context_guard = context_lock.write(); if sig == SIGSTOP || (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP) - && context_guard - .sigcontrol() - .map_or(false, |(_, proc, _)| proc.signal_will_stop(sig))) + && target_proc + .sig_pctl + .as_ref() + .map_or(false, |proc| proc.signal_will_stop(sig))) { todo!("tell kernel to stop process"); /* @@ -1353,25 +1367,23 @@ impl<'a> ProcScheme<'a> { process_lock.write().status = ProcessStatus::Stopped(sig); */ - // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise. - let mut already = false; - for thread in process_lock - .read() - .threads - .iter() - .filter_map(|t| t.upgrade()) - { - let mut thread = thread.write(); - if let Some((tctl, pctl, _)) = thread.sigcontrol() { - if !already { - pctl.pending.fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); - already = true; - } + // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise? + sig_pctl + .pending + .fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); + + for thread in target_proc.threads.iter() { + let thread = thread.borrow(); + if let Some(ref tctl) = thread.sig_ctrl { tctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); } } - return SendResult::SucceededSigchld { orig_signal: sig }; + return SendResult::SucceededSigchld { + orig_signal: sig, + ppid: target_proc.ppid, + pgid: target_proc.pgid, + }; } if sig == SIGKILL { todo!("tell kernel to kill context"); @@ -1385,18 +1397,21 @@ impl<'a> ProcScheme<'a> { // exit() will signal the parent, rather than immediately in kill() return SendResult::Succeeded; } - if let Some((tctl, pctl, sigst)) = context_guard.sigcontrol() - && !pctl.signal_will_ign(sig, is_sigchld_to_parent) - { + if !sig_pctl.signal_will_ign(sig, is_sigchld_to_parent) { match target { - KillTarget::Thread => { + KillTarget::Thread(ref thread_rc) => { + let thread = thread_rc.borrow(); + let Some(ref tctl) = thread.sig_ctrl else { + return SendResult::Invalid; + }; + tctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); let _was_new = tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Release); if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - context_guard.unblock(); + //context_guard.unblock(); *killed_self |= is_self; } } @@ -1408,10 +1423,10 @@ impl<'a> ProcScheme<'a> { } let rtidx = sig_idx - 32; //log::info!("QUEUEING {arg:?} RTIDX {rtidx}"); - if rtidx >= sigst.rtqs.len() { - sigst.rtqs.resize_with(rtidx + 1, VecDeque::new); + if rtidx >= target_proc.rtqs.len() { + target_proc.rtqs.resize_with(rtidx + 1, VecDeque::new); } - let rtq = sigst.rtqs.get_mut(rtidx).unwrap(); + let rtq = target_proc.rtqs.get_mut(rtidx).unwrap(); // TODO: configurable limit? if rtq.len() > 32 { @@ -1421,7 +1436,7 @@ impl<'a> ProcScheme<'a> { rtq.push_back(arg); } KillMode::Idempotent => { - if pctl.pending.load(Ordering::Acquire) & sig_bit(sig) != 0 { + if sig_pctl.pending.load(Ordering::Acquire) & sig_bit(sig) != 0 { // If already pending, do not send this signal. While possible that // another thread is concurrently clearing pending, and that other // spuriously awoken threads would benefit from actually receiving @@ -1436,22 +1451,23 @@ impl<'a> ProcScheme<'a> { if sig_group != 0 { return SendResult::Invalid; } - pctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); + sig_pctl.sender_infos[sig_idx] + .store(sender.raw(), Ordering::Relaxed); } } - pctl.pending.fetch_or(sig_bit(sig), Ordering::Release); - drop(context_guard); + sig_pctl.pending.fetch_or(sig_bit(sig), Ordering::Release); - for thread in proc.read().threads.iter().filter_map(|t| t.upgrade()) { - let mut thread = thread.write(); - let Some((tctl, _, _)) = thread.sigcontrol() else { + for thread in target_proc.threads.iter() { + let thread = thread.borrow(); + let Some(ref tctl) = thread.sig_ctrl else { continue; }; if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - thread.unblock(); + // TODO + //thread.unblock(); *killed_self |= is_self; break; } @@ -1482,7 +1498,7 @@ impl<'a> ProcScheme<'a> { // POSIX XSI allows but does not require SIGCONT to send signals to the parent. //send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?; } - }*/ + } Ok(()) } @@ -1498,7 +1514,7 @@ pub enum KillTarget { } /* pub fn sigdequeue(out: &mut [u8], sig_idx: u32) -> Result<()> { - let Some((_tctl, pctl, st)) = current.sigcontrol() else { + let Some((_tctl, sig_pctl, st)) = current.sigcontrol() else { return Err(Error::new(ESRCH)); }; if sig_idx >= 32 { @@ -1512,7 +1528,7 @@ pub fn sigdequeue(out: &mut [u8], sig_idx: u32) -> Result<()> { return Err(Error::new(EAGAIN)); }; if q.is_empty() { - pctl.pending + sig_pctl.pending .fetch_and(!(1 << (32 + sig_idx as usize)), Ordering::Relaxed); } out.copy_exactly(&front)?; From ae63c54988fec808f6ba1f38471eaa8027731494 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 10:42:04 +0200 Subject: [PATCH 083/135] Implement getsid. --- src/procmgr.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index e4a0f48c73..78cdec1437 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -764,8 +764,12 @@ impl<'a> ProcScheme<'a> { } } ProcCall::Getsid => { - log::error!("GETSID STUB"); - Ready(Response::ok(0, op)) + let req_pid = NonZeroUsize::new(metadata[1] as usize) + .map_or(fd_pid, |n| ProcessId(n.get())); + Ready(Response::new( + self.on_getsid(fd_pid, req_pid).map(|ProcessId(s)| s), + op, + )) } ProcCall::Setsid => { log::error!("SETSID STUB"); @@ -831,6 +835,26 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } + pub fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { + let caller_proc = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); + let requested_proc = self + .processes + .get(&req_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); + + // POSIX allows, but does not require, the implementation to forbid getting the session ID of processes outside + // the current session. + if caller_proc.sid != requested_proc.sid && caller_proc.euid != 0 { + return Err(Error::new(EPERM)); + } + + Ok(requested_proc.sid) + } pub fn on_setpgid( &mut self, caller_pid: ProcessId, From a2861753dec96ade1d83913b2084f95536f9690f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:14:26 +0200 Subject: [PATCH 084/135] Implement setsid. --- src/procmgr.rs | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 78cdec1437..8f7464c6bf 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -742,7 +742,7 @@ impl<'a> ProcScheme<'a> { self.work_on(state, awoken) } ProcCall::SetResugid => { - log::error!("SETRESGUID STUB"); + log::error!("SETRESUGID STUB"); Ready(Response::ok(0, op)) } ProcCall::Setpgid => { @@ -772,8 +772,7 @@ impl<'a> ProcScheme<'a> { )) } ProcCall::Setsid => { - log::error!("SETSID STUB"); - Ready(Response::ok(0, op)) + Ready(Response::new(self.on_setsid(fd_pid).map(|()| 0), op)) } ProcCall::SetResugid => Ready(Response::new( self.on_setresugid(fd_pid, payload).map(|()| 0), @@ -835,6 +834,33 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } + pub fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { + let mut caller_proc = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + + // POSIX: already a process group leader + if caller_proc.pgid == caller_pid { + return Err(Error::new(EPERM)); + } + // TODO: more efficient? + // POSIX: any other process's pgid matches the caller pid + if self + .processes + .values() + .any(|p| p.borrow().pgid == caller_pid) + { + return Err(Error::new(EPERM)); + } + + caller_proc.pgid = caller_pid; + caller_proc.sid = caller_pid; + + // TODO: Remove controlling terminal + Ok(()) + } pub fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { let caller_proc = self .processes From 0c5b47e677116bba526f0612d757fe7968828939 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:24:54 +0200 Subject: [PATCH 085/135] Phase out static mut for global allocator. --- src/lib.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4b76efcf38..6ed7505f9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,8 @@ pub mod start; extern crate alloc; +use core::cell::UnsafeCell; + use syscall::data::Map; use syscall::flag::MapFlags; @@ -52,15 +54,26 @@ struct Allocator; #[global_allocator] static ALLOCATOR: Allocator = Allocator; -static mut HEAP: Option = None; -static mut HEAP_TOP: usize = HEAP_OFF + SIZE; +struct AllocStateInner { + heap: Option, + heap_top: usize, +} +struct AllocState(UnsafeCell); +unsafe impl Send for AllocState {} +unsafe impl Sync for AllocState {} +static ALLOC_STATE: AllocState = AllocState(UnsafeCell::new(AllocStateInner { + heap: None, + heap_top: HEAP_OFF + SIZE, +})); + const SIZE: usize = 1024 * 1024; const HEAP_INCREASE_BY: usize = SIZE; unsafe impl alloc::alloc::GlobalAlloc for Allocator { unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { - let heap = HEAP.get_or_insert_with(|| { - HEAP_TOP = HEAP_OFF + SIZE; + let state = &mut (*ALLOC_STATE.0.get()); + let heap = state.heap.get_or_insert_with(|| { + state.heap_top = HEAP_OFF + SIZE; let _ = syscall::fmap( !0, &Map { @@ -89,7 +102,7 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { &Map { offset: 0, size: HEAP_INCREASE_BY, - address: HEAP_TOP, + address: state.heap_top, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE @@ -98,14 +111,16 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { ) .expect("failed to extend heap"); heap.extend(HEAP_INCREASE_BY); - HEAP_TOP += HEAP_INCREASE_BY; + state.heap_top += HEAP_INCREASE_BY; return self.alloc(layout); } } } unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { - HEAP.as_mut() + (&mut *ALLOC_STATE.0.get()) + .heap + .as_mut() .unwrap() .deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) } From b4f717bac613923f359949f60252bc102f58917c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:34:18 +0200 Subject: [PATCH 086/135] Implement setresugid. --- src/procmgr.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 8f7464c6bf..f5f5624b0d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -262,8 +262,10 @@ struct Process { ruid: u32, euid: u32, + suid: u32, rgid: u32, egid: u32, + sgid: u32, rns: u32, ens: u32, @@ -480,8 +482,10 @@ impl<'a> ProcScheme<'a> { pgid: INIT_PID, ruid: 0, euid: 0, + suid: 0, rgid: 0, egid: 0, + sgid: 0, rns: 1, ens: 1, @@ -512,10 +516,12 @@ impl<'a> ProcScheme<'a> { let Process { pgid, sid, - euid, ruid, - egid, + euid, + suid, rgid, + egid, + sgid, ens, rns, .. @@ -558,9 +564,11 @@ impl<'a> ProcScheme<'a> { pgid, sid, ruid, - rgid, euid, + suid, + rgid, egid, + sgid, rns, ens, @@ -1076,16 +1084,56 @@ impl<'a> ProcScheme<'a> { } } pub fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { - log::info!("ON_SETRESUGID {pid:?} {raw_buf:?}"); - let ids = { + let [new_ruid, new_euid, new_suid, new_rgid, new_egid, new_sgid] = { let raw_ids: [u32; 6] = plain::slice_from_bytes::(raw_buf) .unwrap() .try_into() .map_err(|_| Error::new(EINVAL))?; raw_ids.map(|i| if i == u32::MAX { None } else { Some(i) }) }; - let proc = self.processes.get_mut(&pid).ok_or(Error::new(ESRCH))?; - log::warn!("TODO: on_setresugid({pid:?}): {ids:?}"); + let mut proc = self + .processes + .get(&pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + + let check = |new_ugid: u32, proc: &Process, gid_not_uid: bool| { + if proc.euid == 0 { + return Ok(()); + } + if gid_not_uid && ![proc.rgid, proc.egid, proc.sgid].contains(&new_ugid) { + return Err(Error::new(EPERM)); + } + if !gid_not_uid && ![proc.ruid, proc.euid, proc.suid].contains(&new_ugid) { + return Err(Error::new(EPERM)); + } + Ok(()) + }; + + if let Some(new_ruid) = new_ruid { + check(new_ruid, &*proc, false)?; + proc.ruid = new_ruid; + } + if let Some(new_euid) = new_euid { + check(new_euid, &*proc, false)?; + proc.euid = new_euid; + } + if let Some(new_suid) = new_suid { + check(new_suid, &*proc, false)?; + proc.suid = new_suid; + } + if let Some(new_rgid) = new_rgid { + check(new_rgid, &*proc, true)?; + proc.rgid = new_rgid; + } + if let Some(new_egid) = new_egid { + check(new_egid, &*proc, true)?; + proc.egid = new_egid; + } + if let Some(new_sgid) = new_sgid { + check(new_sgid, &*proc, true)?; + proc.sgid = new_sgid; + } Ok(()) } fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { From 4d18d17dd2446c9453765e34ae05b30510b8061b Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:34:54 +0200 Subject: [PATCH 087/135] Remove redundant match arm. --- src/procmgr.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index f5f5624b0d..8e5aa1fb81 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -749,10 +749,6 @@ impl<'a> ProcScheme<'a> { }); self.work_on(state, awoken) } - ProcCall::SetResugid => { - log::error!("SETRESUGID STUB"); - Ready(Response::ok(0, op)) - } ProcCall::Setpgid => { let target_pid = NonZeroUsize::new(metadata[1] as usize) .map_or(fd_pid, |n| ProcessId(n.get())); From 8434aa9fd843fd2bf4e766d0f46c452dbb7074e5 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:41:24 +0200 Subject: [PATCH 088/135] Add ThisGroup selector for kill. --- src/procmgr.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/procmgr.rs b/src/procmgr.rs index 8e5aa1fb81..db18bd581d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1332,6 +1332,13 @@ impl<'a> ProcScheme<'a> { } ProcKillTarget::All => None, ProcKillTarget::ProcGroup(grp) => Some(ProcessId(grp)), + ProcKillTarget::ThisGroup => Some( + self.processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow() + .pgid, + ), }; for (pid, proc_rc) in self.processes.iter() { From bdf9563688e92c2bb6ffb75ea9fa4108655fe97e Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 13:31:09 +0200 Subject: [PATCH 089/135] Make setresugid atomic. --- src/procmgr.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index db18bd581d..f7d89e321c 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -645,10 +645,12 @@ impl<'a> ProcScheme<'a> { pid: pid.0 as u32, pgid: process.pgid.0 as u32, ppid: process.ppid.0 as u32, - euid: process.euid, - egid: process.egid, ruid: process.ruid, + euid: process.euid, + suid: process.suid, rgid: process.rgid, + egid: process.egid, + sgid: process.sgid, ens: process.ens, rns: process.rns, }; @@ -1093,41 +1095,39 @@ impl<'a> ProcScheme<'a> { .ok_or(Error::new(ESRCH))? .borrow_mut(); - let check = |new_ugid: u32, proc: &Process, gid_not_uid: bool| { - if proc.euid == 0 { - return Ok(()); - } - if gid_not_uid && ![proc.rgid, proc.egid, proc.sgid].contains(&new_ugid) { + if proc.euid != 0 { + if ![new_ruid, new_euid, new_suid] + .iter() + .filter_map(|x| *x) + .all(|new_id| [proc.ruid, proc.euid, proc.suid].contains(&new_id)) + { return Err(Error::new(EPERM)); } - if !gid_not_uid && ![proc.ruid, proc.euid, proc.suid].contains(&new_ugid) { + if ![new_rgid, new_egid, new_sgid] + .iter() + .filter_map(|x| *x) + .all(|new_id| [proc.rgid, proc.egid, proc.sgid].contains(&new_id)) + { return Err(Error::new(EPERM)); } - Ok(()) - }; + } if let Some(new_ruid) = new_ruid { - check(new_ruid, &*proc, false)?; proc.ruid = new_ruid; } if let Some(new_euid) = new_euid { - check(new_euid, &*proc, false)?; proc.euid = new_euid; } if let Some(new_suid) = new_suid { - check(new_suid, &*proc, false)?; proc.suid = new_suid; } if let Some(new_rgid) = new_rgid { - check(new_rgid, &*proc, true)?; proc.rgid = new_rgid; } if let Some(new_egid) = new_egid { - check(new_egid, &*proc, true)?; proc.egid = new_egid; } if let Some(new_sgid) = new_sgid { - check(new_sgid, &*proc, true)?; proc.sgid = new_sgid; } Ok(()) From ae05abf69d6c9efd28ee1174dba12da27f54c9b1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 14:43:35 +0200 Subject: [PATCH 090/135] Fix two instances of double RefCell borrow. --- src/procmgr.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index f7d89e321c..72a46c2ed6 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -841,6 +841,16 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } pub fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { + // TODO: more efficient? + // POSIX: any other process's pgid matches the caller pid + if self + .processes + .values() + .any(|p| p.borrow().pgid == caller_pid) + { + return Err(Error::new(EPERM)); + } + let mut caller_proc = self .processes .get(&caller_pid) @@ -851,15 +861,6 @@ impl<'a> ProcScheme<'a> { if caller_proc.pgid == caller_pid { return Err(Error::new(EPERM)); } - // TODO: more efficient? - // POSIX: any other process's pgid matches the caller pid - if self - .processes - .values() - .any(|p| p.borrow().pgid == caller_pid) - { - return Err(Error::new(EPERM)); - } caller_proc.pgid = caller_pid; caller_proc.sid = caller_pid; @@ -1059,8 +1060,9 @@ impl<'a> ProcScheme<'a> { let this_pgid = proc.pgid; if !self .processes - .values() - .any(|p| p.borrow().pgid == this_pgid) + .iter() + .filter(|(pid, _)| **pid != this_pid) + .any(|(_, p)| p.borrow().pgid == this_pgid) { return Ready(Err(Error::new(ECHILD))); } From 7902fc7c4d61dec920327a42084dc1b974e62fd2 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 16:19:37 +0200 Subject: [PATCH 091/135] Add thread handle type, thus wrapping open_via_dup. --- src/procmgr.rs | 73 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 72a46c2ed6..81d01e062e 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -19,7 +19,7 @@ use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, WaitFlags}; +use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, ThreadCall, WaitFlags}; use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, @@ -221,13 +221,13 @@ pub struct Page { ptr: NonNull, } impl Page { - pub fn map(fd: &FdGuard) -> Result { + pub fn map(fd: &FdGuard, offset: usize) -> Result { Ok(Self { ptr: NonNull::new(unsafe { syscall::fmap( **fd, &syscall::Map { - offset: 0, + offset, size: PAGE_SIZE, flags: MapFlags::PROT_READ, address: 0, @@ -392,6 +392,7 @@ enum WaitpidStatus { enum Handle { Init, Proc(ProcessId), + Thread(Rc>), } #[derive(Clone, Copy, Debug)] @@ -585,7 +586,7 @@ impl<'a> ProcScheme<'a> { self.thread_lookup.insert(thread_ident, thread_weak); Ok(child_pid) } - fn new_thread(&mut self, pid: ProcessId) -> Result { + fn new_thread(&mut self, pid: ProcessId) -> Result>> { // TODO: deduplicate code with fork let proc_rc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; let mut proc = proc_rc.borrow_mut(); @@ -614,15 +615,15 @@ impl<'a> ProcScheme<'a> { let ident = *ctxt_fd; let thread = Rc::new(RefCell::new(Thread { - fd: FdGuard::new(syscall::dup(*ctxt_fd, &[])?), + fd: ctxt_fd, status_hndl, pid, sig_ctrl: None, })); let thread_weak = Rc::downgrade(&thread); - proc.threads.push(thread); + proc.threads.push(Rc::clone(&thread)); self.thread_lookup.insert(ident, thread_weak); - Ok(ctxt_fd) + Ok(thread) } fn on_open(&mut self, path: &str, flags: usize) -> Result { if path == "init" { @@ -659,7 +660,7 @@ impl<'a> ProcScheme<'a> { .ok_or(Error::new(EINVAL))? = metadata; Ok(size_of::()) } - Handle::Init => return Err(Error::new(EBADF)), + Handle::Init | Handle::Thread(_) => return Err(Error::new(EBADF)), } } fn on_dup(&mut self, old_id: usize, buf: &[u8]) -> Result { @@ -674,23 +675,37 @@ impl<'a> ProcScheme<'a> { flags: NewFdFlags::empty(), }) } - b"new-thread" => Ok(OpenResult::OtherScheme { - fd: self.new_thread(pid)?.take(), - }), + b"new-thread" => { + let thread = self.new_thread(pid)?; + Ok(OpenResult::ThisScheme { + number: self.handles.insert(Handle::Thread(thread)), + flags: NewFdFlags::empty(), + }) + } w if w.starts_with(b"thread-") => { let idx = core::str::from_utf8(&w["thread-".len()..]) .ok() .and_then(|s| s.parse::().ok()) .ok_or(Error::new(EINVAL))?; let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?.borrow(); - let thread = process.threads.get(idx).ok_or(Error::new(ENOENT))?.borrow(); + let thread = Rc::clone(process.threads.get(idx).ok_or(Error::new(ENOENT))?); - return Ok(OpenResult::OtherScheme { - fd: syscall::dup(*thread.fd, &[])?, + return Ok(OpenResult::ThisScheme { + number: self.handles.insert(Handle::Thread(thread)), + flags: NewFdFlags::empty(), }); } _ => return Err(Error::new(EINVAL)), }, + Handle::Thread(ref thread_rc) => { + let thread = thread_rc.borrow(); + + // By forwarding all dup calls to the kernel, this fd is now effectively the same + // as the underlying fd since that fd can't do anything itself. + Ok(OpenResult::OtherScheme { + fd: syscall::dup(*thread.fd, buf)?, + }) + } Handle::Init => Err(Error::new(EBADF)), } } @@ -704,6 +719,17 @@ impl<'a> ProcScheme<'a> { let (payload, metadata) = op.payload_and_metadata(); match self.handles[id] { Handle::Init => Response::ready_err(EBADF, op), + Handle::Thread(ref thr) => { + let Some(verb) = ThreadCall::try_from_raw(metadata[0] as usize) else { + return Response::ready_err(EINVAL, op); + }; + match verb { + ThreadCall::SyncSigTctl => Ready(Response::new( + Self::on_sync_sigtctl(&mut *thr.borrow_mut()).map(|()| 0), + op, + )), + } + } Handle::Proc(fd_pid) => { let Some(verb) = ProcCall::try_from_raw(metadata[0] as usize) else { return Response::ready_err(EINVAL, op); @@ -812,6 +838,9 @@ impl<'a> ProcScheme<'a> { op, )) } + ProcCall::SyncSigPctl => { + Ready(Response::new(self.on_sync_sigpctl(fd_pid).map(|()| 0), op)) + } } } } @@ -1605,6 +1634,22 @@ impl<'a> ProcScheme<'a> { Ok(()) } + pub fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { + let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sigcontrol")?); + thread.sig_ctrl.replace(Page::map(&sigcontrol_fd, 0)?); + Ok(()) + } + pub fn on_sync_sigpctl(&mut self, pid: ProcessId) -> Result<()> { + let mut proc = self + .processes + .get(&pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + let any_thread = proc.threads.first().ok_or(Error::new(EINVAL))?; + let sigcontrol_fd = FdGuard::new(syscall::dup(*any_thread.borrow().fd, b"sigcontrol")?); + proc.sig_pctl.replace(Page::map(&sigcontrol_fd, PAGE_SIZE)?); + Ok(()) + } } #[derive(Clone, Copy)] pub enum KillMode { From a47396e2b953dd9e8b975fe43c79e640cdd8eab1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 18:31:26 +0200 Subject: [PATCH 092/135] Fix SyncTctl and SyncPctl. --- src/procmgr.rs | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 81d01e062e..db70365f92 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -29,9 +29,10 @@ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, - Result, RtSigInfo, SenderInfo, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, - EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, - O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, + EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, + EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, + SIGTTOU, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -143,7 +144,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { log::trace!("AWAITING {}", proc.awaiting_threads_term.len(),); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { - log::debug!("TODO: UNKNOWN EVENT"); + log::warn!("TODO: UNKNOWN EVENT"); } } @@ -219,17 +220,19 @@ impl IntoTag for PendingState { #[derive(Debug)] pub struct Page { ptr: NonNull, + off: u16, } impl Page { - pub fn map(fd: &FdGuard, offset: usize) -> Result { + pub fn map(fd: &FdGuard, req_offset: usize, displacement: u16) -> Result { Ok(Self { + off: displacement, ptr: NonNull::new(unsafe { syscall::fmap( **fd, &syscall::Map { - offset, + offset: req_offset, size: PAGE_SIZE, - flags: MapFlags::PROT_READ, + flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_SHARED, address: 0, }, )? as *mut T @@ -242,7 +245,7 @@ impl Deref for Page { type Target = T; fn deref(&self) -> &T { - unsafe { self.ptr.as_ref() } + unsafe { &*self.ptr.as_ptr().byte_add(self.off.into()) } } } impl Drop for Page { @@ -1447,6 +1450,7 @@ impl<'a> ProcScheme<'a> { let target_proc = &mut *target_proc; let Some(ref sig_pctl) = target_proc.sig_pctl else { + log::trace!("No pctl {caller_pid:?}"); return SendResult::Invalid; }; @@ -1534,6 +1538,7 @@ impl<'a> ProcScheme<'a> { KillTarget::Thread(ref thread_rc) => { let thread = thread_rc.borrow(); let Some(ref tctl) = thread.sig_ctrl else { + log::trace!("No tctl"); return SendResult::Invalid; }; @@ -1551,10 +1556,11 @@ impl<'a> ProcScheme<'a> { match mode { KillMode::Queued(arg) => { if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { + log::trace!("Out of range"); return SendResult::Invalid; } let rtidx = sig_idx - 32; - //log::info!("QUEUEING {arg:?} RTIDX {rtidx}"); + //log::trace!("QUEUEING {arg:?} RTIDX {rtidx}"); if rtidx >= target_proc.rtqs.len() { target_proc.rtqs.resize_with(rtidx + 1, VecDeque::new); } @@ -1581,6 +1587,7 @@ impl<'a> ProcScheme<'a> { } if sig_group != 0 { + log::trace!("Invalid sig group"); return SendResult::Invalid; } sig_pctl.sender_infos[sig_idx] @@ -1634,20 +1641,36 @@ impl<'a> ProcScheme<'a> { Ok(()) } + fn real_tctl_pctl_intra_page_offsets(fd: &FdGuard) -> Result<[u16; 2]> { + let mut buf = SetSighandlerData::default(); + let _ = syscall::read(**fd, &mut buf)?; + Ok([ + (buf.thread_control_addr % PAGE_SIZE) as u16, + (buf.proc_control_addr % PAGE_SIZE) as u16, + ]) + } pub fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { - let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sigcontrol")?); - thread.sig_ctrl.replace(Page::map(&sigcontrol_fd, 0)?); + log::trace!("Sync tctl {:?}", thread.pid); + let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sighandler")?); + let [tctl_off, _] = Self::real_tctl_pctl_intra_page_offsets(&sigcontrol_fd)?; + log::trace!("read intra offsets"); + thread + .sig_ctrl + .replace(Page::map(&sigcontrol_fd, 0, tctl_off)?); Ok(()) } pub fn on_sync_sigpctl(&mut self, pid: ProcessId) -> Result<()> { + log::trace!("Sync pctl {pid:?}"); let mut proc = self .processes .get(&pid) .ok_or(Error::new(ESRCH))? .borrow_mut(); let any_thread = proc.threads.first().ok_or(Error::new(EINVAL))?; - let sigcontrol_fd = FdGuard::new(syscall::dup(*any_thread.borrow().fd, b"sigcontrol")?); - proc.sig_pctl.replace(Page::map(&sigcontrol_fd, PAGE_SIZE)?); + let sigcontrol_fd = FdGuard::new(syscall::dup(*any_thread.borrow().fd, b"sighandler")?); + let [_, pctl_off] = Self::real_tctl_pctl_intra_page_offsets(&sigcontrol_fd)?; + proc.sig_pctl + .replace(Page::map(&sigcontrol_fd, PAGE_SIZE, pctl_off)?); Ok(()) } } From 7845d74df16da444294eea25f6b6ef35c4898b20 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 1 Apr 2025 14:06:36 +0200 Subject: [PATCH 093/135] Impl SIGCONT and SIGSTOP. --- src/procmgr.rs | 71 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index db70365f92..b0b6055458 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -28,10 +28,10 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - sig_bit, ContextStatus, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, - Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, - EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, - EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, + sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, + ProcSchemeAttrs, Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, + EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, + ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; @@ -468,7 +468,13 @@ impl<'a> ProcScheme<'a> { self.queue .subscribe(*fd, fd_out, EventFlags::EVENT_READ) .expect("TODO"); - let status_hndl = FdGuard::new(syscall::dup(*fd, b"status").expect("TODO")); + let status_hndl = FdGuard::new( + syscall::dup( + *fd, + alloc::format!("auth-{}-status", **self.auth).as_bytes(), + ) + .expect("TODO"), + ); let thread = Rc::new(RefCell::new(Thread { fd, @@ -534,7 +540,7 @@ impl<'a> ProcScheme<'a> { let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); let attr_fd = FdGuard::new(syscall::dup( *new_ctxt_fd, - alloc::format!("attrs-{}", **self.auth).as_bytes(), + alloc::format!("auth-{}-attrs", **self.auth).as_bytes(), )?); let _ = syscall::write( *attr_fd, @@ -545,7 +551,10 @@ impl<'a> ProcScheme<'a> { ens, }, )?; - let status_fd = FdGuard::new(syscall::dup(*new_ctxt_fd, b"status")?); + let status_fd = FdGuard::new(syscall::dup( + *new_ctxt_fd, + alloc::format!("auth-{}-status", **self.auth).as_bytes(), + )?); self.queue .subscribe(*new_ctxt_fd, *new_ctxt_fd, EventFlags::EVENT_READ) @@ -598,7 +607,7 @@ impl<'a> ProcScheme<'a> { let attr_fd = FdGuard::new(syscall::dup( *ctxt_fd, - alloc::format!("attrs-{}", **self.auth).as_bytes(), + alloc::format!("auth-{}-attrs", **self.auth).as_bytes(), )?); let _ = syscall::write( *attr_fd, @@ -610,7 +619,10 @@ impl<'a> ProcScheme<'a> { }, )?; - let status_hndl = FdGuard::new(syscall::dup(*ctxt_fd, b"status")?); + let status_hndl = FdGuard::new(syscall::dup( + *ctxt_fd, + alloc::format!("auth-{}-status", **self.auth).as_bytes(), + )?); self.queue .subscribe(*ctxt_fd, *status_hndl, EventFlags::EVENT_READ) @@ -1496,25 +1508,25 @@ impl<'a> ProcScheme<'a> { .as_ref() .map_or(false, |proc| proc.signal_will_stop(sig))) { - todo!("tell kernel to stop process"); - /* - context_guard.status = context::Status::Blocked; - drop(context_guard); - process_lock.write().status = ProcessStatus::Stopped(sig); - */ + target_proc.status = ProcessStatus::Stopped(sig); + + for thread in &target_proc.threads { + let thread = thread.borrow(); + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::Stop as usize).to_ne_bytes(), + ) + .expect("TODO"); + if let Some(ref tctl) = thread.sig_ctrl { + tctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); + } + } // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise? sig_pctl .pending .fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); - for thread in target_proc.threads.iter() { - let thread = thread.borrow(); - if let Some(ref tctl) = thread.sig_ctrl { - tctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); - } - } - return SendResult::SucceededSigchld { orig_signal: sig, ppid: target_proc.ppid, @@ -1522,12 +1534,15 @@ impl<'a> ProcScheme<'a> { }; } if sig == SIGKILL { - todo!("tell kernel to kill context"); - /* - context_guard.being_sigkilled = true; - context_guard.unblock(); - drop(context_guard); - */ + for thread in &target_proc.threads { + let thread = thread.borrow(); + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::ForceKill as usize).to_ne_bytes(), + ) + .expect("TODO"); + } + *killed_self |= is_self; // exit() will signal the parent, rather than immediately in kill() From 362daf5ca91fd3b2f75510c003c986aab2ac65f3 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 1 Apr 2025 15:08:01 +0200 Subject: [PATCH 094/135] Add SIGCHLD logic. --- src/procmgr.rs | 82 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b0b6055458..20c65b904c 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -31,8 +31,8 @@ use syscall::{ sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, - SIGTTOU, + ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, + SIGTTIN, SIGTTOU, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -849,7 +849,8 @@ impl<'a> ProcScheme<'a> { let is_sigchld_to_parent = false; Ready(Response::new( - self.on_kill(fd_pid, target, signal, mode).map(|()| 0), + self.on_kill(fd_pid, target, signal, mode, awoken) + .map(|()| 0), op, )) } @@ -1359,7 +1360,9 @@ impl<'a> ProcScheme<'a> { target: ProcKillTarget, signal: u8, mode: KillMode, + awoken: &mut VecDeque, ) -> Result<()> { + log::debug!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; let mut killed_self = false; // TODO @@ -1374,6 +1377,7 @@ impl<'a> ProcScheme<'a> { &mut killed_self, mode, is_sigchld_to_parent, + awoken, ) } ProcKillTarget::All => None, @@ -1398,6 +1402,7 @@ impl<'a> ProcScheme<'a> { &mut killed_self, mode, is_sigchld_to_parent, + awoken, ); match res { Ok(()) => (), @@ -1416,6 +1421,7 @@ impl<'a> ProcScheme<'a> { killed_self: &mut bool, mode: KillMode, is_sigchld_to_parent: bool, + awoken: &mut VecDeque, ) -> Result<()> { let sig = usize::from(signal); debug_assert!(sig <= 64); @@ -1436,7 +1442,7 @@ impl<'a> ProcScheme<'a> { enum SendResult { Succeeded, SucceededSigchld { - orig_signal: usize, + orig_signal: NonZeroU8, ppid: ProcessId, pgid: ProcessId, }, @@ -1455,9 +1461,9 @@ impl<'a> ProcScheme<'a> { // If sig = 0, test that process exists and can be signalled, but don't send any // signal. - if sig == 0 { + let Some(nz_signal) = NonZeroU8::new(signal) else { return SendResult::Succeeded; - } + }; let mut target_proc = target_proc_rc.borrow_mut(); let target_proc = &mut *target_proc; @@ -1528,7 +1534,7 @@ impl<'a> ProcScheme<'a> { .fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); return SendResult::SucceededSigchld { - orig_signal: sig, + orig_signal: nz_signal, ppid: target_proc.ppid, pgid: target_proc.pgid, }; @@ -1563,7 +1569,6 @@ impl<'a> ProcScheme<'a> { tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Release); if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - //context_guard.unblock(); *killed_self |= is_self; } } @@ -1647,10 +1652,64 @@ impl<'a> ProcScheme<'a> { ppid, pgid, orig_signal, - } => {} + } => { + { + let mut parent = self + .processes + .get(&ppid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + parent.waitpid.insert( + WaitpidKey { + pid: Some(target_pid), + pgid: Some(pgid), + }, + ( + target_pid, + WaitpidStatus::Stopped { + signal: orig_signal, + }, + ), + ); + awoken.extend(parent.waitpid_waiting.drain(..)); + } + self.on_send_sig( + // TODO? + ProcessId(1), + KillTarget::Proc(ppid), + SIGCHLD as u8, + killed_self, + KillMode::Idempotent, + true, + awoken, + )?; + } SendResult::SucceededSigcont { ppid, pgid } => { + { + let mut parent = self + .processes + .get(&ppid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + parent.waitpid.insert( + WaitpidKey { + pid: Some(target_pid), + pgid: Some(pgid), + }, + (target_pid, WaitpidStatus::Continued), + ); + awoken.extend(parent.waitpid_waiting.drain(..)); + } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. - //send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?; + self.on_send_sig( + ProcessId(1), + KillTarget::Proc(ppid), + SIGCHLD as u8, + killed_self, + KillMode::Idempotent, + true, + awoken, + )?; } } @@ -1689,11 +1748,12 @@ impl<'a> ProcScheme<'a> { Ok(()) } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum KillMode { Idempotent, Queued(RtSigInfo), } +#[derive(Debug)] pub enum KillTarget { Proc(ProcessId), Thread(Rc>), From 53705b70495ccff497a2f93c54c208d1589d8fcf Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 1 Apr 2025 15:33:50 +0200 Subject: [PATCH 095/135] Some fixes. --- src/procmgr.rs | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 20c65b904c..8530226eb8 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1362,7 +1362,7 @@ impl<'a> ProcScheme<'a> { mode: KillMode, awoken: &mut VecDeque, ) -> Result<()> { - log::debug!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); + log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; let mut killed_self = false; // TODO @@ -1498,8 +1498,11 @@ impl<'a> ProcScheme<'a> { Ordering::Relaxed, ); } - // TODO - //thread.unblock(); + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::Unstop as usize).to_ne_bytes(), + ) + .expect("TODO"); } // POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs. return SendResult::SucceededSigcont { @@ -1570,6 +1573,11 @@ impl<'a> ProcScheme<'a> { if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { *killed_self |= is_self; + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::Interrupt as usize).to_ne_bytes(), + ) + .expect("TODO"); } } KillTarget::Proc(proc) => { @@ -1625,8 +1633,11 @@ impl<'a> ProcScheme<'a> { if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - // TODO - //thread.unblock(); + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::Interrupt as usize).to_ne_bytes(), + ) + .expect("TODO"); *killed_self |= is_self; break; } @@ -1647,7 +1658,10 @@ impl<'a> ProcScheme<'a> { match result { SendResult::Succeeded => (), SendResult::FullQ => return Err(Error::new(EAGAIN)), - SendResult::Invalid => return Err(Error::new(EINVAL)), + SendResult::Invalid => { + log::debug!("Invalid signal configuration"); + return Err(Error::new(EINVAL)); + } SendResult::SucceededSigchld { ppid, pgid, @@ -1673,7 +1687,8 @@ impl<'a> ProcScheme<'a> { ); awoken.extend(parent.waitpid_waiting.drain(..)); } - self.on_send_sig( + // TODO: Just ignore EINVAL (missing signal config) + let _ = self.on_send_sig( // TODO? ProcessId(1), KillTarget::Proc(ppid), @@ -1682,7 +1697,7 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, true, awoken, - )?; + ); } SendResult::SucceededSigcont { ppid, pgid } => { { @@ -1701,7 +1716,8 @@ impl<'a> ProcScheme<'a> { awoken.extend(parent.waitpid_waiting.drain(..)); } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. - self.on_send_sig( + // TODO: Just ignore EINVAL (missing signal config) + let _ = self.on_send_sig( ProcessId(1), KillTarget::Proc(ppid), SIGCHLD as u8, @@ -1709,7 +1725,7 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, true, awoken, - )?; + ); } } From ab3f59b22ba7074eb04fae84aba4fda3e45350b1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 3 Apr 2025 11:31:22 +0200 Subject: [PATCH 096/135] Reacquire thread fd as procmgr-managed before exec init. --- src/exec.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index 12e720e140..589c467e72 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -87,7 +87,8 @@ pub fn main() -> ! { spawn("process manager", &auth, &this_thr_fd, |write_fd| { crate::procmgr::run(write_fd, &auth) }); - let init_proc_fd = unsafe { redox_rt::proc::make_init() }; + let [init_proc_fd, init_thr_fd] = unsafe { redox_rt::proc::make_init() }; + // from this point, this_thr_fd is no longer valid const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; @@ -97,7 +98,7 @@ pub fn main() -> ! { sigprocmask: 0, sigignmask: 0, umask: redox_rt::sys::get_umask(), - thr_fd: **this_thr_fd, + thr_fd: **init_thr_fd, proc_fd: **init_proc_fd, }; @@ -116,7 +117,7 @@ pub fn main() -> ! { fexec_impl( image_file, - this_thr_fd, + init_thr_fd, init_proc_fd, &memory, path.as_bytes(), From d59597b3ddba05ae7a1dcb0a7a679124e201cfee Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 4 Apr 2025 11:12:29 +0200 Subject: [PATCH 097/135] Add TODO for proc termination without explicit exit. --- src/procmgr.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 8530226eb8..21d11da081 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -141,8 +141,13 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } scheme.thread_lookup.remove(&event.data); proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); - log::trace!("AWAITING {}", proc.awaiting_threads_term.len(),); - awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient + + if matches!(proc.status, ProcessStatus::Exiting { .. }) { + log::trace!("WAKING UP {}", proc.awaiting_threads_term.len(),); + awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient + } else { + todo!("handle proc termination without explicit exit"); + } } else { log::warn!("TODO: UNKNOWN EVENT"); } From 944177533118a34e4151143a299ba8a9bf2fcdca Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 4 Apr 2025 11:20:20 +0200 Subject: [PATCH 098/135] Embed Id in VirtualId. --- src/procmgr.rs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 21d11da081..a7677d2300 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -35,6 +35,13 @@ use syscall::{ SIGTTIN, SIGTTOU, }; +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +enum VirtualId { + KernelId(Id), + // TODO: slab or something for better ID reuse + // InternalId(u64), +} + pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::nonblock("proc").expect("failed to open proc scheme socket"); @@ -53,8 +60,8 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let _ = syscall::write(write_fd, &[0]); let _ = syscall::close(write_fd); - let mut states = HashMap::::new(); - let mut awoken = VecDeque::::new(); + let mut states = HashMap::::new(); + let mut awoken = VecDeque::::new(); let mut new_awoken = VecDeque::new(); 'outer: loop { @@ -159,12 +166,12 @@ fn handle_scheme<'a>( req: Request, socket: &'a Socket, scheme: &mut ProcScheme<'a>, - states: &mut HashMap, - awoken: &mut VecDeque, + states: &mut HashMap, + awoken: &mut VecDeque, ) -> Poll { match req.kind() { RequestKind::Call(req) => { - let req_id = req.request_id(); + let req_id = VirtualId::KernelId(req.request_id()); let op = match req.op() { Ok(op) => op, Err(req) => return Response::ready_err(ENOSYS, req), @@ -279,10 +286,10 @@ struct Process { status: ProcessStatus, - awaiting_threads_term: Vec, + awaiting_threads_term: Vec, waitpid: BTreeMap, - waitpid_waiting: VecDeque, + waitpid_waiting: VecDeque, sig_pctl: Option>, rtqs: Vec>, @@ -731,9 +738,9 @@ impl<'a> ProcScheme<'a> { } pub fn on_call( &mut self, - state: VacantEntry, + state: VacantEntry, mut op: OpCall, - awoken: &mut VecDeque, + awoken: &mut VecDeque, ) -> Poll { let id = op.fd; let (payload, metadata) = op.payload_and_metadata(); @@ -963,8 +970,8 @@ impl<'a> ProcScheme<'a> { &mut self, pid: ProcessId, status: i32, - mut state: VacantEntry, - awoken: &mut VecDeque, + mut state: VacantEntry, + awoken: &mut VecDeque, tag: Tag, ) -> Poll { let Some(proc_rc) = self.processes.get(&pid) else { @@ -1010,7 +1017,7 @@ impl<'a> ProcScheme<'a> { this_pid: ProcessId, target: WaitpidTarget, flags: WaitFlags, - req_id: Id, + req_id: VirtualId, ) -> Poll> { if matches!( target, @@ -1276,8 +1283,8 @@ impl<'a> ProcScheme<'a> { } pub fn work_on( &mut self, - mut state_entry: OccupiedEntry, - awoken: &mut VecDeque, + mut state_entry: OccupiedEntry, + awoken: &mut VecDeque, ) -> Poll { let req_id = *state_entry.key(); let mut state = state_entry.get_mut(); @@ -1365,7 +1372,7 @@ impl<'a> ProcScheme<'a> { target: ProcKillTarget, signal: u8, mode: KillMode, - awoken: &mut VecDeque, + awoken: &mut VecDeque, ) -> Result<()> { log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; @@ -1426,7 +1433,7 @@ impl<'a> ProcScheme<'a> { killed_self: &mut bool, mode: KillMode, is_sigchld_to_parent: bool, - awoken: &mut VecDeque, + awoken: &mut VecDeque, ) -> Result<()> { let sig = usize::from(signal); debug_assert!(sig <= 64); From 9c590bf3691fa48b0eca65f6a084a8741392546d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 4 Apr 2025 12:04:38 +0200 Subject: [PATCH 099/135] Fix waitpid hang for exception-caused termination. --- src/procmgr.rs | 99 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 18 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index a7677d2300..070c979771 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -39,7 +39,7 @@ use syscall::{ enum VirtualId { KernelId(Id), // TODO: slab or something for better ID reuse - // InternalId(u64), + InternalId(u64), } pub fn run(write_fd: usize, auth: &FdGuard) { @@ -130,7 +130,8 @@ pub fn run(write_fd: usize, auth: &FdGuard) { continue; }; let thread = thread_rc.borrow(); - let Some(proc_rc) = scheme.processes.get(&thread.pid) else { + let pid = thread.pid; + let Some(proc_rc) = scheme.processes.get(&pid) else { // TODO? continue; }; @@ -153,7 +154,19 @@ pub fn run(write_fd: usize, auth: &FdGuard) { log::trace!("WAKING UP {}", proc.awaiting_threads_term.len(),); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { - todo!("handle proc termination without explicit exit"); + let internal_id = scheme.next_internal_id; + scheme.next_internal_id += 1; + let Entry::Vacant(entry) = states.entry(VirtualId::InternalId(internal_id)) else { + log::error!("internal ID reuse!"); + continue; + }; + drop(thread); + drop(proc); + let Pending = + scheme.on_exit_start(pid, 0 /* TODO */, entry, &mut awoken, None) + else { + unreachable!("not possible with tag=None"); + }; } } else { log::warn!("TODO: UNKNOWN EVENT"); @@ -216,10 +229,10 @@ enum PendingState { flags: WaitFlags, op: OpCall, }, - AwaitingThreadsTermination(ProcessId, Tag), + AwaitingThreadsTermination(ProcessId, Option), Placeholder, } -impl IntoTag for PendingState { +/*impl IntoTag for PendingState { fn into_tag(self) -> Tag { match self { Self::AwaitingThreadsTermination(_, tag) => tag, @@ -227,7 +240,7 @@ impl IntoTag for PendingState { Self::Placeholder => unreachable!(), } } -} +}*/ #[derive(Debug)] pub struct Page { @@ -385,6 +398,8 @@ struct ProcScheme<'a> { thread_lookup: HashMap>>, + next_internal_id: u64, + init_claimed: bool, next_id: ProcessId, @@ -458,6 +473,7 @@ impl<'a> ProcScheme<'a> { handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), + next_internal_id: 1, queue, auth, } @@ -778,9 +794,13 @@ impl<'a> ProcScheme<'a> { .map(|()| 0), op, )), - ProcCall::Exit => { - self.on_exit_start(fd_pid, metadata[1] as i32, state, awoken, op.into_tag()) - } + ProcCall::Exit => self.on_exit_start( + fd_pid, + metadata[1] as i32, + state, + awoken, + Some(op.into_tag()), + ), ProcCall::Waitpid | ProcCall::Waitpgid => { let req_pid = ProcessId(metadata[1] as usize); let target = match (verb, metadata[1] == 0) { @@ -972,10 +992,14 @@ impl<'a> ProcScheme<'a> { status: i32, mut state: VacantEntry, awoken: &mut VecDeque, - tag: Tag, + tag: Option, ) -> Poll { let Some(proc_rc) = self.processes.get(&pid) else { - return Response::ready_err(EBADFD, tag); + return if let Some(tag) = tag { + Response::ready_err(EBADFD, tag) + } else { + Pending + }; }; let mut process_guard = proc_rc.borrow_mut(); let process = &mut *process_guard; @@ -983,8 +1007,20 @@ impl<'a> ProcScheme<'a> { match process.status { ProcessStatus::Stopped(_) | ProcessStatus::PossiblyRunnable => (), //ProcessStatus::Exiting => return Pending, - ProcessStatus::Exiting { .. } => return Response::ready_err(EAGAIN, tag), - ProcessStatus::Exited { .. } => return Response::ready_err(ESRCH, tag), + ProcessStatus::Exiting { .. } => { + return if let Some(tag) = tag { + Response::ready_err(EAGAIN, tag) + } else { + Pending + } + } + ProcessStatus::Exited { .. } => { + return if let Some(tag) = tag { + Response::ready_err(ESRCH, tag) + } else { + Pending + } + } } // TODO: status/signal process.status = ProcessStatus::Exiting { @@ -996,8 +1032,11 @@ impl<'a> ProcScheme<'a> { // to-be-ignored cancellation request to this scheme). for thread in &process.threads { let mut thread = thread.borrow_mut(); + // TODO: cancel all threads anyway on error? if let Err(err) = syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()) { - return Response::ready_err(err.errno, tag); + if let Some(tag) = tag { + return Response::ready_err(err.errno, tag); + } } } @@ -1294,7 +1333,12 @@ impl<'a> ProcScheme<'a> { // TODO PendingState::AwaitingThreadsTermination(current_pid, tag) => { let Some(proc_rc) = self.processes.get(¤t_pid) else { - return Response::ready_err(ESRCH, tag); + return if let Some(tag) = tag { + Response::ready_err(ESRCH, tag) + } else { + state_entry.remove(); + Pending + }; }; let mut proc_guard = proc_rc.borrow_mut(); let proc = &mut *proc_guard; @@ -1303,8 +1347,22 @@ impl<'a> ProcScheme<'a> { log::trace!("WORKING ON AWAIT TERM"); let (signal, status) = match proc.status { ProcessStatus::Exiting { signal, status } => (signal, status), - ProcessStatus::Exited { .. } => return Response::ready_ok(0, tag), - _ => return Response::ready_err(ESRCH, tag), // TODO? + ProcessStatus::Exited { .. } => { + return if let Some(tag) = tag { + Response::ready_ok(0, tag) + } else { + state_entry.remove(); + Pending + } + } + _ => { + return if let Some(tag) = tag { + Response::ready_err(ESRCH, tag) // TODO? + } else { + state_entry.remove(); + Pending + } + } }; // TODO: Properly remove state state_entry.remove(); @@ -1325,7 +1383,12 @@ impl<'a> ProcScheme<'a> { // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } - Ready(Response::new(Ok(0), tag)) + if let Some(tag) = tag { + Ready(Response::new(Ok(0), tag)) + } else { + // state was removed earlier + Pending + } } else { log::trace!("WAITING AGAIN"); proc.awaiting_threads_term.push(req_id); From b7cb70859f1eea5fabcb4cf91366965ba93c09fe Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 4 Apr 2025 12:53:16 +0200 Subject: [PATCH 100/135] Implement ThreadCall::SignalThread. --- src/procmgr.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 070c979771..584e5e66db 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -771,6 +771,14 @@ impl<'a> ProcScheme<'a> { Self::on_sync_sigtctl(&mut *thr.borrow_mut()).map(|()| 0), op, )), + ThreadCall::SignalThread => { + let thr = Rc::clone(thr); + Ready(Response::new( + self.on_kill_thread(&thr, metadata[1] as u8, awoken) + .map(|()| 0), + op, + )) + } } } Handle::Proc(fd_pid) => { @@ -1361,7 +1369,7 @@ impl<'a> ProcScheme<'a> { } else { state_entry.remove(); Pending - } + }; } }; // TODO: Properly remove state @@ -1429,6 +1437,25 @@ impl<'a> ProcScheme<'a> { log::trace!("PROCESSES\n{:#?}", self.processes,); log::trace!("HANDLES\n{:#?}", self.handles,); } + pub fn on_kill_thread( + &mut self, + thread: &Rc>, + signal: u8, + awoken: &mut VecDeque, + ) -> Result<()> { + let mut killed_self = false; // TODO + let is_sigchld_to_parent = false; + let caller_pid = thread.borrow().pid; // TODO: allow this to be specified? + self.on_send_sig( + caller_pid, + KillTarget::Thread(Rc::clone(thread)), + signal, + &mut killed_self, + KillMode::Idempotent, + is_sigchld_to_parent, + awoken, + ) + } pub fn on_kill( &mut self, caller_pid: ProcessId, From 7edba6b84b879eccbed3f50c466c234010136a7b Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 7 Apr 2025 19:10:01 +0200 Subject: [PATCH 101/135] Fix fd mismatch for thread event handling. --- src/procmgr.rs | 66 +++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 584e5e66db..ad1a225158 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -147,6 +147,10 @@ pub fn run(write_fd: usize, auth: &FdGuard) { // spurious event continue; } + + if let Err(err) = scheme.queue.unsubscribe(event.data, event.data) { + log::error!("failed to unsubscribe from fd {}", event.data); + } scheme.thread_lookup.remove(&event.data); proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); @@ -169,7 +173,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { }; } } else { - log::warn!("TODO: UNKNOWN EVENT"); + log::warn!("TODO: UNKNOWN EVENT {event:?}"); } } @@ -452,6 +456,9 @@ impl RawEventQueue { )?; Ok(()) } + pub fn unsubscribe(&self, fd: usize, ident: usize) -> Result<()> { + self.subscribe(fd, ident, EventFlags::empty()) + } pub fn next_event(&self) -> Result { let mut event = Event::default(); let read = syscall::read(*self.0, &mut event)?; @@ -653,7 +660,7 @@ impl<'a> ProcScheme<'a> { )?); self.queue - .subscribe(*ctxt_fd, *status_hndl, EventFlags::EVENT_READ) + .subscribe(*ctxt_fd, *ctxt_fd, EventFlags::EVENT_READ) .expect("TODO"); let ident = *ctxt_fd; @@ -1464,7 +1471,7 @@ impl<'a> ProcScheme<'a> { mode: KillMode, awoken: &mut VecDeque, ) -> Result<()> { - log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); + log::debug!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; let mut killed_self = false; // TODO @@ -1492,6 +1499,7 @@ impl<'a> ProcScheme<'a> { .pgid, ), }; + log::debug!("match group {match_grp:?}"); for (pid, proc_rc) in self.processes.iter() { if match_grp.map_or(false, |g| proc_rc.borrow().pgid != g) { @@ -1525,6 +1533,7 @@ impl<'a> ProcScheme<'a> { is_sigchld_to_parent: bool, awoken: &mut VecDeque, ) -> Result<()> { + log::debug!("SEND_SIG(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let sig = usize::from(signal); debug_assert!(sig <= 64); let sig_group = (sig - 1) / 32; @@ -1570,7 +1579,7 @@ impl<'a> ProcScheme<'a> { let target_proc = &mut *target_proc; let Some(ref sig_pctl) = target_proc.sig_pctl else { - log::trace!("No pctl {caller_pid:?}"); + log::debug!("No pctl {caller_pid:?} => {target_pid:?}"); return SendResult::Invalid; }; @@ -1664,7 +1673,7 @@ impl<'a> ProcScheme<'a> { KillTarget::Thread(ref thread_rc) => { let thread = thread_rc.borrow(); let Some(ref tctl) = thread.sig_ctrl else { - log::trace!("No tctl"); + log::debug!("No tctl"); return SendResult::Invalid; }; @@ -1686,7 +1695,7 @@ impl<'a> ProcScheme<'a> { match mode { KillMode::Queued(arg) => { if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { - log::trace!("Out of range"); + log::debug!("Out of range"); return SendResult::Invalid; } let rtidx = sig_idx - 32; @@ -1717,7 +1726,7 @@ impl<'a> ProcScheme<'a> { } if sig_group != 0 { - log::trace!("Invalid sig group"); + log::debug!("Invalid sig group"); return SendResult::Invalid; } sig_pctl.sender_infos[sig_idx] @@ -1761,7 +1770,7 @@ impl<'a> ProcScheme<'a> { SendResult::Succeeded => (), SendResult::FullQ => return Err(Error::new(EAGAIN)), SendResult::Invalid => { - log::debug!("Invalid signal configuration"); + log::debug!("Invalid signal configuration for {target_pid:?}"); return Err(Error::new(EINVAL)); } SendResult::SucceededSigchld { @@ -1790,16 +1799,17 @@ impl<'a> ProcScheme<'a> { awoken.extend(parent.waitpid_waiting.drain(..)); } // TODO: Just ignore EINVAL (missing signal config) - let _ = self.on_send_sig( - // TODO? - ProcessId(1), - KillTarget::Proc(ppid), - SIGCHLD as u8, - killed_self, - KillMode::Idempotent, - true, - awoken, - ); + if ppid != INIT_PID { + let _ = self.on_send_sig( + INIT_PID, // caller, TODO? + KillTarget::Proc(ppid), + SIGCHLD as u8, + killed_self, + KillMode::Idempotent, + true, + awoken, + ); + } } SendResult::SucceededSigcont { ppid, pgid } => { { @@ -1819,15 +1829,17 @@ impl<'a> ProcScheme<'a> { } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. // TODO: Just ignore EINVAL (missing signal config) - let _ = self.on_send_sig( - ProcessId(1), - KillTarget::Proc(ppid), - SIGCHLD as u8, - killed_self, - KillMode::Idempotent, - true, - awoken, - ); + if ppid != INIT_PID { + let _ = self.on_send_sig( + INIT_PID, // caller, TODO? + KillTarget::Proc(ppid), + SIGCHLD as u8, + killed_self, + KillMode::Idempotent, + true, + awoken, + ); + } } } From 368fe1c186d580de2530c274ccf127c131ce2518 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 10 Apr 2025 15:39:40 +0200 Subject: [PATCH 102/135] Do not internally spawn exit() dying thread was last. --- src/procmgr.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index ad1a225158..c7b42843b1 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -141,12 +141,13 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); let status = usize::from_ne_bytes(buf); - log::trace!("STATUS {status}"); + log::trace!("--STATUS {status}"); if status != ContextStatus::Dead as usize { // spurious event continue; } + log::trace!("--THREAD DIED {}, {}", event.data, thread.pid.0); if let Err(err) = scheme.queue.unsubscribe(event.data, event.data) { log::error!("failed to unsubscribe from fd {}", event.data); @@ -157,7 +158,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { if matches!(proc.status, ProcessStatus::Exiting { .. }) { log::trace!("WAKING UP {}", proc.awaiting_threads_term.len(),); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient - } else { + } else if proc.threads.is_empty() { let internal_id = scheme.next_internal_id; scheme.next_internal_id += 1; let Entry::Vacant(entry) = states.entry(VirtualId::InternalId(internal_id)) else { @@ -1085,11 +1086,13 @@ impl<'a> ProcScheme<'a> { } let proc_rc = self.processes.get(&this_pid).ok_or(Error::new(ESRCH))?; + + log::trace!("WAITPID {target:?}"); + log::trace!("PROCS {:#?}", self.processes); + let mut proc_guard = proc_rc.borrow_mut(); let proc = &mut *proc_guard; - log::trace!("WAITPID"); - let recv_nonblock = |waitpid: &mut BTreeMap, key: &WaitpidKey| -> Option<(ProcessId, WaitpidStatus)> { @@ -1394,7 +1397,7 @@ impl<'a> ProcScheme<'a> { }, (current_pid, WaitpidStatus::Terminated { signal, status }), ); - //log::trace!("AWAKING WAITPID {:?}", parent.waitpid_waiting); + log::trace!("AWAKING WAITPID {:?}", parent.waitpid_waiting); // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } @@ -1471,7 +1474,7 @@ impl<'a> ProcScheme<'a> { mode: KillMode, awoken: &mut VecDeque, ) -> Result<()> { - log::debug!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); + log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; let mut killed_self = false; // TODO @@ -1499,7 +1502,7 @@ impl<'a> ProcScheme<'a> { .pgid, ), }; - log::debug!("match group {match_grp:?}"); + log::trace!("match group {match_grp:?}"); for (pid, proc_rc) in self.processes.iter() { if match_grp.map_or(false, |g| proc_rc.borrow().pgid != g) { From 3f54d0c80c8c9054b9abb7cf3914dff2c7c35cad Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 10 Apr 2025 15:55:18 +0200 Subject: [PATCH 103/135] Remove unnecessary debug. --- src/procmgr.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index c7b42843b1..fbfe79fbb7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1536,7 +1536,7 @@ impl<'a> ProcScheme<'a> { is_sigchld_to_parent: bool, awoken: &mut VecDeque, ) -> Result<()> { - log::debug!("SEND_SIG(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); + log::trace!("SEND_SIG(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let sig = usize::from(signal); debug_assert!(sig <= 64); let sig_group = (sig - 1) / 32; @@ -1582,7 +1582,7 @@ impl<'a> ProcScheme<'a> { let target_proc = &mut *target_proc; let Some(ref sig_pctl) = target_proc.sig_pctl else { - log::debug!("No pctl {caller_pid:?} => {target_pid:?}"); + log::trace!("No pctl {caller_pid:?} => {target_pid:?}"); return SendResult::Invalid; }; @@ -1676,7 +1676,7 @@ impl<'a> ProcScheme<'a> { KillTarget::Thread(ref thread_rc) => { let thread = thread_rc.borrow(); let Some(ref tctl) = thread.sig_ctrl else { - log::debug!("No tctl"); + log::trace!("No tctl"); return SendResult::Invalid; }; @@ -1698,7 +1698,7 @@ impl<'a> ProcScheme<'a> { match mode { KillMode::Queued(arg) => { if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { - log::debug!("Out of range"); + log::trace!("Out of range"); return SendResult::Invalid; } let rtidx = sig_idx - 32; @@ -1729,7 +1729,7 @@ impl<'a> ProcScheme<'a> { } if sig_group != 0 { - log::debug!("Invalid sig group"); + log::trace!("Invalid sig group"); return SendResult::Invalid; } sig_pctl.sender_infos[sig_idx] @@ -1773,7 +1773,7 @@ impl<'a> ProcScheme<'a> { SendResult::Succeeded => (), SendResult::FullQ => return Err(Error::new(EAGAIN)), SendResult::Invalid => { - log::debug!("Invalid signal configuration for {target_pid:?}"); + log::trace!("Invalid signal configuration for {target_pid:?}"); return Err(Error::new(EINVAL)); } SendResult::SucceededSigchld { From 4ff5ddb2420c9e4f8ad9dbab212917070e39d397 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 11 Apr 2025 13:08:54 +0200 Subject: [PATCH 104/135] Update Cargo.lock. --- Cargo.lock | 56 ++++++------------------------------------------------ 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9848284378..7e41408e6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "autocfg" version = "1.4.0" @@ -43,10 +31,10 @@ dependencies = [ ] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" @@ -66,11 +54,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", + "foldhash", ] [[package]] @@ -115,12 +103,6 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - [[package]] name = "plain" version = "0.2.3" @@ -248,29 +230,3 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] From f6898a0e45e6d4289da3a8bb80c5a477dc0c2060 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 11 Apr 2025 14:54:17 +0200 Subject: [PATCH 105/135] Pass exit status to waitpid correctly. --- src/procmgr.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index fbfe79fbb7..6547bb389d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1103,27 +1103,25 @@ impl<'a> ProcScheme<'a> { None } }; - let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| { - match status { - WaitpidStatus::Continued => { - // TODO: Handle None, i.e. restart everything until a match is found - if flags.contains(WaitFlags::WCONTINUED) { - Ready((w_pid.0, 0xffff)) - } else { - Pending - } - } - WaitpidStatus::Stopped { signal } => { - if flags.contains(WaitFlags::WUNTRACED) { - Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) - } else { - Pending - } - } - WaitpidStatus::Terminated { signal, status } => { - Ready((w_pid.0, signal.map_or(0, NonZeroU8::get).into())) + let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| match status { + WaitpidStatus::Continued => { + if flags.contains(WaitFlags::WCONTINUED) { + Ready((w_pid.0, 0xffff)) + } else { + Pending } } + WaitpidStatus::Stopped { signal } => { + if flags.contains(WaitFlags::WUNTRACED) { + Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) + } else { + Pending + } + } + WaitpidStatus::Terminated { signal, status } => Ready(( + w_pid.0, + i32::from(signal.map_or(0, NonZeroU8::get)) | (i32::from(status) << 8), + )), }; match target { From b4d42897d8ebcdbdd2c9efee495d587c11b5b7fc Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 11 Apr 2025 16:09:56 +0200 Subject: [PATCH 106/135] Return ERESTART when caller kills itself. --- src/procmgr.rs | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 6547bb389d..81d32de135 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -31,8 +31,8 @@ use syscall::{ sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, - SIGTTIN, SIGTTOU, + ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, + SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -880,8 +880,6 @@ impl<'a> ProcScheme<'a> { else { return Response::ready_err(EINVAL, op); }; - let mut killed_self = false; - let mode = match verb { ProcCall::Kill => KillMode::Idempotent, ProcCall::Sigq => KillMode::Queued({ @@ -895,7 +893,6 @@ impl<'a> ProcScheme<'a> { _ => unreachable!(), }; - let is_sigchld_to_parent = false; Ready(Response::new( self.on_kill(fd_pid, target, signal, mode, awoken) .map(|()| 0), @@ -1451,9 +1448,12 @@ impl<'a> ProcScheme<'a> { signal: u8, awoken: &mut VecDeque, ) -> Result<()> { - let mut killed_self = false; // TODO + let mut killed_self = false; + let is_sigchld_to_parent = false; + let caller_pid = thread.borrow().pid; // TODO: allow this to be specified? + self.on_send_sig( caller_pid, KillTarget::Thread(Rc::clone(thread)), @@ -1462,7 +1462,14 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, is_sigchld_to_parent, awoken, - ) + )?; + + if killed_self { + // TODO: is this the most accurate error code? + Err(Error::new(ERESTART)) + } else { + Ok(()) + } } pub fn on_kill( &mut self, @@ -1475,8 +1482,12 @@ impl<'a> ProcScheme<'a> { log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; - let mut killed_self = false; // TODO - let is_sigchld_to_parent = false; // TODO + // if this is set and we would otherwise have succeeded, return EINTR so it can check its + // own mask + let mut killed_self = false; + + // SIGCHLD to parent are not generated by on_kill, but by on_send_sig itself + let is_sigchld_to_parent = false; let match_grp = match target { ProcKillTarget::SingleProc(pid) => { @@ -1522,7 +1533,11 @@ impl<'a> ProcScheme<'a> { } } - Ok(()) + if killed_self { + Err(Error::new(ERESTART)) + } else { + Ok(()) + } } pub fn on_send_sig( &self, @@ -1567,9 +1582,11 @@ impl<'a> ProcScheme<'a> { } let result = (|| { - // FIXME - let is_self = false; - //let is_self = context::is_current(&context_lock); + // XXX: It's not currently possible for procmgr to know what thread called, so the + // EINTR will be coarser. That shouldn't affect program logic though, since the + // trampoline always checks the masks anyway. + // TODO: allow regular kill (alongside thread-kill) to operate on *thread fds*? + let is_self = target_pid == caller_pid; // If sig = 0, test that process exists and can be signalled, but don't send any // signal. From dd53cebf9177945d47c4ba3c6831aae9c56152ac Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 13 Apr 2025 21:20:42 +0200 Subject: [PATCH 107/135] Implement Sigdeq, fixing sigqueue test. --- src/procmgr.rs | 79 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 81d32de135..d86a705a0d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -19,7 +19,7 @@ use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, ThreadCall, WaitFlags}; +use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags}; use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, @@ -29,10 +29,10 @@ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, - ProcSchemeAttrs, Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, - EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, - SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, + EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ERESTART, + ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, + SIGTTIN, SIGTTOU, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -883,12 +883,12 @@ impl<'a> ProcScheme<'a> { let mode = match verb { ProcCall::Kill => KillMode::Idempotent, ProcCall::Sigq => KillMode::Queued({ - let mut buf = RtSigInfo::default(); - if payload.len() != buf.len() { + let mut buf = [0_u8; size_of::()]; + if payload.len() != size_of::() { return Response::ready_err(EINVAL, op); } buf.copy_from_slice(payload); - buf + *plain::from_bytes(&buf).unwrap() }), _ => unreachable!(), }; @@ -902,6 +902,10 @@ impl<'a> ProcScheme<'a> { ProcCall::SyncSigPctl => { Ready(Response::new(self.on_sync_sigpctl(fd_pid).map(|()| 0), op)) } + ProcCall::Sigdeq => Ready(Response::new( + self.on_sigdeq(fd_pid, payload).map(|()| 0), + op, + )), } } } @@ -1895,6 +1899,42 @@ impl<'a> ProcScheme<'a> { .replace(Page::map(&sigcontrol_fd, PAGE_SIZE, pctl_off)?); Ok(()) } + fn on_sigdeq(&mut self, pid: ProcessId, payload: &mut [u8]) -> Result<()> { + let sig_idx = { + let bytes = <[u8; 4]>::try_from(payload.get(..4).ok_or(Error::new(EINVAL))?).unwrap(); + u32::from_ne_bytes(bytes) + }; + log::trace!("SIGDEQ {pid:?} idx {sig_idx}"); + let mut dst = payload + .get_mut(..size_of::()) + .ok_or(Error::new(EINVAL))?; + if sig_idx >= 32 { + return Err(Error::new(EINVAL)); + } + let mut proc = self + .processes + .get_mut(&pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + let proc = &mut *proc; + + let pctl = proc.sig_pctl.as_ref().ok_or(Error::new(EBADF))?; + + let q = proc + .rtqs + .get_mut(sig_idx as usize) + .ok_or(Error::new(EAGAIN))?; + let Some(front) = q.pop_front() else { + return Err(Error::new(EAGAIN)); + }; + + if q.is_empty() { + pctl.pending + .fetch_and(!(1 << (32 + sig_idx as usize)), Ordering::Relaxed); + } + dst.copy_from_slice(unsafe { plain::as_bytes(&front) }); + Ok(()) + } } #[derive(Clone, Copy, Debug)] pub enum KillMode { @@ -1906,26 +1946,3 @@ pub enum KillTarget { Proc(ProcessId), Thread(Rc>), } -/* -pub fn sigdequeue(out: &mut [u8], sig_idx: u32) -> Result<()> { - let Some((_tctl, sig_pctl, st)) = current.sigcontrol() else { - return Err(Error::new(ESRCH)); - }; - if sig_idx >= 32 { - return Err(Error::new(EINVAL)); - } - let q = st - .rtqs - .get_mut(sig_idx as usize) - .ok_or(Error::new(EAGAIN))?; - let Some(front) = q.pop_front() else { - return Err(Error::new(EAGAIN)); - }; - if q.is_empty() { - sig_pctl.pending - .fetch_and(!(1 << (32 + sig_idx as usize)), Ordering::Relaxed); - } - out.copy_exactly(&front)?; - Ok(()) -} -*/ From 7bebb711d32c1b1cfcc305deb46f2fc32108fad4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 13 Apr 2025 22:36:57 +0200 Subject: [PATCH 108/135] Properly return EINVAL if sig>64. --- src/procmgr.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index d86a705a0d..7a6be01512 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1555,7 +1555,11 @@ impl<'a> ProcScheme<'a> { ) -> Result<()> { log::trace!("SEND_SIG(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let sig = usize::from(signal); - debug_assert!(sig <= 64); + + if sig > 64 { + return Err(Error::new(EINVAL)); + } + let sig_group = (sig - 1) / 32; let sig_idx = sig - 1; From 8784fb5010da8884c72ab7870cb9c60659bff598 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 14 Apr 2025 13:05:36 +0200 Subject: [PATCH 109/135] Handle stop signals correctly for orphaned pgrps, fixing bash. --- src/procmgr.rs | 231 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 157 insertions(+), 74 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 7a6be01512..dfca883f6a 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -398,6 +398,7 @@ const INIT_PID: ProcessId = ProcessId(1); struct ProcScheme<'a> { processes: HashMap>, DefaultHashBuilder>, + groups: HashMap>>, sessions: HashSet, handles: Slab, @@ -411,6 +412,10 @@ struct ProcScheme<'a> { queue: &'a RawEventQueue, auth: &'a FdGuard, } +#[derive(Debug, Default)] +struct Pgrp { + processes: Vec>>, +} #[derive(Clone, Copy, Debug)] enum WaitpidStatus { Continued, @@ -476,6 +481,7 @@ impl<'a> ProcScheme<'a> { pub fn new(auth: &'a FdGuard, queue: &'a RawEventQueue) -> ProcScheme<'a> { ProcScheme { processes: HashMap::new(), + groups: HashMap::new(), sessions: HashSet::new(), thread_lookup: HashMap::new(), handles: Slab::new(), @@ -519,31 +525,35 @@ impl<'a> ProcScheme<'a> { sig_ctrl: None, })); let thread_weak = Rc::downgrade(&thread); - self.processes.insert( + let process = Rc::new(RefCell::new(Process { + threads: vec![thread], + ppid: INIT_PID, + sid: INIT_PID, + pgid: INIT_PID, + ruid: 0, + euid: 0, + suid: 0, + rgid: 0, + egid: 0, + sgid: 0, + rns: 1, + ens: 1, + + status: ProcessStatus::PossiblyRunnable, + awaiting_threads_term: Vec::new(), + waitpid: BTreeMap::new(), + waitpid_waiting: VecDeque::new(), + + sig_pctl: None, + rtqs: Vec::new(), + })); + self.groups.insert( INIT_PID, - Rc::new(RefCell::new(Process { - threads: vec![thread], - ppid: INIT_PID, - sid: INIT_PID, - pgid: INIT_PID, - ruid: 0, - euid: 0, - suid: 0, - rgid: 0, - egid: 0, - sgid: 0, - rns: 1, - ens: 1, - - status: ProcessStatus::PossiblyRunnable, - awaiting_threads_term: Vec::new(), - waitpid: BTreeMap::new(), - waitpid_waiting: VecDeque::new(), - - sig_pctl: None, - rtqs: Vec::new(), + Rc::new(RefCell::new(Pgrp { + processes: vec![Rc::downgrade(&process)], })), ); + self.processes.insert(INIT_PID, process); self.sessions.insert(INIT_PID); self.thread_lookup.insert(fd_out, thread_weak); @@ -604,33 +614,38 @@ impl<'a> ProcScheme<'a> { sig_ctrl: None, // TODO })); let thread_weak = Rc::downgrade(&thread); + let new_process = Rc::new(RefCell::new(Process { + threads: vec![thread], + ppid: parent_pid, + pgid, + sid, + ruid, + euid, + suid, + rgid, + egid, + sgid, + rns, + ens, - self.processes.insert( - child_pid, - Rc::new(RefCell::new(Process { - threads: vec![thread], - ppid: parent_pid, - pgid, - sid, - ruid, - euid, - suid, - rgid, - egid, - sgid, - rns, - ens, + status: ProcessStatus::PossiblyRunnable, + awaiting_threads_term: Vec::new(), - status: ProcessStatus::PossiblyRunnable, - awaiting_threads_term: Vec::new(), + waitpid: BTreeMap::new(), + waitpid_waiting: VecDeque::new(), - waitpid: BTreeMap::new(), - waitpid_waiting: VecDeque::new(), + sig_pctl: None, // TODO + rtqs: Vec::new(), + })); - sig_pctl: None, // TODO - rtqs: Vec::new(), - })), - ); + if let Some(group) = self.groups.get(&pgid) { + group + .borrow_mut() + .processes + .push(Rc::downgrade(&new_process)); + } + + self.processes.insert(child_pid, new_process); self.thread_lookup.insert(thread_ident, thread_weak); Ok(child_pid) } @@ -760,7 +775,7 @@ impl<'a> ProcScheme<'a> { Handle::Init => Err(Error::new(EBADF)), } } - pub fn on_call( + fn on_call( &mut self, state: VacantEntry, mut op: OpCall, @@ -910,11 +925,7 @@ impl<'a> ProcScheme<'a> { } } } - pub fn on_getpgid( - &mut self, - caller_pid: ProcessId, - target_pid: ProcessId, - ) -> Result { + fn on_getpgid(&mut self, caller_pid: ProcessId, target_pid: ProcessId) -> Result { let caller_proc = self .processes .get(&caller_pid) @@ -934,7 +945,7 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } - pub fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { + fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { // TODO: more efficient? // POSIX: any other process's pgid matches the caller pid if self @@ -945,24 +956,26 @@ impl<'a> ProcScheme<'a> { return Err(Error::new(EPERM)); } - let mut caller_proc = self - .processes - .get(&caller_pid) - .ok_or(Error::new(ESRCH))? - .borrow_mut(); + let caller_proc_rc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + let mut caller_proc = caller_proc_rc.borrow_mut(); // POSIX: already a process group leader if caller_proc.pgid == caller_pid { return Err(Error::new(EPERM)); } - caller_proc.pgid = caller_pid; caller_proc.sid = caller_pid; + Self::set_pgid( + caller_proc_rc, + &mut *caller_proc, + &mut self.groups, + caller_pid, + )?; // TODO: Remove controlling terminal Ok(()) } - pub fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { + fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { let caller_proc = self .processes .get(&caller_pid) @@ -982,7 +995,7 @@ impl<'a> ProcScheme<'a> { Ok(requested_proc.sid) } - pub fn on_setpgid( + fn on_setpgid( &mut self, caller_pid: ProcessId, target_pid: ProcessId, @@ -993,17 +1006,48 @@ impl<'a> ProcScheme<'a> { let proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; let mut proc = proc_rc.borrow_mut(); + if proc.pgid == new_pgid { + return Ok(()); + } + // Session leaders cannot have their pgid changed. if proc.sid == target_pid { return Err(Error::new(EPERM)); } // TODO: other security checks + Self::set_pgid(proc_rc, &mut *proc, &mut self.groups, new_pgid)?; + Ok(()) + } + fn set_pgid( + proc_rc: &Rc>, + proc: &mut Process, + groups: &mut HashMap>>, + new_pgid: ProcessId, + ) -> Result<()> { + let old_pgid = proc.pgid; + assert_ne!(old_pgid, new_pgid); + + let proc_weak = Rc::downgrade(&proc_rc); + let shall_remove = { + let mut old_group = groups.get(&old_pgid).ok_or(Error::new(ESRCH))?.borrow_mut(); + old_group.processes.retain(|w| !Weak::ptr_eq(w, &proc_weak)); + old_group.processes.is_empty() + }; + if shall_remove { + groups.remove(&old_pgid); + } + groups + .entry(new_pgid) + .or_default() + .borrow_mut() + .processes + .push(proc_weak); proc.pgid = new_pgid; Ok(()) } - pub fn on_exit_start( + fn on_exit_start( &mut self, pid: ProcessId, status: i32, @@ -1068,7 +1112,7 @@ impl<'a> ProcScheme<'a> { awoken, ) } - pub fn on_waitpid( + fn on_waitpid( &mut self, this_pid: ProcessId, target: WaitpidTarget, @@ -1196,7 +1240,7 @@ impl<'a> ProcScheme<'a> { } } } - pub fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { + fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { let [new_ruid, new_euid, new_suid, new_rgid, new_egid, new_sgid] = { let raw_ids: [u32; 6] = plain::slice_from_bytes::(raw_buf) .unwrap() @@ -1278,7 +1322,7 @@ impl<'a> ProcScheme<'a> { }*/ todo!() } - pub fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { + fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; let mut process = proc_rc.borrow_mut(); @@ -1337,7 +1381,7 @@ impl<'a> ProcScheme<'a> { } Ok(()) } - pub fn work_on( + fn work_on( &mut self, mut state_entry: OccupiedEntry, awoken: &mut VecDeque, @@ -1446,7 +1490,7 @@ impl<'a> ProcScheme<'a> { log::trace!("PROCESSES\n{:#?}", self.processes,); log::trace!("HANDLES\n{:#?}", self.handles,); } - pub fn on_kill_thread( + fn on_kill_thread( &mut self, thread: &Rc>, signal: u8, @@ -1475,7 +1519,7 @@ impl<'a> ProcScheme<'a> { Ok(()) } } - pub fn on_kill( + fn on_kill( &mut self, caller_pid: ProcessId, target: ProcKillTarget, @@ -1543,7 +1587,7 @@ impl<'a> ProcScheme<'a> { Ok(()) } } - pub fn on_send_sig( + fn on_send_sig( &self, caller_pid: ProcessId, target: KillTarget, @@ -1601,10 +1645,10 @@ impl<'a> ProcScheme<'a> { let Some(nz_signal) = NonZeroU8::new(signal) else { return SendResult::Succeeded; }; - let mut target_proc = target_proc_rc.borrow_mut(); - let target_proc = &mut *target_proc; + let mut target_proc_guard = target_proc_rc.borrow_mut(); + let mut target_proc = &mut *target_proc_guard; - let Some(ref sig_pctl) = target_proc.sig_pctl else { + let Some(mut sig_pctl) = target_proc.sig_pctl.as_ref() else { log::trace!("No pctl {caller_pid:?} => {target_pid:?}"); return SendResult::Invalid; }; @@ -1647,13 +1691,29 @@ impl<'a> ProcScheme<'a> { pgid: target_proc.pgid, }; } + let is_conditional_stop = matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP); if sig == SIGSTOP - || (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP) + || (is_conditional_stop && target_proc .sig_pctl .as_ref() .map_or(false, |proc| proc.signal_will_stop(sig))) { + if is_conditional_stop { + let pgid = target_proc.pgid; + drop(target_proc_guard); + + if self.pgrp_is_orphaned(pgid).unwrap_or(true) { + // POSIX requires that processes in orphaned process groups never be stopped in + // due to SIGTTIN/SIGTTOU/SIGTSTP. + return SendResult::Succeeded; + } + + target_proc_guard = target_proc_rc.borrow_mut(); + target_proc = &mut *target_proc_guard; + sig_pctl = target_proc.sig_pctl.as_mut().expect("already checked"); + } + target_proc.status = ProcessStatus::Stopped(sig); for thread in &target_proc.threads { @@ -1879,7 +1939,7 @@ impl<'a> ProcScheme<'a> { (buf.proc_control_addr % PAGE_SIZE) as u16, ]) } - pub fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { + fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { log::trace!("Sync tctl {:?}", thread.pid); let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sighandler")?); let [tctl_off, _] = Self::real_tctl_pctl_intra_page_offsets(&sigcontrol_fd)?; @@ -1889,7 +1949,7 @@ impl<'a> ProcScheme<'a> { .replace(Page::map(&sigcontrol_fd, 0, tctl_off)?); Ok(()) } - pub fn on_sync_sigpctl(&mut self, pid: ProcessId) -> Result<()> { + fn on_sync_sigpctl(&mut self, pid: ProcessId) -> Result<()> { log::trace!("Sync pctl {pid:?}"); let mut proc = self .processes @@ -1939,6 +1999,29 @@ impl<'a> ProcScheme<'a> { dst.copy_from_slice(unsafe { plain::as_bytes(&front) }); Ok(()) } + fn pgrp_is_orphaned(&self, grp: ProcessId) -> Option { + let group = self.groups.get(&grp)?.borrow(); + + let mut still_true = true; + + for process_rc in group.processes.iter().filter_map(Weak::upgrade) { + let process = process_rc.borrow(); + let Some(parent_rc) = self.processes.get(&process_rc.borrow().ppid) else { + // TODO: what to do here? + continue; + }; + let parent = parent_rc.borrow(); + + // POSIX defines orphaned process groups as those where + // + // forall process in group, + // process's parent pgid == process's pgid + // OR + // process's session id != process's session id + still_true &= parent.pgid == process.pgid || parent.sid != process.sid; + } + Some(still_true) + } } #[derive(Clone, Copy, Debug)] pub enum KillMode { From 0036fffd8299a2193be973992206ab26222b77c5 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 15 Apr 2025 15:23:43 +0200 Subject: [PATCH 110/135] WIP: Recognize SIGKILL and unhandled exceptions. --- src/procmgr.rs | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index dfca883f6a..977e267278 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -6,8 +6,8 @@ use core::num::{NonZeroU8, NonZeroUsize}; use core::ops::Deref; use core::ptr::NonNull; use core::sync::atomic::Ordering; -use core::task::Poll; use core::task::Poll::*; +use core::task::{Context, Poll}; use alloc::collections::btree_map::BTreeMap; use alloc::collections::VecDeque; @@ -28,11 +28,11 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, - ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, - EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ERESTART, - ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, - SIGTTIN, SIGTTOU, + sig_bit, ContextStatus, ContextVerb, CtxtStsBuf, Error, Event, EventFlags, FobtainFdFlags, + MapFlags, ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, + EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, + ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, + SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -137,16 +137,24 @@ pub fn run(write_fd: usize, auth: &FdGuard) { }; let mut proc = proc_rc.borrow_mut(); log::trace!("THREAD EVENT FROM {}, {}", event.data, thread.pid.0); - let mut buf = 0_usize.to_ne_bytes(); - let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); - let status = usize::from_ne_bytes(buf); + let mut sts_buf = CtxtStsBuf::default(); + let _ = syscall::read(*thread.status_hndl, &mut sts_buf).unwrap(); - log::trace!("--STATUS {status}"); - - if status != ContextStatus::Dead as usize { + let status = if sts_buf.status == ContextStatus::Dead as usize { + // dont-care, already called explicit exit() + 0 + } else if sts_buf.status == ContextStatus::ForceKilled as usize { + // TODO: "killed by SIGKILL" + 1 + } else if sts_buf.status == ContextStatus::UnhandledExcp as usize { + // TODO: translate arch-specific exception kind + // into signal (SIGSEGV, SIGBUS, SIGILL, SIGFPE) + 1 + } else { // spurious event continue; - } + }; + log::trace!("--THREAD DIED {}, {}", event.data, thread.pid.0); if let Err(err) = scheme.queue.unsubscribe(event.data, event.data) { @@ -167,9 +175,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { }; drop(thread); drop(proc); - let Pending = - scheme.on_exit_start(pid, 0 /* TODO */, entry, &mut awoken, None) - else { + let Pending = scheme.on_exit_start(pid, status, entry, &mut awoken, None) else { unreachable!("not possible with tag=None"); }; } From baeb9d0f9c3c8ad76936cc5059f75a6b5a22230d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 11:18:07 +0200 Subject: [PATCH 111/135] Add reference to NLnet project. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 2396cc28a1..a56ea88e81 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,10 @@ To learn how to do development with this system component inside the Redox build To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page. This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux. + +## Funding - Process Manager (`src/procmgr.rs`) + +The _Unix-style Signals and Process Management_ project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals). + +[NLnet foundation logo](https://nlnet.nl) +[NGI Zero Logo](https://nlnet.nl/core) From 713ba3b23dd3239802a599b9c23abc940dc721b0 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 11:58:44 +0200 Subject: [PATCH 112/135] Import sig numbers from redox_rt rather than syscall. --- src/procmgr.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 977e267278..b677414d32 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -19,7 +19,10 @@ use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags}; +use redox_rt::protocol::{ + ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags, SIGCHLD, SIGCONT, + SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, +}; use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, @@ -31,8 +34,7 @@ use syscall::{ sig_bit, ContextStatus, ContextVerb, CtxtStsBuf, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, - SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] From 4b27f83bf9337044653fe81f37c719c981394209 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 23:01:09 +0200 Subject: [PATCH 113/135] Handle both 'any' and 'any pgid-matching' child in waitpid. --- src/procmgr.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b677414d32..643bbdeede 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1178,11 +1178,17 @@ impl<'a> ProcScheme<'a> { }; match target { - // TODO: not the same WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember => { - if let Some((wid, (w_pid, status))) = - proc.waitpid.first_key_value().map(|(k, v)| (*k, *v)) - { + let kv = (if matches!(target, WaitpidTarget::AnyChild) { + proc.waitpid.first_key_value() + } else { + proc.waitpid.get_key_value(&WaitpidKey { + pid: None, + pgid: Some(proc.pgid), + }) + }) + .map(|(k, v)| (*k, *v)); + if let Some((wid, (w_pid, status))) = kv { let _ = proc.waitpid.remove(&wid); grim_reaper(w_pid, status).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { @@ -1399,7 +1405,6 @@ impl<'a> ProcScheme<'a> { let this_state = core::mem::replace(state, PendingState::Placeholder); match this_state { PendingState::Placeholder => return Pending, // unreachable!(), - // TODO PendingState::AwaitingThreadsTermination(current_pid, tag) => { let Some(proc_rc) = self.processes.get(¤t_pid) else { return if let Some(tag) = tag { @@ -1892,7 +1897,7 @@ impl<'a> ProcScheme<'a> { ); awoken.extend(parent.waitpid_waiting.drain(..)); } - // TODO: Just ignore EINVAL (missing signal config) + // TODO: Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { let _ = self.on_send_sig( INIT_PID, // caller, TODO? @@ -1922,7 +1927,7 @@ impl<'a> ProcScheme<'a> { awoken.extend(parent.waitpid_waiting.drain(..)); } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. - // TODO: Just ignore EINVAL (missing signal config) + // TODO: Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { let _ = self.on_send_sig( INIT_PID, // caller, TODO? From 898c5268ef54af338c16944963898214ead01de4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 23:05:33 +0200 Subject: [PATCH 114/135] Fix misc TODOs. --- src/procmgr.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 643bbdeede..f15d78cffb 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -134,7 +134,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let thread = thread_rc.borrow(); let pid = thread.pid; let Some(proc_rc) = scheme.processes.get(&pid) else { - // TODO? + // TODO(err)? continue; }; let mut proc = proc_rc.borrow_mut(); @@ -167,7 +167,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { if matches!(proc.status, ProcessStatus::Exiting { .. }) { log::trace!("WAKING UP {}", proc.awaiting_threads_term.len(),); - awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient + awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO(opt) } else if proc.threads.is_empty() { let internal_id = scheme.next_internal_id; scheme.next_internal_id += 1; @@ -450,7 +450,7 @@ enum WaitpidTarget { AnyChild, AnyGroupMember, } -// TODO: Add 'syscall' backend for redox-event so it can act both as library-ABI frontend and +// TODO(feat): Add 'syscall' backend for redox-event so it can act both as library-ABI frontend and // backend struct RawEventQueue(FdGuard); impl RawEventQueue { @@ -1132,7 +1132,7 @@ impl<'a> ProcScheme<'a> { WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember ) { // Check for existence of child. - // TODO: inefficient, keep refcount? + // TODO(opt): inefficient, keep refcount? if !self.processes.values().any(|p| p.borrow().ppid == this_pid) { return Ready(Err(Error::new(ECHILD))); } @@ -1445,7 +1445,7 @@ impl<'a> ProcScheme<'a> { let (ppid, pgid) = (proc.ppid, proc.pgid); if let Some(parent_rc) = self.processes.get(&ppid) { let mut parent = parent_rc.borrow_mut(); - // TODO: transfer children to parent, and all of self.waitpid + // TODO(posix): transfer children to parent, and all of self.waitpid parent.waitpid.insert( WaitpidKey { pid: Some(current_pid), @@ -1454,7 +1454,7 @@ impl<'a> ProcScheme<'a> { (current_pid, WaitpidStatus::Terminated { signal, status }), ); log::trace!("AWAKING WAITPID {:?}", parent.waitpid_waiting); - // TODO: inefficient + // TODO(opt): inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } if let Some(tag) = tag { @@ -1513,7 +1513,7 @@ impl<'a> ProcScheme<'a> { let is_sigchld_to_parent = false; - let caller_pid = thread.borrow().pid; // TODO: allow this to be specified? + let caller_pid = thread.borrow().pid; // TODO(feat): allow this to be specified? self.on_send_sig( caller_pid, @@ -1628,7 +1628,12 @@ impl<'a> ProcScheme<'a> { let sender = SenderInfo { pid: caller_pid.0 as u32, - ruid: 0, // TODO + ruid: self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow() + .ruid, }; enum SendResult { @@ -1650,7 +1655,7 @@ impl<'a> ProcScheme<'a> { // XXX: It's not currently possible for procmgr to know what thread called, so the // EINTR will be coarser. That shouldn't affect program logic though, since the // trampoline always checks the masks anyway. - // TODO: allow regular kill (alongside thread-kill) to operate on *thread fds*? + // TODO(feat): allow regular kill (alongside thread-kill) to operate on *thread fds*? let is_self = target_pid == caller_pid; // If sig = 0, test that process exists and can be signalled, but don't send any @@ -1804,7 +1809,7 @@ impl<'a> ProcScheme<'a> { } let rtq = target_proc.rtqs.get_mut(rtidx).unwrap(); - // TODO: configurable limit? + // TODO(feat): configurable limit? if rtq.len() > 32 { return SendResult::FullQ; } @@ -1897,7 +1902,7 @@ impl<'a> ProcScheme<'a> { ); awoken.extend(parent.waitpid_waiting.drain(..)); } - // TODO: Just ignore EINVAL (missing signal config), otherwise handle error? + // TODO(err): Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { let _ = self.on_send_sig( INIT_PID, // caller, TODO? @@ -1927,7 +1932,7 @@ impl<'a> ProcScheme<'a> { awoken.extend(parent.waitpid_waiting.drain(..)); } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. - // TODO: Just ignore EINVAL (missing signal config), otherwise handle error? + // TODO(err): Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { let _ = self.on_send_sig( INIT_PID, // caller, TODO? @@ -2020,7 +2025,7 @@ impl<'a> ProcScheme<'a> { for process_rc in group.processes.iter().filter_map(Weak::upgrade) { let process = process_rc.borrow(); let Some(parent_rc) = self.processes.get(&process_rc.borrow().ppid) else { - // TODO: what to do here? + // TODO(err): what to do here? continue; }; let parent = parent_rc.borrow(); From c881b1469862bda41782176f880693be1815dcdd Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 23:45:06 +0200 Subject: [PATCH 115/135] Allow indicating 'exited from signal'. --- src/procmgr.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index f15d78cffb..d2804c4cc6 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -835,7 +835,7 @@ impl<'a> ProcScheme<'a> { )), ProcCall::Exit => self.on_exit_start( fd_pid, - metadata[1] as i32, + metadata[1] as u16, state, awoken, Some(op.into_tag()), @@ -1058,7 +1058,7 @@ impl<'a> ProcScheme<'a> { fn on_exit_start( &mut self, pid: ProcessId, - status: i32, + status: u16, mut state: VacantEntry, awoken: &mut VecDeque, tag: Option, @@ -1091,11 +1091,29 @@ impl<'a> ProcScheme<'a> { } } } - // TODO: status/signal - process.status = ProcessStatus::Exiting { - status: status as u8, - signal: None, + + // Forbid the caller from giving statuses corresponding to e.g. WIFCONTINUED which exit() + // obviously can never be. + + log::debug!("Killed with raw status {status:?}"); + + // TODO: Are WIFEXITED and WIFSIGNALED mutually exclusive? + let (status, signal) = if status & 0xff == status { + (status as u8, None) + } else { + // TODO: Only allow valid and catchable signal numbers. + let sig = (status >> 8) as u8; + if !matches!(sig, 1..=64) { + return if let Some(tag) = tag { + Response::ready_err(EINVAL, tag) + } else { + Pending + }; + } + (0, NonZeroU8::new(sig)) }; + + process.status = ProcessStatus::Exiting { status, signal }; if !process.threads.is_empty() { // terminate all threads (possibly including the caller, resulting in EINTR and a // to-be-ignored cancellation request to this scheme). From 26371995cbc72ecf2ea35eccbe6ab15b37b10ed6 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Apr 2025 00:21:21 +0200 Subject: [PATCH 116/135] Fix kill(getpid(), ...). --- src/procmgr.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index d2804c4cc6..7f59565a66 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1095,7 +1095,7 @@ impl<'a> ProcScheme<'a> { // Forbid the caller from giving statuses corresponding to e.g. WIFCONTINUED which exit() // obviously can never be. - log::debug!("Killed with raw status {status:?}"); + log::trace!("Killed with raw status {status:?}"); // TODO: Are WIFEXITED and WIFSIGNALED mutually exclusive? let (status, signal) = if status & 0xff == status { @@ -1570,7 +1570,7 @@ impl<'a> ProcScheme<'a> { let match_grp = match target { ProcKillTarget::SingleProc(pid) => { - return self.on_send_sig( + self.on_send_sig( caller_pid, KillTarget::Proc(ProcessId(pid)), signal, @@ -1578,7 +1578,12 @@ impl<'a> ProcScheme<'a> { mode, is_sigchld_to_parent, awoken, - ) + )?; + return if killed_self { + Err(Error::new(ERESTART)) + } else { + Ok(()) + }; } ProcKillTarget::All => None, ProcKillTarget::ProcGroup(grp) => Some(ProcessId(grp)), From 486c8496b567c7ae07060152ea6ef9e2fd0935dd Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Apr 2025 00:28:52 +0200 Subject: [PATCH 117/135] Add missing kill uid checks. --- src/procmgr.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 7f59565a66..12659ae3bf 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1660,6 +1660,7 @@ impl<'a> ProcScheme<'a> { }; enum SendResult { + LacksPermission, Succeeded, SucceededSigchld { orig_signal: NonZeroU8, @@ -1673,6 +1674,14 @@ impl<'a> ProcScheme<'a> { FullQ, Invalid, } + let (caller_euid, caller_ruid) = { + let caller = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); + (caller.euid, caller.ruid) + }; let result = (|| { // XXX: It's not currently possible for procmgr to know what thread called, so the @@ -1681,13 +1690,21 @@ impl<'a> ProcScheme<'a> { // TODO(feat): allow regular kill (alongside thread-kill) to operate on *thread fds*? let is_self = target_pid == caller_pid; + let mut target_proc_guard = target_proc_rc.borrow_mut(); + let mut target_proc = &mut *target_proc_guard; + + if caller_euid != 0 + && caller_euid != target_proc.ruid + && caller_ruid != target_proc.ruid + { + return SendResult::LacksPermission; + } + // If sig = 0, test that process exists and can be signalled, but don't send any // signal. let Some(nz_signal) = NonZeroU8::new(signal) else { return SendResult::Succeeded; }; - let mut target_proc_guard = target_proc_rc.borrow_mut(); - let mut target_proc = &mut *target_proc_guard; let Some(mut sig_pctl) = target_proc.sig_pctl.as_ref() else { log::trace!("No pctl {caller_pid:?} => {target_pid:?}"); @@ -1894,6 +1911,9 @@ impl<'a> ProcScheme<'a> { })(); match result { + // TODO: succeed even if *some* (when group/all procs is specified) fail? + SendResult::LacksPermission => return Err(Error::new(EPERM)), + SendResult::Succeeded => (), SendResult::FullQ => return Err(Error::new(EAGAIN)), SendResult::Invalid => { From 3e58f6ad16bfba8762b2e7ef7503bbb10856ec98 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Apr 2025 00:56:16 +0200 Subject: [PATCH 118/135] Mod sig_idx by 32. --- src/procmgr.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 12659ae3bf..e765023875 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1641,7 +1641,7 @@ impl<'a> ProcScheme<'a> { } let sig_group = (sig - 1) / 32; - let sig_idx = sig - 1; + let sig_idx = (sig - 1) % 32; let target_pid = match target { KillTarget::Proc(pid) => pid, @@ -1838,11 +1838,11 @@ impl<'a> ProcScheme<'a> { KillTarget::Proc(proc) => { match mode { KillMode::Queued(arg) => { - if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { + if sig_group != 1 { log::trace!("Out of range"); return SendResult::Invalid; } - let rtidx = sig_idx - 32; + let rtidx = sig_idx; //log::trace!("QUEUEING {arg:?} RTIDX {rtidx}"); if rtidx >= target_proc.rtqs.len() { target_proc.rtqs.resize_with(rtidx + 1, VecDeque::new); @@ -1918,7 +1918,7 @@ impl<'a> ProcScheme<'a> { SendResult::FullQ => return Err(Error::new(EAGAIN)), SendResult::Invalid => { log::trace!("Invalid signal configuration for {target_pid:?}"); - return Err(Error::new(EINVAL)); + return Err(Error::new(ESRCH)); } SendResult::SucceededSigchld { ppid, From 771993b260b4a50ea3bdb47be6f7fb7e92839b3f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Apr 2025 13:15:34 +0200 Subject: [PATCH 119/135] Potential fix for KillThread for sig>32. --- src/procmgr.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index e765023875..302ddd9056 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1822,11 +1822,10 @@ impl<'a> ProcScheme<'a> { }; tctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); + let bit = 1 << sig_idx; - let _was_new = - tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Release); - if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 - { + let _was_new = tctl.word[sig_group].fetch_or(bit, Ordering::Release); + if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & bit != 0 { *killed_self |= is_self; let _ = syscall::write( *thread.status_hndl, From 5cbcb06727aa8436a72282b89e8c6eb2cf94abc6 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 18 Apr 2025 10:48:48 +0200 Subject: [PATCH 120/135] Fix ECHILD check and reap, fixing bash. --- src/procmgr.rs | 199 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 140 insertions(+), 59 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 302ddd9056..7e335248b7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -876,7 +876,8 @@ impl<'a> ProcScheme<'a> { )) } else { Ready(Response::new( - self.on_setpgid(fd_pid, target_pid, new_pgid).map(|()| 0), + self.on_setpgid(fd_pid, target_pid, new_pgid, awoken) + .map(|()| 0), op, )) } @@ -889,9 +890,10 @@ impl<'a> ProcScheme<'a> { op, )) } - ProcCall::Setsid => { - Ready(Response::new(self.on_setsid(fd_pid).map(|()| 0), op)) - } + ProcCall::Setsid => Ready(Response::new( + self.on_setsid(fd_pid, awoken).map(|()| 0), + op, + )), ProcCall::SetResugid => Ready(Response::new( self.on_setresugid(fd_pid, payload).map(|()| 0), op, @@ -934,6 +936,7 @@ impl<'a> ProcScheme<'a> { } } fn on_getpgid(&mut self, caller_pid: ProcessId, target_pid: ProcessId) -> Result { + log::trace!("GETPGID from {caller_pid:?} target {target_pid:?}"); let caller_proc = self .processes .get(&caller_pid) @@ -953,7 +956,7 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } - fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { + fn on_setsid(&mut self, caller_pid: ProcessId, awoken: &mut VecDeque) -> Result<()> { // TODO: more efficient? // POSIX: any other process's pgid matches the caller pid if self @@ -966,19 +969,28 @@ impl<'a> ProcScheme<'a> { let caller_proc_rc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; let mut caller_proc = caller_proc_rc.borrow_mut(); + let mut parent = (caller_proc.ppid != caller_pid) + .then(|| { + self.processes + .get(&caller_proc.ppid) + .map(|p| p.borrow_mut()) + }) + .ok_or(Error::new(ESRCH))?; // POSIX: already a process group leader if caller_proc.pgid == caller_pid { return Err(Error::new(EPERM)); } - caller_proc.sid = caller_pid; Self::set_pgid( caller_proc_rc, &mut *caller_proc, + parent.as_deref_mut(), &mut self.groups, caller_pid, + awoken, )?; + caller_proc.sid = caller_pid; // TODO: Remove controlling terminal Ok(()) @@ -1008,12 +1020,17 @@ impl<'a> ProcScheme<'a> { caller_pid: ProcessId, target_pid: ProcessId, new_pgid: ProcessId, + awoken: &mut VecDeque, ) -> Result<()> { - let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + //let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; let proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; let mut proc = proc_rc.borrow_mut(); + let mut parent = (proc.ppid != target_pid) + .then(|| self.processes.get(&proc.ppid).map(|p| p.borrow_mut())) + .ok_or(Error::new(ESRCH))?; + if proc.pgid == new_pgid { return Ok(()); } @@ -1023,20 +1040,36 @@ impl<'a> ProcScheme<'a> { return Err(Error::new(EPERM)); } - // TODO: other security checks - Self::set_pgid(proc_rc, &mut *proc, &mut self.groups, new_pgid)?; + // TODO: other security checks? + Self::set_pgid( + proc_rc, + &mut *proc, + parent.as_deref_mut(), + &mut self.groups, + new_pgid, + awoken, + )?; Ok(()) } fn set_pgid( proc_rc: &Rc>, proc: &mut Process, + parent: Option<&mut Process>, groups: &mut HashMap>>, new_pgid: ProcessId, + awoken: &mut VecDeque, ) -> Result<()> { let old_pgid = proc.pgid; assert_ne!(old_pgid, new_pgid); + if let Some(parent) = parent { + // Some waitpid waiters may end up waiting for no children, if a child sets its pgid + // and the parent was waiting with a pgid filter. Ensure the waiter is awoken and + // possibly returns ECHILD. + awoken.extend(parent.waitpid_waiting.drain(..)); + } + let proc_weak = Rc::downgrade(&proc_rc); let shall_remove = { let mut old_group = groups.get(&old_pgid).ok_or(Error::new(ESRCH))?.borrow_mut(); @@ -1158,7 +1191,6 @@ impl<'a> ProcScheme<'a> { let proc_rc = self.processes.get(&this_pid).ok_or(Error::new(ESRCH))?; - log::trace!("WAITPID {target:?}"); log::trace!("PROCS {:#?}", self.processes); let mut proc_guard = proc_rc.borrow_mut(); @@ -1174,26 +1206,30 @@ impl<'a> ProcScheme<'a> { None } }; - let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| match status { - WaitpidStatus::Continued => { - if flags.contains(WaitFlags::WCONTINUED) { - Ready((w_pid.0, 0xffff)) - } else { - Pending + let mut grim_reaper = + |w_pid: ProcessId, status: WaitpidStatus, scheme: &mut ProcScheme| match status { + WaitpidStatus::Continued => { + if flags.contains(WaitFlags::WCONTINUED) { + Ready((w_pid.0, 0xffff)) + } else { + Pending + } } - } - WaitpidStatus::Stopped { signal } => { - if flags.contains(WaitFlags::WUNTRACED) { - Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) - } else { - Pending + WaitpidStatus::Stopped { signal } => { + if flags.contains(WaitFlags::WUNTRACED) { + Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) + } else { + Pending + } } - } - WaitpidStatus::Terminated { signal, status } => Ready(( - w_pid.0, - i32::from(signal.map_or(0, NonZeroU8::get)) | (i32::from(status) << 8), - )), - }; + WaitpidStatus::Terminated { signal, status } => { + scheme.reap(w_pid); + Ready(( + w_pid.0, + i32::from(signal.map_or(0, NonZeroU8::get)) | (i32::from(status) << 8), + )) + } + }; match target { WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember => { @@ -1208,7 +1244,8 @@ impl<'a> ProcScheme<'a> { .map(|(k, v)| (*k, *v)); if let Some((wid, (w_pid, status))) = kv { let _ = proc.waitpid.remove(&wid); - grim_reaper(w_pid, status).map(Ok) + drop(proc_guard); + grim_reaper(w_pid, status, self).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -1232,11 +1269,15 @@ impl<'a> ProcScheme<'a> { }; if let ProcessStatus::Exited { status, signal } = target_proc.status { let _ = recv_nonblock(&mut proc.waitpid, &key); - grim_reaper(pid, WaitpidStatus::Terminated { signal, status }).map(Ok) + drop(proc_guard); + drop(target_proc); + grim_reaper(pid, WaitpidStatus::Terminated { signal, status }, self).map(Ok) } else { let res = recv_nonblock(&mut proc.waitpid, &key); if let Some((w_pid, status)) = res { - grim_reaper(w_pid, status).map(Ok) + drop(proc_guard); + drop(target_proc); + grim_reaper(w_pid, status, self).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -1246,13 +1287,18 @@ impl<'a> ProcScheme<'a> { } } WaitpidTarget::ProcGroup(pgid) => { - let this_pgid = proc.pgid; - if !self - .processes - .iter() - .filter(|(pid, _)| **pid != this_pid) - .any(|(_, p)| p.borrow().pgid == this_pgid) - { + if let Some(group_rc) = self.groups.get(&pgid) { + let group = group_rc.borrow(); + if !group + .processes + .iter() + .filter_map(Weak::upgrade) + .filter(|r| !Rc::ptr_eq(r, proc_rc)) + .any(|p| p.borrow().ppid == this_pid) + { + return Ready(Err(Error::new(ECHILD))); + } + } else { return Ready(Err(Error::new(ECHILD))); } @@ -1262,7 +1308,8 @@ impl<'a> ProcScheme<'a> { }; if let Some(&(w_pid, status)) = proc.waitpid.get(&key) { let _ = proc.waitpid.remove(&key); - grim_reaper(w_pid, status).map(Ok) + drop(proc_guard); + grim_reaper(w_pid, status, self).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -1272,6 +1319,38 @@ impl<'a> ProcScheme<'a> { } } } + fn reap(&mut self, pid: ProcessId) { + let Entry::Occupied(entry) = self.processes.entry(pid) else { + return; + }; + let pgid = { + let proc = entry.get().borrow(); + if !proc.threads.is_empty() { + log::error!( + "reaping process (pid {pid:?} with remaining threads: {:#?}", + proc.threads + ); + return; + } + proc.pgid + }; + let proc_rc = entry.remove(); + let proc_weak = Rc::downgrade(&proc_rc); + + let Entry::Occupied(group) = self.groups.entry(pgid) else { + log::error!("Process missing from its group"); + return; + }; + group + .get() + .borrow_mut() + .processes + .retain(|p| !Weak::ptr_eq(&proc_weak, p)); + if group.get().borrow_mut().processes.is_empty() { + group.remove(); + } + // TODO: notify parent's other waiters if ECHILD would now occur? + } fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { let [new_ruid, new_euid, new_suid, new_rgid, new_egid, new_sgid] = { let raw_ids: [u32; 6] = plain::slice_from_bytes::(raw_buf) @@ -1343,17 +1422,6 @@ impl<'a> ProcScheme<'a> { procs: &self.processes, } } - fn check_waitpid_queues( - &mut self, - waiter: ProcessId, - target: WaitpidTarget, - mask: WaitFlags, - ) -> Option<(ProcessId, i32)> { - /*match target { - //WaitpidTarget::SingleProc(target_pid) => , - }*/ - todo!() - } fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; let mut process = proc_rc.borrow_mut(); @@ -1494,9 +1562,13 @@ impl<'a> ProcScheme<'a> { flags, mut op, } => { - log::trace!("WORKING ON AWAIT STS CHANGE"); + log::trace!("WAITPID {req_id:?}, {waiter:?}: {target:?} flags {flags:?}"); + let res = self.on_waitpid(waiter, target, flags, req_id); + log::trace!( + "WAITPID {req_id:?}, {waiter:?}: {target:?} flags {flags:?} -> {res:?}" + ); - match self.on_waitpid(waiter, target, flags, req_id) { + match res { Ready(Ok((pid, status))) => { if let Ok(status_out) = plain::from_mut_bytes::(op.payload()) { *status_out = status; @@ -1597,6 +1669,8 @@ impl<'a> ProcScheme<'a> { }; log::trace!("match group {match_grp:?}"); + let mut err_opt = None; + for (pid, proc_rc) in self.processes.iter() { if match_grp.map_or(false, |g| proc_rc.borrow().pgid != g) { continue; @@ -1612,12 +1686,15 @@ impl<'a> ProcScheme<'a> { ); match res { Ok(()) => (), - Err(err) if num_succeeded > 0 => break, - Err(err) => return Err(err), + Err(err) => err_opt = Some(err), } } - if killed_self { + if num_succeeded == 0 + && let Some(err) = err_opt + { + Err(err) + } else if killed_self { Err(Error::new(ERESTART)) } else { Ok(()) @@ -1946,7 +2023,7 @@ impl<'a> ProcScheme<'a> { } // TODO(err): Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { - let _ = self.on_send_sig( + if let Err(err) = self.on_send_sig( INIT_PID, // caller, TODO? KillTarget::Proc(ppid), SIGCHLD as u8, @@ -1954,7 +2031,9 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, true, awoken, - ); + ) { + log::trace!("failed to SIGCHLD parent (SIGSTOP): {err}"); + } } } SendResult::SucceededSigcont { ppid, pgid } => { @@ -1976,7 +2055,7 @@ impl<'a> ProcScheme<'a> { // POSIX XSI allows but does not require SIGCONT to send signals to the parent. // TODO(err): Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { - let _ = self.on_send_sig( + if let Err(err) = self.on_send_sig( INIT_PID, // caller, TODO? KillTarget::Proc(ppid), SIGCHLD as u8, @@ -1984,7 +2063,9 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, true, awoken, - ); + ) { + log::trace!("failed to SIGCHLD parent (SIGCONT): {err}"); + } } } } From ba823cf5f1fc52986e8c6a1c7f0cd2ffcec0c01c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 18 Apr 2025 20:07:59 +0200 Subject: [PATCH 121/135] Add ProcCall::Getppid and implement reparenting. --- src/procmgr.rs | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 7e335248b7..4869a6de61 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -321,9 +321,9 @@ struct Process { rtqs: Vec>, } #[derive(Copy, Clone, Debug)] -pub struct WaitpidKey { - pub pid: Option, - pub pgid: Option, +struct WaitpidKey { + pid: Option, + pgid: Option, } // TODO: Is this valid? (transitive?) @@ -890,6 +890,10 @@ impl<'a> ProcScheme<'a> { op, )) } + ProcCall::Getppid => Ready(Response::new( + self.on_getppid(fd_pid).map(|ProcessId(p)| p), + op, + )), ProcCall::Setsid => Ready(Response::new( self.on_setsid(fd_pid, awoken).map(|()| 0), op, @@ -995,6 +999,17 @@ impl<'a> ProcScheme<'a> { // TODO: Remove controlling terminal Ok(()) } + fn on_getppid(&mut self, caller_pid: ProcessId) -> Result { + log::trace!("GETPPID {caller_pid:?}"); + let ppid = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow() + .ppid; + log::trace!("GETPPID {caller_pid:?} -> {ppid:?}"); + Ok(ppid) + } fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { let caller_proc = self .processes @@ -1528,10 +1543,25 @@ impl<'a> ProcScheme<'a> { state_entry.remove(); proc.status = ProcessStatus::Exited { signal, status }; + let (ppid, pgid) = (proc.ppid, proc.pgid); + drop(proc_guard); + if let Some(parent_rc) = self.processes.get(&ppid) { let mut parent = parent_rc.borrow_mut(); - // TODO(posix): transfer children to parent, and all of self.waitpid + + // Transfer children to parent (TODO: to init) + for child_rc in self + .processes + .values() + .filter(|p| !Rc::ptr_eq(p, parent_rc)) + .filter(|p| p.borrow().ppid == current_pid) + { + let mut child = child_rc.borrow_mut(); + child.ppid = ppid; + parent.waitpid.append(&mut child.waitpid); + } + parent.waitpid.insert( WaitpidKey { pid: Some(current_pid), @@ -2165,12 +2195,12 @@ impl<'a> ProcScheme<'a> { } } #[derive(Clone, Copy, Debug)] -pub enum KillMode { +enum KillMode { Idempotent, Queued(RtSigInfo), } #[derive(Debug)] -pub enum KillTarget { +enum KillTarget { Proc(ProcessId), Thread(Rc>), } From 7fc0f04f0354154d4fea5b776990112795250871 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 18 Apr 2025 22:07:40 +0200 Subject: [PATCH 122/135] Store process name. --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/procmgr.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 7e41408e6c..c348ca3848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.4.0" @@ -18,6 +24,7 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" name = "bootstrap" version = "0.0.0" dependencies = [ + "arrayvec", "hashbrown", "linked_list_allocator", "log", diff --git a/Cargo.toml b/Cargo.toml index 4b2536203d..37064b84b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } redox-path = "0.3.1" slab = { version = "0.4.9", default-features = false } +arrayvec = { version = "0.7.6", default-features = false } [profile.release] panic = "abort" diff --git a/src/procmgr.rs b/src/procmgr.rs index 4869a6de61..1e37ea3ff7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -5,6 +5,7 @@ use core::mem::size_of; use core::num::{NonZeroU8, NonZeroUsize}; use core::ops::Deref; use core::ptr::NonNull; +use core::str::FromStr; use core::sync::atomic::Ordering; use core::task::Poll::*; use core::task::{Context, Poll}; @@ -15,6 +16,7 @@ use alloc::rc::{Rc, Weak}; use alloc::vec; use alloc::vec::Vec; +use arrayvec::ArrayString; use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; @@ -294,12 +296,15 @@ impl Drop for Page { } } +const NAME_CAPAC: usize = 32; + #[derive(Debug)] struct Process { threads: Vec>>, ppid: ProcessId, pgid: ProcessId, sid: ProcessId, + name: ArrayString, ruid: u32, euid: u32, @@ -546,6 +551,7 @@ impl<'a> ProcScheme<'a> { sgid: 0, rns: 1, ens: 1, + name: ArrayString::<32>::from_str("[init]").unwrap(), status: ProcessStatus::PossiblyRunnable, awaiting_threads_term: Vec::new(), @@ -588,6 +594,7 @@ impl<'a> ProcScheme<'a> { sgid, ens, rns, + name, .. } = *proc_guard.borrow(); @@ -603,6 +610,7 @@ impl<'a> ProcScheme<'a> { euid, egid, ens, + debug_name: arraystring_to_bytes(name), }, )?; let status_fd = FdGuard::new(syscall::dup( @@ -635,6 +643,7 @@ impl<'a> ProcScheme<'a> { sgid, rns, ens, + name, status: ProcessStatus::PossiblyRunnable, awaiting_threads_term: Vec::new(), @@ -675,6 +684,7 @@ impl<'a> ProcScheme<'a> { euid: proc.euid, egid: proc.egid, ens: proc.ens, + debug_name: arraystring_to_bytes(proc.name), }, )?; @@ -935,6 +945,10 @@ impl<'a> ProcScheme<'a> { self.on_sigdeq(fd_pid, payload).map(|()| 0), op, )), + ProcCall::Rename => Ready(Response::new( + self.on_proc_rename(fd_pid, payload).map(|()| 0), + op, + )), } } } @@ -2110,6 +2124,16 @@ impl<'a> ProcScheme<'a> { (buf.proc_control_addr % PAGE_SIZE) as u16, ]) } + fn on_proc_rename(&mut self, pid: ProcessId, new_name_raw: &[u8]) -> Result<()> { + let new_name = core::str::from_utf8(new_name_raw).map_err(|_| Error::new(EINVAL))?; + let mut proc = self + .processes + .get(&pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + proc.name = ArrayString::from_str(&new_name[..new_name.len().min(NAME_CAPAC)]).unwrap(); + Ok(()) + } fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { log::trace!("Sync tctl {:?}", thread.pid); let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sighandler")?); @@ -2204,3 +2228,9 @@ enum KillTarget { Proc(ProcessId), Thread(Rc>), } +fn arraystring_to_bytes(s: ArrayString) -> [u8; C] { + let mut buf = [0_u8; C]; + let min = buf.len().min(s.len()); + buf[..min].copy_from_slice(&s.as_bytes()[..min]); + buf +} From 1b092a11d5d2052edff2dc283a96dd8f3d8af6f6 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 18 Apr 2025 22:44:11 +0200 Subject: [PATCH 123/135] Add /scheme/proc/ps handler for reading info. --- src/procmgr.rs | 121 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 15 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 1e37ea3ff7..403bb1cc2b 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -199,6 +199,7 @@ fn handle_scheme<'a>( ) -> Poll { match req.kind() { RequestKind::Call(req) => { + let caller = req.caller(); let req_id = VirtualId::KernelId(req.request_id()); let op = match req.op() { Ok(op) => op, @@ -206,11 +207,14 @@ fn handle_scheme<'a>( }; match op { Op::Open(op) => Ready(Response::open_dup_like( - scheme.on_open(op.path(), op.flags), + scheme.on_open(op.path(), op.flags, &caller), op, )), Op::Dup(op) => Ready(Response::open_dup_like(scheme.on_dup(op.fd, op.buf()), op)), - Op::Read(mut op) => Ready(Response::new(scheme.on_read(op.fd, op.buf()), op)), + Op::Read(mut op) => Ready(Response::new( + scheme.on_read(op.fd, op.offset, op.buf()), + op, + )), Op::Call(op) => scheme.on_call( { // TODO: cleanup @@ -224,9 +228,25 @@ fn handle_scheme<'a>( op, awoken, ), + Op::Fsize { req, fd } => { + if let Handle::Ps(ref b) = &scheme.handles[fd] { + Response::ready_ok(b.len(), req) + } else { + Response::ready_err(EOPNOTSUPP, req) + } + } + Op::Fstat(mut op) => { + if let Handle::Ps(ref b) = &scheme.handles[op.fd] { + op.buf().st_size = b.len() as _; + op.buf().st_mode = syscall::MODE_FILE | 0o444; + Response::ready_ok(0, op) + } else { + Response::ready_err(EOPNOTSUPP, op) + } + } _ => { log::trace!("UNKNOWN: {op:?}"); - Ready(Response::new(Err(Error::new(ENOSYS)), op)) + Response::ready_err(ENOSYS, op) } } } @@ -446,6 +466,9 @@ enum Handle { Init, Proc(ProcessId), Thread(Rc>), + + // TODO: stateless API, perhaps using intermediate daemon for providing a file-like API + Ps(Vec), } #[derive(Clone, Copy, Debug)] @@ -709,19 +732,30 @@ impl<'a> ProcScheme<'a> { self.thread_lookup.insert(ident, thread_weak); Ok(thread) } - fn on_open(&mut self, path: &str, flags: usize) -> Result { - if path == "init" { - if core::mem::replace(&mut self.init_claimed, true) { - return Err(Error::new(EEXIST)); + fn on_open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + let path = path.trim_start_matches('/'); + Ok(match path { + "init" => { + if core::mem::replace(&mut self.init_claimed, true) { + return Err(Error::new(EEXIST)); + } + OpenResult::ThisScheme { + number: self.handles.insert(Handle::Init), + flags: NewFdFlags::empty(), + } } - return Ok(OpenResult::ThisScheme { - number: self.handles.insert(Handle::Init), - flags: NewFdFlags::empty(), - }); - } - Err(Error::new(ENOENT)) + "ps" => { + let data = self.ps_data(ctx)?; + OpenResult::ThisScheme { + number: self.handles.insert(Handle::Ps(data)), + flags: NewFdFlags::POSITIONED, + } + } + + _ => return Err(Error::new(ENOENT)), + }) } - fn on_read(&mut self, id: usize, buf: &mut [u8]) -> Result { + fn on_read(&mut self, id: usize, offset: u64, buf: &mut [u8]) -> Result { match self.handles[id] { Handle::Proc(pid) => { let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; @@ -744,6 +778,15 @@ impl<'a> ProcScheme<'a> { .ok_or(Error::new(EINVAL))? = metadata; Ok(size_of::()) } + Handle::Ps(ref src_buf) => { + let src_buf = usize::try_from(offset) + .ok() + .and_then(|o| src_buf.get(o..)) + .unwrap_or(&[]); + let len = src_buf.len().min(buf.len()); + buf[..len].copy_from_slice(&src_buf[..len]); + Ok(len) + } Handle::Init | Handle::Thread(_) => return Err(Error::new(EBADF)), } } @@ -790,7 +833,7 @@ impl<'a> ProcScheme<'a> { fd: syscall::dup(*thread.fd, buf)?, }) } - Handle::Init => Err(Error::new(EBADF)), + Handle::Init | Handle::Ps(_) => Err(Error::new(EBADF)), } } fn on_call( @@ -951,6 +994,7 @@ impl<'a> ProcScheme<'a> { )), } } + Handle::Ps(_) => Response::ready_err(EOPNOTSUPP, op), } } fn on_getpgid(&mut self, caller_pid: ProcessId, target_pid: ProcessId) -> Result { @@ -2217,6 +2261,53 @@ impl<'a> ProcScheme<'a> { } Some(still_true) } + fn ps_data(&mut self, _ctx: &CallerCtx) -> Result> { + // TODO: enforce uid == 0? + + let mut string = alloc::format!( + "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}\n", + "PID", + "PGID", + "PPID", + "SID", + "RUID", + "RGID", + "RNS", + "EUID", + "EGID", + "ENS", + "STATUS", + "NAME", + ); + for (pid, process_rc) in self.processes.iter() { + let process = process_rc.borrow(); + let status = match process.status { + ProcessStatus::PossiblyRunnable => "R", + ProcessStatus::Stopped(_) => "S", + ProcessStatus::Exiting { .. } => "E", + ProcessStatus::Exited { .. } => "X", + }; + use core::fmt::Write; + writeln!( + string, + "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}", + pid.0, + process.pgid.0, + process.ppid.0, + process.sid.0, + process.ruid, + process.rgid, + process.rns, + process.euid, + process.egid, + process.ens, + status, + process.name, + ) + .unwrap(); + } + Ok(string.into_bytes()) + } } #[derive(Clone, Copy, Debug)] enum KillMode { From c6de9f768c1fa8b2d9398251083925b65e411f52 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 00:10:23 +0200 Subject: [PATCH 124/135] Make thread fds Weak; reparent to init, not ppid. --- src/procmgr.rs | 77 +++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 403bb1cc2b..cde4beb374 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -35,8 +35,8 @@ use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, ContextVerb, CtxtStsBuf, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, - EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, + EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, + EOWNERDEAD, EPERM, ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -465,7 +465,11 @@ enum WaitpidStatus { enum Handle { Init, Proc(ProcessId), - Thread(Rc>), + + // Needs to be weak so the thread is owned only by the process. Otherwise there would be a + // cyclic reference since the underlying context's file table almost certainly contains the + // thread fd itself, linked to this handle. + Thread(Weak>), // TODO: stateless API, perhaps using intermediate daemon for providing a file-like API Ps(Vec), @@ -805,7 +809,7 @@ impl<'a> ProcScheme<'a> { b"new-thread" => { let thread = self.new_thread(pid)?; Ok(OpenResult::ThisScheme { - number: self.handles.insert(Handle::Thread(thread)), + number: self.handles.insert(Handle::Thread(Rc::downgrade(&thread))), flags: NewFdFlags::empty(), }) } @@ -815,7 +819,7 @@ impl<'a> ProcScheme<'a> { .and_then(|s| s.parse::().ok()) .ok_or(Error::new(EINVAL))?; let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?.borrow(); - let thread = Rc::clone(process.threads.get(idx).ok_or(Error::new(ENOENT))?); + let thread = Rc::downgrade(process.threads.get(idx).ok_or(Error::new(ENOENT))?); return Ok(OpenResult::ThisScheme { number: self.handles.insert(Handle::Thread(thread)), @@ -824,7 +828,8 @@ impl<'a> ProcScheme<'a> { } _ => return Err(Error::new(EINVAL)), }, - Handle::Thread(ref thread_rc) => { + Handle::Thread(ref thread_weak) => { + let thread_rc = thread_weak.upgrade().ok_or(Error::new(EOWNERDEAD))?; let thread = thread_rc.borrow(); // By forwarding all dup calls to the kernel, this fd is now effectively the same @@ -846,7 +851,10 @@ impl<'a> ProcScheme<'a> { let (payload, metadata) = op.payload_and_metadata(); match self.handles[id] { Handle::Init => Response::ready_err(EBADF, op), - Handle::Thread(ref thr) => { + Handle::Thread(ref thr_weak) => { + let Some(thr) = thr_weak.upgrade() else { + return Response::ready_err(EOWNERDEAD, op); + }; let Some(verb) = ThreadCall::try_from_raw(metadata[0] as usize) else { return Response::ready_err(EINVAL, op); }; @@ -855,14 +863,11 @@ impl<'a> ProcScheme<'a> { Self::on_sync_sigtctl(&mut *thr.borrow_mut()).map(|()| 0), op, )), - ThreadCall::SignalThread => { - let thr = Rc::clone(thr); - Ready(Response::new( - self.on_kill_thread(&thr, metadata[1] as u8, awoken) - .map(|()| 0), - op, - )) - } + ThreadCall::SignalThread => Ready(Response::new( + self.on_kill_thread(&thr, metadata[1] as u8, awoken) + .map(|()| 0), + op, + )), } } Handle::Proc(fd_pid) => { @@ -1606,20 +1611,24 @@ impl<'a> ProcScheme<'a> { drop(proc_guard); if let Some(parent_rc) = self.processes.get(&ppid) { - let mut parent = parent_rc.borrow_mut(); - - // Transfer children to parent (TODO: to init) - for child_rc in self - .processes - .values() - .filter(|p| !Rc::ptr_eq(p, parent_rc)) - .filter(|p| p.borrow().ppid == current_pid) - { - let mut child = child_rc.borrow_mut(); - child.ppid = ppid; - parent.waitpid.append(&mut child.waitpid); + if let Some(init_rc) = self.processes.get(&INIT_PID) { + let mut init = init_rc.borrow_mut(); + // Transfer children to init + for child_rc in self + .processes + .values() + .filter(|p| !Rc::ptr_eq(p, init_rc)) + .filter(|p| p.borrow().ppid == current_pid) + { + let mut child = child_rc.borrow_mut(); + child.ppid = INIT_PID; + init.waitpid.append(&mut child.waitpid); + } + awoken.extend(init.waitpid_waiting.drain(..)); } + let mut parent = parent_rc.borrow_mut(); + parent.waitpid.insert( WaitpidKey { pid: Some(current_pid), @@ -2265,7 +2274,7 @@ impl<'a> ProcScheme<'a> { // TODO: enforce uid == 0? let mut string = alloc::format!( - "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}\n", + "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}\n", "PID", "PGID", "PPID", @@ -2276,6 +2285,7 @@ impl<'a> ProcScheme<'a> { "EUID", "EGID", "ENS", + "NTHRD", "STATUS", "NAME", ); @@ -2290,7 +2300,7 @@ impl<'a> ProcScheme<'a> { use core::fmt::Write; writeln!( string, - "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}", + "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}", pid.0, process.pgid.0, process.ppid.0, @@ -2301,11 +2311,20 @@ impl<'a> ProcScheme<'a> { process.euid, process.egid, process.ens, + process.threads.len(), status, process.name, ) .unwrap(); } + + // Useful for debugging memory leaks. + log::trace!("NEXT FD: {}", { + let nextfd = syscall::dup(0, &[]).unwrap(); + syscall::close(nextfd); + nextfd + }); + Ok(string.into_bytes()) } } From 02e9a2a19c46eb863e6a97836b4ea0f2b0c5d627 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 12:53:06 +0200 Subject: [PATCH 125/135] Update redox_scheme. --- Cargo.lock | 3 ++- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c348ca3848..e133cfc084 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,7 +163,8 @@ dependencies = [ [[package]] name = "redox-scheme" version = "0.6.0" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#aee599d75a51cda40c23ca91689b1c92cc4a8557" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6469e083bf650071e9a80003c53171dfccc5ef17ae1d24972396c3c792e7c709" dependencies = [ "libredox", "redox_syscall", diff --git a/Cargo.toml b/Cargo.toml index 37064b84b5..fd7dcd39e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ log = { version = "0.4", default-features = false } plain = "0.2" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = { version = "0.5.4", default-features = false } -redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } +redox-scheme = { version = "0.6", default-features = false } #redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } redox-path = "0.3.1" From 10a41dbcf35e88c2ef8e58365e31bfb3d7c485ae Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 13:17:48 +0200 Subject: [PATCH 126/135] Synchronize kernel proc attrs properly. --- src/procmgr.rs | 56 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index cde4beb374..299307e446 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -626,20 +626,6 @@ impl<'a> ProcScheme<'a> { } = *proc_guard.borrow(); let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); - let attr_fd = FdGuard::new(syscall::dup( - *new_ctxt_fd, - alloc::format!("auth-{}-attrs", **self.auth).as_bytes(), - )?); - let _ = syscall::write( - *attr_fd, - &ProcSchemeAttrs { - pid: child_pid.0 as u32, - euid, - egid, - ens, - debug_name: arraystring_to_bytes(name), - }, - )?; let status_fd = FdGuard::new(syscall::dup( *new_ctxt_fd, alloc::format!("auth-{}-status", **self.auth).as_bytes(), @@ -681,6 +667,12 @@ impl<'a> ProcScheme<'a> { sig_pctl: None, // TODO rtqs: Vec::new(), })); + if let Err(err) = new_process + .borrow_mut() + .sync_kernel_attrs(child_pid, self.auth) + { + log::warn!("Failed to set kernel attrs when forking: {err}"); + } if let Some(group) = self.groups.get(&pgid) { group @@ -700,6 +692,7 @@ impl<'a> ProcScheme<'a> { let ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); + // TODO: sync_kernel_attrs? let attr_fd = FdGuard::new(syscall::dup( *ctxt_fd, alloc::format!("auth-{}-attrs", **self.auth).as_bytes(), @@ -1478,6 +1471,9 @@ impl<'a> ProcScheme<'a> { if let Some(new_sgid) = new_sgid { proc.sgid = new_sgid; } + if let Err(err) = proc.sync_kernel_attrs(pid, self.auth) { + log::warn!("Failed to sync proc attrs in setresugid: {err}"); + } Ok(()) } fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { @@ -1553,10 +1549,14 @@ impl<'a> ProcScheme<'a> { if setrns { process.rns = rns.unwrap(); } - if setens { process.ens = ens.unwrap(); } + if setrns || setens { + if let Err(err) = process.sync_kernel_attrs(pid, self.auth) { + log::warn!("Failed to sync kernel attrs in setrens: {err}"); + } + } Ok(()) } fn work_on( @@ -2185,6 +2185,9 @@ impl<'a> ProcScheme<'a> { .ok_or(Error::new(ESRCH))? .borrow_mut(); proc.name = ArrayString::from_str(&new_name[..new_name.len().min(NAME_CAPAC)]).unwrap(); + if let Err(err) = proc.sync_kernel_attrs(pid, self.auth) { + log::warn!("Failed to set kernel attrs when renaming proc: {err}"); + } Ok(()) } fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { @@ -2344,3 +2347,26 @@ fn arraystring_to_bytes(s: ArrayString) -> [u8; C] { buf[..min].copy_from_slice(&s.as_bytes()[..min]); buf } +impl Process { + fn sync_kernel_attrs(&mut self, my_pid: ProcessId, auth: &FdGuard) -> Result<()> { + // TODO: continue with other threads if one fails? + for thread_rc in &self.threads { + let thread = thread_rc.borrow(); + let attr_fd = FdGuard::new(syscall::dup( + *thread.fd, + alloc::format!("auth-{}-attrs", **auth).as_bytes(), + )?); + let _ = syscall::write( + *attr_fd, + &ProcSchemeAttrs { + pid: my_pid.0 as u32, + euid: self.euid, + egid: self.egid, + ens: self.ens, + debug_name: arraystring_to_bytes(self.name), + }, + )?; + } + Ok(()) + } +} From 47784aa56d45085b6959d4dd817ec395989b39a7 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 18:50:31 +0200 Subject: [PATCH 127/135] Update redox-rt. --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e133cfc084..5378f7e163 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#e93311da1f12c942118cbde2b38643f574348606" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#a2ab572c8b9f1de8592d68c632b9910a191034fc" [[package]] name = "goblin" @@ -151,7 +151,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#e93311da1f12c942118cbde2b38643f574348606" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#a2ab572c8b9f1de8592d68c632b9910a191034fc" dependencies = [ "bitflags", "generic-rt", @@ -173,7 +173,7 @@ dependencies = [ [[package]] name = "redox_syscall" version = "0.5.11" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=nuke_proc#8a80d1f58bc92d8c0eb73a939420fb30fe1e85d0" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=nuke_proc#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6" dependencies = [ "bitflags", ] From 0c72bd778d0def87668a077c7b48b00d5b3b9c5c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 19:29:17 +0200 Subject: [PATCH 128/135] Use redox-os/syscall git dep. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5378f7e163..52ed00be1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#a2ab572c8b9f1de8592d68c632b9910a191034fc" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#9e73f93c5cd1bf4c7c2c2f530d2c0420737ad751" [[package]] name = "goblin" @@ -151,7 +151,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#a2ab572c8b9f1de8592d68c632b9910a191034fc" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#9e73f93c5cd1bf4c7c2c2f530d2c0420737ad751" dependencies = [ "bitflags", "generic-rt", @@ -173,7 +173,7 @@ dependencies = [ [[package]] name = "redox_syscall" version = "0.5.11" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=nuke_proc#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6" +source = "git+https://gitlab.redox-os.org/redox-os/syscall.git?branch=master#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index fd7dcd39e3..be04431e45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,4 @@ lto = "fat" panic = "abort" [patch.crates-io] -redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "nuke_proc" } +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", branch = "master" } From 883b05b89981790719cadc6f9faf5fa7d87742f0 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 20:10:04 +0200 Subject: [PATCH 129/135] Use redox-os/relibc git dep. --- Cargo.lock | 4 ++-- Cargo.toml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52ed00be1b..13ab2e20b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#9e73f93c5cd1bf4c7c2c2f530d2c0420737ad751" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#040009ab3d384e75d62b7e7dd3176d910203501a" [[package]] name = "goblin" @@ -151,7 +151,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#9e73f93c5cd1bf4c7c2c2f530d2c0420737ad751" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#040009ab3d384e75d62b7e7dd3176d910203501a" dependencies = [ "bitflags", "generic-rt", diff --git a/Cargo.toml b/Cargo.toml index be04431e45..b41643737a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,7 @@ plain = "0.2" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = { version = "0.5.4", default-features = false } redox-scheme = { version = "0.6", default-features = false } -#redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } -redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } +redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", default-features = false } redox-path = "0.3.1" slab = { version = "0.4.9", default-features = false } arrayvec = { version = "0.7.6", default-features = false } From 6e89e4528bfccd20f3cfe4f75fae7f1cedcb2fb5 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 20 Apr 2025 16:15:18 +0200 Subject: [PATCH 130/135] Enforce stricter setpgid checks. --- src/procmgr.rs | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 299307e446..6d26a24a02 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1093,17 +1093,30 @@ impl<'a> ProcScheme<'a> { new_pgid: ProcessId, awoken: &mut VecDeque, ) -> Result<()> { - //let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + let caller_sid = caller_proc.borrow().sid; let proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; let mut proc = proc_rc.borrow_mut(); - let mut parent = (proc.ppid != target_pid) - .then(|| self.processes.get(&proc.ppid).map(|p| p.borrow_mut())) - .ok_or(Error::new(ESRCH))?; + if proc.ppid != caller_pid && target_pid != caller_pid { + return Err(Error::new(ESRCH)); + } - if proc.pgid == new_pgid { - return Ok(()); + let mut parent = if proc.ppid == target_pid { + None // init + } else { + Some( + self.processes + .get(&proc.ppid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(), + ) + }; + + // Cannot change the pgid of a process in a different session. + if caller_sid != proc.sid { + return Err(Error::new(EPERM)); } // Session leaders cannot have their pgid changed. @@ -1111,7 +1124,14 @@ impl<'a> ProcScheme<'a> { return Err(Error::new(EPERM)); } - // TODO: other security checks? + if proc.pgid == new_pgid { + return Ok(()); + } + + if new_pgid != target_pid && !self.groups.contains_key(&new_pgid) { + return Err(Error::new(EPERM)); + } + Self::set_pgid( proc_rc, &mut *proc, @@ -1270,8 +1290,11 @@ impl<'a> ProcScheme<'a> { let recv_nonblock = |waitpid: &mut BTreeMap, key: &WaitpidKey| -> Option<(ProcessId, WaitpidStatus)> { - if let Some((pid, sts)) = waitpid.get(key).map(|(k, v)| (*k, *v)) { + if let Some((pid, mut sts)) = waitpid.get(key).map(|(k, v)| (*k, *v)) { waitpid.remove(key); + /*while let Some((_, new_sts)) = waitpid.remove(&WaitpidKey { pid: Some(pid), pgid: None }) { + sts = new_sts; + }*/ Some((pid, sts)) } else { None From bb570062e55ac8389aaa30c795651a4ef0c1eddc Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 20 Apr 2025 16:35:25 +0200 Subject: [PATCH 131/135] Allow making setpgid return EACCESS, e.g. after execv. --- Cargo.lock | 8 ++++---- src/procmgr.rs | 33 +++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13ab2e20b6..7fcc3f58f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#040009ab3d384e75d62b7e7dd3176d910203501a" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#696ae4eca1df5e3b76a57f62903ee1a94b9c6aa6" [[package]] name = "goblin" @@ -151,7 +151,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#040009ab3d384e75d62b7e7dd3176d910203501a" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#696ae4eca1df5e3b76a57f62903ee1a94b9c6aa6" dependencies = [ "bitflags", "generic-rt", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6469e083bf650071e9a80003c53171dfccc5ef17ae1d24972396c3c792e7c709" +checksum = "a14f94bc95b388d8f40a61f25577863f1cf151aaf600a5b7778d1a2018ff7742" dependencies = [ "libredox", "redox_syscall", diff --git a/src/procmgr.rs b/src/procmgr.rs index 6d26a24a02..732df8c1bf 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -35,7 +35,7 @@ use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, ContextVerb, CtxtStsBuf, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, - EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, + EACCES, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EOWNERDEAD, EPERM, ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, }; @@ -336,6 +336,7 @@ struct Process { ens: u32, status: ProcessStatus, + disabled_setpgid: bool, awaiting_threads_term: Vec, @@ -581,6 +582,7 @@ impl<'a> ProcScheme<'a> { name: ArrayString::<32>::from_str("[init]").unwrap(), status: ProcessStatus::PossiblyRunnable, + disabled_setpgid: false, awaiting_threads_term: Vec::new(), waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), @@ -659,6 +661,7 @@ impl<'a> ProcScheme<'a> { name, status: ProcessStatus::PossiblyRunnable, + disabled_setpgid: false, awaiting_threads_term: Vec::new(), waitpid: BTreeMap::new(), @@ -990,6 +993,14 @@ impl<'a> ProcScheme<'a> { self.on_proc_rename(fd_pid, payload).map(|()| 0), op, )), + ProcCall::DisableSetpgid => { + if let Some(proc) = self.processes.get(&fd_pid) { + proc.borrow_mut().disabled_setpgid = true; + Response::ready_ok(0, op) + } else { + Response::ready_err(ESRCH, op) + } + } } } Handle::Ps(_) => Response::ready_err(EOPNOTSUPP, op), @@ -1114,24 +1125,30 @@ impl<'a> ProcScheme<'a> { ) }; - // Cannot change the pgid of a process in a different session. - if caller_sid != proc.sid { - return Err(Error::new(EPERM)); - } - // Session leaders cannot have their pgid changed. if proc.sid == target_pid { return Err(Error::new(EPERM)); } - if proc.pgid == new_pgid { - return Ok(()); + // Cannot change the pgid of a process in a different session. + if caller_sid != proc.sid { + return Err(Error::new(EPERM)); } + // New pgid must either already exit, or be the same as the target pid. if new_pgid != target_pid && !self.groups.contains_key(&new_pgid) { return Err(Error::new(EPERM)); } + // After execv(), i.e. ProcCall::DisableSetpgid, setpgid shall return EACCESS + if proc.disabled_setpgid { + return Err(Error::new(EACCES)); + } + + if proc.pgid == new_pgid { + return Ok(()); + } + Self::set_pgid( proc_rc, &mut *proc, From 74eb75f0e73a463b659919aa16dad5bcbbc6ce6a Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 20 Apr 2025 16:50:45 +0200 Subject: [PATCH 132/135] Fix EACCES check. --- src/procmgr.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 732df8c1bf..b25ab0a2ff 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1140,8 +1140,9 @@ impl<'a> ProcScheme<'a> { return Err(Error::new(EPERM)); } - // After execv(), i.e. ProcCall::DisableSetpgid, setpgid shall return EACCESS - if proc.disabled_setpgid { + // After execv(), i.e. ProcCall::DisableSetpgid, setpgid where target_pid is a child + // process of the calling process, shall return EACCESS. + if proc.ppid == caller_pid && proc.disabled_setpgid { return Err(Error::new(EACCES)); } From 7f08436bb02e20f19dbd91d7644c3648b53d826e Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 21 Apr 2025 13:46:03 +0200 Subject: [PATCH 133/135] Return EBADFD/ECHILD in waitpid, not ESRCH. --- src/procmgr.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b25ab0a2ff..a6fdac25eb 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1298,7 +1298,7 @@ impl<'a> ProcScheme<'a> { } } - let proc_rc = self.processes.get(&this_pid).ok_or(Error::new(ESRCH))?; + let proc_rc = self.processes.get(&this_pid).ok_or(Error::new(EBADFD))?; log::trace!("PROCS {:#?}", self.processes); @@ -1369,7 +1369,7 @@ impl<'a> ProcScheme<'a> { if this_pid == pid { return Ready(Err(Error::new(EINVAL))); } - let target_proc_rc = self.processes.get(&pid).ok_or(Error::new(ESRCH))?; + let target_proc_rc = self.processes.get(&pid).ok_or(Error::new(ECHILD))?; let mut target_proc = target_proc_rc.borrow_mut(); if target_proc.ppid != this_pid { From fd80526dc647ebca499695eab1aa892a0181ee27 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 20 Apr 2025 23:18:55 +0200 Subject: [PATCH 134/135] Implement SIGCONT+SIGHUP on exit when new pgrp becomes orphaned. --- src/procmgr.rs | 111 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 17 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index a6fdac25eb..2432671212 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -22,7 +22,7 @@ use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; use redox_rt::protocol::{ - ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags, SIGCHLD, SIGCONT, + ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags, SIGCHLD, SIGCONT, SIGHUP, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; use redox_scheme::scheme::{IntoTag, Op, OpCall}; @@ -321,6 +321,7 @@ const NAME_CAPAC: usize = 32; #[derive(Debug)] struct Process { threads: Vec>>, + pid: ProcessId, ppid: ProcessId, pgid: ProcessId, sid: ProcessId, @@ -568,6 +569,7 @@ impl<'a> ProcScheme<'a> { let thread_weak = Rc::downgrade(&thread); let process = Rc::new(RefCell::new(Process { threads: vec![thread], + pid: INIT_PID, ppid: INIT_PID, sid: INIT_PID, pgid: INIT_PID, @@ -648,6 +650,7 @@ impl<'a> ProcScheme<'a> { let new_process = Rc::new(RefCell::new(Process { threads: vec![thread], ppid: parent_pid, + pid: child_pid, pgid, sid, ruid, @@ -1032,8 +1035,8 @@ impl<'a> ProcScheme<'a> { // POSIX: any other process's pgid matches the caller pid if self .processes - .values() - .any(|p| p.borrow().pgid == caller_pid) + .iter() + .any(|(pid, rc)| *pid != caller_pid && rc.borrow().pgid == caller_pid) { return Err(Error::new(EPERM)); } @@ -1653,19 +1656,85 @@ impl<'a> ProcScheme<'a> { if let Some(parent_rc) = self.processes.get(&ppid) { if let Some(init_rc) = self.processes.get(&INIT_PID) { - let mut init = init_rc.borrow_mut(); + awoken.extend(init_rc.borrow_mut().waitpid_waiting.drain(..)); + + // TODO(opt): Store list of children in each process? + let children_iter = || { + self.processes + .values() + .filter(|p| !Rc::ptr_eq(p, init_rc)) + .filter(|p| p.borrow().ppid == current_pid) + }; + + // TODO(opt): Avoid allocation? + let affected_pgids = children_iter() + .map(|child_rc| { + let child_pgid = child_rc.borrow().pgid; + (child_pgid, self.pgrp_is_orphaned(child_pgid)) + }) + .chain(Some((pgid, self.pgrp_is_orphaned(pgid)))) + .collect::>>(); + // Transfer children to init - for child_rc in self - .processes - .values() - .filter(|p| !Rc::ptr_eq(p, init_rc)) - .filter(|p| p.borrow().ppid == current_pid) - { + for child_rc in children_iter() { let mut child = child_rc.borrow_mut(); + log::trace!( + "Reparenting {:?} (ppid {:?}) => {:?}", + child.pid, + child.ppid, + INIT_PID + ); child.ppid = INIT_PID; - init.waitpid.append(&mut child.waitpid); + init_rc.borrow_mut().waitpid.append(&mut child.waitpid); + drop(child); + } + // Check if any process group ID would become orphaned as a result of + // this exit. + for (affected_pgid, was_orphaned) in affected_pgids { + let is_orphaned = self.pgrp_is_orphaned(affected_pgid); + + if !was_orphaned.unwrap_or(false) + && is_orphaned.unwrap_or(false) + && let Some(group) = + self.groups.get(&affected_pgid).map(|r| r.borrow()) + { + for process_rc in + group.processes.iter().filter_map(|w| Weak::upgrade(&w)) + { + if !matches!( + process_rc.borrow().status, + ProcessStatus::Stopped(_) + ) { + continue; + } + let sighup_pid = process_rc.borrow().pid; + log::trace!("SENDING SIGCONT TO {sighup_pid:?}"); + if let Err(err) = self.on_send_sig( + INIT_PID, + KillTarget::Proc(sighup_pid), + SIGCONT as u8, + &mut false, + KillMode::Idempotent, + false, + awoken, + ) { + log::warn!("Failed to send newly-orphaned-pgid SIGHUP to PID {sighup_pid:?}: {err}"); + } + log::trace!("SENDING SIGHUP TO {sighup_pid:?}"); + if let Err(err) = self.on_send_sig( + INIT_PID, + KillTarget::Proc(sighup_pid), + SIGHUP as u8, + &mut false, + KillMode::Idempotent, + false, + awoken, + ) { + log::warn!("Failed to send newly-orphaned-pgid SIGHUP to PID {sighup_pid:?}: {err}"); + } + } + } } - awoken.extend(init.waitpid_waiting.drain(..)); } let mut parent = parent_rc.borrow_mut(); @@ -2306,11 +2375,19 @@ impl<'a> ProcScheme<'a> { // POSIX defines orphaned process groups as those where // - // forall process in group, - // process's parent pgid == process's pgid - // OR - // process's session id != process's session id - still_true &= parent.pgid == process.pgid || parent.sid != process.sid; + // forall process in group, parent = process.parent, + // parent's pgid == process's pgid + // OR + // parent's session id != process's session id + let cond = parent.pgid == process.pgid || parent.sid != process.sid; + if !cond { + log::trace!( + "COUNTEREXAMPLE: process {:#?} parent {:#?}", + process, + parent + ); + } + still_true &= cond; } Some(still_true) } From 6898d8b14c078805b7b0b6c4b5d9359ca911bbc6 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 21 Apr 2025 18:34:25 +0200 Subject: [PATCH 135/135] Filter out trailing NUL bytes when renaming procs. --- src/procmgr.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 2432671212..b73c91a5ed 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -2288,12 +2288,19 @@ impl<'a> ProcScheme<'a> { ]) } fn on_proc_rename(&mut self, pid: ProcessId, new_name_raw: &[u8]) -> Result<()> { - let new_name = core::str::from_utf8(new_name_raw).map_err(|_| Error::new(EINVAL))?; + let name_len = new_name_raw + .iter() + .position(|c| *c == 0) + .unwrap_or(new_name_raw.len()); + + let new_name = + core::str::from_utf8(&new_name_raw[..name_len]).map_err(|_| Error::new(EINVAL))?; let mut proc = self .processes .get(&pid) .ok_or(Error::new(ESRCH))? .borrow_mut(); + proc.name = ArrayString::from_str(&new_name[..new_name.len().min(NAME_CAPAC)]).unwrap(); if let Err(err) = proc.sync_kernel_attrs(pid, self.auth) { log::warn!("Failed to set kernel attrs when renaming proc: {err}");